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 aws_ssm_association resource #8376

Closed
wants to merge 2 commits into from
Closed
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
1 change: 1 addition & 0 deletions builtin/providers/aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ func Provider() terraform.ResourceProvider {
"aws_security_group": resourceAwsSecurityGroup(),
"aws_security_group_rule": resourceAwsSecurityGroupRule(),
"aws_simpledb_domain": resourceAwsSimpleDBDomain(),
"aws_ssm_association": resourceAwsSsmAssociation(),
"aws_ssm_document": resourceAwsSsmDocument(),
"aws_spot_instance_request": resourceAwsSpotInstanceRequest(),
"aws_spot_fleet_request": resourceAwsSpotFleetRequest(),
Expand Down
127 changes: 127 additions & 0 deletions builtin/providers/aws/resource_aws_ssm_association.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package aws

import (
"fmt"
"log"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsSsmAssociation() *schema.Resource {
return &schema.Resource{
Create: resourceAwsSsmAssociationCreate,
Read: resourceAwsSsmAssociationRead,
Delete: resourceAwsSsmAssociationDelete,

Schema: map[string]*schema.Schema{
"instance_id": {
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
"name": {
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
"parameters": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

If this isn't updatable, then it should be marked as ForceNew: true,

ForceNew: true,
Computed: true,
},
},
}
}

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

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)),
}

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

resp, err := ssmconn.CreateAssociation(assosciationInput)

if err != nil {
return errwrap.Wrapf("[ERROR] Error creating SSM association: {{err}}", err)
}

if resp.AssociationDescription != nil {
d.SetId(*resp.AssociationDescription.Name)
} else {
return fmt.Errorf("[ERROR] AssociationDescription was nil")
}

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())

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

resp, err := ssmconn.DescribeAssociation(params)

if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a chance that someone will manually delete the association from the console? What will happen if so?

return errwrap.Wrapf("[ERROR] Error reading SSM association: {{err}}", err)
}

if resp.AssociationDescription != nil {
association := resp.AssociationDescription

d.Set("instance_id", association.InstanceId)
d.Set("name", association.Name)
d.Set("parameters", association.Parameters)

} else {
return fmt.Errorf("[ERROR] AssociationDescription was nil")
}

return nil
}

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

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)),
}

_, err := ssmconn.DeleteAssociation(params)

if err != nil {
return errwrap.Wrapf("[ERROR] Error deleting SSM association: {{err}}", err)
}

return nil
}

func expandDocumentParameters(params map[string]interface{}) map[string][]*string {
var docParams = make(map[string][]*string)
for k, v := range params {
var values []*string
values[0] = aws.String(v.(string))
docParams[k] = values
}

return docParams
}
136 changes: 136 additions & 0 deletions builtin/providers/aws/resource_aws_ssm_association_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package aws

import (
"fmt"
"testing"

"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/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSSSMAssociation_basic(t *testing.T) {
name := acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSSSMAssociationDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSSSMAssociationBasicConfig(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]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No SSM Assosciation ID is set")
}

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"]),
})

if err != nil {
return fmt.Errorf("Could not descripbe the assosciation - %s", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

what do we want the test behaviour to be if there is an empty response for DescribeAssociation?


return nil
}
}

func testAccCheckAWSSSMAssociationDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ssmconn

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_ssm_association" {
continue
}

out, err := conn.DescribeAssociation(&ssm.DescribeAssociationInput{
Copy link
Contributor

Choose a reason for hiding this comment

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

what is out is nil or has a nil Association?

Name: aws.String(rs.Primary.Attributes["name"]),
InstanceId: aws.String(rs.Primary.Attributes["instance_id"]),
})

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

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

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

func testAccAWSSSMAssociationBasicConfig(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_instance" "foo" {
# eu-west-1
ami = "ami-f77ac884"
availability_zone = "eu-west-1a"
instance_type = "t2.small"
security_groups = ["${aws_security_group.tf_test_foo.name}"]
}

resource "aws_ssm_document" "foo_document" {
name = "test_document_association-%s",
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 = "test_document_association-%s",
instance_id = "${aws_instance.foo.id}"
}
`, rName, rName, rName)
}
79 changes: 79 additions & 0 deletions website/source/docs/providers/aws/r/ssm_association.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
layout: "aws"
page_title: "AWS: aws_ssm_association"
sidebar_current: "docs-aws-resource-ssm-association"
description: |-
Assosciates an SSM Document to an instance.
---

# aws\_ssm\_association

Assosciates an SSM Document to an instance.

## Example Usage

```
resource "aws_security_group" "tf_test_foo" {
name = "tf_test_foo"
description = "foo"
ingress {
protocol = "icmp"
from_port = -1
to_port = -1
cidr_blocks = ["0.0.0.0/0"]
}
}

resource "aws_instance" "foo" {
# eu-west-1
ami = "ami-f77ac884"
availability_zone = "eu-west-1a"
instance_type = "t2.small"
security_groups = ["${aws_security_group.tf_test_foo.name}"]
}

resource "aws_ssm_document" "foo_document" {
name = "test_document_association-%s",
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 = "test_document_association-%s",
instance_id = "${aws_instance.foo.id}"
}

```

## Argument Reference

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.
* `parameters` - (Optional) Additional parameters to pass to the SSM document.

## Attributes Reference

The following attributes are exported:

* `name` - The name of the SSM document to apply.
* `instance_ids` - The instance id that the SSM document was applied to.
* `parameters` - Additional parameters passed to the SSM document.