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

feat: add support for retry policies and dead letter config for aws_cloudwatch_event_target resources #17241

Merged
merged 11 commits into from
Feb 18, 2021
7 changes: 7 additions & 0 deletions .changelog/17241.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_cloudwatch_event_target: Adds `retry_policy` attributes
```

```release-note:enhancement
resource/aws_cloudwatch_event_target: Adds `dead_letter_config` attributes
```
106 changes: 106 additions & 0 deletions aws/resource_aws_cloudwatch_event_target.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,40 @@ func resourceAwsCloudWatchEventTarget() *schema.Resource {
},
},
},

"retry_policy": {
sthulb marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"maximum_event_age_in_seconds": {
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntAtLeast(60),
},
"maximum_retry_attempts": {
Type: schema.TypeInt,
Optional: true,
},
},
},
},

"dead_letter_config": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"arn": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validateArn,
},
},
},
},
},
}
}
Expand Down Expand Up @@ -350,6 +384,18 @@ func resourceAwsCloudWatchEventTargetRead(d *schema.ResourceData, meta interface
}
}

if t.RetryPolicy != nil {
if err := d.Set("retry_policy", flatternAwsCloudWatchEventTargetRetryPolicy(t.RetryPolicy)); err != nil {
return fmt.Errorf("Error setting retry_policy error: #{err}")
}
}

if t.DeadLetterConfig != nil {
if err := d.Set("dead_letter_config", flatternAwsCloudWatchEventTargetDeadLetterConfig(t.DeadLetterConfig)); err != nil {
return fmt.Errorf("Error setting dead_letter_config error: #{err}")
}
}

return nil
}

Expand Down Expand Up @@ -434,6 +480,14 @@ func buildPutTargetInputStruct(d *schema.ResourceData) *events.PutTargetsInput {
e.InputTransformer = expandAwsCloudWatchEventTransformerParameters(v.([]interface{}))
}

if v, ok := d.GetOk("retry_policy"); ok {
e.RetryPolicy = expandAwsCloudWatchEventRetryPolicyParameters(v.([]interface{}))
}

if v, ok := d.GetOk("dead_letter_config"); ok {
e.DeadLetterConfig = expandAwsCloudWatchEventDeadLetterConfigParameters(v.([]interface{}))
}

input := events.PutTargetsInput{
Rule: aws.String(d.Get("rule").(string)),
Targets: []*events.Target{e},
Expand Down Expand Up @@ -485,6 +539,39 @@ func expandAwsCloudWatchEventTargetEcsParameters(config []interface{}) *events.E

return ecsParameters
}

func expandAwsCloudWatchEventRetryPolicyParameters(rp []interface{}) *events.RetryPolicy {
retryPolicy := &events.RetryPolicy{}

for _, v := range rp {
params := v.(map[string]interface{})

if val, ok := params["maximum_event_age_in_seconds"].(int); ok {
retryPolicy.MaximumEventAgeInSeconds = aws.Int64(int64(val))
}

if val, ok := params["maximum_retry_attempts"].(int); ok {
retryPolicy.MaximumRetryAttempts = aws.Int64(int64(val))
}
}

return retryPolicy
}

func expandAwsCloudWatchEventDeadLetterConfigParameters(dlp []interface{}) *events.DeadLetterConfig {
deadLetterConfig := &events.DeadLetterConfig{}

for _, v := range dlp {
params := v.(map[string]interface{})

if val, ok := params["arn"].(string); ok && val != "" {
deadLetterConfig.Arn = aws.String(val)
}
}

return deadLetterConfig
}

func expandAwsCloudWatchEventTargetEcsParametersNetworkConfiguration(nc []interface{}) *events.NetworkConfiguration {
if len(nc) == 0 {
return nil
Expand Down Expand Up @@ -658,6 +745,25 @@ func flattenAwsCloudWatchInputTransformer(inputTransformer *events.InputTransfor
return result
}

func flatternAwsCloudWatchEventTargetRetryPolicy(rp *events.RetryPolicy) []map[string]interface{} {
config := make(map[string]interface{})

config["maximum_event_age_in_seconds"] = aws.Int64Value(rp.MaximumEventAgeInSeconds)
config["maximum_retry_attempts"] = aws.Int64Value(rp.MaximumRetryAttempts)

result := []map[string]interface{}{config}
return result
}

func flatternAwsCloudWatchEventTargetDeadLetterConfig(dlc *events.DeadLetterConfig) []map[string]interface{} {
config := make(map[string]interface{})

config["arn"] = aws.StringValue(dlc.Arn)

result := []map[string]interface{}{config}
return result
}

func resourceAwsCloudWatchEventTargetImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
busName, ruleName, targetID, err := tfevents.TargetParseImportID(d.Id())
if err != nil {
Expand Down
118 changes: 118 additions & 0 deletions aws/resource_aws_cloudwatch_event_target_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,39 @@ func TestAccAWSCloudWatchEventTarget_GeneratedTargetId(t *testing.T) {
})
}

func TestAccAWSCloudWatchEventTarget_RetryPolicy_DeadLetterConfig(t *testing.T) {
resourceName := "aws_cloudwatch_event_target.test"
kinesisStreamResourceName := "aws_kinesis_stream.test"
queueResourceName := "aws_sqs_queue.test"
var v events.Target

ruleName := acctest.RandomWithPrefix("tf-acc-cw-event-rule-full")
ssmDocumentName := acctest.RandomWithPrefix("tf_ssm_Document")
targetID := acctest.RandomWithPrefix("tf-acc-cw-target-full")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSCloudWatchEventTargetDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSCloudWatchEventTargetConfig_retryPolicyDlc(ruleName, targetID, ssmDocumentName),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudWatchEventTargetExists(resourceName, &v),
resource.TestCheckResourceAttr(resourceName, "rule", ruleName),
resource.TestCheckResourceAttr(resourceName, "target_id", targetID),
resource.TestCheckResourceAttrPair(resourceName, "arn", kinesisStreamResourceName, "arn"),
testAccCheckResourceAttrEquivalentJSON(resourceName, "input", `{"source": ["aws.cloudtrail"]}`),
resource.TestCheckResourceAttr(resourceName, "input_path", ""),
resource.TestCheckResourceAttr(resourceName, "retry_policy.0.maximum_event_age_in_seconds", "60"),
resource.TestCheckResourceAttr(resourceName, "retry_policy.0.maximum_retry_attempts", "5"),
resource.TestCheckResourceAttrPair(resourceName, "dead_letter_config.0.arn", queueResourceName, "arn"),
),
},
},
})
}

func TestAccAWSCloudWatchEventTarget_full(t *testing.T) {
resourceName := "aws_cloudwatch_event_target.test"
kinesisStreamResourceName := "aws_kinesis_stream.test"
Expand Down Expand Up @@ -626,6 +659,8 @@ func testAccAWSCloudWatchEventTargetImportStateIdFunc(resourceName string) resou
return "", fmt.Errorf("Not found: %s", resourceName)
}

fmt.Printf("%#v", rs.Primary.Attributes)

return fmt.Sprintf("%s/%s/%s", rs.Primary.Attributes["event_bus_name"], rs.Primary.Attributes["rule"], rs.Primary.Attributes["target_id"]), nil
}
}
Expand Down Expand Up @@ -730,6 +765,89 @@ resource "aws_sns_topic" "test" {
`, ruleName, snsTopicName)
}

func testAccAWSCloudWatchEventTargetConfig_retryPolicyDlc(ruleName, targetName, rName string) string {
return fmt.Sprintf(`
resource "aws_cloudwatch_event_rule" "test" {
name = %[1]q
schedule_expression = "rate(1 hour)"
role_arn = aws_iam_role.test.arn
}

resource "aws_iam_role" "test" {
name = %[2]q

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

resource "aws_iam_role_policy" "test" {
name = "%[2]s_policy"
role = aws_iam_role.test.id

policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"kinesis:PutRecord",
"kinesis:PutRecords"
],
"Resource": [
"*"
],
"Effect": "Allow"
}
]
}
EOF
}

resource "aws_sqs_queue" "test" {
}

resource "aws_cloudwatch_event_target" "test" {
rule = aws_cloudwatch_event_rule.test.name
target_id = %[3]q

input = <<INPUT
{ "source": ["aws.cloudtrail"] }
INPUT

arn = aws_kinesis_stream.test.arn

retry_policy {
maximum_event_age_in_seconds = 60
maximum_retry_attempts = 5
}

dead_letter_config {
arn = aws_sqs_queue.test.arn
}
}

resource "aws_kinesis_stream" "test" {
name = "%[2]s_kinesis_test"
shard_count = 1
}

data "aws_partition" "current" {}
`, ruleName, rName, targetName)
}

func testAccAWSCloudWatchEventTargetConfig_full(ruleName, targetName, rName string) string {
return fmt.Sprintf(`
resource "aws_cloudwatch_event_rule" "test" {
Expand Down
11 changes: 11 additions & 0 deletions website/docs/r/cloudwatch_event_target.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ The following arguments are supported:
* `kinesis_target` - (Optional) Parameters used when you are using the rule to invoke an Amazon Kinesis Stream. Documented below. A maximum of 1 are allowed.
* `sqs_target` - (Optional) Parameters used when you are using the rule to invoke an Amazon SQS Queue. Documented below. A maximum of 1 are allowed.
* `input_transformer` - (Optional) Parameters used when you are providing a custom input to a target based on certain event data. Conflicts with `input` and `input_path`.
* `retry_policy` - (Optional) Parameters used when you are providing retry policies. Documented below. A maximum of 1 are allowed.
* `dead_letter_config` - (Optional) Parameters used when you are providing a dead letter conifg. Documented below. A maximum of 1 are allowed.

`run_command_targets` support the following:

Expand Down Expand Up @@ -354,6 +356,15 @@ For more information, see [Task Networking](https://docs.aws.amazon.com/AmazonEC

* `input_template` - (Required) Template to customize data sent to the target. Must be valid JSON. To send a string value, the string value must include double quotes. Values must be escaped for both JSON and Terraform, e.g. `"\"Your string goes here.\\nA new line.\""`

`retry_policy` support the following:

* `maximum_event_age_in_seconds` - (Optional) The age in seconds to continue to make retry attempts.
* `maximum_retry_attempts` - (Optional) maximum number of retry attempts to make before the request fails

`dead_letter_config` support the following:

* `arn` - (Optional) - ARN of the SQS queue specified as the target for the dead-letter queue.

## Import

EventBridge Targets can be imported using `event_bus_name/rule-name/target-id` (if you omit `event_bus_name`, the `default` event bus will be used).
Expand Down