diff --git a/internal/service/sesv2/configuration_set_event_destination.go b/internal/service/sesv2/configuration_set_event_destination.go index 788b31604b5..a7c729f39d4 100644 --- a/internal/service/sesv2/configuration_set_event_destination.go +++ b/internal/service/sesv2/configuration_set_event_destination.go @@ -77,6 +77,23 @@ func ResourceConfigurationSetEventDestination() *schema.Resource { Type: schema.TypeBool, Optional: true, }, + "kinesis_firehose_destination": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "delivery_stream_arn": { + Type: schema.TypeString, + Required: true, + }, + "iam_role_arn": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, "matching_event_types": { Type: schema.TypeList, Required: true, @@ -251,6 +268,10 @@ func flattenEventDestination(apiObject types.EventDestination) map[string]interf m["cloud_watch_destination"] = []interface{}{flattenCloudWatchDestination(v)} } + if v := apiObject.KinesisFirehoseDestination; v != nil { + m["kinesis_firehose_destination"] = []interface{}{flattenKinesisFirehoseDestination(v)} + } + if v := apiObject.MatchingEventTypes; v != nil { m["matching_event_types"] = enum.Slice(apiObject.MatchingEventTypes...) } @@ -286,6 +307,24 @@ func flattenCloudWatchDimensionConfigurations(apiObjects []types.CloudWatchDimen return l } +func flattenKinesisFirehoseDestination(apiObject *types.KinesisFirehoseDestination) map[string]interface{} { + if apiObject == nil { + return nil + } + + m := map[string]interface{}{} + + if v := apiObject.DeliveryStreamArn; v != nil { + m["delivery_stream_arn"] = aws.ToString(v) + } + + if v := apiObject.IamRoleArn; v != nil { + m["iam_role_arn"] = aws.ToString(v) + } + + return m +} + func flattenCloudWatchDimensionConfiguration(apiObject types.CloudWatchDimensionConfiguration) map[string]interface{} { m := map[string]interface{}{ "dimension_value_source": string(apiObject.DimensionValueSource), @@ -317,6 +356,10 @@ func expandEventDestination(tfMap map[string]interface{}) *types.EventDestinatio a.Enabled = v } + if v, ok := tfMap["kinesis_firehose_destination"].([]interface{}); ok && len(v) > 0 && v[0] != nil { + a.KinesisFirehoseDestination = expandKinesisFirehoseDestination(v[0].(map[string]interface{})) + } + if v, ok := tfMap["matching_event_types"].([]interface{}); ok && len(v) > 0 { a.MatchingEventTypes = stringsToEventTypes(flex.ExpandStringList(v)) } @@ -338,6 +381,24 @@ func expandCloudWatchDestination(tfMap map[string]interface{}) *types.CloudWatch return a } +func expandKinesisFirehoseDestination(tfMap map[string]interface{}) *types.KinesisFirehoseDestination { + if tfMap == nil { + return nil + } + + a := &types.KinesisFirehoseDestination{} + + if v, ok := tfMap["delivery_stream_arn"].(string); ok && v != "" { + a.DeliveryStreamArn = aws.String(v) + } + + if v, ok := tfMap["iam_role_arn"].(string); ok && v != "" { + a.IamRoleArn = aws.String(v) + } + + return a +} + func expandCloudWatchDimensionConfigurations(tfList []interface{}) []types.CloudWatchDimensionConfiguration { if len(tfList) == 0 { return nil diff --git a/internal/service/sesv2/configuration_set_event_destination_test.go b/internal/service/sesv2/configuration_set_event_destination_test.go index ea95dc072b7..0f6ccf74eac 100644 --- a/internal/service/sesv2/configuration_set_event_destination_test.go +++ b/internal/service/sesv2/configuration_set_event_destination_test.go @@ -28,20 +28,75 @@ func TestAccSESV2ConfigurationSetEventDestination_basic(t *testing.T) { CheckDestroy: testAccCheckConfigurationSetEventDestinationDestroy, Steps: []resource.TestStep{ { - Config: testAccConfigurationSetEventDestinationConfig_basic(rName), + Config: testAccConfigurationSetEventDestinationConfig_cloudWatchDestination(rName), Check: resource.ComposeTestCheckFunc( testAccCheckConfigurationSetEventDestinationExists(resourceName), resource.TestCheckResourceAttr(resourceName, "configuration_set_name", rName), resource.TestCheckResourceAttr(resourceName, "event_destination.#", "1"), + resource.TestCheckResourceAttr(resourceName, "event_destination.0.enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "event_destination.0.matching_event_types.#", "1"), + resource.TestCheckResourceAttr(resourceName, "event_destination.0.matching_event_types.0", "SEND"), + resource.TestCheckResourceAttr(resourceName, "event_destination_name", rName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccSESV2ConfigurationSetEventDestination_cloudWatchDestination(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_sesv2_configuration_set_event_destination.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, names.SESV2EndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckConfigurationSetEventDestinationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigurationSetEventDestinationConfig_cloudWatchDestination(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigurationSetEventDestinationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "event_destination.#", "1"), resource.TestCheckResourceAttr(resourceName, "event_destination.0.cloud_watch_destination.#", "1"), resource.TestCheckResourceAttr(resourceName, "event_destination.0.cloud_watch_destination.0.dimension_configuration.#", "1"), resource.TestCheckResourceAttr(resourceName, "event_destination.0.cloud_watch_destination.0.dimension_configuration.0.default_dimension_value", rName), resource.TestCheckResourceAttr(resourceName, "event_destination.0.cloud_watch_destination.0.dimension_configuration.0.dimension_name", rName), resource.TestCheckResourceAttr(resourceName, "event_destination.0.cloud_watch_destination.0.dimension_configuration.0.dimension_value_source", "MESSAGE_TAG"), - resource.TestCheckResourceAttr(resourceName, "event_destination.0.enabled", "false"), - resource.TestCheckResourceAttr(resourceName, "event_destination.0.matching_event_types.#", "1"), - resource.TestCheckResourceAttr(resourceName, "event_destination.0.matching_event_types.0", "SEND"), - resource.TestCheckResourceAttr(resourceName, "event_destination_name", rName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccSESV2ConfigurationSetEventDestination_kinesisFirehoseDestination(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_sesv2_configuration_set_event_destination.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, names.SESV2EndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckConfigurationSetEventDestinationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigurationSetEventDestinationConfig_kinesisFirehoseDestination(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigurationSetEventDestinationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "event_destination.#", "1"), + resource.TestCheckResourceAttr(resourceName, "event_destination.0.kinesis_firehose_destination.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "event_destination.0.kinesis_firehose_destination.0.delivery_stream_arn", "aws_kinesis_firehose_delivery_stream.test", "arn"), + resource.TestCheckResourceAttrPair(resourceName, "event_destination.0.kinesis_firehose_destination.0.iam_role_arn", "aws_iam_role.delivery_stream", "arn"), ), }, { @@ -64,7 +119,7 @@ func TestAccSESV2ConfigurationSetEventDestination_disappears(t *testing.T) { CheckDestroy: testAccCheckConfigurationSetEventDestinationDestroy, Steps: []resource.TestStep{ { - Config: testAccConfigurationSetEventDestinationConfig_basic(rName), + Config: testAccConfigurationSetEventDestinationConfig_cloudWatchDestination(rName), Check: resource.ComposeTestCheckFunc( testAccCheckConfigurationSetEventDestinationExists(resourceName), acctest.CheckResourceDisappears(acctest.Provider, tfsesv2.ResourceConfigurationSetEventDestination(), resourceName), @@ -122,7 +177,7 @@ func testAccCheckConfigurationSetEventDestinationExists(name string) resource.Te } } -func testAccConfigurationSetEventDestinationConfig_basic(rName string) string { +func testAccConfigurationSetEventDestinationConfig_cloudWatchDestination(rName string) string { return fmt.Sprintf(` resource "aws_sesv2_configuration_set" "test" { configuration_set_name = %[1]q @@ -133,15 +188,120 @@ resource "aws_sesv2_configuration_set_event_destination" "test" { event_destination_name = %[1]q event_destination { - cloud_watch_destination { - dimension_configuration { - default_dimension_value = %[1]q - dimension_name = %[1]q - dimension_value_source = "MESSAGE_TAG" + cloud_watch_destination { + dimension_configuration { + default_dimension_value = %[1]q + dimension_name = %[1]q + dimension_value_source = "MESSAGE_TAG" + } + } + + matching_event_types = ["SEND"] + } +} +`, rName) +} + +func testAccConfigurationSetEventDestinationConfig_kinesisFirehoseDestination(rName string) string { + return fmt.Sprintf(` +resource "aws_s3_bucket" "test" { + bucket = %[1]q +} + +resource "aws_s3_bucket_acl" "test" { + bucket = aws_s3_bucket.test.id + acl = "private" +} + +resource "aws_iam_role" "bucket" { + name = "%[1]s2" + + assume_role_policy = <<EOF + { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": { + "Service": "firehose.amazonaws.com" + }, + "Effect": "Allow" } + ] + } + EOF +} + +resource "aws_kinesis_firehose_delivery_stream" "test" { + name = %[1]q + destination = "s3" + + s3_configuration { + role_arn = aws_iam_role.bucket.arn + bucket_arn = aws_s3_bucket.test.arn + } +} + + +resource "aws_iam_role" "delivery_stream" { + name = "%[1]s1" + + assume_role_policy = <<EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": { + "Service": "ses.amazonaws.com" + }, + "Effect": "Allow" } + ] +} + EOF +} + +resource "aws_iam_role_policy" "delivery_stream" { + name = %[1]q + role = aws_iam_role.delivery_stream.id + + policy = <<EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": "firehose:*", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "kinesis:*", + "Effect": "Allow", + "Resource": "*" + } + ] +} + EOF +} + +resource "aws_sesv2_configuration_set" "test" { + configuration_set_name = %[1]q +} + +resource "aws_sesv2_configuration_set_event_destination" "test" { + depends_on = [aws_iam_role_policy.delivery_stream] + + configuration_set_name = aws_sesv2_configuration_set.test.configuration_set_name + event_destination_name = %[1]q + + event_destination { + kinesis_firehose_destination { + delivery_stream_arn = aws_kinesis_firehose_delivery_stream.test.arn + iam_role_arn = aws_iam_role.delivery_stream.arn + } - matching_event_types = ["SEND"] + matching_event_types = ["SEND"] } } `, rName)