Skip to content

Commit

Permalink
Merge pull request #15922 from hashicorp/add-eventbus-to-cloudwatch-e…
Browse files Browse the repository at this point in the history
…vent-permission

Adds custom event bus support to EventBridge permission resource
  • Loading branch information
gdavison authored Nov 5, 2020
2 parents 7c67611 + 730ea62 commit 66d57fe
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 81 deletions.
21 changes: 21 additions & 0 deletions aws/internal/service/cloudwatchevents/id.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,27 @@ import (

const DefaultEventBusName = "default"

const PermissionIDSeparator = "/"

func PermissionCreateID(eventBusName, statementID string) string {
if eventBusName == "" || eventBusName == DefaultEventBusName {
return statementID
}
return eventBusName + PermissionIDSeparator + statementID
}

func PermissionParseID(id string) (string, string, error) {
parts := strings.Split(id, PermissionIDSeparator)
if len(parts) == 1 && parts[0] != "" {
return DefaultEventBusName, parts[0], nil
}
if len(parts) == 2 && parts[0] != "" && parts[1] != "" {
return parts[0], parts[1], nil
}

return "", "", fmt.Errorf("unexpected format for ID (%q), expected <event-bus-name>"+PermissionIDSeparator+"<statement-id> or <statement-id>", id)
}

const ruleIDSeparator = "/"

func RuleCreateID(eventBusName, ruleName string) string {
Expand Down
92 changes: 62 additions & 30 deletions aws/resource_aws_cloudwatch_event_permission.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"log"
"regexp"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/arn"
Expand All @@ -14,6 +13,8 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
tfevents "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cloudwatchevents"
iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter"
)

func resourceAwsCloudWatchEventPermission() *schema.Resource {
Expand Down Expand Up @@ -57,6 +58,13 @@ func resourceAwsCloudWatchEventPermission() *schema.Resource {
},
},
},
"event_bus_name": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateCloudWatchEventBusName,
Default: tfevents.DefaultEventBusName,
},
"principal": {
Type: schema.TypeString,
Required: true,
Expand All @@ -75,42 +83,52 @@ func resourceAwsCloudWatchEventPermission() *schema.Resource {
func resourceAwsCloudWatchEventPermissionCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).cloudwatcheventsconn

eventBusName := d.Get("event_bus_name").(string)
statementID := d.Get("statement_id").(string)

input := events.PutPermissionInput{
Action: aws.String(d.Get("action").(string)),
Condition: expandCloudWatchEventsCondition(d.Get("condition").([]interface{})),
Principal: aws.String(d.Get("principal").(string)),
StatementId: aws.String(statementID),
Action: aws.String(d.Get("action").(string)),
Condition: expandCloudWatchEventsCondition(d.Get("condition").([]interface{})),
EventBusName: aws.String(eventBusName),
Principal: aws.String(d.Get("principal").(string)),
StatementId: aws.String(statementID),
}

log.Printf("[DEBUG] Creating CloudWatch Events permission: %s", input)
_, err := conn.PutPermission(&input)
if err != nil {
return fmt.Errorf("Creating CloudWatch Events permission failed: %s", err.Error())
return fmt.Errorf("Creating CloudWatch Events permission failed: %w", err)
}

d.SetId(statementID)
id := tfevents.PermissionCreateID(eventBusName, statementID)
d.SetId(id)

return resourceAwsCloudWatchEventPermissionRead(d, meta)
}

// See also: https://docs.aws.amazon.com/AmazonCloudWatchEvents/latest/APIReference/API_DescribeEventBus.html
func resourceAwsCloudWatchEventPermissionRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).cloudwatcheventsconn
input := events.DescribeEventBusInput{}

eventBusName, statementID, err := tfevents.PermissionParseID(d.Id())
if err != nil {
return fmt.Errorf("error reading CloudWatch Events permission (%s): %w", d.Id(), err)
}
input := events.DescribeEventBusInput{
Name: aws.String(eventBusName),
}
var output *events.DescribeEventBusOutput
var policyStatement *CloudWatchEventPermissionPolicyStatement

// Especially with concurrent PutPermission calls there can be a slight delay
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
err = resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError {
log.Printf("[DEBUG] Reading CloudWatch Events bus: %s", input)
output, err := conn.DescribeEventBus(&input)
output, err = conn.DescribeEventBus(&input)
if err != nil {
return resource.NonRetryableError(fmt.Errorf("Reading CloudWatch Events permission '%s' failed: %s", d.Id(), err.Error()))
return resource.NonRetryableError(fmt.Errorf("reading CloudWatch Events permission (%s) failed: %w", d.Id(), err))
}

policyStatement, err = getPolicyStatement(output, d.Id())
policyStatement, err = getPolicyStatement(output, statementID)
if err != nil {
return resource.RetryableError(err)
}
Expand All @@ -120,24 +138,28 @@ func resourceAwsCloudWatchEventPermissionRead(d *schema.ResourceData, meta inter
if isResourceTimeoutError(err) {
output, err = conn.DescribeEventBus(&input)
if output != nil {
policyStatement, err = getPolicyStatement(output, d.Id())
policyStatement, err = getPolicyStatement(output, statementID)
}
}

if isResourceNotFoundError(err) {
log.Printf("[WARN] %s", err)
log.Printf("[WARN] CloudWatch Events permission (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}
if err != nil {
// Missing statement inside valid policy
return err
return fmt.Errorf("error reading CloudWatch Events permission (%s): %w", d.Id(), err)
}

d.Set("action", policyStatement.Action)
busName := aws.StringValue(output.Name)
if busName == "" {
busName = tfevents.DefaultEventBusName
}
d.Set("event_bus_name", busName)

if err := d.Set("condition", flattenCloudWatchEventPermissionPolicyStatementCondition(policyStatement.Condition)); err != nil {
return fmt.Errorf("error setting condition: %s", err)
return fmt.Errorf("error setting condition: %w", err)
}

principalString, ok := policyStatement.Principal.(string)
Expand All @@ -147,7 +169,7 @@ func resourceAwsCloudWatchEventPermissionRead(d *schema.ResourceData, meta inter
principalMap := policyStatement.Principal.(map[string]interface{})
policyARN, err := arn.Parse(principalMap["AWS"].(string))
if err != nil {
return fmt.Errorf("Reading CloudWatch Events permission '%s' failed: %s", d.Id(), err)
return fmt.Errorf("error reading CloudWatch Events permission (%s): %w", d.Id(), err)
}
d.Set("principal", policyARN.AccountID)
}
Expand All @@ -161,15 +183,14 @@ func getPolicyStatement(output *events.DescribeEventBusOutput, statementID strin

if output == nil || output.Policy == nil {
return nil, &resource.NotFoundError{
Message: fmt.Sprintf("CloudWatch Events permission %q not found"+
"in given results from DescribeEventBus", statementID),
Message: fmt.Sprintf("CloudWatch Events permission %q not found", statementID),
LastResponse: output,
}
}

err := json.Unmarshal([]byte(*output.Policy), &policyDoc)
if err != nil {
return nil, fmt.Errorf("Reading CloudWatch Events permission '%s' failed: %s", statementID, err)
return nil, fmt.Errorf("error reading CloudWatch Events permission (%s): %w", statementID, err)
}

return findCloudWatchEventPermissionPolicyStatementByID(&policyDoc, statementID)
Expand All @@ -178,40 +199,51 @@ func getPolicyStatement(output *events.DescribeEventBusOutput, statementID strin
func resourceAwsCloudWatchEventPermissionUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).cloudwatcheventsconn

eventBusName, statementID, err := tfevents.PermissionParseID(d.Id())
if err != nil {
return fmt.Errorf("error updating CloudWatch Events permission (%s): %w", d.Id(), err)
}
input := events.PutPermissionInput{
Action: aws.String(d.Get("action").(string)),
Condition: expandCloudWatchEventsCondition(d.Get("condition").([]interface{})),
Principal: aws.String(d.Get("principal").(string)),
StatementId: aws.String(d.Get("statement_id").(string)),
Action: aws.String(d.Get("action").(string)),
Condition: expandCloudWatchEventsCondition(d.Get("condition").([]interface{})),
EventBusName: aws.String(eventBusName),
Principal: aws.String(d.Get("principal").(string)),
StatementId: aws.String(statementID),
}

log.Printf("[DEBUG] Update CloudWatch Events permission: %s", input)
_, err := conn.PutPermission(&input)
_, err = conn.PutPermission(&input)
if isAWSErr(err, events.ErrCodeResourceNotFoundException, "") {
log.Printf("[WARN] CloudWatch Events permission %q not found, removing from state", d.Id())
d.SetId("")
return nil
}
if err != nil {
return fmt.Errorf("Updating CloudWatch Events permission '%s' failed: %s", d.Id(), err.Error())
return fmt.Errorf("error updating CloudWatch Events permission (%s): %w", d.Id(), err)
}

return resourceAwsCloudWatchEventPermissionRead(d, meta)
}

func resourceAwsCloudWatchEventPermissionDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).cloudwatcheventsconn

eventBusName, statementID, err := tfevents.PermissionParseID(d.Id())
if err != nil {
return fmt.Errorf("error deleting CloudWatch Events permission (%s): %w", d.Id(), err)
}
input := events.RemovePermissionInput{
StatementId: aws.String(d.Id()),
EventBusName: aws.String(eventBusName),
StatementId: aws.String(statementID),
}

log.Printf("[DEBUG] Delete CloudWatch Events permission: %s", input)
_, err := conn.RemovePermission(&input)
_, err = conn.RemovePermission(&input)
if isAWSErr(err, events.ErrCodeResourceNotFoundException, "") {
return nil
}
if err != nil {
return fmt.Errorf("Deleting CloudWatch Events permission '%s' failed: %s", d.Id(), err.Error())
return fmt.Errorf("error deleting CloudWatch Events permission (%s): %w", d.Id(), err)
}
return nil
}
Expand Down
Loading

0 comments on commit 66d57fe

Please sign in to comment.