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