github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/aws/resource_aws_iam_server_certificate.go (about)

     1  package aws
     2  
     3  import (
     4  	"crypto/sha1"
     5  	"encoding/hex"
     6  	"fmt"
     7  	"log"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/aws/aws-sdk-go/aws"
    12  	"github.com/aws/aws-sdk-go/aws/awserr"
    13  	"github.com/aws/aws-sdk-go/service/iam"
    14  	"github.com/hashicorp/terraform/helper/resource"
    15  	"github.com/hashicorp/terraform/helper/schema"
    16  )
    17  
    18  func resourceAwsIAMServerCertificate() *schema.Resource {
    19  	return &schema.Resource{
    20  		Create: resourceAwsIAMServerCertificateCreate,
    21  		Read:   resourceAwsIAMServerCertificateRead,
    22  		Delete: resourceAwsIAMServerCertificateDelete,
    23  
    24  		Schema: map[string]*schema.Schema{
    25  			"certificate_body": &schema.Schema{
    26  				Type:      schema.TypeString,
    27  				Required:  true,
    28  				ForceNew:  true,
    29  				StateFunc: normalizeCert,
    30  			},
    31  
    32  			"certificate_chain": &schema.Schema{
    33  				Type:      schema.TypeString,
    34  				Optional:  true,
    35  				ForceNew:  true,
    36  				StateFunc: normalizeCert,
    37  			},
    38  
    39  			"path": &schema.Schema{
    40  				Type:     schema.TypeString,
    41  				Optional: true,
    42  				Default:  "/",
    43  				ForceNew: true,
    44  			},
    45  
    46  			"private_key": &schema.Schema{
    47  				Type:      schema.TypeString,
    48  				Required:  true,
    49  				ForceNew:  true,
    50  				StateFunc: normalizeCert,
    51  			},
    52  
    53  			"name": &schema.Schema{
    54  				Type:          schema.TypeString,
    55  				Optional:      true,
    56  				Computed:      true,
    57  				ForceNew:      true,
    58  				ConflictsWith: []string{"name_prefix"},
    59  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    60  					value := v.(string)
    61  					if len(value) > 128 {
    62  						errors = append(errors, fmt.Errorf(
    63  							"%q cannot be longer than 128 characters", k))
    64  					}
    65  					return
    66  				},
    67  			},
    68  
    69  			"name_prefix": &schema.Schema{
    70  				Type:     schema.TypeString,
    71  				Optional: true,
    72  				ForceNew: true,
    73  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    74  					value := v.(string)
    75  					if len(value) > 30 {
    76  						errors = append(errors, fmt.Errorf(
    77  							"%q cannot be longer than 30 characters, name is limited to 128", k))
    78  					}
    79  					return
    80  				},
    81  			},
    82  
    83  			"arn": &schema.Schema{
    84  				Type:     schema.TypeString,
    85  				Optional: true,
    86  				Computed: true,
    87  			},
    88  		},
    89  	}
    90  }
    91  
    92  func resourceAwsIAMServerCertificateCreate(d *schema.ResourceData, meta interface{}) error {
    93  	conn := meta.(*AWSClient).iamconn
    94  
    95  	var sslCertName string
    96  	if v, ok := d.GetOk("name"); ok {
    97  		sslCertName = v.(string)
    98  	} else if v, ok := d.GetOk("name_prefix"); ok {
    99  		sslCertName = resource.PrefixedUniqueId(v.(string))
   100  	} else {
   101  		sslCertName = resource.UniqueId()
   102  	}
   103  
   104  	createOpts := &iam.UploadServerCertificateInput{
   105  		CertificateBody:       aws.String(d.Get("certificate_body").(string)),
   106  		PrivateKey:            aws.String(d.Get("private_key").(string)),
   107  		ServerCertificateName: aws.String(sslCertName),
   108  	}
   109  
   110  	if v, ok := d.GetOk("certificate_chain"); ok {
   111  		createOpts.CertificateChain = aws.String(v.(string))
   112  	}
   113  
   114  	if v, ok := d.GetOk("path"); ok {
   115  		createOpts.Path = aws.String(v.(string))
   116  	}
   117  
   118  	log.Printf("[DEBUG] Creating IAM Server Certificate with opts: %s", createOpts)
   119  	resp, err := conn.UploadServerCertificate(createOpts)
   120  	if err != nil {
   121  		if awsErr, ok := err.(awserr.Error); ok {
   122  			return fmt.Errorf("[WARN] Error uploading server certificate, error: %s: %s", awsErr.Code(), awsErr.Message())
   123  		}
   124  		return fmt.Errorf("[WARN] Error uploading server certificate, error: %s", err)
   125  	}
   126  
   127  	d.SetId(*resp.ServerCertificateMetadata.ServerCertificateId)
   128  	d.Set("name", sslCertName)
   129  
   130  	return resourceAwsIAMServerCertificateRead(d, meta)
   131  }
   132  
   133  func resourceAwsIAMServerCertificateRead(d *schema.ResourceData, meta interface{}) error {
   134  	conn := meta.(*AWSClient).iamconn
   135  	resp, err := conn.GetServerCertificate(&iam.GetServerCertificateInput{
   136  		ServerCertificateName: aws.String(d.Get("name").(string)),
   137  	})
   138  
   139  	if err != nil {
   140  		if awsErr, ok := err.(awserr.Error); ok {
   141  			if awsErr.Code() == "NoSuchEntity" {
   142  				log.Printf("[WARN] IAM Server Cert (%s) not found, removing from state", d.Id())
   143  				d.SetId("")
   144  				return nil
   145  			}
   146  			return fmt.Errorf("[WARN] Error reading IAM Server Certificate: %s: %s", awsErr.Code(), awsErr.Message())
   147  		}
   148  		return fmt.Errorf("[WARN] Error reading IAM Server Certificate: %s", err)
   149  	}
   150  
   151  	// these values should always be present, and have a default if not set in
   152  	// configuration, and so safe to reference with nil checks
   153  	d.Set("certificate_body", normalizeCert(resp.ServerCertificate.CertificateBody))
   154  
   155  	c := normalizeCert(resp.ServerCertificate.CertificateChain)
   156  	if c != "" {
   157  		d.Set("certificate_chain", c)
   158  	}
   159  
   160  	d.Set("path", resp.ServerCertificate.ServerCertificateMetadata.Path)
   161  	d.Set("arn", resp.ServerCertificate.ServerCertificateMetadata.Arn)
   162  
   163  	return nil
   164  }
   165  
   166  func resourceAwsIAMServerCertificateDelete(d *schema.ResourceData, meta interface{}) error {
   167  	conn := meta.(*AWSClient).iamconn
   168  	log.Printf("[INFO] Deleting IAM Server Certificate: %s", d.Id())
   169  	err := resource.Retry(3*time.Minute, func() *resource.RetryError {
   170  		_, err := conn.DeleteServerCertificate(&iam.DeleteServerCertificateInput{
   171  			ServerCertificateName: aws.String(d.Get("name").(string)),
   172  		})
   173  
   174  		if err != nil {
   175  			if awsErr, ok := err.(awserr.Error); ok {
   176  				if awsErr.Code() == "DeleteConflict" && strings.Contains(awsErr.Message(), "currently in use by arn") {
   177  					log.Printf("[WARN] Conflict deleting server certificate: %s, retrying", awsErr.Message())
   178  					return resource.RetryableError(err)
   179  				}
   180  				if awsErr.Code() == "NoSuchEntity" {
   181  					log.Printf("[WARN] IAM Server Certificate (%s) not found, removing from state", d.Id())
   182  					d.SetId("")
   183  					return nil
   184  				}
   185  			}
   186  			return resource.NonRetryableError(err)
   187  		}
   188  		return nil
   189  	})
   190  
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	d.SetId("")
   196  	return nil
   197  }
   198  
   199  func normalizeCert(cert interface{}) string {
   200  	if cert == nil || cert == (*string)(nil) {
   201  		return ""
   202  	}
   203  
   204  	var rawCert string
   205  	switch cert.(type) {
   206  	case string:
   207  		rawCert = cert.(string)
   208  	case *string:
   209  		rawCert = *cert.(*string)
   210  	default:
   211  		return ""
   212  	}
   213  
   214  	cleanVal := sha1.Sum(stripCR([]byte(strings.TrimSpace(rawCert))))
   215  	return hex.EncodeToString(cleanVal[:])
   216  }
   217  
   218  // strip CRs from raw literals. Lifted from go/scanner/scanner.go
   219  // See https://github.com/golang/go/blob/release-branch.go1.6/src/go/scanner/scanner.go#L479
   220  func stripCR(b []byte) []byte {
   221  	c := make([]byte, len(b))
   222  	i := 0
   223  	for _, ch := range b {
   224  		if ch != '\r' {
   225  			c[i] = ch
   226  			i++
   227  		}
   228  	}
   229  	return c[:i]
   230  }