github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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 }, 84 } 85 } 86 87 func resourceAwsKmsKeyCreate(d *schema.ResourceData, meta interface{}) error { 88 conn := meta.(*AWSClient).kmsconn 89 90 // Allow aws to chose default values if we don't pass them 91 var req kms.CreateKeyInput 92 if v, exists := d.GetOk("description"); exists { 93 req.Description = aws.String(v.(string)) 94 } 95 if v, exists := d.GetOk("key_usage"); exists { 96 req.KeyUsage = aws.String(v.(string)) 97 } 98 if v, exists := d.GetOk("policy"); exists { 99 req.Policy = aws.String(v.(string)) 100 } 101 102 var resp *kms.CreateKeyOutput 103 // AWS requires any principal in the policy to exist before the key is created. 104 // The KMS service's awareness of principals is limited by "eventual consistency". 105 // They acknowledge this here: 106 // http://docs.aws.amazon.com/kms/latest/APIReference/API_CreateKey.html 107 err := resource.Retry(30*time.Second, func() *resource.RetryError { 108 var err error 109 resp, err = conn.CreateKey(&req) 110 if isAWSErr(err, "MalformedPolicyDocumentException", "") { 111 return resource.RetryableError(err) 112 } 113 return resource.NonRetryableError(err) 114 }) 115 if err != nil { 116 return err 117 } 118 119 d.SetId(*resp.KeyMetadata.KeyId) 120 d.Set("key_id", resp.KeyMetadata.KeyId) 121 122 return _resourceAwsKmsKeyUpdate(d, meta, true) 123 } 124 125 func resourceAwsKmsKeyRead(d *schema.ResourceData, meta interface{}) error { 126 conn := meta.(*AWSClient).kmsconn 127 128 req := &kms.DescribeKeyInput{ 129 KeyId: aws.String(d.Id()), 130 } 131 resp, err := conn.DescribeKey(req) 132 if err != nil { 133 return err 134 } 135 metadata := resp.KeyMetadata 136 137 if *metadata.KeyState == "PendingDeletion" { 138 log.Printf("[WARN] Removing KMS key %s because it's already gone", d.Id()) 139 d.SetId("") 140 return nil 141 } 142 143 d.SetId(*metadata.KeyId) 144 145 d.Set("arn", metadata.Arn) 146 d.Set("key_id", metadata.KeyId) 147 d.Set("description", metadata.Description) 148 d.Set("key_usage", metadata.KeyUsage) 149 d.Set("is_enabled", metadata.Enabled) 150 151 p, err := conn.GetKeyPolicy(&kms.GetKeyPolicyInput{ 152 KeyId: metadata.KeyId, 153 PolicyName: aws.String("default"), 154 }) 155 if err != nil { 156 return err 157 } 158 159 policy, err := normalizeJsonString(*p.Policy) 160 if err != nil { 161 return errwrap.Wrapf("policy contains an invalid JSON: {{err}}", err) 162 } 163 d.Set("policy", policy) 164 165 krs, err := conn.GetKeyRotationStatus(&kms.GetKeyRotationStatusInput{ 166 KeyId: metadata.KeyId, 167 }) 168 if err != nil { 169 return err 170 } 171 d.Set("enable_key_rotation", krs.KeyRotationEnabled) 172 173 return nil 174 } 175 176 func resourceAwsKmsKeyUpdate(d *schema.ResourceData, meta interface{}) error { 177 return _resourceAwsKmsKeyUpdate(d, meta, false) 178 } 179 180 // We expect new keys to be enabled already 181 // but there is no easy way to differentiate between Update() 182 // called from Create() and regular update, so we have this wrapper 183 func _resourceAwsKmsKeyUpdate(d *schema.ResourceData, meta interface{}, isFresh bool) error { 184 conn := meta.(*AWSClient).kmsconn 185 186 if d.HasChange("is_enabled") && d.Get("is_enabled").(bool) && !isFresh { 187 // Enable before any attributes will be modified 188 if err := updateKmsKeyStatus(conn, d.Id(), d.Get("is_enabled").(bool)); err != nil { 189 return err 190 } 191 } 192 193 if d.HasChange("enable_key_rotation") { 194 if err := updateKmsKeyRotationStatus(conn, d); err != nil { 195 return err 196 } 197 } 198 199 if d.HasChange("description") { 200 if err := resourceAwsKmsKeyDescriptionUpdate(conn, d); err != nil { 201 return err 202 } 203 } 204 if d.HasChange("policy") { 205 if err := resourceAwsKmsKeyPolicyUpdate(conn, d); err != nil { 206 return err 207 } 208 } 209 210 if d.HasChange("is_enabled") && !d.Get("is_enabled").(bool) { 211 // Only disable when all attributes are modified 212 // because we cannot modify disabled keys 213 if err := updateKmsKeyStatus(conn, d.Id(), d.Get("is_enabled").(bool)); err != nil { 214 return err 215 } 216 } 217 218 return resourceAwsKmsKeyRead(d, meta) 219 } 220 221 func resourceAwsKmsKeyDescriptionUpdate(conn *kms.KMS, d *schema.ResourceData) error { 222 description := d.Get("description").(string) 223 keyId := d.Get("key_id").(string) 224 225 log.Printf("[DEBUG] KMS key: %s, update description: %s", keyId, description) 226 227 req := &kms.UpdateKeyDescriptionInput{ 228 Description: aws.String(description), 229 KeyId: aws.String(keyId), 230 } 231 _, err := conn.UpdateKeyDescription(req) 232 return err 233 } 234 235 func resourceAwsKmsKeyPolicyUpdate(conn *kms.KMS, d *schema.ResourceData) error { 236 policy, err := normalizeJsonString(d.Get("policy").(string)) 237 if err != nil { 238 return errwrap.Wrapf("policy contains an invalid JSON: {{err}}", err) 239 } 240 keyId := d.Get("key_id").(string) 241 242 log.Printf("[DEBUG] KMS key: %s, update policy: %s", keyId, policy) 243 244 req := &kms.PutKeyPolicyInput{ 245 KeyId: aws.String(keyId), 246 Policy: aws.String(policy), 247 PolicyName: aws.String("default"), 248 } 249 _, err = conn.PutKeyPolicy(req) 250 return err 251 } 252 253 func updateKmsKeyStatus(conn *kms.KMS, id string, shouldBeEnabled bool) error { 254 var err error 255 256 if shouldBeEnabled { 257 log.Printf("[DEBUG] Enabling KMS key %q", id) 258 _, err = conn.EnableKey(&kms.EnableKeyInput{ 259 KeyId: aws.String(id), 260 }) 261 } else { 262 log.Printf("[DEBUG] Disabling KMS key %q", id) 263 _, err = conn.DisableKey(&kms.DisableKeyInput{ 264 KeyId: aws.String(id), 265 }) 266 } 267 268 if err != nil { 269 return fmt.Errorf("Failed to set KMS key %q status to %t: %q", 270 id, shouldBeEnabled, err.Error()) 271 } 272 273 // Wait for propagation since KMS is eventually consistent 274 wait := resource.StateChangeConf{ 275 Pending: []string{fmt.Sprintf("%t", !shouldBeEnabled)}, 276 Target: []string{fmt.Sprintf("%t", shouldBeEnabled)}, 277 Timeout: 20 * time.Minute, 278 MinTimeout: 2 * time.Second, 279 ContinuousTargetOccurence: 10, 280 Refresh: func() (interface{}, string, error) { 281 log.Printf("[DEBUG] Checking if KMS key %s enabled status is %t", 282 id, shouldBeEnabled) 283 resp, err := conn.DescribeKey(&kms.DescribeKeyInput{ 284 KeyId: aws.String(id), 285 }) 286 if err != nil { 287 return resp, "FAILED", err 288 } 289 status := fmt.Sprintf("%t", *resp.KeyMetadata.Enabled) 290 log.Printf("[DEBUG] KMS key %s status received: %s, retrying", id, status) 291 292 return resp, status, nil 293 }, 294 } 295 296 _, err = wait.WaitForState() 297 if err != nil { 298 return fmt.Errorf("Failed setting KMS key status to %t: %s", shouldBeEnabled, err) 299 } 300 301 return nil 302 } 303 304 func updateKmsKeyRotationStatus(conn *kms.KMS, d *schema.ResourceData) error { 305 var err error 306 shouldEnableRotation := d.Get("enable_key_rotation").(bool) 307 if shouldEnableRotation { 308 log.Printf("[DEBUG] Enabling key rotation for KMS key %q", d.Id()) 309 _, err = conn.EnableKeyRotation(&kms.EnableKeyRotationInput{ 310 KeyId: aws.String(d.Id()), 311 }) 312 } else { 313 log.Printf("[DEBUG] Disabling key rotation for KMS key %q", d.Id()) 314 _, err = conn.DisableKeyRotation(&kms.DisableKeyRotationInput{ 315 KeyId: aws.String(d.Id()), 316 }) 317 } 318 319 if err != nil { 320 return fmt.Errorf("Failed to set key rotation for %q to %t: %q", 321 d.Id(), shouldEnableRotation, err.Error()) 322 } 323 324 // Wait for propagation since KMS is eventually consistent 325 wait := resource.StateChangeConf{ 326 Pending: []string{fmt.Sprintf("%t", !shouldEnableRotation)}, 327 Target: []string{fmt.Sprintf("%t", shouldEnableRotation)}, 328 Timeout: 5 * time.Minute, 329 MinTimeout: 1 * time.Second, 330 ContinuousTargetOccurence: 5, 331 Refresh: func() (interface{}, string, error) { 332 log.Printf("[DEBUG] Checking if KMS key %s rotation status is %t", 333 d.Id(), shouldEnableRotation) 334 resp, err := conn.GetKeyRotationStatus(&kms.GetKeyRotationStatusInput{ 335 KeyId: aws.String(d.Id()), 336 }) 337 if err != nil { 338 return resp, "FAILED", err 339 } 340 status := fmt.Sprintf("%t", *resp.KeyRotationEnabled) 341 log.Printf("[DEBUG] KMS key %s rotation status received: %s, retrying", d.Id(), status) 342 343 return resp, status, nil 344 }, 345 } 346 347 _, err = wait.WaitForState() 348 if err != nil { 349 return fmt.Errorf("Failed setting KMS key rotation status to %t: %s", shouldEnableRotation, err) 350 } 351 352 return nil 353 } 354 355 func resourceAwsKmsKeyDelete(d *schema.ResourceData, meta interface{}) error { 356 conn := meta.(*AWSClient).kmsconn 357 keyId := d.Get("key_id").(string) 358 359 req := &kms.ScheduleKeyDeletionInput{ 360 KeyId: aws.String(keyId), 361 } 362 if v, exists := d.GetOk("deletion_window_in_days"); exists { 363 req.PendingWindowInDays = aws.Int64(int64(v.(int))) 364 } 365 _, err := conn.ScheduleKeyDeletion(req) 366 if err != nil { 367 return err 368 } 369 370 // Wait for propagation since KMS is eventually consistent 371 wait := resource.StateChangeConf{ 372 Pending: []string{"Enabled", "Disabled"}, 373 Target: []string{"PendingDeletion"}, 374 Timeout: 20 * time.Minute, 375 MinTimeout: 2 * time.Second, 376 ContinuousTargetOccurence: 10, 377 Refresh: func() (interface{}, string, error) { 378 log.Printf("[DEBUG] Checking if KMS key %s state is PendingDeletion", keyId) 379 resp, err := conn.DescribeKey(&kms.DescribeKeyInput{ 380 KeyId: aws.String(keyId), 381 }) 382 if err != nil { 383 return resp, "Failed", err 384 } 385 386 metadata := *resp.KeyMetadata 387 log.Printf("[DEBUG] KMS key %s state is %s, retrying", keyId, *metadata.KeyState) 388 389 return resp, *metadata.KeyState, nil 390 }, 391 } 392 393 _, err = wait.WaitForState() 394 if err != nil { 395 return fmt.Errorf("Failed deactivating KMS key %s: %s", keyId, err) 396 } 397 398 log.Printf("[DEBUG] KMS Key %s deactivated.", keyId) 399 d.SetId("") 400 return nil 401 }