Skip to content

Commit

Permalink
Merge pull request #3928 from TimeIncOSS/aws-kms
Browse files Browse the repository at this point in the history
provider/aws: Add support for KMS
  • Loading branch information
phinze committed Mar 10, 2016
2 parents f691b89 + dde91b8 commit d129447
Show file tree
Hide file tree
Showing 13 changed files with 3,837 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions builtin/providers/aws/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions builtin/providers/aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
160 changes: 160 additions & 0 deletions builtin/providers/aws/resource_aws_kms_alias.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
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(
"%q 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,
},
},
}
}

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)

alias, err := findKmsAliasByName(conn, name, nil)
if err != nil {
return err
}
if alias == nil {
log.Printf("[DEBUG] Removing KMS Alias %q as it's already gone", name)
d.SetId("")
return nil
}

log.Printf("[DEBUG] Found KMS Alias: %s", alias)

d.Set("arn", alias.AliasArn)
d.Set("target_key_id", alias.TargetKeyId)

return nil
}

func resourceAwsKmsAliasUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).kmsconn

if d.HasChange("target_key_id") {
err := resourceAwsKmsAliasTargetUpdate(conn, d)
if 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)
if err != nil {
return err
}

log.Printf("[DEBUG] KMS Alias: %s deleted.", name)
d.SetId("")
return nil
}

// API by default limits results to 50 aliases
// This is how we make sure we won't miss any alias
// See http://docs.aws.amazon.com/kms/latest/APIReference/API_ListAliases.html
func findKmsAliasByName(conn *kms.KMS, name string, marker *string) (*kms.AliasListEntry, error) {
req := kms.ListAliasesInput{
Limit: aws.Int64(int64(100)),
}
if marker != nil {
req.Marker = marker
}

log.Printf("[DEBUG] Listing KMS aliases: %s", req)
resp, err := conn.ListAliases(&req)
if err != nil {
return nil, err
}

for _, entry := range resp.Aliases {
if *entry.AliasName == name {
return entry, nil
}
}
if *resp.Truncated {
log.Printf("[DEBUG] KMS alias list is truncated, listing more via %s", *resp.NextMarker)
return findKmsAliasByName(conn, name, resp.NextMarker)
}

return nil, nil
}
128 changes: 128 additions & 0 deletions builtin/providers/aws/resource_aws_kms_alias_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package aws

import (
"fmt"
"testing"
"time"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSKmsAlias_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSKmsAliasDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSKmsSingleAlias,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSKmsAliasExists("aws_kms_alias.single"),
),
},
resource.TestStep{
Config: testAccAWSKmsSingleAlias_modified,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSKmsAliasExists("aws_kms_alias.single"),
),
},
},
})
}

func TestAccAWSKmsAlias_multiple(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSKmsAliasDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSKmsMultipleAliases,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSKmsAliasExists("aws_kms_alias.one"),
testAccCheckAWSKmsAliasExists("aws_kms_alias.two"),
),
},
},
})
}

func testAccCheckAWSKmsAliasDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).kmsconn

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_kms_alias" {
continue
}

entry, err := findKmsAliasByName(conn, rs.Primary.ID, nil)
if err != nil {
return err
}
if entry != nil {
return fmt.Errorf("KMS alias still exists:\n%#v", entry)
}

return nil
}

return nil
}

func testAccCheckAWSKmsAliasExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
_, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}

return nil
}
}

var kmsAliasTimestamp = time.Now().Format(time.RFC1123)
var testAccAWSKmsSingleAlias = fmt.Sprintf(`
resource "aws_kms_key" "one" {
description = "Terraform acc test One %s"
deletion_window_in_days = 7
}
resource "aws_kms_key" "two" {
description = "Terraform acc test Two %s"
deletion_window_in_days = 7
}
resource "aws_kms_alias" "single" {
name = "alias/tf-acc-key-alias"
target_key_id = "${aws_kms_key.one.key_id}"
}`, kmsAliasTimestamp, kmsAliasTimestamp)

var testAccAWSKmsSingleAlias_modified = fmt.Sprintf(`
resource "aws_kms_key" "one" {
description = "Terraform acc test One %s"
deletion_window_in_days = 7
}
resource "aws_kms_key" "two" {
description = "Terraform acc test Two %s"
deletion_window_in_days = 7
}
resource "aws_kms_alias" "single" {
name = "alias/tf-acc-key-alias"
target_key_id = "${aws_kms_key.two.key_id}"
}`, kmsAliasTimestamp, kmsAliasTimestamp)

var testAccAWSKmsMultipleAliases = fmt.Sprintf(`
resource "aws_kms_key" "single" {
description = "Terraform acc test One %s"
deletion_window_in_days = 7
}
resource "aws_kms_alias" "one" {
name = "alias/tf-acc-key-alias-one"
target_key_id = "${aws_kms_key.single.key_id}"
}
resource "aws_kms_alias" "two" {
name = "alias/tf-acc-key-alias-two"
target_key_id = "${aws_kms_key.single.key_id}"
}`, kmsAliasTimestamp)
Loading

0 comments on commit d129447

Please sign in to comment.