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