Skip to content

Commit d129447

Browse files
committed
Merge pull request hashicorp#3928 from TimeIncOSS/aws-kms
provider/aws: Add support for KMS
2 parents f691b89 + dde91b8 commit d129447

13 files changed

Lines changed: 3837 additions & 0 deletions

File tree

Godeps/Godeps.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

builtin/providers/aws/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
"github.com/aws/aws-sdk-go/service/glacier"
4343
"github.com/aws/aws-sdk-go/service/iam"
4444
"github.com/aws/aws-sdk-go/service/kinesis"
45+
"github.com/aws/aws-sdk-go/service/kms"
4546
"github.com/aws/aws-sdk-go/service/lambda"
4647
"github.com/aws/aws-sdk-go/service/opsworks"
4748
"github.com/aws/aws-sdk-go/service/rds"
@@ -97,6 +98,7 @@ type AWSClient struct {
9798
rdsconn *rds.RDS
9899
iamconn *iam.IAM
99100
kinesisconn *kinesis.Kinesis
101+
kmsconn *kms.KMS
100102
firehoseconn *firehose.Firehose
101103
elasticacheconn *elasticache.ElastiCache
102104
elasticbeanstalkconn *elasticbeanstalk.ElasticBeanstalk
@@ -294,6 +296,8 @@ func (c *Config) Client() (interface{}, error) {
294296
log.Println("[INFO] Initializing Redshift SDK connection")
295297
client.redshiftconn = redshift.New(sess)
296298

299+
log.Println("[INFO] Initializing KMS connection")
300+
client.kmsconn = kms.New(sess)
297301
}
298302

299303
if len(errs) > 0 {

builtin/providers/aws/provider.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ func Provider() terraform.ResourceProvider {
184184
"aws_key_pair": resourceAwsKeyPair(),
185185
"aws_kinesis_firehose_delivery_stream": resourceAwsKinesisFirehoseDeliveryStream(),
186186
"aws_kinesis_stream": resourceAwsKinesisStream(),
187+
"aws_kms_alias": resourceAwsKmsAlias(),
188+
"aws_kms_key": resourceAwsKmsKey(),
187189
"aws_lambda_function": resourceAwsLambdaFunction(),
188190
"aws_lambda_event_source_mapping": resourceAwsLambdaEventSourceMapping(),
189191
"aws_lambda_alias": resourceAwsLambdaAlias(),
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package aws
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"regexp"
7+
8+
"github.com/hashicorp/terraform/helper/schema"
9+
10+
"github.com/aws/aws-sdk-go/aws"
11+
"github.com/aws/aws-sdk-go/service/kms"
12+
)
13+
14+
func resourceAwsKmsAlias() *schema.Resource {
15+
return &schema.Resource{
16+
Create: resourceAwsKmsAliasCreate,
17+
Read: resourceAwsKmsAliasRead,
18+
Update: resourceAwsKmsAliasUpdate,
19+
Delete: resourceAwsKmsAliasDelete,
20+
21+
Schema: map[string]*schema.Schema{
22+
"arn": &schema.Schema{
23+
Type: schema.TypeString,
24+
Computed: true,
25+
},
26+
"name": &schema.Schema{
27+
Type: schema.TypeString,
28+
Required: true,
29+
ForceNew: true,
30+
ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
31+
value := v.(string)
32+
if !regexp.MustCompile(`^(alias\/)[a-zA-Z0-9:/_-]+$`).MatchString(value) {
33+
es = append(es, fmt.Errorf(
34+
"%q must begin with 'alias/' and be comprised of only [a-zA-Z0-9:/_-]", k))
35+
}
36+
return
37+
},
38+
},
39+
"target_key_id": &schema.Schema{
40+
Type: schema.TypeString,
41+
Required: true,
42+
},
43+
},
44+
}
45+
}
46+
47+
func resourceAwsKmsAliasCreate(d *schema.ResourceData, meta interface{}) error {
48+
conn := meta.(*AWSClient).kmsconn
49+
name := d.Get("name").(string)
50+
targetKeyId := d.Get("target_key_id").(string)
51+
52+
log.Printf("[DEBUG] KMS alias create name: %s, target_key: %s", name, targetKeyId)
53+
54+
req := &kms.CreateAliasInput{
55+
AliasName: aws.String(name),
56+
TargetKeyId: aws.String(targetKeyId),
57+
}
58+
_, err := conn.CreateAlias(req)
59+
if err != nil {
60+
return err
61+
}
62+
d.SetId(name)
63+
return resourceAwsKmsAliasRead(d, meta)
64+
}
65+
66+
func resourceAwsKmsAliasRead(d *schema.ResourceData, meta interface{}) error {
67+
conn := meta.(*AWSClient).kmsconn
68+
name := d.Get("name").(string)
69+
70+
alias, err := findKmsAliasByName(conn, name, nil)
71+
if err != nil {
72+
return err
73+
}
74+
if alias == nil {
75+
log.Printf("[DEBUG] Removing KMS Alias %q as it's already gone", name)
76+
d.SetId("")
77+
return nil
78+
}
79+
80+
log.Printf("[DEBUG] Found KMS Alias: %s", alias)
81+
82+
d.Set("arn", alias.AliasArn)
83+
d.Set("target_key_id", alias.TargetKeyId)
84+
85+
return nil
86+
}
87+
88+
func resourceAwsKmsAliasUpdate(d *schema.ResourceData, meta interface{}) error {
89+
conn := meta.(*AWSClient).kmsconn
90+
91+
if d.HasChange("target_key_id") {
92+
err := resourceAwsKmsAliasTargetUpdate(conn, d)
93+
if err != nil {
94+
return err
95+
}
96+
}
97+
return nil
98+
}
99+
100+
func resourceAwsKmsAliasTargetUpdate(conn *kms.KMS, d *schema.ResourceData) error {
101+
name := d.Get("name").(string)
102+
targetKeyId := d.Get("target_key_id").(string)
103+
104+
log.Printf("[DEBUG] KMS alias: %s, update target: %s", name, targetKeyId)
105+
106+
req := &kms.UpdateAliasInput{
107+
AliasName: aws.String(name),
108+
TargetKeyId: aws.String(targetKeyId),
109+
}
110+
_, err := conn.UpdateAlias(req)
111+
112+
return err
113+
}
114+
115+
func resourceAwsKmsAliasDelete(d *schema.ResourceData, meta interface{}) error {
116+
conn := meta.(*AWSClient).kmsconn
117+
name := d.Get("name").(string)
118+
119+
req := &kms.DeleteAliasInput{
120+
AliasName: aws.String(name),
121+
}
122+
_, err := conn.DeleteAlias(req)
123+
if err != nil {
124+
return err
125+
}
126+
127+
log.Printf("[DEBUG] KMS Alias: %s deleted.", name)
128+
d.SetId("")
129+
return nil
130+
}
131+
132+
// API by default limits results to 50 aliases
133+
// This is how we make sure we won't miss any alias
134+
// See http://docs.aws.amazon.com/kms/latest/APIReference/API_ListAliases.html
135+
func findKmsAliasByName(conn *kms.KMS, name string, marker *string) (*kms.AliasListEntry, error) {
136+
req := kms.ListAliasesInput{
137+
Limit: aws.Int64(int64(100)),
138+
}
139+
if marker != nil {
140+
req.Marker = marker
141+
}
142+
143+
log.Printf("[DEBUG] Listing KMS aliases: %s", req)
144+
resp, err := conn.ListAliases(&req)
145+
if err != nil {
146+
return nil, err
147+
}
148+
149+
for _, entry := range resp.Aliases {
150+
if *entry.AliasName == name {
151+
return entry, nil
152+
}
153+
}
154+
if *resp.Truncated {
155+
log.Printf("[DEBUG] KMS alias list is truncated, listing more via %s", *resp.NextMarker)
156+
return findKmsAliasByName(conn, name, resp.NextMarker)
157+
}
158+
159+
return nil, nil
160+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package aws
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
"time"
7+
8+
"github.com/hashicorp/terraform/helper/resource"
9+
"github.com/hashicorp/terraform/terraform"
10+
)
11+
12+
func TestAccAWSKmsAlias_basic(t *testing.T) {
13+
resource.Test(t, resource.TestCase{
14+
PreCheck: func() { testAccPreCheck(t) },
15+
Providers: testAccProviders,
16+
CheckDestroy: testAccCheckAWSKmsAliasDestroy,
17+
Steps: []resource.TestStep{
18+
resource.TestStep{
19+
Config: testAccAWSKmsSingleAlias,
20+
Check: resource.ComposeTestCheckFunc(
21+
testAccCheckAWSKmsAliasExists("aws_kms_alias.single"),
22+
),
23+
},
24+
resource.TestStep{
25+
Config: testAccAWSKmsSingleAlias_modified,
26+
Check: resource.ComposeTestCheckFunc(
27+
testAccCheckAWSKmsAliasExists("aws_kms_alias.single"),
28+
),
29+
},
30+
},
31+
})
32+
}
33+
34+
func TestAccAWSKmsAlias_multiple(t *testing.T) {
35+
resource.Test(t, resource.TestCase{
36+
PreCheck: func() { testAccPreCheck(t) },
37+
Providers: testAccProviders,
38+
CheckDestroy: testAccCheckAWSKmsAliasDestroy,
39+
Steps: []resource.TestStep{
40+
resource.TestStep{
41+
Config: testAccAWSKmsMultipleAliases,
42+
Check: resource.ComposeTestCheckFunc(
43+
testAccCheckAWSKmsAliasExists("aws_kms_alias.one"),
44+
testAccCheckAWSKmsAliasExists("aws_kms_alias.two"),
45+
),
46+
},
47+
},
48+
})
49+
}
50+
51+
func testAccCheckAWSKmsAliasDestroy(s *terraform.State) error {
52+
conn := testAccProvider.Meta().(*AWSClient).kmsconn
53+
54+
for _, rs := range s.RootModule().Resources {
55+
if rs.Type != "aws_kms_alias" {
56+
continue
57+
}
58+
59+
entry, err := findKmsAliasByName(conn, rs.Primary.ID, nil)
60+
if err != nil {
61+
return err
62+
}
63+
if entry != nil {
64+
return fmt.Errorf("KMS alias still exists:\n%#v", entry)
65+
}
66+
67+
return nil
68+
}
69+
70+
return nil
71+
}
72+
73+
func testAccCheckAWSKmsAliasExists(name string) resource.TestCheckFunc {
74+
return func(s *terraform.State) error {
75+
_, ok := s.RootModule().Resources[name]
76+
if !ok {
77+
return fmt.Errorf("Not found: %s", name)
78+
}
79+
80+
return nil
81+
}
82+
}
83+
84+
var kmsAliasTimestamp = time.Now().Format(time.RFC1123)
85+
var testAccAWSKmsSingleAlias = fmt.Sprintf(`
86+
resource "aws_kms_key" "one" {
87+
description = "Terraform acc test One %s"
88+
deletion_window_in_days = 7
89+
}
90+
resource "aws_kms_key" "two" {
91+
description = "Terraform acc test Two %s"
92+
deletion_window_in_days = 7
93+
}
94+
95+
resource "aws_kms_alias" "single" {
96+
name = "alias/tf-acc-key-alias"
97+
target_key_id = "${aws_kms_key.one.key_id}"
98+
}`, kmsAliasTimestamp, kmsAliasTimestamp)
99+
100+
var testAccAWSKmsSingleAlias_modified = fmt.Sprintf(`
101+
resource "aws_kms_key" "one" {
102+
description = "Terraform acc test One %s"
103+
deletion_window_in_days = 7
104+
}
105+
resource "aws_kms_key" "two" {
106+
description = "Terraform acc test Two %s"
107+
deletion_window_in_days = 7
108+
}
109+
110+
resource "aws_kms_alias" "single" {
111+
name = "alias/tf-acc-key-alias"
112+
target_key_id = "${aws_kms_key.two.key_id}"
113+
}`, kmsAliasTimestamp, kmsAliasTimestamp)
114+
115+
var testAccAWSKmsMultipleAliases = fmt.Sprintf(`
116+
resource "aws_kms_key" "single" {
117+
description = "Terraform acc test One %s"
118+
deletion_window_in_days = 7
119+
}
120+
121+
resource "aws_kms_alias" "one" {
122+
name = "alias/tf-acc-key-alias-one"
123+
target_key_id = "${aws_kms_key.single.key_id}"
124+
}
125+
resource "aws_kms_alias" "two" {
126+
name = "alias/tf-acc-key-alias-two"
127+
target_key_id = "${aws_kms_key.single.key_id}"
128+
}`, kmsAliasTimestamp)

0 commit comments

Comments
 (0)