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

* provider/aws: Add support for targets to aws_ssm_association

Fixes: #13975

```
% make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSSSMAssociation_'
==> Checking that code complies with gofmt requirements...
go generate $(go list ./... | grep -v /terraform/vendor/)
2017/05/05 20:32:43 Generated command/internal_plugin_list.go
TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSSSMAssociation_ -timeout 120m
=== RUN   TestAccAWSSSMAssociation_basic
--- PASS: TestAccAWSSSMAssociation_basic (139.13s)
=== RUN   TestAccAWSSSMAssociation_withTargets
--- PASS: TestAccAWSSSMAssociation_withTargets (33.19s)
PASS
ok  	github.com/hashicorp/terraform/builtin/providers/aws	172.343s
```

* Update ssm_association.html.markdown
This commit is contained in:
Paul Stack 2017-05-09 17:48:57 +03:00 committed by GitHub
parent 3230217dc5
commit 9dd4e5bcb0
6 changed files with 150 additions and 61 deletions

View File

@ -17,22 +17,46 @@ func resourceAwsSsmAssociation() *schema.Resource {
Delete: resourceAwsSsmAssociationDelete, Delete: resourceAwsSsmAssociationDelete,
Schema: map[string]*schema.Schema{ Schema: map[string]*schema.Schema{
"association_id": {
Type: schema.TypeString,
Computed: true,
},
"instance_id": { "instance_id": {
Type: schema.TypeString, Type: schema.TypeString,
ForceNew: true, ForceNew: true,
Required: true, Optional: true,
}, },
"name": { "name": {
Type: schema.TypeString, Type: schema.TypeString,
ForceNew: true, ForceNew: true,
Required: true, Required: true,
}, },
"parameters": &schema.Schema{ "parameters": {
Type: schema.TypeMap, Type: schema.TypeMap,
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
Computed: 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},
},
},
},
},
}, },
} }
} }
@ -43,14 +67,21 @@ func resourceAwsSsmAssociationCreate(d *schema.ResourceData, meta interface{}) e
log.Printf("[DEBUG] SSM association create: %s", d.Id()) log.Printf("[DEBUG] SSM association create: %s", d.Id())
assosciationInput := &ssm.CreateAssociationInput{ assosciationInput := &ssm.CreateAssociationInput{
Name: aws.String(d.Get("name").(string)), Name: aws.String(d.Get("name").(string)),
InstanceId: aws.String(d.Get("instance_id").(string)), }
if v, ok := d.GetOk("instance_id"); ok {
assosciationInput.InstanceId = aws.String(v.(string))
} }
if v, ok := d.GetOk("parameters"); ok { if v, ok := d.GetOk("parameters"); ok {
assosciationInput.Parameters = expandSSMDocumentParameters(v.(map[string]interface{})) assosciationInput.Parameters = expandSSMDocumentParameters(v.(map[string]interface{}))
} }
if _, ok := d.GetOk("targets"); ok {
assosciationInput.Targets = expandAwsSsmTargets(d)
}
resp, err := ssmconn.CreateAssociation(assosciationInput) resp, err := ssmconn.CreateAssociation(assosciationInput)
if err != nil { if err != nil {
return errwrap.Wrapf("[ERROR] Error creating SSM association: {{err}}", err) return errwrap.Wrapf("[ERROR] Error creating SSM association: {{err}}", err)
@ -61,6 +92,7 @@ func resourceAwsSsmAssociationCreate(d *schema.ResourceData, meta interface{}) e
} }
d.SetId(*resp.AssociationDescription.Name) d.SetId(*resp.AssociationDescription.Name)
d.Set("association_id", resp.AssociationDescription.AssociationId)
return resourceAwsSsmAssociationRead(d, meta) return resourceAwsSsmAssociationRead(d, meta)
} }
@ -68,11 +100,10 @@ func resourceAwsSsmAssociationCreate(d *schema.ResourceData, meta interface{}) e
func resourceAwsSsmAssociationRead(d *schema.ResourceData, meta interface{}) error { func resourceAwsSsmAssociationRead(d *schema.ResourceData, meta interface{}) error {
ssmconn := meta.(*AWSClient).ssmconn ssmconn := meta.(*AWSClient).ssmconn
log.Printf("[DEBUG] Reading SSM Assosciation: %s", d.Id()) log.Printf("[DEBUG] Reading SSM Association: %s", d.Id())
params := &ssm.DescribeAssociationInput{ params := &ssm.DescribeAssociationInput{
Name: aws.String(d.Get("name").(string)), AssociationId: aws.String(d.Get("association_id").(string)),
InstanceId: aws.String(d.Get("instance_id").(string)),
} }
resp, err := ssmconn.DescribeAssociation(params) resp, err := ssmconn.DescribeAssociation(params)
@ -88,6 +119,11 @@ func resourceAwsSsmAssociationRead(d *schema.ResourceData, meta interface{}) err
d.Set("instance_id", association.InstanceId) d.Set("instance_id", association.InstanceId)
d.Set("name", association.Name) d.Set("name", association.Name)
d.Set("parameters", association.Parameters) 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 return nil
} }
@ -98,8 +134,7 @@ func resourceAwsSsmAssociationDelete(d *schema.ResourceData, meta interface{}) e
log.Printf("[DEBUG] Deleting SSM Assosciation: %s", d.Id()) log.Printf("[DEBUG] Deleting SSM Assosciation: %s", d.Id())
params := &ssm.DeleteAssociationInput{ params := &ssm.DeleteAssociationInput{
Name: aws.String(d.Get("name").(string)), AssociationId: aws.String(d.Get("association_id").(string)),
InstanceId: aws.String(d.Get("instance_id").(string)),
} }
_, err := ssmconn.DeleteAssociation(params) _, err := ssmconn.DeleteAssociation(params)

View File

@ -19,7 +19,7 @@ func TestAccAWSSSMAssociation_basic(t *testing.T) {
Providers: testAccProviders, Providers: testAccProviders,
CheckDestroy: testAccCheckAWSSSMAssociationDestroy, CheckDestroy: testAccCheckAWSSSMAssociationDestroy,
Steps: []resource.TestStep{ Steps: []resource.TestStep{
resource.TestStep{ {
Config: testAccAWSSSMAssociationBasicConfig(name), Config: testAccAWSSSMAssociationBasicConfig(name),
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
testAccCheckAWSSSMAssociationExists("aws_ssm_association.foo"), testAccCheckAWSSSMAssociationExists("aws_ssm_association.foo"),
@ -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 { func testAccCheckAWSSSMAssociationExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error { return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n] rs, ok := s.RootModule().Resources[n]
@ -43,12 +60,14 @@ func testAccCheckAWSSSMAssociationExists(n string) resource.TestCheckFunc {
conn := testAccProvider.Meta().(*AWSClient).ssmconn conn := testAccProvider.Meta().(*AWSClient).ssmconn
_, err := conn.DescribeAssociation(&ssm.DescribeAssociationInput{ _, err := conn.DescribeAssociation(&ssm.DescribeAssociationInput{
Name: aws.String(rs.Primary.Attributes["name"]), AssociationId: aws.String(rs.Primary.Attributes["association_id"]),
InstanceId: aws.String(rs.Primary.Attributes["instance_id"]),
}) })
if err != nil { 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 return nil
@ -64,24 +83,57 @@ func testAccCheckAWSSSMAssociationDestroy(s *terraform.State) error {
} }
out, err := conn.DescribeAssociation(&ssm.DescribeAssociationInput{ out, err := conn.DescribeAssociation(&ssm.DescribeAssociationInput{
Name: aws.String(rs.Primary.Attributes["name"]), AssociationId: aws.String(rs.Primary.Attributes["association_id"]),
InstanceId: aws.String(rs.Primary.Attributes["instance_id"]),
}) })
if err != nil { if err != nil {
// InvalidDocument means it's gone, this is good if wserr, ok := err.(awserr.Error); ok && wserr.Code() == "AssociationDoesNotExist" {
if wserr, ok := err.(awserr.Error); ok && wserr.Code() == "InvalidDocument" {
return nil return nil
} }
return err return err
} }
if out != nil { 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 { func testAccAWSSSMAssociationBasicConfig(rName string) string {

View File

@ -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 { func resourceAwsSsmMaintenanceWindowTargetCreate(d *schema.ResourceData, meta interface{}) error {
ssmconn := meta.(*AWSClient).ssmconn ssmconn := meta.(*AWSClient).ssmconn
@ -101,7 +65,7 @@ func resourceAwsSsmMaintenanceWindowTargetCreate(d *schema.ResourceData, meta in
params := &ssm.RegisterTargetWithMaintenanceWindowInput{ params := &ssm.RegisterTargetWithMaintenanceWindowInput{
WindowId: aws.String(d.Get("window_id").(string)), WindowId: aws.String(d.Get("window_id").(string)),
ResourceType: aws.String(d.Get("resource_type").(string)), ResourceType: aws.String(d.Get("resource_type").(string)),
Targets: expandAwsSsmMaintenanceWindowTargets(d), Targets: expandAwsSsmTargets(d),
} }
if v, ok := d.GetOk("owner_information"); ok { if v, ok := d.GetOk("owner_information"); ok {
@ -145,7 +109,7 @@ func resourceAwsSsmMaintenanceWindowTargetRead(d *schema.ResourceData, meta inte
d.Set("window_id", t.WindowId) d.Set("window_id", t.WindowId)
d.Set("resource_type", t.ResourceType) 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) return fmt.Errorf("[DEBUG] Error setting targets error: %#v", err)
} }
} }

View File

@ -144,7 +144,7 @@ func resourceAwsSsmMaintenanceWindowTaskCreate(d *schema.ResourceData, meta inte
TaskType: aws.String(d.Get("task_type").(string)), TaskType: aws.String(d.Get("task_type").(string)),
ServiceRoleArn: aws.String(d.Get("service_role_arn").(string)), ServiceRoleArn: aws.String(d.Get("service_role_arn").(string)),
TaskArn: aws.String(d.Get("task_arn").(string)), TaskArn: aws.String(d.Get("task_arn").(string)),
Targets: expandAwsSsmMaintenanceWindowTargets(d), Targets: expandAwsSsmTargets(d),
} }
if v, ok := d.GetOk("priority"); ok { if v, ok := d.GetOk("priority"); ok {
@ -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) return fmt.Errorf("[DEBUG] Error setting targets error: %#v", err)
} }
} }

View File

@ -28,6 +28,7 @@ import (
"github.com/aws/aws-sdk-go/service/rds" "github.com/aws/aws-sdk-go/service/rds"
"github.com/aws/aws-sdk-go/service/redshift" "github.com/aws/aws-sdk-go/service/redshift"
"github.com/aws/aws-sdk-go/service/route53" "github.com/aws/aws-sdk-go/service/route53"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
@ -2085,3 +2086,39 @@ func sliceContainsMap(l []interface{}, m map[string]interface{}) (int, bool) {
return -1, false return -1, false
} }
func expandAwsSsmTargets(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 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
}

View File

@ -6,7 +6,7 @@ description: |-
Assosciates an SSM Document to an instance. Assosciates an SSM Document to an instance.
--- ---
# aws\_ssm\_association # aws_ssm_association
Assosciates an SSM Document to an instance. Assosciates an SSM Document to an instance.
@ -68,8 +68,9 @@ resource "aws_ssm_association" "foo" {
The following arguments are supported: The following arguments are supported:
* `name` - (Required) The name of the SSM document to apply. * `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. * `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 ## Attributes Reference