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  }