From 6648df7acc80ef15d9de0a8f85613fee88fdf3cf Mon Sep 17 00:00:00 2001 From: Philip Witty Date: Wed, 9 Sep 2015 16:58:24 +0100 Subject: [PATCH] Added AWS KMS key & alias support --- builtin/providers/aws/config.go | 4 + builtin/providers/aws/provider.go | 2 + .../providers/aws/resource_aws_kms_alias.go | 131 +++++++++++++ builtin/providers/aws/resource_aws_kms_key.go | 177 ++++++++++++++++++ 4 files changed, 314 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_kms_alias.go create mode 100644 builtin/providers/aws/resource_aws_kms_key.go diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go index dc422efe3f..a747a7d165 100644 --- a/builtin/providers/aws/config.go +++ b/builtin/providers/aws/config.go @@ -42,6 +42,7 @@ import ( "github.com/aws/aws-sdk-go/service/glacier" "github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/kinesis" + "github.com/aws/aws-sdk-go/service/kms" "github.com/aws/aws-sdk-go/service/lambda" "github.com/aws/aws-sdk-go/service/opsworks" "github.com/aws/aws-sdk-go/service/rds" @@ -97,6 +98,7 @@ type AWSClient struct { rdsconn *rds.RDS iamconn *iam.IAM kinesisconn *kinesis.Kinesis + kmsconn *kms.KMS firehoseconn *firehose.Firehose elasticacheconn *elasticache.ElastiCache elasticbeanstalkconn *elasticbeanstalk.ElasticBeanstalk @@ -294,6 +296,8 @@ func (c *Config) Client() (interface{}, error) { log.Println("[INFO] Initializing Redshift SDK connection") client.redshiftconn = redshift.New(sess) + log.Println("[INFO] Initializing KMS connection") + client.kmsconn = kms.New(sess) } if len(errs) > 0 { diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index a1144cbd63..e469c67569 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -184,6 +184,8 @@ func Provider() terraform.ResourceProvider { "aws_key_pair": resourceAwsKeyPair(), "aws_kinesis_firehose_delivery_stream": resourceAwsKinesisFirehoseDeliveryStream(), "aws_kinesis_stream": resourceAwsKinesisStream(), + "aws_kms_alias": resourceAwsKmsAlias(), + "aws_kms_key": resourceAwsKmsKey(), "aws_lambda_function": resourceAwsLambdaFunction(), "aws_lambda_event_source_mapping": resourceAwsLambdaEventSourceMapping(), "aws_lambda_alias": resourceAwsLambdaAlias(), diff --git a/builtin/providers/aws/resource_aws_kms_alias.go b/builtin/providers/aws/resource_aws_kms_alias.go new file mode 100644 index 0000000000..3eb39dedfa --- /dev/null +++ b/builtin/providers/aws/resource_aws_kms_alias.go @@ -0,0 +1,131 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + + "github.com/hashicorp/terraform/helper/schema" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/kms" +) + +func resourceAwsKmsAlias() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsKmsAliasCreate, + Read: resourceAwsKmsAliasRead, + Update: resourceAwsKmsAliasUpdate, + Delete: resourceAwsKmsAliasDelete, + + Schema: map[string]*schema.Schema{ + "arn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + value := v.(string) + if !regexp.MustCompile(`^(alias\/)[a-zA-Z0-9:/_-]+$`).MatchString(value) { + es = append(es, fmt.Errorf( + "name must begin with 'alias/' and be comprised of only [a-zA-Z0-9:/_-]", k)) + } + return + }, + }, + "target_key_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + }, + } +} + +func resourceAwsKmsAliasCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).kmsconn + name := d.Get("name").(string) + targetKeyId := d.Get("target_key_id").(string) + + log.Printf("[DEBUG] KMS alias create name: %s, target_key: %s", name, targetKeyId) + + req := &kms.CreateAliasInput{ + AliasName: aws.String(name), + TargetKeyId: aws.String(targetKeyId), + } + _, err := conn.CreateAlias(req) + if err != nil { + return err + } + d.SetId(name) + return resourceAwsKmsAliasRead(d, meta) +} + +func resourceAwsKmsAliasRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).kmsconn + name := d.Get("name").(string) + + req := &kms.ListAliasesInput{} + resp, err := conn.ListAliases(req) + if err != nil { + return err + } + for _,e := range resp.Aliases { + if name == *e.AliasName { + if err := d.Set("arn", e.AliasArn); err != nil { + return err + } + if err := d.Set("target_key_id", e.TargetKeyId); err != nil { + return err + } + return nil + } + } + + log.Printf("[DEBUG] KMS alias read: alias not found") + d.SetId("") + return nil +} + +func resourceAwsKmsAliasUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).kmsconn + + if d.HasChange("target_key_id") { + if err := resourceAwsKmsAliasTargetUpdate(conn, d); err != nil { + return err + } + } + return nil +} + +func resourceAwsKmsAliasTargetUpdate(conn *kms.KMS, d *schema.ResourceData) error { + name := d.Get("name").(string) + targetKeyId := d.Get("target_key_id").(string) + + log.Printf("[DEBUG] KMS alias: %s, update target: %s", name, targetKeyId) + + req := &kms.UpdateAliasInput{ + AliasName: aws.String(name), + TargetKeyId: aws.String(targetKeyId), + } + _, err := conn.UpdateAlias(req) + + return err +} + +func resourceAwsKmsAliasDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).kmsconn + name := d.Get("name").(string) + + req := &kms.DeleteAliasInput{ + AliasName: aws.String(name), + } + _, err := conn.DeleteAlias(req) + + log.Printf("[DEBUG] KMS Alias: %s deleted.", name) + d.SetId("") + return err +} diff --git a/builtin/providers/aws/resource_aws_kms_key.go b/builtin/providers/aws/resource_aws_kms_key.go new file mode 100644 index 0000000000..354764ca6a --- /dev/null +++ b/builtin/providers/aws/resource_aws_kms_key.go @@ -0,0 +1,177 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/kms" +) + +func resourceAwsKmsKey() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsKmsKeyCreate, + Read: resourceAwsKmsKeyRead, + Update: resourceAwsKmsKeyUpdate, + Delete: resourceAwsKmsKeyDelete, + + Schema: map[string]*schema.Schema{ + "arn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "key_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "enabled": &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: false, + }, + "key_usage": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + value := v.(string) + if !(value == "ENCRYPT_DECRYPT" || value == "") { + es = append(es, fmt.Errorf( + "key_usage must be ENCRYPT_DECRYPT or not specified")) + } + return + }, + }, + "policy": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: false, + }, + }, + } +} + +func resourceAwsKmsKeyCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).kmsconn + + // Allow aws to chose default values if we don't pass them + var req kms.CreateKeyInput + if v, exists := d.GetOk("description"); exists { + req.Description = aws.String(v.(string)) + } + if v, exists := d.GetOk("key_usage"); exists { + req.KeyUsage = aws.String(v.(string)) + } + if v, exists := d.GetOk("policy"); exists { + req.Policy = aws.String(v.(string)) + } + + resp, err := conn.CreateKey(&req) + if err != nil { + return err + } + return resourceAwsKmsKeyReadResult(d, resp.KeyMetadata) +} + +func resourceAwsKmsKeyRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).kmsconn + keyId := d.Get("key_id").(string) + + req := &kms.DescribeKeyInput{ + KeyId: aws.String(keyId), + } + resp, err := conn.DescribeKey(req) + if err != nil { + return err + } + return resourceAwsKmsKeyReadResult(d, resp.KeyMetadata) +} + +func resourceAwsKmsKeyReadResult(d *schema.ResourceData, metadata *kms.KeyMetadata) error { + d.SetId(*metadata.KeyId) + + if err := d.Set("arn", metadata.Arn); err != nil { + return err + } + if err := d.Set("key_id", metadata.KeyId); err != nil { + return err + } + if err := d.Set("enabled", metadata.Enabled); err != nil { + return err + } + if err := d.Set("description", metadata.Description); err != nil { + return err + } + if err := d.Set("key_usage", metadata.KeyUsage); err != nil { + return err + } + return nil +} + +func resourceAwsKmsKeyUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).kmsconn + + if d.HasChange("description") { + if err := resourceAwsKmsKeyDescriptionUpdate(conn, d); err != nil { + return err + } + } + if d.HasChange("policy") { + if err := resourceAwsKmsKeyPolicyUpdate(conn, d); err != nil { + return err + } + } + return resourceAwsKmsKeyRead(d, meta) +} + +func resourceAwsKmsKeyDescriptionUpdate(conn *kms.KMS, d *schema.ResourceData) error { + description := d.Get("description").(string) + keyId := d.Get("key_id").(string) + + log.Printf("[DEBUG] KMS key: %s, update description: %s", keyId, description) + + req := &kms.UpdateKeyDescriptionInput{ + Description: aws.String(description), + KeyId: aws.String(keyId), + } + _, err := conn.UpdateKeyDescription(req) + return err +} + +func resourceAwsKmsKeyPolicyUpdate(conn *kms.KMS, d *schema.ResourceData) error { + policy := d.Get("policy").(string) + keyId := d.Get("key_id").(string) + + log.Printf("[DEBUG] KMS key: %s, update policy: %s", keyId, policy) + + req := &kms.PutKeyPolicyInput{ + KeyId: aws.String(keyId), + Policy: aws.String(policy), + PolicyName: aws.String("default"), + } + _, err := conn.PutKeyPolicy(req) + return err +} + +func resourceAwsKmsKeyDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).kmsconn + keyId := d.Get("key_id").(string) + + req := &kms.DisableKeyInput{ + KeyId: aws.String(keyId), + } + _, err := conn.DisableKey(req) + + log.Printf("[DEBUG] KMS Key: %s deactivated.", keyId) + d.SetId("") + return err +}