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 }