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