From 55583961653be15b674016a4fce62610a8978402 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Wed, 16 Aug 2017 18:49:18 +0300 Subject: [PATCH] Support updates for aws_ssm_association & aws_ssm_patch_baseline resources (#1421) * Update SSM Association Schedule Expression * Update SSM Association Output Location * Add support for SSM Association Document Version * Add acceptance test for AWS SSM Assocation with Parameters * Add support for SSM Association Parameter Updates * Add support for ssm_patch_baseline resource updates --- aws/resource_aws_ssm_association.go | 47 ++- aws/resource_aws_ssm_association_test.go | 342 +++++++++++++++++++ aws/resource_aws_ssm_patch_baseline.go | 50 ++- aws/resource_aws_ssm_patch_baseline_test.go | 79 ++++- website/docs/r/ssm_association.html.markdown | 1 + 5 files changed, 505 insertions(+), 14 deletions(-) diff --git a/aws/resource_aws_ssm_association.go b/aws/resource_aws_ssm_association.go index c1ea0c8a6c4..e3f080dedd0 100644 --- a/aws/resource_aws_ssm_association.go +++ b/aws/resource_aws_ssm_association.go @@ -14,6 +14,7 @@ func resourceAwsSsmAssociation() *schema.Resource { return &schema.Resource{ Create: resourceAwsSsmAssociationCreate, Read: resourceAwsSsmAssociationRead, + Update: resourceAwsSsmAssocationUpdate, Delete: resourceAwsSsmAssociationDelete, Schema: map[string]*schema.Schema{ @@ -26,6 +27,11 @@ func resourceAwsSsmAssociation() *schema.Resource { ForceNew: true, Optional: true, }, + "document_version": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, "name": { Type: schema.TypeString, ForceNew: true, @@ -34,19 +40,16 @@ func resourceAwsSsmAssociation() *schema.Resource { "parameters": { Type: schema.TypeMap, Optional: true, - 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": { @@ -97,6 +100,10 @@ func resourceAwsSsmAssociationCreate(d *schema.ResourceData, meta interface{}) e assosciationInput.InstanceId = aws.String(v.(string)) } + if v, ok := d.GetOk("document_version"); ok { + assosciationInput.DocumentVersion = aws.String(v.(string)) + } + if v, ok := d.GetOk("schedule_expression"); ok { assosciationInput.ScheduleExpression = aws.String(v.(string)) } @@ -152,6 +159,7 @@ func resourceAwsSsmAssociationRead(d *schema.ResourceData, meta interface{}) err d.Set("parameters", association.Parameters) d.Set("association_id", association.AssociationId) d.Set("schedule_expression", association.ScheduleExpression) + d.Set("document_version", association.DocumentVersion) if err := d.Set("targets", flattenAwsSsmTargets(association.Targets)); err != nil { return fmt.Errorf("[DEBUG] Error setting targets error: %#v", err) @@ -164,6 +172,39 @@ func resourceAwsSsmAssociationRead(d *schema.ResourceData, meta interface{}) err return nil } +func resourceAwsSsmAssocationUpdate(d *schema.ResourceData, meta interface{}) error { + ssmconn := meta.(*AWSClient).ssmconn + + log.Printf("[DEBUG] SSM association update: %s", d.Id()) + + associationInput := &ssm.UpdateAssociationInput{ + AssociationId: aws.String(d.Get("association_id").(string)), + } + + if d.HasChange("schedule_expression") { + associationInput.ScheduleExpression = aws.String(d.Get("schedule_expression").(string)) + } + + if d.HasChange("document_version") { + associationInput.DocumentVersion = aws.String(d.Get("document_version").(string)) + } + + if d.HasChange("parameters") { + associationInput.Parameters = expandSSMDocumentParameters(d.Get("parameters").(map[string]interface{})) + } + + if d.HasChange("output_location") { + associationInput.OutputLocation = expandSSMAssociationOutputLocation(d.Get("output_location").([]interface{})) + } + + _, err := ssmconn.UpdateAssociation(associationInput) + if err != nil { + return errwrap.Wrapf("[ERROR] Error updating SSM association: {{err}}", err) + } + + return resourceAwsSsmAssociationRead(d, meta) +} + func resourceAwsSsmAssociationDelete(d *schema.ResourceData, meta interface{}) error { ssmconn := meta.(*AWSClient).ssmconn diff --git a/aws/resource_aws_ssm_association_test.go b/aws/resource_aws_ssm_association_test.go index 5d084e45499..bea24bd3158 100644 --- a/aws/resource_aws_ssm_association_test.go +++ b/aws/resource_aws_ssm_association_test.go @@ -46,6 +46,52 @@ func TestAccAWSSSMAssociation_withTargets(t *testing.T) { }) } +func TestAccAWSSSMAssociation_withParameters(t *testing.T) { + name := acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSSMAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSSMAssociationBasicConfigWithParameters(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSSMAssociationExists("aws_ssm_association.foo"), + resource.TestCheckResourceAttr( + "aws_ssm_association.foo", "parameters.Directory", "myWorkSpace"), + ), + }, + { + Config: testAccAWSSSMAssociationBasicConfigWithParametersUpdated(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSSMAssociationExists("aws_ssm_association.foo"), + resource.TestCheckResourceAttr( + "aws_ssm_association.foo", "parameters.Directory", "myWorkSpaceUpdated"), + ), + }, + }, + }) +} + +func TestAccAWSSSMAssociation_withDocumentVersion(t *testing.T) { + name := acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSSMAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSSMAssociationBasicConfigWithDocumentVersion(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSSMAssociationExists("aws_ssm_association.foo"), + resource.TestCheckResourceAttr( + "aws_ssm_association.foo", "document_version", "1"), + ), + }, + }, + }) +} + func TestAccAWSSSMAssociation_withOutputLocation(t *testing.T) { name := acctest.RandString(10) resource.Test(t, resource.TestCase{ @@ -63,6 +109,26 @@ func TestAccAWSSSMAssociation_withOutputLocation(t *testing.T) { "aws_ssm_association.foo", "output_location.0.s3_key_prefix", "SSMAssociation"), ), }, + { + Config: testAccAWSSSMAssociationBasicConfigWithOutPutLocationUpdateBucketName(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-updated-%s", name)), + resource.TestCheckResourceAttr( + "aws_ssm_association.foo", "output_location.0.s3_key_prefix", "SSMAssociation"), + ), + }, + { + Config: testAccAWSSSMAssociationBasicConfigWithOutPutLocationUpdateKeyPrefix(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-updated-%s", name)), + resource.TestCheckResourceAttr( + "aws_ssm_association.foo", "output_location.0.s3_key_prefix", "UpdatedAssociation"), + ), + }, }, }) } @@ -82,6 +148,14 @@ func TestAccAWSSSMAssociation_withScheduleExpression(t *testing.T) { "aws_ssm_association.foo", "schedule_expression", "cron(0 16 ? * TUE *)"), ), }, + { + Config: testAccAWSSSMAssociationBasicConfigWithScheduleExpressionUpdated(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSSMAssociationExists("aws_ssm_association.foo"), + resource.TestCheckResourceAttr( + "aws_ssm_association.foo", "schedule_expression", "cron(0 16 ? * WED *)"), + ), + }, }, }) } @@ -141,6 +215,92 @@ func testAccCheckAWSSSMAssociationDestroy(s *terraform.State) error { return fmt.Errorf("Default error in SSM Association Test") } +func testAccAWSSSMAssociationBasicConfigWithParameters(rName string) string { + return fmt.Sprintf(` +resource "aws_ssm_document" "foo_document" { + name = "test_document_association-%s", + document_type = "Command" + content = <<-DOC + { + "schemaVersion": "1.2", + "description": "Check ip configuration of a Linux instance.", + "parameters": { + "Directory": { + "description":"(Optional) The path to the working directory on your instance.", + "default":"", + "type": "String", + "maxChars": 4096 + } + }, + "runtimeConfig": { + "aws:runShellScript": { + "properties": [ + { + "id": "0.aws:runShellScript", + "runCommand": ["ifconfig"] + } + ] + } + } + } + DOC +} + +resource "aws_ssm_association" "foo" { + name = "${aws_ssm_document.foo_document.name}", + parameters { + Directory = "myWorkSpace" + } + targets { + key = "tag:Name" + values = ["acceptanceTest"] + } +}`, rName) +} + +func testAccAWSSSMAssociationBasicConfigWithParametersUpdated(rName string) string { + return fmt.Sprintf(` +resource "aws_ssm_document" "foo_document" { + name = "test_document_association-%s", + document_type = "Command" + content = <<-DOC + { + "schemaVersion": "1.2", + "description": "Check ip configuration of a Linux instance.", + "parameters": { + "Directory": { + "description":"(Optional) The path to the working directory on your instance.", + "default":"", + "type": "String", + "maxChars": 4096 + } + }, + "runtimeConfig": { + "aws:runShellScript": { + "properties": [ + { + "id": "0.aws:runShellScript", + "runCommand": ["ifconfig"] + } + ] + } + } + } + DOC +} + +resource "aws_ssm_association" "foo" { + name = "${aws_ssm_document.foo_document.name}", + parameters { + Directory = "myWorkSpaceUpdated" + } + targets { + key = "tag:Name" + values = ["acceptanceTest"] + } +}`, rName) +} + func testAccAWSSSMAssociationBasicConfigWithTargets(rName string) string { return fmt.Sprintf(` resource "aws_ssm_document" "foo_document" { @@ -227,6 +387,54 @@ resource "aws_ssm_association" "foo" { `, rName, rName, rName) } +func testAccAWSSSMAssociationBasicConfigWithDocumentVersion(rName string) string { + return fmt.Sprintf(` +resource "aws_security_group" "tf_test_foo" { + name = "tf_test_foo-%s" + description = "foo" + ingress { + protocol = "icmp" + from_port = -1 + to_port = -1 + cidr_blocks = ["0.0.0.0/0"] + } +} + +resource "aws_ssm_document" "foo_document" { + name = "test_document_association-%s", + document_type = "Command" + content = <