diff --git a/.changelog/25913.txt b/.changelog/25913.txt new file mode 100644 index 000000000000..0beaaf8fb77a --- /dev/null +++ b/.changelog/25913.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_flow_log: Add `transit_gateway_id` and `transit_gateway_attachment_id` arguments +``` \ No newline at end of file diff --git a/internal/service/ec2/vpc_flow_log.go b/internal/service/ec2/vpc_flow_log.go index 4567efbfc67b..fbd05b43609a 100644 --- a/internal/service/ec2/vpc_flow_log.go +++ b/internal/service/ec2/vpc_flow_log.go @@ -66,7 +66,7 @@ func ResourceFlowLog() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - ExactlyOneOf: []string{"eni_id", "subnet_id", "vpc_id"}, + ExactlyOneOf: []string{"eni_id", "subnet_id", "vpc_id", "transit_gateway_id", "transit_gateway_attachment_id"}, }, "iam_role_arn": { Type: schema.TypeString, @@ -114,21 +114,33 @@ func ResourceFlowLog() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - ExactlyOneOf: []string{"eni_id", "subnet_id", "vpc_id"}, + ExactlyOneOf: []string{"eni_id", "subnet_id", "vpc_id", "transit_gateway_id", "transit_gateway_attachment_id"}, }, "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), "traffic_type": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, ValidateFunc: validation.StringInSlice(ec2.TrafficType_Values(), false), }, + "transit_gateway_attachment_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ExactlyOneOf: []string{"eni_id", "subnet_id", "vpc_id", "transit_gateway_id", "transit_gateway_attachment_id"}, + }, + "transit_gateway_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ExactlyOneOf: []string{"eni_id", "subnet_id", "vpc_id", "transit_gateway_id", "transit_gateway_attachment_id"}, + }, "vpc_id": { Type: schema.TypeString, Optional: true, ForceNew: true, - ExactlyOneOf: []string{"eni_id", "subnet_id", "vpc_id"}, + ExactlyOneOf: []string{"eni_id", "subnet_id", "vpc_id", "transit_gateway_id", "transit_gateway_attachment_id"}, }, }, @@ -151,6 +163,14 @@ func resourceLogFlowCreate(d *schema.ResourceData, meta interface{}) error { ID: d.Get("vpc_id").(string), Type: ec2.FlowLogsResourceTypeVpc, }, + { + ID: d.Get("transit_gateway_id").(string), + Type: ec2.FlowLogsResourceTypeTransitGateway, + }, + { + ID: d.Get("transit_gateway_attachment_id").(string), + Type: ec2.FlowLogsResourceTypeTransitGatewayAttachment, + }, { ID: d.Get("subnet_id").(string), Type: ec2.FlowLogsResourceTypeSubnet, @@ -171,7 +191,12 @@ func resourceLogFlowCreate(d *schema.ResourceData, meta interface{}) error { LogDestinationType: aws.String(d.Get("log_destination_type").(string)), ResourceIds: aws.StringSlice([]string{resourceID}), ResourceType: aws.String(resourceType), - TrafficType: aws.String(d.Get("traffic_type").(string)), + } + + if resourceType != ec2.FlowLogsResourceTypeTransitGateway && resourceType != ec2.FlowLogsResourceTypeTransitGatewayAttachment { + if v, ok := d.GetOk("traffic_type"); ok { + input.TrafficType = aws.String(v.(string)) + } } if v, ok := d.GetOk("destination_options"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { @@ -242,7 +267,9 @@ func resourceLogFlowRead(d *schema.ResourceData, meta interface{}) error { AccountID: meta.(*conns.AWSClient).AccountID, Resource: fmt.Sprintf("vpc-flow-log/%s", d.Id()), }.String() + d.Set("arn", arn) + if fl.DestinationOptions != nil { if err := d.Set("destination_options", []interface{}{flattenDestinationOptionsResponse(fl.DestinationOptions)}); err != nil { return fmt.Errorf("error setting destination_options: %w", err) @@ -250,23 +277,33 @@ func resourceLogFlowRead(d *schema.ResourceData, meta interface{}) error { } else { d.Set("destination_options", nil) } + d.Set("iam_role_arn", fl.DeliverLogsPermissionArn) d.Set("log_destination", fl.LogDestination) d.Set("log_destination_type", fl.LogDestinationType) d.Set("log_format", fl.LogFormat) d.Set("log_group_name", fl.LogGroupName) d.Set("max_aggregation_interval", fl.MaxAggregationInterval) - d.Set("traffic_type", fl.TrafficType) switch resourceID := aws.StringValue(fl.ResourceId); { case strings.HasPrefix(resourceID, "vpc-"): d.Set("vpc_id", resourceID) + case strings.HasPrefix(resourceID, "tgw-"): + if strings.HasPrefix(resourceID, "tgw-attach-") { + d.Set("transit_gateway_attachment_id", resourceID) + } else { + d.Set("transit_gateway_id", resourceID) + } case strings.HasPrefix(resourceID, "subnet-"): d.Set("subnet_id", resourceID) case strings.HasPrefix(resourceID, "eni-"): d.Set("eni_id", resourceID) } + if !strings.HasPrefix(aws.StringValue(fl.ResourceId), "tgw-") { + d.Set("traffic_type", fl.TrafficType) + } + tags := KeyValueTags(fl.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) //lintignore:AWSR002 diff --git a/internal/service/ec2/vpc_flow_log_test.go b/internal/service/ec2/vpc_flow_log_test.go index 5fbf474b223b..5f04e6e729dd 100644 --- a/internal/service/ec2/vpc_flow_log_test.go +++ b/internal/service/ec2/vpc_flow_log_test.go @@ -124,6 +124,78 @@ func TestAccVPCFlowLog_subnetID(t *testing.T) { }) } +func TestAccVPCFlowLog_transitGatewayID(t *testing.T) { + var flowLog ec2.FlowLog + cloudwatchLogGroupResourceName := "aws_cloudwatch_log_group.test" + iamRoleResourceName := "aws_iam_role.test" + resourceName := "aws_flow_log.test" + transitGatewayResourceName := "aws_ec2_transit_gateway.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckFlowLogDestroy, + Steps: []resource.TestStep{ + { + Config: testAccVPCFlowLogConfig_transitGatewayId(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFlowLogExists(resourceName, &flowLog), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`vpc-flow-log/fl-.+`)), + resource.TestCheckResourceAttrPair(resourceName, "iam_role_arn", iamRoleResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination", ""), + resource.TestCheckResourceAttr(resourceName, "log_destination_type", "cloud-watch-logs"), + resource.TestCheckResourceAttrPair(resourceName, "log_group_name", cloudwatchLogGroupResourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "max_aggregation_interval", "60"), + resource.TestCheckResourceAttrPair(resourceName, "transit_gateway_id", transitGatewayResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccVPCFlowLog_transitGatewayAttachmentID(t *testing.T) { + var flowLog ec2.FlowLog + cloudwatchLogGroupResourceName := "aws_cloudwatch_log_group.test" + iamRoleResourceName := "aws_iam_role.test" + resourceName := "aws_flow_log.test" + transitGatewayAttachmentResourceName := "aws_ec2_transit_gateway_vpc_attachment.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckFlowLogDestroy, + Steps: []resource.TestStep{ + { + Config: testAccVPCFlowLogConfig_transitGatewayAttachmentId(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFlowLogExists(resourceName, &flowLog), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`vpc-flow-log/fl-.+`)), + resource.TestCheckResourceAttrPair(resourceName, "iam_role_arn", iamRoleResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination", ""), + resource.TestCheckResourceAttr(resourceName, "log_destination_type", "cloud-watch-logs"), + resource.TestCheckResourceAttrPair(resourceName, "log_group_name", cloudwatchLogGroupResourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "max_aggregation_interval", "60"), + resource.TestCheckResourceAttrPair(resourceName, "transit_gateway_attachment_id", transitGatewayAttachmentResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccVPCFlowLog_LogDestinationType_cloudWatchLogs(t *testing.T) { var flowLog ec2.FlowLog cloudwatchLogGroupResourceName := "aws_cloudwatch_log_group.test" @@ -519,7 +591,7 @@ resource "aws_vpc" "test" { } func testAccVPCFlowLogConfig_destinationTypeCloudWatchLogs(rName string) string { - return testAccFlowLogConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccFlowLogConfigBase(rName), fmt.Sprintf(` data "aws_partition" "current" {} resource "aws_iam_role" "test" { @@ -556,11 +628,11 @@ resource "aws_flow_log" "test" { traffic_type = "ALL" vpc_id = aws_vpc.test.id } -`, rName) +`, rName)) } func testAccVPCFlowLogConfig_destinationTypeS3(rName string) string { - return testAccFlowLogConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccFlowLogConfigBase(rName), fmt.Sprintf(` resource "aws_s3_bucket" "test" { bucket = %[1]q force_destroy = true @@ -572,11 +644,11 @@ resource "aws_flow_log" "test" { traffic_type = "ALL" vpc_id = aws_vpc.test.id } -`, rName) +`, rName)) } func testAccVPCFlowLogConfig_destinationTypeS3Invalid(rName string) string { - return testAccFlowLogConfigBase(rName) + ` + return acctest.ConfigCompose(testAccFlowLogConfigBase(rName), ` data "aws_partition" "current" {} resource "aws_flow_log" "test" { @@ -585,11 +657,11 @@ resource "aws_flow_log" "test" { traffic_type = "ALL" vpc_id = aws_vpc.test.id } -` +`) } func testAccVPCFlowLogConfig_destinationTypeS3DOPlainText(rName string) string { - return testAccFlowLogConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccFlowLogConfigBase(rName), fmt.Sprintf(` resource "aws_s3_bucket" "test" { bucket = %[1]q force_destroy = true @@ -604,11 +676,11 @@ resource "aws_flow_log" "test" { file_format = "plain-text" } } -`, rName) +`, rName)) } func testAccVPCFlowLogConfig_destinationTypeS3DOPlainTextHiveCompatiblePerHour(rName string) string { - return testAccFlowLogConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccFlowLogConfigBase(rName), fmt.Sprintf(` resource "aws_s3_bucket" "test" { bucket = %[1]q force_destroy = true @@ -625,11 +697,11 @@ resource "aws_flow_log" "test" { per_hour_partition = true } } -`, rName) +`, rName)) } func testAccVPCFlowLogConfig_destinationTypeS3DOParquet(rName string) string { - return testAccFlowLogConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccFlowLogConfigBase(rName), fmt.Sprintf(` resource "aws_s3_bucket" "test" { bucket = %[1]q force_destroy = true @@ -644,11 +716,11 @@ resource "aws_flow_log" "test" { file_format = "parquet" } } -`, rName) +`, rName)) } func testAccVPCFlowLogConfig_destinationTypeS3DOParquetHiveCompatible(rName string) string { - return testAccFlowLogConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccFlowLogConfigBase(rName), fmt.Sprintf(` resource "aws_s3_bucket" "test" { bucket = %[1]q force_destroy = true @@ -664,11 +736,11 @@ resource "aws_flow_log" "test" { hive_compatible_partitions = true } } -`, rName) +`, rName)) } func testAccVPCFlowLogConfig_destinationTypeS3DOParquetHiveCompatiblePerHour(rName string) string { - return testAccFlowLogConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccFlowLogConfigBase(rName), fmt.Sprintf(` resource "aws_s3_bucket" "test" { bucket = %[1]q force_destroy = true @@ -685,11 +757,11 @@ resource "aws_flow_log" "test" { per_hour_partition = true } } -`, rName) +`, rName)) } func testAccVPCFlowLogConfig_subnetID(rName string) string { - return testAccFlowLogConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccFlowLogConfigBase(rName), fmt.Sprintf(` resource "aws_subnet" "test" { cidr_block = "10.0.1.0/24" vpc_id = aws_vpc.test.id @@ -734,11 +806,11 @@ resource "aws_flow_log" "test" { subnet_id = aws_subnet.test.id traffic_type = "ALL" } -`, rName) +`, rName)) } func testAccVPCFlowLogConfig_id(rName string) string { - return testAccFlowLogConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccFlowLogConfigBase(rName), fmt.Sprintf(` data "aws_partition" "current" {} resource "aws_iam_role" "test" { @@ -774,11 +846,11 @@ resource "aws_flow_log" "test" { traffic_type = "ALL" vpc_id = aws_vpc.test.id } -`, rName) +`, rName)) } func testAccVPCFlowLogConfig_format(rName string) string { - return testAccFlowLogConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccFlowLogConfigBase(rName), fmt.Sprintf(` data "aws_partition" "current" {} resource "aws_iam_role" "test" { @@ -820,11 +892,11 @@ resource "aws_flow_log" "test" { vpc_id = aws_vpc.test.id log_format = "$${version} $${vpc-id} $${subnet-id}" } -`, rName) +`, rName)) } func testAccVPCFlowLogConfig_tags1(rName, tagKey1, tagValue1 string) string { - return testAccFlowLogConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccFlowLogConfigBase(rName), fmt.Sprintf(` data "aws_partition" "current" {} resource "aws_iam_role" "test" { @@ -864,11 +936,11 @@ resource "aws_flow_log" "test" { %[2]q = %[3]q } } -`, rName, tagKey1, tagValue1) +`, rName, tagKey1, tagValue1)) } func testAccVPCFlowLogConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return testAccFlowLogConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccFlowLogConfigBase(rName), fmt.Sprintf(` data "aws_partition" "current" {} resource "aws_iam_role" "test" { @@ -909,11 +981,11 @@ resource "aws_flow_log" "test" { %[4]q = %[5]q } } -`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } func testAccVPCFlowLogConfig_maxAggregationInterval(rName string) string { - return testAccFlowLogConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccFlowLogConfigBase(rName), fmt.Sprintf(` data "aws_partition" "current" {} resource "aws_iam_role" "test" { @@ -951,5 +1023,116 @@ resource "aws_flow_log" "test" { max_aggregation_interval = 60 } -`, rName) +`, rName)) +} + +func testAccVPCFlowLogConfig_transitGatewayId(rName string) string { + return acctest.ConfigCompose(testAccFlowLogConfigBase(rName), fmt.Sprintf(` +resource "aws_ec2_transit_gateway" "test" { + tags = { + Name = %[1]q + } +} + +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = <