Skip to content

Commit

Permalink
Merge pull request #40607 from jsakama/f-aws_cloudtrail_event_data_st…
Browse files Browse the repository at this point in the history
…ore-ingestion-suspend

feat: Add CloudTrail Event Data Store suspend ingestion
  • Loading branch information
ewbankkit authored Jan 27, 2025
2 parents 171ca48 + fb29660 commit b4203d2
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 30 deletions.
3 changes: 3 additions & 0 deletions .changelog/40607.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_cloudtrail_event_data_store: Add `suspend` argument
```
149 changes: 137 additions & 12 deletions internal/service/cloudtrail/event_data_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package cloudtrail

import (
"context"
"fmt"
"log"
"time"

Expand All @@ -19,6 +20,7 @@ import (
"github.com/hashicorp/terraform-provider-aws/internal/enum"
"github.com/hashicorp/terraform-provider-aws/internal/errs"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
"github.com/hashicorp/terraform-provider-aws/internal/sdkv2/types/nullable"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
Expand Down Expand Up @@ -177,6 +179,11 @@ func resourceEventDataStore() *schema.Resource {
validation.IntBetween(7, 2555),
),
},
"suspend": {
Type: nullable.TypeNullableBool,
Optional: true,
ValidateFunc: nullable.ValidateTypeStringNullableBool,
},
names.AttrTags: tftags.TagsSchema(),
names.AttrTagsAll: tftags.TagsSchemaComputed(),
"termination_protection_enabled": {
Expand Down Expand Up @@ -211,6 +218,12 @@ func resourceEventDataStoreCreate(ctx context.Context, d *schema.ResourceData, m
input.KmsKeyId = aws.String(v.(string))
}

if v, ok := d.GetOk("suspend"); ok {
if v, null, _ := nullable.Bool(v.(string)).ValueBool(); !null {
input.StartIngestion = aws.Bool(!v)
}
}

output, err := conn.CreateEventDataStore(ctx, input)

if err != nil {
Expand All @@ -219,7 +232,7 @@ func resourceEventDataStoreCreate(ctx context.Context, d *schema.ResourceData, m

d.SetId(aws.ToString(output.EventDataStoreArn))

if _, err := waitEventDataStoreAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil {
if _, err := waitEventDataStoreCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for CloudTrail Event Data Store (%s) create: %s", name, err)
}

Expand Down Expand Up @@ -261,7 +274,7 @@ func resourceEventDataStoreUpdate(ctx context.Context, d *schema.ResourceData, m
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).CloudTrailClient(ctx)

if d.HasChangesExcept(names.AttrTags, names.AttrTagsAll) {
if d.HasChangesExcept("suspend", names.AttrTags, names.AttrTagsAll) {
input := &cloudtrail.UpdateEventDataStoreInput{
EventDataStore: aws.String(d.Id()),
}
Expand Down Expand Up @@ -300,11 +313,26 @@ func resourceEventDataStoreUpdate(ctx context.Context, d *schema.ResourceData, m
return sdkdiag.AppendErrorf(diags, "updating CloudTrail Event Data Store (%s): %s", d.Id(), err)
}

if _, err := waitEventDataStoreAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
if _, err := waitEventDataStoreUpdated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for CloudTrail Event Data Store (%s) update: %s", d.Id(), err)
}
}

if d.HasChange("suspend") {
v := d.Get("suspend")
if v, null, _ := nullable.Bool(v.(string)).ValueBool(); !null {
if v {
if err := stopEventDataStoreIngestion(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
return sdkdiag.AppendFromErr(diags, err)
}
} else {
if err := startEventDataStoreIngestion(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
return sdkdiag.AppendFromErr(diags, err)
}
}
}
}

return append(diags, resourceEventDataStoreRead(ctx, d, meta)...)
}

Expand Down Expand Up @@ -337,7 +365,24 @@ func findEventDataStoreByARN(ctx context.Context, conn *cloudtrail.Client, arn s
EventDataStore: aws.String(arn),
}

output, err := conn.GetEventDataStore(ctx, &input)
output, err := findEventDataStore(ctx, conn, &input)

if err != nil {
return nil, err
}

if status := output.Status; status == types.EventDataStoreStatusPendingDeletion {
return nil, &retry.NotFoundError{
Message: string(status),
LastRequest: input,
}
}

return output, nil
}

func findEventDataStore(ctx context.Context, conn *cloudtrail.Client, input *cloudtrail.GetEventDataStoreInput) (*cloudtrail.GetEventDataStoreOutput, error) {
output, err := conn.GetEventDataStore(ctx, input)

if errs.IsA[*types.EventDataStoreNotFoundException](err) {
return nil, &retry.NotFoundError{
Expand All @@ -354,13 +399,6 @@ func findEventDataStoreByARN(ctx context.Context, conn *cloudtrail.Client, arn s
return nil, tfresource.NewEmptyResultError(input)
}

if status := output.Status; status == types.EventDataStoreStatusPendingDeletion {
return nil, &retry.NotFoundError{
Message: string(status),
LastRequest: input,
}
}

return output, nil
}

Expand All @@ -380,7 +418,60 @@ func statusEventDataStore(ctx context.Context, conn *cloudtrail.Client, arn stri
}
}

func waitEventDataStoreAvailable(ctx context.Context, conn *cloudtrail.Client, arn string, timeout time.Duration) (*cloudtrail.GetEventDataStoreOutput, error) { //nolint:unparam
func startEventDataStoreIngestion(ctx context.Context, conn *cloudtrail.Client, arn string, timeout time.Duration) error {
input := cloudtrail.StartEventDataStoreIngestionInput{
EventDataStore: aws.String(arn),
}

_, err := conn.StartEventDataStoreIngestion(ctx, &input)

if err != nil {
return fmt.Errorf("starting CloudTrail Event Data Store (%s) ingestion: %w", arn, err)
}

if _, err := waitEventDataStoreIngestionStarted(ctx, conn, arn, timeout); err != nil {
return fmt.Errorf("waiting for CloudTrail Event Data Store (%s) ingestion start: %w", arn, err)
}

return nil
}

func stopEventDataStoreIngestion(ctx context.Context, conn *cloudtrail.Client, arn string, timeout time.Duration) error {
input := cloudtrail.StopEventDataStoreIngestionInput{
EventDataStore: aws.String(arn),
}

_, err := conn.StopEventDataStoreIngestion(ctx, &input)

if err != nil {
return fmt.Errorf("stopping CloudTrail Event Data Store (%s) ingestion: %w", arn, err)
}

if _, err := waitEventDataStoreIngestionStopped(ctx, conn, arn, timeout); err != nil {
return fmt.Errorf("waiting for CloudTrail Event Data Store (%s) ingestion stop: %w", arn, err)
}

return nil
}

func waitEventDataStoreCreated(ctx context.Context, conn *cloudtrail.Client, arn string, timeout time.Duration) (*cloudtrail.GetEventDataStoreOutput, error) { //nolint:unparam
stateConf := &retry.StateChangeConf{
Pending: enum.Slice(types.EventDataStoreStatusCreated),
Target: enum.Slice(types.EventDataStoreStatusEnabled, types.EventDataStoreStatusStoppedIngestion),
Refresh: statusEventDataStore(ctx, conn, arn),
Timeout: timeout,
}

outputRaw, err := stateConf.WaitForStateContext(ctx)

if output, ok := outputRaw.(*cloudtrail.GetEventDataStoreOutput); ok {
return output, err
}

return nil, err
}

func waitEventDataStoreUpdated(ctx context.Context, conn *cloudtrail.Client, arn string, timeout time.Duration) (*cloudtrail.GetEventDataStoreOutput, error) { //nolint:unparam
stateConf := &retry.StateChangeConf{
Pending: enum.Slice(types.EventDataStoreStatusCreated),
Target: enum.Slice(types.EventDataStoreStatusEnabled),
Expand Down Expand Up @@ -413,3 +504,37 @@ func waitEventDataStoreDeleted(ctx context.Context, conn *cloudtrail.Client, arn

return nil, err
}

func waitEventDataStoreIngestionStarted(ctx context.Context, conn *cloudtrail.Client, arn string, timeout time.Duration) (*cloudtrail.GetEventDataStoreOutput, error) { //nolint:unparam
stateConf := &retry.StateChangeConf{
Pending: enum.Slice(types.EventDataStoreStatusStartingIngestion),
Target: enum.Slice(types.EventDataStoreStatusEnabled),
Refresh: statusEventDataStore(ctx, conn, arn),
Timeout: timeout,
}

outputRaw, err := stateConf.WaitForStateContext(ctx)

if output, ok := outputRaw.(*cloudtrail.GetEventDataStoreOutput); ok {
return output, err
}

return nil, err
}

func waitEventDataStoreIngestionStopped(ctx context.Context, conn *cloudtrail.Client, arn string, timeout time.Duration) (*cloudtrail.GetEventDataStoreOutput, error) { //nolint:unparam
stateConf := &retry.StateChangeConf{
Pending: enum.Slice(types.EventDataStoreStatusStoppingIngestion),
Target: enum.Slice(types.EventDataStoreStatusStoppedIngestion),
Refresh: statusEventDataStore(ctx, conn, arn),
Timeout: timeout,
}

outputRaw, err := stateConf.WaitForStateContext(ctx)

if output, ok := outputRaw.(*cloudtrail.GetEventDataStoreOutput); ok {
return output, err
}

return nil, err
}
Loading

0 comments on commit b4203d2

Please sign in to comment.