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  }