From 542711d8f2b15e7792dc56e5fd81e9c4ffe0a888 Mon Sep 17 00:00:00 2001 From: Anthony Wat Date: Sun, 7 Jul 2024 18:55:59 -0400 Subject: [PATCH] feat: Add resource filtering support to aws_oam_link --- .changelog/38277.txt | 7 + internal/service/oam/link.go | 145 ++++++++++- internal/service/oam/link_data_source.go | 47 +++- internal/service/oam/link_data_source_test.go | 216 ++++++++++++++++- internal/service/oam/link_test.go | 226 +++++++++++++++++- .../service/oam/links_data_source_test.go | 2 +- internal/service/oam/oam_test.go | 50 ++++ internal/service/oam/sink_data_source_test.go | 2 +- internal/service/oam/sink_policy_test.go | 4 +- internal/service/oam/sink_test.go | 6 +- .../service/oam/sinks_data_source_test.go | 2 +- website/docs/d/oam_link.html.markdown | 20 ++ website/docs/r/oam_link.html.markdown | 50 ++++ 13 files changed, 752 insertions(+), 25 deletions(-) create mode 100644 .changelog/38277.txt create mode 100644 internal/service/oam/oam_test.go diff --git a/.changelog/38277.txt b/.changelog/38277.txt new file mode 100644 index 00000000000..1c7fff45e33 --- /dev/null +++ b/.changelog/38277.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_oam_link: Add `link_configuration` argument +``` + +```release-note:enhancement +data-source/aws_oam_link: Add `link_configuration` attribute +``` \ No newline at end of file diff --git a/internal/service/oam/link.go b/internal/service/oam/link.go index 4d79a0fac2c..4fd5fb1c355 100644 --- a/internal/service/oam/link.go +++ b/internal/service/oam/link.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/enum" @@ -58,6 +59,43 @@ func ResourceLink() *schema.Resource { Required: true, ForceNew: true, }, + "link_configuration": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "log_group_configuration": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + names.AttrFilter: { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 2000), + }, + }, + }, + }, + "metric_configuration": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + names.AttrFilter: { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 2000), + }, + }, + }, + }, + }, + }, + }, "link_id": { Type: schema.TypeString, Computed: true, @@ -98,10 +136,11 @@ func resourceLinkCreate(ctx context.Context, d *schema.ResourceData, meta interf conn := meta.(*conns.AWSClient).ObservabilityAccessManagerClient(ctx) in := &oam.CreateLinkInput{ - LabelTemplate: aws.String(d.Get("label_template").(string)), - ResourceTypes: flex.ExpandStringyValueSet[types.ResourceType](d.Get("resource_types").(*schema.Set)), - SinkIdentifier: aws.String(d.Get("sink_identifier").(string)), - Tags: getTagsIn(ctx), + LabelTemplate: aws.String(d.Get("label_template").(string)), + LinkConfiguration: expandLinkConfiguration(d.Get("link_configuration").([]interface{})), + ResourceTypes: flex.ExpandStringyValueSet[types.ResourceType](d.Get("resource_types").(*schema.Set)), + SinkIdentifier: aws.String(d.Get("sink_identifier").(string)), + Tags: getTagsIn(ctx), } out, err := conn.CreateLink(ctx, in) @@ -137,6 +176,7 @@ func resourceLinkRead(ctx context.Context, d *schema.ResourceData, meta interfac d.Set(names.AttrARN, out.Arn) d.Set("label", out.Label) d.Set("label_template", out.LabelTemplate) + d.Set("link_configuration", flattenLinkConfiguration(out.LinkConfiguration)) d.Set("link_id", out.Id) d.Set("resource_types", flex.FlattenStringValueList(out.ResourceTypes)) d.Set("sink_arn", out.SinkArn) @@ -155,8 +195,13 @@ func resourceLinkUpdate(ctx context.Context, d *schema.ResourceData, meta interf Identifier: aws.String(d.Id()), } - if d.HasChanges("resource_types") { + if d.HasChanges("resource_types", "link_configuration") { in.ResourceTypes = flex.ExpandStringyValueSet[types.ResourceType](d.Get("resource_types").(*schema.Set)) + + if d.HasChanges("link_configuration") { + in.LinkConfiguration = expandLinkConfiguration(d.Get("link_configuration").([]interface{})) + } + update = true } @@ -216,3 +261,93 @@ func findLinkByID(ctx context.Context, conn *oam.Client, id string) (*oam.GetLin return out, nil } + +func expandLinkConfiguration(l []interface{}) *types.LinkConfiguration { + if len(l) == 0 || l[0] == nil { + return nil + } + + config := &types.LinkConfiguration{} + + m := l[0].(map[string]interface{}) + if v, ok := m["log_group_configuration"]; ok { + config.LogGroupConfiguration = expandLogGroupConfiguration(v.([]interface{})) + } + if v, ok := m["metric_configuration"]; ok { + config.MetricConfiguration = expandMetricConfiguration(v.([]interface{})) + } + + return config +} + +func expandLogGroupConfiguration(l []interface{}) *types.LogGroupConfiguration { + if len(l) == 0 || l[0] == nil { + return nil + } + + config := &types.LogGroupConfiguration{} + + m := l[0].(map[string]interface{}) + if v, ok := m[names.AttrFilter]; ok && v != "" { + config.Filter = aws.String(v.(string)) + } + + return config +} + +func expandMetricConfiguration(l []interface{}) *types.MetricConfiguration { + if len(l) == 0 || l[0] == nil { + return nil + } + + config := &types.MetricConfiguration{} + + m := l[0].(map[string]interface{}) + if v, ok := m[names.AttrFilter]; ok && v != "" { + config.Filter = aws.String(v.(string)) + } + + return config +} + +func flattenLinkConfiguration(a *types.LinkConfiguration) []interface{} { + if a == nil { + return []interface{}{} + } + m := map[string]interface{}{} + + if a.LogGroupConfiguration != nil { + m["log_group_configuration"] = flattenLogGroupConfiguration(a.LogGroupConfiguration) + } + if a.MetricConfiguration != nil { + m["metric_configuration"] = flattenMetricConfiguration(a.MetricConfiguration) + } + + return []interface{}{m} +} + +func flattenLogGroupConfiguration(a *types.LogGroupConfiguration) []interface{} { + if a == nil { + return []interface{}{} + } + m := map[string]interface{}{} + + if a.Filter != nil { + m[names.AttrFilter] = aws.ToString(a.Filter) + } + + return []interface{}{m} +} + +func flattenMetricConfiguration(a *types.MetricConfiguration) []interface{} { + if a == nil { + return []interface{}{} + } + m := map[string]interface{}{} + + if a.Filter != nil { + m[names.AttrFilter] = aws.ToString(a.Filter) + } + + return []interface{}{m} +} diff --git a/internal/service/oam/link_data_source.go b/internal/service/oam/link_data_source.go index af8b0ff3620..e4739a74bd0 100644 --- a/internal/service/oam/link_data_source.go +++ b/internal/service/oam/link_data_source.go @@ -26,22 +26,54 @@ func DataSourceLink() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "link_id": { + "label": { Type: schema.TypeString, Computed: true, }, - "link_identifier": { + "label_template": { Type: schema.TypeString, - Required: true, + Computed: true, }, - "label": { - Type: schema.TypeString, + "link_configuration": { + Type: schema.TypeList, Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "log_group_configuration": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + names.AttrFilter: { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "metric_configuration": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + names.AttrFilter: { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, }, - "label_template": { + "link_id": { Type: schema.TypeString, Computed: true, }, + "link_identifier": { + Type: schema.TypeString, + Required: true, + }, "resource_types": { Type: schema.TypeSet, Computed: true, @@ -76,9 +108,10 @@ func dataSourceLinkRead(ctx context.Context, d *schema.ResourceData, meta interf d.SetId(aws.ToString(out.Arn)) d.Set(names.AttrARN, out.Arn) - d.Set("link_id", out.Id) d.Set("label", out.Label) d.Set("label_template", out.LabelTemplate) + d.Set("link_configuration", flattenLinkConfiguration(out.LinkConfiguration)) + d.Set("link_id", out.Id) d.Set("resource_types", flex.FlattenStringValueList(out.ResourceTypes)) d.Set("sink_arn", out.SinkArn) diff --git a/internal/service/oam/link_data_source_test.go b/internal/service/oam/link_data_source_test.go index 0cd91e3462c..779424fcad3 100644 --- a/internal/service/oam/link_data_source_test.go +++ b/internal/service/oam/link_data_source_test.go @@ -14,7 +14,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccObservabilityAccessManagerLinkDataSource_basic(t *testing.T) { +func testAccObservabilityAccessManagerLinkDataSource_basic(t *testing.T) { if testing.Short() { t.Skip("skipping long-running test in short mode") } @@ -51,6 +51,90 @@ func TestAccObservabilityAccessManagerLinkDataSource_basic(t *testing.T) { }) } +func testAccObservabilityAccessManagerLinkDataSource_logGroupConfiguration(t *testing.T) { + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + dataSourceName := "data.aws_oam_link.test" + filter := "LogGroupName LIKE 'aws/lambda/%' OR LogGroupName LIKE 'AWSLogs%'" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckAlternateAccount(t) + acctest.PreCheckPartitionHasService(t, names.ObservabilityAccessManagerEndpointID) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.ObservabilityAccessManagerServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5FactoriesAlternate(ctx, t), + Steps: []resource.TestStep{ + { + Config: testAccLinkDataSourceConfig_logGroupConfiguration(rName, filter), + Check: resource.ComposeTestCheckFunc( + acctest.MatchResourceAttrRegionalARN(dataSourceName, names.AttrARN, "oam", regexache.MustCompile(`link/+.`)), + resource.TestCheckResourceAttrSet(dataSourceName, "label"), + resource.TestCheckResourceAttr(dataSourceName, "label_template", "$AccountName"), + resource.TestCheckResourceAttr(dataSourceName, "link_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(dataSourceName, "link_configuration.0.log_group_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(dataSourceName, "link_configuration.0.log_group_configuration.0.filter", filter), + resource.TestCheckResourceAttr(dataSourceName, "link_configuration.0.metric_configuration.#", acctest.Ct0), + resource.TestCheckResourceAttrSet(dataSourceName, "link_id"), + resource.TestCheckResourceAttr(dataSourceName, "resource_types.#", acctest.Ct1), + resource.TestCheckResourceAttr(dataSourceName, "resource_types.0", "AWS::Logs::LogGroup"), + resource.TestCheckResourceAttrSet(dataSourceName, "sink_arn"), + resource.TestCheckResourceAttr(dataSourceName, acctest.CtTagsPercent, acctest.Ct1), + resource.TestCheckResourceAttr(dataSourceName, acctest.CtTagsKey1, acctest.CtValue1), + ), + }, + }, + }) +} + +func testAccObservabilityAccessManagerLinkDataSource_metricConfiguration(t *testing.T) { + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + dataSourceName := "data.aws_oam_link.test" + filter := "Namespace IN ('AWS/EC2', 'AWS/ELB', 'AWS/S3')" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckAlternateAccount(t) + acctest.PreCheckPartitionHasService(t, names.ObservabilityAccessManagerEndpointID) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.ObservabilityAccessManagerServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5FactoriesAlternate(ctx, t), + Steps: []resource.TestStep{ + { + Config: testAccLinkDataSourceConfig_metricConfiguration(rName, filter), + Check: resource.ComposeTestCheckFunc( + acctest.MatchResourceAttrRegionalARN(dataSourceName, names.AttrARN, "oam", regexache.MustCompile(`link/+.`)), + resource.TestCheckResourceAttrSet(dataSourceName, "label"), + resource.TestCheckResourceAttr(dataSourceName, "label_template", "$AccountName"), + resource.TestCheckResourceAttr(dataSourceName, "link_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(dataSourceName, "link_configuration.0.log_group_configuration.#", acctest.Ct0), + resource.TestCheckResourceAttr(dataSourceName, "link_configuration.0.metric_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(dataSourceName, "link_configuration.0.metric_configuration.0.filter", filter), + resource.TestCheckResourceAttrSet(dataSourceName, "link_id"), + resource.TestCheckResourceAttr(dataSourceName, "resource_types.#", acctest.Ct1), + resource.TestCheckResourceAttr(dataSourceName, "resource_types.0", "AWS::CloudWatch::Metric"), + resource.TestCheckResourceAttrSet(dataSourceName, "sink_arn"), + resource.TestCheckResourceAttr(dataSourceName, acctest.CtTagsPercent, acctest.Ct1), + resource.TestCheckResourceAttr(dataSourceName, acctest.CtTagsKey1, acctest.CtValue1), + ), + }, + }, + }) +} + func testAccLinkDataSourceConfig_basic(rName string) string { return acctest.ConfigCompose( acctest.ConfigAlternateAccountProvider(), @@ -110,3 +194,133 @@ data aws_oam_link "test" { } `, rName)) } + +func testAccLinkDataSourceConfig_logGroupConfiguration(rName, filter string) string { + return acctest.ConfigCompose( + acctest.ConfigAlternateAccountProvider(), + fmt.Sprintf(` +data "aws_caller_identity" "source" {} +data "aws_partition" "source" {} + +data "aws_caller_identity" "monitoring" { + provider = "awsalternate" +} +data "aws_partition" "monitoring" { + provider = "awsalternate" +} + +resource "aws_oam_sink" "test" { + provider = "awsalternate" + + name = %[1]q +} + +resource "aws_oam_sink_policy" "test" { + provider = "awsalternate" + + sink_identifier = aws_oam_sink.test.id + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = ["oam:CreateLink", "oam:UpdateLink"] + Effect = "Allow" + Resource = "*" + Principal = { + "AWS" = "arn:${data.aws_partition.source.partition}:iam::${data.aws_caller_identity.source.account_id}:root" + } + Condition = { + "ForAnyValue:StringEquals" = { + "oam:ResourceTypes" = ["AWS::CloudWatch::Metric", "AWS::Logs::LogGroup"] + } + } + } + ] + }) +} + +resource "aws_oam_link" "test" { + label_template = "$AccountName" + link_configuration { + log_group_configuration { + filter = %[2]q + } + } + resource_types = ["AWS::Logs::LogGroup"] + sink_identifier = aws_oam_sink.test.id + + tags = { + key1 = "value1" + } +} + +data aws_oam_link "test" { + link_identifier = aws_oam_link.test.id +} +`, rName, filter)) +} + +func testAccLinkDataSourceConfig_metricConfiguration(rName, filter string) string { + return acctest.ConfigCompose( + acctest.ConfigAlternateAccountProvider(), + fmt.Sprintf(` +data "aws_caller_identity" "source" {} +data "aws_partition" "source" {} + +data "aws_caller_identity" "monitoring" { + provider = "awsalternate" +} +data "aws_partition" "monitoring" { + provider = "awsalternate" +} + +resource "aws_oam_sink" "test" { + provider = "awsalternate" + + name = %[1]q +} + +resource "aws_oam_sink_policy" "test" { + provider = "awsalternate" + + sink_identifier = aws_oam_sink.test.id + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = ["oam:CreateLink", "oam:UpdateLink"] + Effect = "Allow" + Resource = "*" + Principal = { + "AWS" = "arn:${data.aws_partition.source.partition}:iam::${data.aws_caller_identity.source.account_id}:root" + } + Condition = { + "ForAnyValue:StringEquals" = { + "oam:ResourceTypes" = ["AWS::CloudWatch::Metric", "AWS::Logs::LogGroup"] + } + } + } + ] + }) +} + +resource "aws_oam_link" "test" { + label_template = "$AccountName" + link_configuration { + metric_configuration { + filter = %[2]q + } + } + resource_types = ["AWS::CloudWatch::Metric"] + sink_identifier = aws_oam_sink.test.id + + tags = { + key1 = "value1" + } +} + +data aws_oam_link "test" { + link_identifier = aws_oam_link.test.id +} +`, rName, filter)) +} diff --git a/internal/service/oam/link_test.go b/internal/service/oam/link_test.go index 8314997face..673748efb99 100644 --- a/internal/service/oam/link_test.go +++ b/internal/service/oam/link_test.go @@ -23,7 +23,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccObservabilityAccessManagerLink_basic(t *testing.T) { +func testAccObservabilityAccessManagerLink_basic(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -67,7 +67,7 @@ func TestAccObservabilityAccessManagerLink_basic(t *testing.T) { }) } -func TestAccObservabilityAccessManagerLink_disappears(t *testing.T) { +func testAccObservabilityAccessManagerLink_disappears(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -100,7 +100,7 @@ func TestAccObservabilityAccessManagerLink_disappears(t *testing.T) { }) } -func TestAccObservabilityAccessManagerLink_update(t *testing.T) { +func testAccObservabilityAccessManagerLink_update(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -159,7 +159,7 @@ func TestAccObservabilityAccessManagerLink_update(t *testing.T) { }) } -func TestAccObservabilityAccessManagerLink_tags(t *testing.T) { +func testAccObservabilityAccessManagerLink_tags(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -214,6 +214,110 @@ func TestAccObservabilityAccessManagerLink_tags(t *testing.T) { }) } +func testAccObservabilityAccessManagerLink_logGroupConfiguration(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var link oam.GetLinkOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_oam_link.test" + filter1 := "LogGroupName LIKE 'aws/lambda/%' OR LogGroupName LIKE 'AWSLogs%'" + filter2 := "LogGroupName NOT IN ('Private-Log-Group', 'Private-Log-Group-2')" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckAlternateAccount(t) + acctest.PreCheckPartitionHasService(t, names.ObservabilityAccessManagerEndpointID) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.ObservabilityAccessManagerServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5FactoriesAlternate(ctx, t), + CheckDestroy: testAccCheckLinkDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccLinkConfig_logGroupConfiguration(rName, filter1), + Check: resource.ComposeTestCheckFunc( + testAccCheckLinkExists(ctx, resourceName, &link), + resource.TestCheckResourceAttr(resourceName, "link_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "link_configuration.0.log_group_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "link_configuration.0.metric_configuration.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "link_configuration.0.log_group_configuration.0.filter", filter1), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccLinkConfig_logGroupConfiguration(rName, filter2), + Check: resource.ComposeTestCheckFunc( + testAccCheckLinkExists(ctx, resourceName, &link), + resource.TestCheckResourceAttr(resourceName, "link_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "link_configuration.0.log_group_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "link_configuration.0.metric_configuration.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "link_configuration.0.log_group_configuration.0.filter", filter2), + ), + }, + }, + }) +} + +func testAccObservabilityAccessManagerLink_metricConfiguration(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var link oam.GetLinkOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_oam_link.test" + filter1 := "Namespace IN ('AWS/EC2', 'AWS/ELB', 'AWS/S3')" + filter2 := "Namespace NOT LIKE 'AWS/%'" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckAlternateAccount(t) + acctest.PreCheckPartitionHasService(t, names.ObservabilityAccessManagerEndpointID) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.ObservabilityAccessManagerServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5FactoriesAlternate(ctx, t), + CheckDestroy: testAccCheckLinkDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccLinkConfig_metricConfiguration(rName, filter1), + Check: resource.ComposeTestCheckFunc( + testAccCheckLinkExists(ctx, resourceName, &link), + resource.TestCheckResourceAttr(resourceName, "link_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "link_configuration.0.log_group_configuration.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "link_configuration.0.metric_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "link_configuration.0.metric_configuration.0.filter", filter1), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccLinkConfig_metricConfiguration(rName, filter2), + Check: resource.ComposeTestCheckFunc( + testAccCheckLinkExists(ctx, resourceName, &link), + resource.TestCheckResourceAttr(resourceName, "link_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "link_configuration.0.log_group_configuration.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "link_configuration.0.metric_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "link_configuration.0.metric_configuration.0.filter", filter2), + ), + }, + }, + }) +} + func testAccCheckLinkDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).ObservabilityAccessManagerClient(ctx) @@ -484,3 +588,117 @@ resource "aws_oam_link" "test" { } `, rName, tag1Key, tag1Value, tag2Key, tag2Value)) } + +func testAccLinkConfig_logGroupConfiguration(rName, filter string) string { + return acctest.ConfigCompose( + acctest.ConfigAlternateAccountProvider(), + fmt.Sprintf(` +data "aws_caller_identity" "source" {} +data "aws_partition" "source" {} + +data "aws_caller_identity" "monitoring" { + provider = "awsalternate" +} +data "aws_partition" "monitoring" { + provider = "awsalternate" +} + +resource "aws_oam_sink" "test" { + provider = "awsalternate" + + name = %[1]q +} + +resource "aws_oam_sink_policy" "test" { + provider = "awsalternate" + + sink_identifier = aws_oam_sink.test.id + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = ["oam:CreateLink", "oam:UpdateLink"] + Effect = "Allow" + Resource = "*" + Principal = { + "AWS" = "arn:${data.aws_partition.source.partition}:iam::${data.aws_caller_identity.source.account_id}:root" + } + Condition = { + "ForAnyValue:StringEquals" = { + "oam:ResourceTypes" = ["AWS::CloudWatch::Metric", "AWS::Logs::LogGroup"] + } + } + } + ] + }) +} + +resource "aws_oam_link" "test" { + label_template = "$AccountName" + link_configuration { + log_group_configuration { + filter = %[2]q + } + } + resource_types = ["AWS::Logs::LogGroup"] + sink_identifier = aws_oam_sink.test.id +} +`, rName, filter)) +} + +func testAccLinkConfig_metricConfiguration(rName, filter string) string { + return acctest.ConfigCompose( + acctest.ConfigAlternateAccountProvider(), + fmt.Sprintf(` +data "aws_caller_identity" "source" {} +data "aws_partition" "source" {} + +data "aws_caller_identity" "monitoring" { + provider = "awsalternate" +} +data "aws_partition" "monitoring" { + provider = "awsalternate" +} + +resource "aws_oam_sink" "test" { + provider = "awsalternate" + + name = %[1]q +} + +resource "aws_oam_sink_policy" "test" { + provider = "awsalternate" + + sink_identifier = aws_oam_sink.test.id + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = ["oam:CreateLink", "oam:UpdateLink"] + Effect = "Allow" + Resource = "*" + Principal = { + "AWS" = "arn:${data.aws_partition.source.partition}:iam::${data.aws_caller_identity.source.account_id}:root" + } + Condition = { + "ForAnyValue:StringEquals" = { + "oam:ResourceTypes" = ["AWS::CloudWatch::Metric", "AWS::Logs::LogGroup"] + } + } + } + ] + }) +} + +resource "aws_oam_link" "test" { + label_template = "$AccountName" + link_configuration { + metric_configuration { + filter = %[2]q + } + } + resource_types = ["AWS::CloudWatch::Metric"] + sink_identifier = aws_oam_sink.test.id +} +`, rName, filter)) +} diff --git a/internal/service/oam/links_data_source_test.go b/internal/service/oam/links_data_source_test.go index b8ff6e59582..8381ed46512 100644 --- a/internal/service/oam/links_data_source_test.go +++ b/internal/service/oam/links_data_source_test.go @@ -14,7 +14,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccObservabilityAccessManagerLinksDataSource_basic(t *testing.T) { +func testAccObservabilityAccessManagerLinksDataSource_basic(t *testing.T) { if testing.Short() { t.Skip("skipping long-running test in short mode") } diff --git a/internal/service/oam/oam_test.go b/internal/service/oam/oam_test.go new file mode 100644 index 00000000000..2a599ed9664 --- /dev/null +++ b/internal/service/oam/oam_test.go @@ -0,0 +1,50 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package oam_test + +import ( + "testing" + + "github.com/hashicorp/terraform-provider-aws/internal/acctest" +) + +func TestAccObservabilityAccessManager_serial(t *testing.T) { + t.Parallel() + + testCases := map[string]map[string]func(t *testing.T){ + "Link": { + acctest.CtBasic: testAccObservabilityAccessManagerLink_basic, + acctest.CtDisappears: testAccObservabilityAccessManagerLink_disappears, + "update": testAccObservabilityAccessManagerLink_update, + "tags": testAccObservabilityAccessManagerLink_tags, + "logGroupConfiguration": testAccObservabilityAccessManagerLink_logGroupConfiguration, + "metricConfiguration": testAccObservabilityAccessManagerLink_metricConfiguration, + }, + "LinkDataSource": { + acctest.CtBasic: testAccObservabilityAccessManagerLinkDataSource_basic, + "logGroupConfiguration": testAccObservabilityAccessManagerLinkDataSource_logGroupConfiguration, + "metricConfiguration": testAccObservabilityAccessManagerLinkDataSource_metricConfiguration, + }, + "LinksDataSource": { + acctest.CtBasic: testAccObservabilityAccessManagerLinksDataSource_basic, + }, + "Sink": { + acctest.CtBasic: testAccObservabilityAccessManagerSink_basic, + acctest.CtDisappears: testAccObservabilityAccessManagerSink_disappears, + "tags": testAccObservabilityAccessManagerSink_tags, + }, + "SinkDataSource": { + acctest.CtBasic: testAccObservabilityAccessManagerSinkDataSource_basic, + }, + "SinkPolicy": { + acctest.CtBasic: testAccObservabilityAccessManagerSinkPolicy_basic, + "update": testAccObservabilityAccessManagerSinkPolicy_update, + }, + "SinksDataSource": { + acctest.CtBasic: testAccObservabilityAccessManagerSinksDataSource_basic, + }, + } + + acctest.RunSerialTests2Levels(t, testCases, 0) +} diff --git a/internal/service/oam/sink_data_source_test.go b/internal/service/oam/sink_data_source_test.go index fd0dafe4826..870eaaa01e1 100644 --- a/internal/service/oam/sink_data_source_test.go +++ b/internal/service/oam/sink_data_source_test.go @@ -15,7 +15,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccObservabilityAccessManagerSinkDataSource_basic(t *testing.T) { +func testAccObservabilityAccessManagerSinkDataSource_basic(t *testing.T) { if testing.Short() { t.Skip("skipping long-running test in short mode") } diff --git a/internal/service/oam/sink_policy_test.go b/internal/service/oam/sink_policy_test.go index 87230425a45..eb4136cc23c 100644 --- a/internal/service/oam/sink_policy_test.go +++ b/internal/service/oam/sink_policy_test.go @@ -24,7 +24,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccObservabilityAccessManagerSinkPolicy_basic(t *testing.T) { +func testAccObservabilityAccessManagerSinkPolicy_basic(t *testing.T) { if testing.Short() { t.Skip("skipping long-running test in short mode") } @@ -83,7 +83,7 @@ func TestAccObservabilityAccessManagerSinkPolicy_basic(t *testing.T) { }) } -func TestAccObservabilityAccessManagerSinkPolicy_update(t *testing.T) { +func testAccObservabilityAccessManagerSinkPolicy_update(t *testing.T) { if testing.Short() { t.Skip("skipping long-running test in short mode") } diff --git a/internal/service/oam/sink_test.go b/internal/service/oam/sink_test.go index 3ff84579dde..7d0ce3854bf 100644 --- a/internal/service/oam/sink_test.go +++ b/internal/service/oam/sink_test.go @@ -23,7 +23,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccObservabilityAccessManagerSink_basic(t *testing.T) { +func testAccObservabilityAccessManagerSink_basic(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -62,7 +62,7 @@ func TestAccObservabilityAccessManagerSink_basic(t *testing.T) { }) } -func TestAccObservabilityAccessManagerSink_disappears(t *testing.T) { +func testAccObservabilityAccessManagerSink_disappears(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -94,7 +94,7 @@ func TestAccObservabilityAccessManagerSink_disappears(t *testing.T) { }) } -func TestAccObservabilityAccessManagerSink_tags(t *testing.T) { +func testAccObservabilityAccessManagerSink_tags(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") diff --git a/internal/service/oam/sinks_data_source_test.go b/internal/service/oam/sinks_data_source_test.go index a2f3e26a973..48fb5725652 100644 --- a/internal/service/oam/sinks_data_source_test.go +++ b/internal/service/oam/sinks_data_source_test.go @@ -14,7 +14,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccObservabilityAccessManagerSinksDataSource_basic(t *testing.T) { +func testAccObservabilityAccessManagerSinksDataSource_basic(t *testing.T) { if testing.Short() { t.Skip("skipping long-running test in short mode") } diff --git a/website/docs/d/oam_link.html.markdown b/website/docs/d/oam_link.html.markdown index 5ce026dd80e..2c197ba6162 100644 --- a/website/docs/d/oam_link.html.markdown +++ b/website/docs/d/oam_link.html.markdown @@ -34,6 +34,26 @@ This data source exports the following attributes in addition to the arguments a * `id` - ARN of the link. * `label` - Label that is assigned to this link. * `label_template` - Human-readable name used to identify this source account when you are viewing data from it in the monitoring account. +* `link_configuration` - Configuration for creating filters that specify that only some metric namespaces or log groups are to be shared from the source account to the monitoring account. See [`link_configuration` Block](#link_configuration-block) for details. * `link_id` - ID string that AWS generated as part of the link ARN. * `resource_types` - Types of data that the source account shares with the monitoring account. * `sink_arn` - ARN of the sink that is used for this link. + +### `link_configuration` Block + +The `link_configuration` configuration block supports the following arguments: + +* `log_group_configuration` - Configuration for filtering which log groups are to send log events from the source account to the monitoring account. See [`log_group_configuration` Block](#log_group_configuration-block) for details. +* `metric_configuration` - Configuration for filtering which metric namespaces are to be shared from the source account to the monitoring account. See [`metric_configuration` Block](#metric_configuration-block) for details. + +### `log_group_configuration` Block + +The `log_group_configuration` configuration block supports the following arguments: + +* `filter` - Filter string that specifies which log groups are to share their log events with the monitoring account. See [LogGroupConfiguration](https://docs.aws.amazon.com/OAM/latest/APIReference/API_LogGroupConfiguration.html) for details. + +### `metric_configuration` Block + +The `metric_configuration` configuration block supports the following arguments: + +* `filter` - Filter string that specifies which metrics are to be shared with the monitoring account. See [MetricConfiguration](https://docs.aws.amazon.com/OAM/latest/APIReference/API_MetricConfiguration.html) for details. diff --git a/website/docs/r/oam_link.html.markdown b/website/docs/r/oam_link.html.markdown index 1f71a1a99d1..298aef9caf1 100644 --- a/website/docs/r/oam_link.html.markdown +++ b/website/docs/r/oam_link.html.markdown @@ -25,6 +25,36 @@ resource "aws_oam_link" "example" { } ``` +### Log Group Filtering + +```terraform +resource "aws_oam_link" "example" { + label_template = "$AccountName" + link_configuration { + log_group_configuration { + filter = "LogGroupName LIKE 'aws/lambda/%' OR LogGroupName LIKE 'AWSLogs%'" + } + } + resource_types = ["AWS::Logs::LogGroup"] + sink_identifier = aws_oam_sink.test.id +} +``` + +### Metric Filtering + +```terraform +resource "aws_oam_link" "example" { + label_template = "$AccountName" + link_configuration { + metric_configuration { + filter = "Namespace IN ('AWS/EC2', 'AWS/ELB', 'AWS/S3')" + } + } + resource_types = ["AWS::CloudWatch::Metric"] + sink_identifier = aws_oam_sink.test.id +} +``` + ## Argument Reference The following arguments are required: @@ -35,8 +65,28 @@ The following arguments are required: The following arguments are optional: +* `link_configuration` - (Optional) Configuration for creating filters that specify that only some metric namespaces or log groups are to be shared from the source account to the monitoring account. See [`link_configuration` Block](#link_configuration-block) for details. * `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. +### `link_configuration` Block + +The `link_configuration` configuration block supports the following arguments: + +* `log_group_configuration` - (Optional) Configuration for filtering which log groups are to send log events from the source account to the monitoring account. See [`log_group_configuration` Block](#log_group_configuration-block) for details. +* `metric_configuration` - (Optional) Configuration for filtering which metric namespaces are to be shared from the source account to the monitoring account. See [`metric_configuration` Block](#metric_configuration-block) for details. + +### `log_group_configuration` Block + +The `log_group_configuration` configuration block supports the following arguments: + +* `filter` - (Required) Filter string that specifies which log groups are to share their log events with the monitoring account. See [LogGroupConfiguration](https://docs.aws.amazon.com/OAM/latest/APIReference/API_LogGroupConfiguration.html) for details. + +### `metric_configuration` Block + +The `metric_configuration` configuration block supports the following arguments: + +* `filter` - (Required) Filter string that specifies which metrics are to be shared with the monitoring account. See [MetricConfiguration](https://docs.aws.amazon.com/OAM/latest/APIReference/API_MetricConfiguration.html) for details. + ## Attribute Reference This resource exports the following attributes in addition to the arguments above: