Skip to content
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

provider/aws: Add support for targets to aws_ssm_association #14246

Merged
merged 2 commits into from
May 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 44 additions & 9 deletions builtin/providers/aws/resource_aws_ssm_association.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,46 @@ func resourceAwsSsmAssociation() *schema.Resource {
Delete: resourceAwsSsmAssociationDelete,

Schema: map[string]*schema.Schema{
"association_id": {
Type: schema.TypeString,
Computed: true,
},
"instance_id": {
Type: schema.TypeString,
ForceNew: true,
Required: true,
Optional: true,
},
"name": {
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
"parameters": &schema.Schema{
"parameters": {
Type: schema.TypeMap,
Optional: true,
ForceNew: true,
Computed: true,
},
"targets": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"key": {
Type: schema.TypeString,
Required: true,
},
"values": {
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
},
},
},
}
}
Expand All @@ -43,14 +67,21 @@ func resourceAwsSsmAssociationCreate(d *schema.ResourceData, meta interface{}) e
log.Printf("[DEBUG] SSM association create: %s", d.Id())

assosciationInput := &ssm.CreateAssociationInput{
Name: aws.String(d.Get("name").(string)),
InstanceId: aws.String(d.Get("instance_id").(string)),
Name: aws.String(d.Get("name").(string)),
}

if v, ok := d.GetOk("instance_id"); ok {
assosciationInput.InstanceId = aws.String(v.(string))
}

if v, ok := d.GetOk("parameters"); ok {
assosciationInput.Parameters = expandSSMDocumentParameters(v.(map[string]interface{}))
}

if _, ok := d.GetOk("targets"); ok {
assosciationInput.Targets = expandAwsSsmTargets(d)
}

resp, err := ssmconn.CreateAssociation(assosciationInput)
if err != nil {
return errwrap.Wrapf("[ERROR] Error creating SSM association: {{err}}", err)
Expand All @@ -61,18 +92,18 @@ func resourceAwsSsmAssociationCreate(d *schema.ResourceData, meta interface{}) e
}

d.SetId(*resp.AssociationDescription.Name)
d.Set("association_id", resp.AssociationDescription.AssociationId)

return resourceAwsSsmAssociationRead(d, meta)
}

func resourceAwsSsmAssociationRead(d *schema.ResourceData, meta interface{}) error {
ssmconn := meta.(*AWSClient).ssmconn

log.Printf("[DEBUG] Reading SSM Assosciation: %s", d.Id())
log.Printf("[DEBUG] Reading SSM Association: %s", d.Id())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


params := &ssm.DescribeAssociationInput{
Name: aws.String(d.Get("name").(string)),
InstanceId: aws.String(d.Get("instance_id").(string)),
AssociationId: aws.String(d.Get("association_id").(string)),
}

resp, err := ssmconn.DescribeAssociation(params)
Expand All @@ -88,6 +119,11 @@ func resourceAwsSsmAssociationRead(d *schema.ResourceData, meta interface{}) err
d.Set("instance_id", association.InstanceId)
d.Set("name", association.Name)
d.Set("parameters", association.Parameters)
d.Set("association_id", association.AssociationId)

if err := d.Set("targets", flattenAwsSsmTargets(association.Targets)); err != nil {
return fmt.Errorf("[DEBUG] Error setting targets error: %#v", err)
}

return nil
}
Expand All @@ -98,8 +134,7 @@ func resourceAwsSsmAssociationDelete(d *schema.ResourceData, meta interface{}) e
log.Printf("[DEBUG] Deleting SSM Assosciation: %s", d.Id())

params := &ssm.DeleteAssociationInput{
Name: aws.String(d.Get("name").(string)),
InstanceId: aws.String(d.Get("instance_id").(string)),
AssociationId: aws.String(d.Get("association_id").(string)),
}

_, err := ssmconn.DeleteAssociation(params)
Expand Down
72 changes: 62 additions & 10 deletions builtin/providers/aws/resource_aws_ssm_association_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func TestAccAWSSSMAssociation_basic(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSSSMAssociationDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAWSSSMAssociationBasicConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSSSMAssociationExists("aws_ssm_association.foo"),
Expand All @@ -29,6 +29,23 @@ func TestAccAWSSSMAssociation_basic(t *testing.T) {
})
}

func TestAccAWSSSMAssociation_withTargets(t *testing.T) {
name := acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSSSMAssociationDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSSSMAssociationBasicConfigWithTargets(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSSSMAssociationExists("aws_ssm_association.foo"),
),
},
},
})
}

func testAccCheckAWSSSMAssociationExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
Expand All @@ -43,12 +60,14 @@ func testAccCheckAWSSSMAssociationExists(n string) resource.TestCheckFunc {
conn := testAccProvider.Meta().(*AWSClient).ssmconn

_, err := conn.DescribeAssociation(&ssm.DescribeAssociationInput{
Name: aws.String(rs.Primary.Attributes["name"]),
InstanceId: aws.String(rs.Primary.Attributes["instance_id"]),
AssociationId: aws.String(rs.Primary.Attributes["association_id"]),
})

if err != nil {
return fmt.Errorf("Could not descripbe the assosciation - %s", err)
if wserr, ok := err.(awserr.Error); ok && wserr.Code() == "AssociationDoesNotExist" {
return nil
}
return err
}

return nil
Expand All @@ -64,24 +83,57 @@ func testAccCheckAWSSSMAssociationDestroy(s *terraform.State) error {
}

out, err := conn.DescribeAssociation(&ssm.DescribeAssociationInput{
Name: aws.String(rs.Primary.Attributes["name"]),
InstanceId: aws.String(rs.Primary.Attributes["instance_id"]),
AssociationId: aws.String(rs.Primary.Attributes["association_id"]),
})

if err != nil {
// InvalidDocument means it's gone, this is good
if wserr, ok := err.(awserr.Error); ok && wserr.Code() == "InvalidDocument" {
if wserr, ok := err.(awserr.Error); ok && wserr.Code() == "AssociationDoesNotExist" {
return nil
}
return err
}

if out != nil {
return fmt.Errorf("Expected AWS SSM Assosciation to be gone, but was still found")
return fmt.Errorf("Expected AWS SSM Association to be gone, but was still found")
}
}

return fmt.Errorf("Default error in SSM Assosciation Test")
return fmt.Errorf("Default error in SSM Association Test")
}

func testAccAWSSSMAssociationBasicConfigWithTargets(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": {

},
"runtimeConfig": {
"aws:runShellScript": {
"properties": [
{
"id": "0.aws:runShellScript",
"runCommand": ["ifconfig"]
}
]
}
}
}
DOC
}

resource "aws_ssm_association" "foo" {
name = "${aws_ssm_document.foo_document.name}",
targets {
key = "tag:Name"
values = ["acceptanceTest"]
}
}`, rName)
}

func testAccAWSSSMAssociationBasicConfig(rName string) string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,42 +57,6 @@ func resourceAwsSsmMaintenanceWindowTarget() *schema.Resource {
}
}

func expandAwsSsmMaintenanceWindowTargets(d *schema.ResourceData) []*ssm.Target {
var targets []*ssm.Target

targetConfig := d.Get("targets").([]interface{})

for _, tConfig := range targetConfig {
config := tConfig.(map[string]interface{})

target := &ssm.Target{
Key: aws.String(config["key"].(string)),
Values: expandStringList(config["values"].([]interface{})),
}

targets = append(targets, target)
}

return targets
}

func flattenAwsSsmMaintenanceWindowTargets(targets []*ssm.Target) []map[string]interface{} {
if len(targets) == 0 {
return nil
}

result := make([]map[string]interface{}, 0, len(targets))
target := targets[0]

t := make(map[string]interface{})
t["key"] = *target.Key
t["values"] = flattenStringList(target.Values)

result = append(result, t)

return result
}

func resourceAwsSsmMaintenanceWindowTargetCreate(d *schema.ResourceData, meta interface{}) error {
ssmconn := meta.(*AWSClient).ssmconn

Expand All @@ -101,7 +65,7 @@ func resourceAwsSsmMaintenanceWindowTargetCreate(d *schema.ResourceData, meta in
params := &ssm.RegisterTargetWithMaintenanceWindowInput{
WindowId: aws.String(d.Get("window_id").(string)),
ResourceType: aws.String(d.Get("resource_type").(string)),
Targets: expandAwsSsmMaintenanceWindowTargets(d),
Targets: expandAwsSsmTargets(d),
}

if v, ok := d.GetOk("owner_information"); ok {
Expand Down Expand Up @@ -145,7 +109,7 @@ func resourceAwsSsmMaintenanceWindowTargetRead(d *schema.ResourceData, meta inte
d.Set("window_id", t.WindowId)
d.Set("resource_type", t.ResourceType)

if err := d.Set("targets", flattenAwsSsmMaintenanceWindowTargets(t.Targets)); err != nil {
if err := d.Set("targets", flattenAwsSsmTargets(t.Targets)); err != nil {
return fmt.Errorf("[DEBUG] Error setting targets error: %#v", err)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func resourceAwsSsmMaintenanceWindowTaskCreate(d *schema.ResourceData, meta inte
TaskType: aws.String(d.Get("task_type").(string)),
ServiceRoleArn: aws.String(d.Get("service_role_arn").(string)),
TaskArn: aws.String(d.Get("task_arn").(string)),
Targets: expandAwsSsmMaintenanceWindowTargets(d),
Targets: expandAwsSsmTargets(d),
}

if v, ok := d.GetOk("priority"); ok {
Expand Down Expand Up @@ -196,7 +196,7 @@ func resourceAwsSsmMaintenanceWindowTaskRead(d *schema.ResourceData, meta interf
}
}

if err := d.Set("targets", flattenAwsSsmMaintenanceWindowTargets(t.Targets)); err != nil {
if err := d.Set("targets", flattenAwsSsmTargets(t.Targets)); err != nil {
return fmt.Errorf("[DEBUG] Error setting targets error: %#v", err)
}
}
Expand Down
37 changes: 37 additions & 0 deletions builtin/providers/aws/structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/aws/aws-sdk-go/service/rds"
"github.com/aws/aws-sdk-go/service/redshift"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/hashicorp/terraform/helper/schema"
"gopkg.in/yaml.v2"
)
Expand Down Expand Up @@ -2085,3 +2086,39 @@ func sliceContainsMap(l []interface{}, m map[string]interface{}) (int, bool) {

return -1, false
}

func expandAwsSsmTargets(d *schema.ResourceData) []*ssm.Target {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for adding these here

var targets []*ssm.Target

targetConfig := d.Get("targets").([]interface{})

for _, tConfig := range targetConfig {
config := tConfig.(map[string]interface{})

target := &ssm.Target{
Key: aws.String(config["key"].(string)),
Values: expandStringList(config["values"].([]interface{})),
}

targets = append(targets, target)
}

return targets
}

func flattenAwsSsmTargets(targets []*ssm.Target) []map[string]interface{} {
if len(targets) == 0 {
return nil
}

result := make([]map[string]interface{}, 0, len(targets))
target := targets[0]

t := make(map[string]interface{})
t["key"] = *target.Key
t["values"] = flattenStringList(target.Values)

result = append(result, t)

return result
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ description: |-
Assosciates an SSM Document to an instance.
---

# aws\_ssm\_association
# aws_ssm_association

Assosciates an SSM Document to an instance.

Expand Down Expand Up @@ -68,8 +68,9 @@ resource "aws_ssm_association" "foo" {
The following arguments are supported:

* `name` - (Required) The name of the SSM document to apply.
* `instance_id` - (Required) The instance id to apply an SSM document to.
* `instance_id` - (Optional) The instance id to apply an SSM document to.
* `parameters` - (Optional) Additional parameters to pass to the SSM document.
* `targets` - (Optional) The targets (either instances or tags). Instances are specified using Key=instanceids,Values=instanceid1,instanceid2. Tags are specified using Key=tag name,Values=tag value. Only 1 target is currently supported by AWS.

## Attributes Reference

Expand Down