From 7d79ebc87af3de85d35bc6958b6fb10ff99c919a Mon Sep 17 00:00:00 2001 From: stack72 Date: Thu, 27 Jul 2017 12:20:25 +0300 Subject: [PATCH] resource/aws_ssm_association: Add support for OutputLocation and Schedule Expression Fixes: #1246 ``` % make testacc TEST=./aws TESTARGS='-run=TestAccAWSSSMAssociation_' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -run=TestAccAWSSSMAssociation_ -timeout 120m === RUN TestAccAWSSSMAssociation_basic --- PASS: TestAccAWSSSMAssociation_basic (135.40s) === RUN TestAccAWSSSMAssociation_withTargets --- PASS: TestAccAWSSSMAssociation_withTargets (32.06s) === RUN TestAccAWSSSMAssociation_withOutputLocation --- PASS: TestAccAWSSSMAssociation_withOutputLocation (58.07s) === RUN TestAccAWSSSMAssociation_withScheduleExpression --- PASS: TestAccAWSSSMAssociation_withScheduleExpression (31.17s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 256.727s ``` --- aws/resource_aws_ssm_association.go | 76 ++++++++++++ aws/resource_aws_ssm_association_test.go | 120 +++++++++++++++++++ website/docs/r/ssm_association.html.markdown | 7 ++ 3 files changed, 203 insertions(+) diff --git a/aws/resource_aws_ssm_association.go b/aws/resource_aws_ssm_association.go index be83ce767b84..c1ea0c8a6c47 100644 --- a/aws/resource_aws_ssm_association.go +++ b/aws/resource_aws_ssm_association.go @@ -37,6 +37,29 @@ func resourceAwsSsmAssociation() *schema.Resource { ForceNew: true, Computed: true, }, + "schedule_expression": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "output_location": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "s3_bucket_name": { + Type: schema.TypeString, + Required: true, + }, + "s3_key_prefix": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, "targets": { Type: schema.TypeList, Optional: true, @@ -74,6 +97,10 @@ func resourceAwsSsmAssociationCreate(d *schema.ResourceData, meta interface{}) e assosciationInput.InstanceId = aws.String(v.(string)) } + if v, ok := d.GetOk("schedule_expression"); ok { + assosciationInput.ScheduleExpression = aws.String(v.(string)) + } + if v, ok := d.GetOk("parameters"); ok { assosciationInput.Parameters = expandSSMDocumentParameters(v.(map[string]interface{})) } @@ -82,6 +109,10 @@ func resourceAwsSsmAssociationCreate(d *schema.ResourceData, meta interface{}) e assosciationInput.Targets = expandAwsSsmTargets(d) } + if v, ok := d.GetOk("output_location"); ok { + assosciationInput.OutputLocation = expandSSMAssociationOutputLocation(v.([]interface{})) + } + resp, err := ssmconn.CreateAssociation(assosciationInput) if err != nil { return errwrap.Wrapf("[ERROR] Error creating SSM association: {{err}}", err) @@ -120,11 +151,16 @@ func resourceAwsSsmAssociationRead(d *schema.ResourceData, meta interface{}) err d.Set("name", association.Name) d.Set("parameters", association.Parameters) d.Set("association_id", association.AssociationId) + d.Set("schedule_expression", association.ScheduleExpression) if err := d.Set("targets", flattenAwsSsmTargets(association.Targets)); err != nil { return fmt.Errorf("[DEBUG] Error setting targets error: %#v", err) } + if err := d.Set("output_location", flattenAwsSsmAssociationOutoutLocation(association.OutputLocation)); err != nil { + return fmt.Errorf("[DEBUG] Error setting output_location error: %#v", err) + } + return nil } @@ -156,3 +192,43 @@ func expandSSMDocumentParameters(params map[string]interface{}) map[string][]*st return docParams } + +func expandSSMAssociationOutputLocation(config []interface{}) *ssm.InstanceAssociationOutputLocation { + if config == nil { + return nil + } + + //We only allow 1 Item so we can grab the first in the list only + locationConfig := config[0].(map[string]interface{}) + + S3OutputLocation := &ssm.S3OutputLocation{ + OutputS3BucketName: aws.String(locationConfig["s3_bucket_name"].(string)), + } + + if v, ok := locationConfig["s3_key_prefix"]; ok { + S3OutputLocation.OutputS3KeyPrefix = aws.String(v.(string)) + } + + return &ssm.InstanceAssociationOutputLocation{ + S3Location: S3OutputLocation, + } +} + +func flattenAwsSsmAssociationOutoutLocation(location *ssm.InstanceAssociationOutputLocation) []map[string]interface{} { + if location == nil { + return nil + } + + result := make([]map[string]interface{}, 0) + item := make(map[string]interface{}) + + item["s3_bucket_name"] = *location.S3Location.OutputS3BucketName + + if location.S3Location.OutputS3KeyPrefix != nil { + item["s3_key_prefix"] = *location.S3Location.OutputS3KeyPrefix + } + + result = append(result, item) + + return result +} diff --git a/aws/resource_aws_ssm_association_test.go b/aws/resource_aws_ssm_association_test.go index 0dfc752db016..5d084e454995 100644 --- a/aws/resource_aws_ssm_association_test.go +++ b/aws/resource_aws_ssm_association_test.go @@ -46,6 +46,46 @@ func TestAccAWSSSMAssociation_withTargets(t *testing.T) { }) } +func TestAccAWSSSMAssociation_withOutputLocation(t *testing.T) { + name := acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSSMAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSSMAssociationBasicConfigWithOutPutLocation(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSSMAssociationExists("aws_ssm_association.foo"), + resource.TestCheckResourceAttr( + "aws_ssm_association.foo", "output_location.0.s3_bucket_name", fmt.Sprintf("tf-acc-test-ssmoutput-%s", name)), + resource.TestCheckResourceAttr( + "aws_ssm_association.foo", "output_location.0.s3_key_prefix", "SSMAssociation"), + ), + }, + }, + }) +} + +func TestAccAWSSSMAssociation_withScheduleExpression(t *testing.T) { + name := acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSSMAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSSMAssociationBasicConfigWithScheduleExpression(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSSMAssociationExists("aws_ssm_association.foo"), + resource.TestCheckResourceAttr( + "aws_ssm_association.foo", "schedule_expression", "cron(0 16 ? * TUE *)"), + ), + }, + }, + }) +} + func testAccCheckAWSSSMAssociationExists(n string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -186,3 +226,83 @@ resource "aws_ssm_association" "foo" { } `, rName, rName, rName) } + +func testAccAWSSSMAssociationBasicConfigWithScheduleExpression(rName string) string { + return fmt.Sprintf(` +resource "aws_ssm_document" "foo_document" { + name = "test_document_association-%s", + document_type = "Command" + content = <