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_ami: warn when most_recent is true and filters are missing #40211

Merged
merged 2 commits into from
Nov 21, 2024
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/40211.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
data-source/aws_ami: Add warning diagnostic when `most_recent` is true and certain filter criteria are missing
```
27 changes: 27 additions & 0 deletions internal/service/ec2/ec2_ami_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ func dataSourceAMIRead(ctx context.Context, d *schema.ResourceData, meta interfa
input.Owners = flex.ExpandStringValueList(v.([]interface{}))
}

diags = checkMostRecentAndMissingFilters(diags, input, d.Get(names.AttrMostRecent).(bool))

images, err := findImages(ctx, conn, input)

if err != nil {
Expand Down Expand Up @@ -418,3 +420,28 @@ func flattenAMIStateReason(m *awstypes.StateReason) map[string]interface{} {
}
return s
}

// checkMostRecentAndMissingFilters appends a diagnostic if the provided configuration
// uses the most recent image and is not filtered by owner or image ID
func checkMostRecentAndMissingFilters(diags diag.Diagnostics, input *ec2.DescribeImagesInput, mostRecent bool) diag.Diagnostics {
filtered := false
for _, f := range input.Filters {
name := aws.ToString(f.Name)
if name == "image-id" || name == "owner-id" {
filtered = true
}
}

if mostRecent && len(input.Owners) == 0 && !filtered {
return append(diags, diag.Diagnostic{
Severity: diag.Warning,
Summary: "Most Recent Image Not Filtered",
Detail: `"most_recent" is set to "true" and results are not filtered by owner or image ID. ` +
"With this configuration, a third party may introduce a new image which " +
"will be returned by this data source. Consider filtering by owner or image ID " +
"to avoid this possibility.",
})
}

return diags
}
80 changes: 79 additions & 1 deletion internal/service/ec2/ec2_ami_data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,90 @@ import (
"testing"

"github.com/YakDriver/regexache"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
awstypes "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-provider-aws/internal/acctest"
tfec2 "github.com/hashicorp/terraform-provider-aws/internal/service/ec2"
"github.com/hashicorp/terraform-provider-aws/names"
)

func TestCheckMostRecentAndMissingFilters(t *testing.T) {
t.Parallel()

tests := []struct {
name string
input *ec2.DescribeImagesInput
mostRecent bool
wantDiag bool
}{
{
name: "most_recent false",
input: &ec2.DescribeImagesInput{},
mostRecent: false,
},
{
name: "image-id filter",
input: &ec2.DescribeImagesInput{
Filters: []awstypes.Filter{
{
Name: aws.String("image-id"),
Values: []string{"ami-123"},
},
},
},
mostRecent: true,
},
{
name: "owner-id filter",
input: &ec2.DescribeImagesInput{
Filters: []awstypes.Filter{
{
Name: aws.String("owner-id"),
Values: []string{"amazon"},
},
},
},
mostRecent: true,
},
{
name: "owners argument",
input: &ec2.DescribeImagesInput{
Owners: []string{"amazon"},
},
mostRecent: true,
},
{
name: "missing filters",
input: &ec2.DescribeImagesInput{
Filters: []awstypes.Filter{
{
Name: aws.String("name"), // nosemgrep:ci.literal-Name-string-test-constant,ci.literal-name-string-constant
Values: []string{"some-ami-name-*"},
},
},
},
mostRecent: true,
wantDiag: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var diags diag.Diagnostics
got := tfec2.CheckMostRecentAndMissingFilters(diags, tt.input, tt.mostRecent)
if (len(got) > 0) != tt.wantDiag {
t.Errorf("CheckMostRecentAndMissingFilters() diag = %v, wantErr %v", got, tt.wantDiag)
return
}
})
}
}

func TestAccEC2AMIDataSource_linuxInstance(t *testing.T) {
ctx := acctest.Context(t)
datasourceName := "data.aws_ami.test"
Expand Down Expand Up @@ -350,7 +428,7 @@ data "aws_ami" "test" {

filter {
name = "name"
values = ["AwsMarketPublished_IBM App Connect v12.0.12.0 and IBM MQ v9.3.0.16 with RapidDeploy 5.1.12 -422d2ddd-3288-4067-be37-4e2a69450606"]
values = ["AwsMarketPublished_IBM App Connect v13.0.1.0 and IBM MQ v9.4.0.5 with RapidDeploy 5.1.15 by-422d2ddd-3288-4067-be37-4e2a69450606"]
}
}
`
1 change: 1 addition & 0 deletions internal/service/ec2/exports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ var (
ResourceVerifiedAccessTrustProvider = resourceVerifiedAccessTrustProvider
ResourceVolumeAttachment = resourceVolumeAttachment

CheckMostRecentAndMissingFilters = checkMostRecentAndMissingFilters
CustomFiltersSchema = customFiltersSchema
CustomerGatewayConfigurationToTunnelInfo = customerGatewayConfigurationToTunnelInfo
ErrCodeDefaultSubnetAlreadyExistsInAvailabilityZone = errCodeDefaultSubnetAlreadyExistsInAvailabilityZone
Expand Down
Loading