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

d/aws_ssm_instances #23162

Merged
merged 8 commits into from
Feb 16, 2022
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
3 changes: 3 additions & 0 deletions .changelog/23162.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-data-source
aws_ssm_instances
```
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,7 @@ func Provider() *schema.Provider {
"aws_sqs_queue": sqs.DataSourceQueue(),

"aws_ssm_document": ssm.DataSourceDocument(),
"aws_ssm_instances": ssm.DataSourceInstances(),
"aws_ssm_parameter": ssm.DataSourceParameter(),
"aws_ssm_parameters_by_path": ssm.DataSourceParametersByPath(),
"aws_ssm_patch_baseline": ssm.DataSourcePatchBaseline(),
Expand Down
129 changes: 129 additions & 0 deletions internal/service/ssm/instances_data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package ssm

import (
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/flex"
)

func DataSourceInstances() *schema.Resource {
return &schema.Resource{
Read: dataSourceInstancesRead,
Schema: map[string]*schema.Schema{
"filter": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},

"values": {
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
},
},
"ids": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
}
}

func dataSourceInstancesRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).SSMConn

input := &ssm.DescribeInstanceInformationInput{}

if v, ok := d.GetOk("filter"); ok {
input.Filters = expandInstanceInformationStringFilters(v.(*schema.Set).List())
}

var results []*ssm.InstanceInformation

err := conn.DescribeInstanceInformationPages(input, func(page *ssm.DescribeInstanceInformationOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, instanceInformation := range page.InstanceInformationList {
if instanceInformation == nil {
continue
}

results = append(results, instanceInformation)
}

return !lastPage
})

if err != nil {
return fmt.Errorf("error reading SSM Instances: %w", err)
}

var instanceIDs []string

for _, r := range results {
instanceIDs = append(instanceIDs, aws.StringValue(r.InstanceId))
}

d.SetId(meta.(*conns.AWSClient).Region)
d.Set("ids", instanceIDs)

return nil
}

func expandInstanceInformationStringFilters(tfList []interface{}) []*ssm.InstanceInformationStringFilter {
if len(tfList) == 0 {
return nil
}

var apiObjects []*ssm.InstanceInformationStringFilter

for _, tfMapRaw := range tfList {
tfMap, ok := tfMapRaw.(map[string]interface{})

if !ok {
continue
}

apiObject := expandInstanceInformationStringFilter(tfMap)

if apiObject == nil {
continue
}

apiObjects = append(apiObjects, apiObject)
}

return apiObjects
}

func expandInstanceInformationStringFilter(tfMap map[string]interface{}) *ssm.InstanceInformationStringFilter {
if tfMap == nil {
return nil
}

apiObject := &ssm.InstanceInformationStringFilter{}

if v, ok := tfMap["name"].(string); ok && v != "" {
apiObject.Key = aws.String(v)
}

if v, ok := tfMap["values"].([]interface{}); ok && len(v) > 0 {
apiObject.Values = flex.ExpandStringList(v)
}

return apiObject
}
173 changes: 173 additions & 0 deletions internal/service/ssm/instances_data_source_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package ssm_test

import (
"fmt"
"log"
"testing"
"time"

"github.com/aws/aws-sdk-go/service/ssm"
sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/hashicorp/terraform-provider-aws/internal/acctest"
)

func TestAccSSMInstancesDataSource_filter(t *testing.T) {
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
dataSourceName := "data.aws_ssm_instances.test"
resourceName := "aws_instance.test"

registrationSleep := func() resource.TestCheckFunc {
return func(s *terraform.State) error {
log.Print("[DEBUG] Test: Sleep to allow SSM Agent to register EC2 instance as a managed node.")
time.Sleep(1 * time.Minute)
return nil
}
}

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: acctest.ErrorCheck(t, ssm.EndpointsID),
Providers: acctest.Providers,
Steps: []resource.TestStep{
{
Config: testAccCheckInstancesDataSourceConfig_filter_instance(rName),
},
{
Config: testAccCheckInstancesDataSourceConfig_filter_dataSource(rName),
Check: resource.ComposeAggregateTestCheckFunc(
registrationSleep(),
resource.TestCheckResourceAttr(dataSourceName, "ids.#", "1"),
resource.TestCheckResourceAttrPair(dataSourceName, "ids.0", resourceName, "id"),
),
},
},
})
}

func testAccCheckInstancesDataSourceConfig_filter_instance(rName string) string {
return acctest.ConfigCompose(
acctest.ConfigAvailableAZsNoOptInDefaultExclude(),
acctest.AvailableEC2InstanceTypeForRegion("t2.micro", "t3.micro"),
fmt.Sprintf(`
data "aws_partition" "current" {}

data "aws_iam_policy" "test" {
name = "AmazonSSMManagedInstanceCore"
}

resource "aws_iam_role" "test" {
name = %[1]q
managed_policy_arns = [data.aws_iam_policy.test.arn]

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"ec2.${data.aws_partition.current.dns_suffix}"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
}
EOF
}

resource "aws_iam_instance_profile" "test" {
name = %[1]q
role = aws_iam_role.test.name
}

resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"

tags = {
Name = %[1]q
}
}

resource "aws_subnet" "test" {
vpc_id = aws_vpc.test.id
cidr_block = "10.0.0.0/24"
availability_zone = data.aws_availability_zones.available.names[0]

map_public_ip_on_launch = true

tags = {
Name = %[1]q
}
}

resource "aws_internet_gateway" "test" {
vpc_id = aws_vpc.test.id

tags = {
Name = %[1]q
}
}

resource "aws_route_table" "test" {
vpc_id = aws_vpc.test.id

route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.test.id
}

tags = {
Name = %[1]q
}
}

resource "aws_main_route_table_association" "test" {
route_table_id = aws_route_table.test.id
vpc_id = aws_vpc.test.id
}

data "aws_ami" "test" {
most_recent = true
owners = ["amazon"]

filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
}

resource "aws_instance" "test" {
ami = data.aws_ami.test.id
instance_type = data.aws_ec2_instance_type_offering.available.instance_type
iam_instance_profile = aws_iam_instance_profile.test.name

vpc_security_group_ids = [aws_vpc.test.default_security_group_id]
subnet_id = aws_subnet.test.id
associate_public_ip_address = true
depends_on = [aws_main_route_table_association.test]

tags = {
Name = %[1]q
}
}
`, rName))
}

func testAccCheckInstancesDataSourceConfig_filter_dataSource(rName string) string {
return acctest.ConfigCompose(
testAccCheckInstancesDataSourceConfig_filter_instance(rName),
`
data "aws_ssm_instances" "test" {
filter {
name = "InstanceIds"
values = [aws_instance.test.id]
}
}
`)
}
37 changes: 37 additions & 0 deletions website/docs/d/ssm_instances.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
subcategory: "SSM"
layout: "aws"
page_title: "AWS: aws_ssm_instances"
description: |-
Get information on SSM managed instances.
---

# Data Source: aws_ssm_instances

Use this data source to get the instance IDs of SSM managed instances.

## Example Usage

```terraform
data "aws_ssm_instances" "example" {
filter {
name = "PlatformTypes"
values = ["Linux"]
}
}
```

## Argument Reference

* `filter` - (Optional) Configuration block(s) for filtering. Detailed below.

### filter Configuration Block

The following arguments are supported by the `filter` configuration block:

* `name` - (Required) The name of the filter field. Valid values can be found in the [SSM InstanceInformationStringFilter API Reference](https://docs.aws.amazon.com/systems-manager/latest/APIReference/API_InstanceInformationStringFilter.html).
* `values` - (Required) Set of values that are accepted for the given filter field. Results will be selected if any given value matches.

## Attributes Reference

* `ids` - Set of instance IDs of the matched SSM managed instances.