-
Notifications
You must be signed in to change notification settings - Fork 9.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New Resource: aws_ssm_resource_data_sync #1895
Changes from 4 commits
e3912c2
360b110
cb588ab
926668e
879b0f8
2e1236e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/aws/awserr" | ||
"github.com/aws/aws-sdk-go/service/ssm" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func resourceAwsSsmResourceDataSync() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceAwsSsmResourceDataSyncCreate, | ||
Read: resourceAwsSsmResourceDataSyncRead, | ||
Delete: resourceAwsSsmResourceDataSyncDelete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"s3_destination": { | ||
Type: schema.TypeList, | ||
Required: true, | ||
ForceNew: true, | ||
MaxItems: 1, | ||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"kms_key_arn": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
"bucket_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"prefix": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
"region": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"sync_format": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Default: ssm.ResourceDataSyncS3FormatJsonSerDe, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceAwsSsmResourceDataSyncCreate(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).ssmconn | ||
|
||
input := &ssm.CreateResourceDataSyncInput{ | ||
S3Destination: expandSsmResourceDataSyncS3Destination(d), | ||
SyncName: aws.String(d.Get("name").(string)), | ||
} | ||
|
||
_, err := conn.CreateResourceDataSync(input) | ||
if err != nil { | ||
return err | ||
} | ||
d.SetId(d.Get("name").(string)) | ||
return resourceAwsSsmResourceDataSyncRead(d, meta) | ||
} | ||
|
||
func resourceAwsSsmResourceDataSyncRead(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).ssmconn | ||
|
||
syncItem, err := findResourceDataSyncItem(conn, d.Get("name").(string)) | ||
if err != nil { | ||
return err | ||
} | ||
if syncItem == nil { | ||
d.SetId("") | ||
return nil | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a few things missing here - we need to re-set all the fields here based on what came from the API, e.g. d.Set("s3_destination", flattenS3Destination(ds.S3Destination)) where the flattener will turn the SDK object into and secondly if there's no sync found we should remove it from state by calling |
||
d.Set("s3_destination", flattenSsmResourceDataSyncS3Destination(syncItem.S3Destination)) | ||
return nil | ||
} | ||
|
||
func resourceAwsSsmResourceDataSyncDelete(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).ssmconn | ||
|
||
input := &ssm.DeleteResourceDataSyncInput{ | ||
SyncName: aws.String(d.Get("name").(string)), | ||
} | ||
|
||
_, err := conn.DeleteResourceDataSync(input) | ||
if err != nil { | ||
if isAWSErr(err, ssm.ErrCodeResourceDataSyncNotFoundException, "") { | ||
return nil | ||
} | ||
return err | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All of the above can be IMO simplified to something like this: if isAWSErr(err, ssm.ErrCodeResourceDataSyncNotFoundException, "") {
return nil
}
return err |
||
return nil | ||
} | ||
|
||
func findResourceDataSyncItem(conn *ssm.SSM, name string) (*ssm.ResourceDataSyncItem, error) { | ||
nextToken := "" | ||
for { | ||
input := &ssm.ListResourceDataSyncInput{} | ||
if nextToken != "" { | ||
input.NextToken = aws.String(nextToken) | ||
} | ||
resp, err := conn.ListResourceDataSync(input) | ||
if err != nil { | ||
return nil, err | ||
} | ||
for _, v := range resp.ResourceDataSyncItems { | ||
if *v.SyncName == name { | ||
return v, nil | ||
} | ||
} | ||
if resp.NextToken == nil { | ||
break | ||
} | ||
nextToken = *resp.NextToken | ||
} | ||
return nil, nil | ||
} | ||
|
||
func flattenSsmResourceDataSyncS3Destination(dest *ssm.ResourceDataSyncS3Destination) []interface{} { | ||
result := make(map[string]interface{}) | ||
result["bucket_name"] = *dest.BucketName | ||
result["region"] = *dest.Region | ||
result["sync_format"] = *dest.SyncFormat | ||
if dest.AWSKMSKeyARN != nil { | ||
result["kms_key_arn"] = *dest.AWSKMSKeyARN | ||
} | ||
if dest.Prefix != nil { | ||
result["prefix"] = *dest.Prefix | ||
} | ||
return []interface{}{result} | ||
} | ||
|
||
func expandSsmResourceDataSyncS3Destination(d *schema.ResourceData) *ssm.ResourceDataSyncS3Destination { | ||
raw := d.Get("s3_destination").([]interface{})[0].(map[string]interface{}) | ||
s3dest := &ssm.ResourceDataSyncS3Destination{ | ||
BucketName: aws.String(raw["bucket_name"].(string)), | ||
Region: aws.String(raw["region"].(string)), | ||
SyncFormat: aws.String(raw["sync_format"].(string)), | ||
} | ||
if v, ok := raw["kms_key_arn"].(string); ok && v != "" { | ||
s3dest.AWSKMSKeyARN = aws.String(v) | ||
} | ||
if v, ok := raw["prefix"].(string); ok && v != "" { | ||
s3dest.Prefix = aws.String(v) | ||
} | ||
return s3dest | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"testing" | ||
|
||
"github.com/aws/aws-sdk-go/aws/awserr" | ||
"github.com/hashicorp/terraform/helper/acctest" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
func TestAccAWSSsmResourceDataSync_basic(t *testing.T) { | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckAWSSsmResourceDataSyncDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccSsmResourceDataSyncConfig(acctest.RandInt(), acctest.RandString(5)), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckAWSSsmResourceDataSyncExists("aws_ssm_resource_data_sync.foo"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckAWSSsmResourceDataSyncDestroy(s *terraform.State) error { | ||
conn := testAccProvider.Meta().(*AWSClient).ssmconn | ||
|
||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "aws_ssm_resource_data_sync" { | ||
continue | ||
} | ||
syncItem, err := findResourceDataSyncItem(conn, rs.Primary.Attributes["name"]) | ||
if err != nil { | ||
return err | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Once you have decoupled the logic for finding the right data sync, it can also be reused here 😉 |
||
if syncItem != nil { | ||
return fmt.Errorf("Resource Data Sync (%s) found", rs.Primary.Attributes["name"]) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func testAccCheckAWSSsmResourceDataSyncExists(name string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
log.Println(s.RootModule().Resources) | ||
_, ok := s.RootModule().Resources[name] | ||
if !ok { | ||
return fmt.Errorf("Not found: %s", name) | ||
} | ||
return nil | ||
} | ||
} | ||
|
||
func testAccSsmResourceDataSyncConfig(rInt int, rName string) string { | ||
return fmt.Sprintf(` | ||
resource "aws_s3_bucket" "hoge" { | ||
bucket = "tf-test-bucket-%d" | ||
region = "us-west-2" | ||
force_destroy = true | ||
} | ||
|
||
resource "aws_s3_bucket_policy" "hoge" { | ||
bucket = "${aws_s3_bucket.hoge.bucket}" | ||
policy = <<EOF | ||
{ | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Sid": "SSMBucketPermissionsCheck", | ||
"Effect": "Allow", | ||
"Principal": { | ||
"Service": "ssm.amazonaws.com" | ||
}, | ||
"Action": "s3:GetBucketAcl", | ||
"Resource": "arn:aws:s3:::tf-test-bucket-%d" | ||
}, | ||
{ | ||
"Sid": " SSMBucketDelivery", | ||
"Effect": "Allow", | ||
"Principal": { | ||
"Service": "ssm.amazonaws.com" | ||
}, | ||
"Action": "s3:PutObject", | ||
"Resource": ["arn:aws:s3:::tf-test-bucket-%d/*"], | ||
"Condition": { | ||
"StringEquals": { | ||
"s3:x-amz-acl": "bucket-owner-full-control" | ||
} | ||
} | ||
} | ||
] | ||
} | ||
EOF | ||
} | ||
|
||
resource "aws_ssm_resource_data_sync" "foo" { | ||
name = "tf-test-ssm-%s" | ||
s3_destination = { | ||
bucket_name = "${aws_s3_bucket.hoge.bucket}" | ||
region = "${aws_s3_bucket.hoge.region}" | ||
} | ||
} | ||
`, rInt, rInt, rInt, rName) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
--- | ||
layout: "aws" | ||
page_title: "AWS: aws_ssm_resource_data_sync" | ||
sidebar_current: "docs-aws-resource-ssm-resource-data-sync" | ||
description: |- | ||
Provides a SSM resource data sync. | ||
--- | ||
|
||
# aws_athena_database | ||
|
||
Provides a SSM resource data sync. | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
resource "aws_s3_bucket" "hoge" { | ||
bucket = "tf-test-bucket-1234" | ||
region = "us-east-1" | ||
} | ||
|
||
resource "aws_s3_bucket_policy" "hoge" { | ||
bucket = "${aws_s3_bucket.hoge.bucket}" | ||
policy = <<EOF | ||
{ | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Sid": "SSMBucketPermissionsCheck", | ||
"Effect": "Allow", | ||
"Principal": { | ||
"Service": "ssm.amazonaws.com" | ||
}, | ||
"Action": "s3:GetBucketAcl", | ||
"Resource": "arn:aws:s3:::tf-test-bucket-1234" | ||
}, | ||
{ | ||
"Sid": " SSMBucketDelivery", | ||
"Effect": "Allow", | ||
"Principal": { | ||
"Service": "ssm.amazonaws.com" | ||
}, | ||
"Action": "s3:PutObject", | ||
"Resource": ["arn:aws:s3:::tf-test-bucket-1234/*"], | ||
"Condition": { | ||
"StringEquals": { | ||
"s3:x-amz-acl": "bucket-owner-full-control" | ||
} | ||
} | ||
} | ||
] | ||
} | ||
EOF | ||
} | ||
|
||
resource "aws_ssm_resource_data_sync" "foo" { | ||
name = "foo" | ||
s3_destination = { | ||
bucket_name = "${aws_s3_bucket.hoge.bucket}" | ||
region = "${aws_s3_bucket.hoge.region}" | ||
} | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `name` - (Required) Name for the configuration. | ||
* `s3_destination` - (Required) Amazon S3 configuration details for the sync. | ||
|
||
## s3_destination | ||
|
||
`s3_destination` supports the following: | ||
|
||
* `bucket_name` - (Required) Name of S3 bucket where the aggregated data is stored. | ||
* `region` - (Required) Region with the bucket targeted by the Resource Data Sync. | ||
* `kms_key_arn` - (Optional) ARN of an encryption key for a destination in Amazon S3. | ||
* `prefix` - (Optional) Prefix for the bucket. | ||
* `sync_format` - (Optional) A supported sync format. Only JsonSerDe is currently supported. Defaults to JsonSerDe. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mind moving this for loop into a separate function, e.g.
findResourceDataSync(conn *ssm.SSM, name string) *ssm.ResourceDataSyncItem
?