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