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  }