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