github.com/dougneal/terraform@v0.6.15-0.20170330092735-b6a3840768a4/builtin/providers/aws/resource_aws_kms_key.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/service/kms" 10 "github.com/hashicorp/errwrap" 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 func resourceAwsKmsKey() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceAwsKmsKeyCreate, 18 Read: resourceAwsKmsKeyRead, 19 Update: resourceAwsKmsKeyUpdate, 20 Delete: resourceAwsKmsKeyDelete, 21 22 Importer: &schema.ResourceImporter{ 23 State: schema.ImportStatePassthrough, 24 }, 25 26 Schema: map[string]*schema.Schema{ 27 "arn": &schema.Schema{ 28 Type: schema.TypeString, 29 Computed: true, 30 }, 31 "key_id": &schema.Schema{ 32 Type: schema.TypeString, 33 Computed: true, 34 }, 35 "description": &schema.Schema{ 36 Type: schema.TypeString, 37 Optional: true, 38 Computed: true, 39 }, 40 "key_usage": &schema.Schema{ 41 Type: schema.TypeString, 42 Optional: true, 43 Computed: true, 44 ForceNew: true, 45 ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { 46 value := v.(string) 47 if !(value == "ENCRYPT_DECRYPT" || value == "") { 48 es = append(es, fmt.Errorf( 49 "%q must be ENCRYPT_DECRYPT or not specified", k)) 50 } 51 return 52 }, 53 }, 54 "policy": &schema.Schema{ 55 Type: schema.TypeString, 56 Optional: true, 57 Computed: true, 58 ValidateFunc: validateJsonString, 59 DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs, 60 }, 61 "is_enabled": &schema.Schema{ 62 Type: schema.TypeBool, 63 Optional: true, 64 Default: true, 65 }, 66 "enable_key_rotation": &schema.Schema{ 67 Type: schema.TypeBool, 68 Optional: true, 69 Default: false, 70 }, 71 "deletion_window_in_days": &schema.Schema{ 72 Type: schema.TypeInt, 73 Optional: true, 74 ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { 75 value := v.(int) 76 if value > 30 || value < 7 { 77 es = append(es, fmt.Errorf( 78 "%q must be between 7 and 30 days inclusive", k)) 79 } 80 return 81 }, 82 }, 83 "tags": tagsSchema(), 84 }, 85 } 86 } 87 88 func resourceAwsKmsKeyCreate(d *schema.ResourceData, meta interface{}) error { 89 conn := meta.(*AWSClient).kmsconn 90 91 // Allow aws to chose default values if we don't pass them 92 var req kms.CreateKeyInput 93 if v, exists := d.GetOk("description"); exists { 94 req.Description = aws.String(v.(string)) 95 } 96 if v, exists := d.GetOk("key_usage"); exists { 97 req.KeyUsage = aws.String(v.(string)) 98 } 99 if v, exists := d.GetOk("policy"); exists { 100 req.Policy = aws.String(v.(string)) 101 } 102 if v, exists := d.GetOk("tags"); exists { 103 req.Tags = tagsFromMapKMS(v.(map[string]interface{})) 104 } 105 106 var resp *kms.CreateKeyOutput 107 // AWS requires any principal in the policy to exist before the key is created. 108 // The KMS service's awareness of principals is limited by "eventual consistency". 109 // They acknowledge this here: 110 // http://docs.aws.amazon.com/kms/latest/APIReference/API_CreateKey.html 111 err := resource.Retry(30*time.Second, func() *resource.RetryError { 112 var err error 113 resp, err = conn.CreateKey(&req) 114 if isAWSErr(err, "MalformedPolicyDocumentException", "") { 115 return resource.RetryableError(err) 116 } 117 return resource.NonRetryableError(err) 118 }) 119 if err != nil { 120 return err 121 } 122 123 d.SetId(*resp.KeyMetadata.KeyId) 124 d.Set("key_id", resp.KeyMetadata.KeyId) 125 126 return _resourceAwsKmsKeyUpdate(d, meta, true) 127 } 128 129 func resourceAwsKmsKeyRead(d *schema.ResourceData, meta interface{}) error { 130 conn := meta.(*AWSClient).kmsconn 131 132 req := &kms.DescribeKeyInput{ 133 KeyId: aws.String(d.Id()), 134 } 135 resp, err := conn.DescribeKey(req) 136 if err != nil { 137 return err 138 } 139 metadata := resp.KeyMetadata 140 141 if *metadata.KeyState == "PendingDeletion" { 142 log.Printf("[WARN] Removing KMS key %s because it's already gone", d.Id()) 143 d.SetId("") 144 return nil 145 } 146 147 d.SetId(*metadata.KeyId) 148 149 d.Set("arn", metadata.Arn) 150 d.Set("key_id", metadata.KeyId) 151 d.Set("description", metadata.Description) 152 d.Set("key_usage", metadata.KeyUsage) 153 d.Set("is_enabled", metadata.Enabled) 154 155 p, err := conn.GetKeyPolicy(&kms.GetKeyPolicyInput{ 156 KeyId: metadata.KeyId, 157 PolicyName: aws.String("default"), 158 }) 159 if err != nil { 160 return err 161 } 162 163 policy, err := normalizeJsonString(*p.Policy) 164 if err != nil { 165 return errwrap.Wrapf("policy contains an invalid JSON: {{err}}", err) 166 } 167 d.Set("policy", policy) 168 169 krs, err := conn.GetKeyRotationStatus(&kms.GetKeyRotationStatusInput{ 170 KeyId: metadata.KeyId, 171 }) 172 if err != nil { 173 return err 174 } 175 d.Set("enable_key_rotation", krs.KeyRotationEnabled) 176 177 tagList, err := conn.ListResourceTags(&kms.ListResourceTagsInput{ 178 KeyId: metadata.KeyId, 179 }) 180 if err != nil { 181 return fmt.Errorf("Failed to get KMS key tags (key: %s): %s", d.Get("key_id").(string), err) 182 } 183 d.Set("tags", tagsToMapKMS(tagList.Tags)) 184 185 return nil 186 } 187 188 func resourceAwsKmsKeyUpdate(d *schema.ResourceData, meta interface{}) error { 189 return _resourceAwsKmsKeyUpdate(d, meta, false) 190 } 191 192 // We expect new keys to be enabled already 193 // but there is no easy way to differentiate between Update() 194 // called from Create() and regular update, so we have this wrapper 195 func _resourceAwsKmsKeyUpdate(d *schema.ResourceData, meta interface{}, isFresh bool) error { 196 conn := meta.(*AWSClient).kmsconn 197 198 if d.HasChange("is_enabled") && d.Get("is_enabled").(bool) && !isFresh { 199 // Enable before any attributes will be modified 200 if err := updateKmsKeyStatus(conn, d.Id(), d.Get("is_enabled").(bool)); err != nil { 201 return err 202 } 203 } 204 205 if d.HasChange("enable_key_rotation") { 206 if err := updateKmsKeyRotationStatus(conn, d); err != nil { 207 return err 208 } 209 } 210 211 if d.HasChange("description") { 212 if err := resourceAwsKmsKeyDescriptionUpdate(conn, d); err != nil { 213 return err 214 } 215 } 216 if d.HasChange("policy") { 217 if err := resourceAwsKmsKeyPolicyUpdate(conn, d); err != nil { 218 return err 219 } 220 } 221 222 if d.HasChange("is_enabled") && !d.Get("is_enabled").(bool) { 223 // Only disable when all attributes are modified 224 // because we cannot modify disabled keys 225 if err := updateKmsKeyStatus(conn, d.Id(), d.Get("is_enabled").(bool)); err != nil { 226 return err 227 } 228 } 229 230 if err := setTagsKMS(conn, d, d.Id()); err != nil { 231 return err 232 } 233 234 return resourceAwsKmsKeyRead(d, meta) 235 } 236 237 func resourceAwsKmsKeyDescriptionUpdate(conn *kms.KMS, d *schema.ResourceData) error { 238 description := d.Get("description").(string) 239 keyId := d.Get("key_id").(string) 240 241 log.Printf("[DEBUG] KMS key: %s, update description: %s", keyId, description) 242 243 req := &kms.UpdateKeyDescriptionInput{ 244 Description: aws.String(description), 245 KeyId: aws.String(keyId), 246 } 247 _, err := conn.UpdateKeyDescription(req) 248 return err 249 } 250 251 func resourceAwsKmsKeyPolicyUpdate(conn *kms.KMS, d *schema.ResourceData) error { 252 policy, err := normalizeJsonString(d.Get("policy").(string)) 253 if err != nil { 254 return errwrap.Wrapf("policy contains an invalid JSON: {{err}}", err) 255 } 256 keyId := d.Get("key_id").(string) 257 258 log.Printf("[DEBUG] KMS key: %s, update policy: %s", keyId, policy) 259 260 req := &kms.PutKeyPolicyInput{ 261 KeyId: aws.String(keyId), 262 Policy: aws.String(policy), 263 PolicyName: aws.String("default"), 264 } 265 _, err = conn.PutKeyPolicy(req) 266 return err 267 } 268 269 func updateKmsKeyStatus(conn *kms.KMS, id string, shouldBeEnabled bool) error { 270 var err error 271 272 if shouldBeEnabled { 273 log.Printf("[DEBUG] Enabling KMS key %q", id) 274 _, err = conn.EnableKey(&kms.EnableKeyInput{ 275 KeyId: aws.String(id), 276 }) 277 } else { 278 log.Printf("[DEBUG] Disabling KMS key %q", id) 279 _, err = conn.DisableKey(&kms.DisableKeyInput{ 280 KeyId: aws.String(id), 281 }) 282 } 283 284 if err != nil { 285 return fmt.Errorf("Failed to set KMS key %q status to %t: %q", 286 id, shouldBeEnabled, err.Error()) 287 } 288 289 // Wait for propagation since KMS is eventually consistent 290 wait := resource.StateChangeConf{ 291 Pending: []string{fmt.Sprintf("%t", !shouldBeEnabled)}, 292 Target: []string{fmt.Sprintf("%t", shouldBeEnabled)}, 293 Timeout: 20 * time.Minute, 294 MinTimeout: 2 * time.Second, 295 ContinuousTargetOccurence: 10, 296 Refresh: func() (interface{}, string, error) { 297 log.Printf("[DEBUG] Checking if KMS key %s enabled status is %t", 298 id, shouldBeEnabled) 299 resp, err := conn.DescribeKey(&kms.DescribeKeyInput{ 300 KeyId: aws.String(id), 301 }) 302 if err != nil { 303 return resp, "FAILED", err 304 } 305 status := fmt.Sprintf("%t", *resp.KeyMetadata.Enabled) 306 log.Printf("[DEBUG] KMS key %s status received: %s, retrying", id, status) 307 308 return resp, status, nil 309 }, 310 } 311 312 _, err = wait.WaitForState() 313 if err != nil { 314 return fmt.Errorf("Failed setting KMS key status to %t: %s", shouldBeEnabled, err) 315 } 316 317 return nil 318 } 319 320 func updateKmsKeyRotationStatus(conn *kms.KMS, d *schema.ResourceData) error { 321 var err error 322 shouldEnableRotation := d.Get("enable_key_rotation").(bool) 323 if shouldEnableRotation { 324 log.Printf("[DEBUG] Enabling key rotation for KMS key %q", d.Id()) 325 _, err = conn.EnableKeyRotation(&kms.EnableKeyRotationInput{ 326 KeyId: aws.String(d.Id()), 327 }) 328 } else { 329 log.Printf("[DEBUG] Disabling key rotation for KMS key %q", d.Id()) 330 _, err = conn.DisableKeyRotation(&kms.DisableKeyRotationInput{ 331 KeyId: aws.String(d.Id()), 332 }) 333 } 334 335 if err != nil { 336 return fmt.Errorf("Failed to set key rotation for %q to %t: %q", 337 d.Id(), shouldEnableRotation, err.Error()) 338 } 339 340 // Wait for propagation since KMS is eventually consistent 341 wait := resource.StateChangeConf{ 342 Pending: []string{fmt.Sprintf("%t", !shouldEnableRotation)}, 343 Target: []string{fmt.Sprintf("%t", shouldEnableRotation)}, 344 Timeout: 5 * time.Minute, 345 MinTimeout: 1 * time.Second, 346 ContinuousTargetOccurence: 5, 347 Refresh: func() (interface{}, string, error) { 348 log.Printf("[DEBUG] Checking if KMS key %s rotation status is %t", 349 d.Id(), shouldEnableRotation) 350 resp, err := conn.GetKeyRotationStatus(&kms.GetKeyRotationStatusInput{ 351 KeyId: aws.String(d.Id()), 352 }) 353 if err != nil { 354 return resp, "FAILED", err 355 } 356 status := fmt.Sprintf("%t", *resp.KeyRotationEnabled) 357 log.Printf("[DEBUG] KMS key %s rotation status received: %s, retrying", d.Id(), status) 358 359 return resp, status, nil 360 }, 361 } 362 363 _, err = wait.WaitForState() 364 if err != nil { 365 return fmt.Errorf("Failed setting KMS key rotation status to %t: %s", shouldEnableRotation, err) 366 } 367 368 return nil 369 } 370 371 func resourceAwsKmsKeyDelete(d *schema.ResourceData, meta interface{}) error { 372 conn := meta.(*AWSClient).kmsconn 373 keyId := d.Get("key_id").(string) 374 375 req := &kms.ScheduleKeyDeletionInput{ 376 KeyId: aws.String(keyId), 377 } 378 if v, exists := d.GetOk("deletion_window_in_days"); exists { 379 req.PendingWindowInDays = aws.Int64(int64(v.(int))) 380 } 381 _, err := conn.ScheduleKeyDeletion(req) 382 if err != nil { 383 return err 384 } 385 386 // Wait for propagation since KMS is eventually consistent 387 wait := resource.StateChangeConf{ 388 Pending: []string{"Enabled", "Disabled"}, 389 Target: []string{"PendingDeletion"}, 390 Timeout: 20 * time.Minute, 391 MinTimeout: 2 * time.Second, 392 ContinuousTargetOccurence: 10, 393 Refresh: func() (interface{}, string, error) { 394 log.Printf("[DEBUG] Checking if KMS key %s state is PendingDeletion", keyId) 395 resp, err := conn.DescribeKey(&kms.DescribeKeyInput{ 396 KeyId: aws.String(keyId), 397 }) 398 if err != nil { 399 return resp, "Failed", err 400 } 401 402 metadata := *resp.KeyMetadata 403 log.Printf("[DEBUG] KMS key %s state is %s, retrying", keyId, *metadata.KeyState) 404 405 return resp, *metadata.KeyState, nil 406 }, 407 } 408 409 _, err = wait.WaitForState() 410 if err != nil { 411 return fmt.Errorf("Failed deactivating KMS key %s: %s", keyId, err) 412 } 413 414 log.Printf("[DEBUG] KMS Key %s deactivated.", keyId) 415 d.SetId("") 416 return nil 417 }