From f376f3ffdf3e7e85951efba3512d4e48dd5f98b1 Mon Sep 17 00:00:00 2001 From: ParthaI Date: Tue, 17 Dec 2024 17:55:21 +0530 Subject: [PATCH] Add table aws_scheduler_schedule Closes #2304 --- aws/plugin.go | 1 + aws/service.go | 15 +- aws/table_aws_scheduler_schedule.go | 238 ++++++++++++++++++++++++++ docs/tables/aws_scheduler_schedule.md | 222 ++++++++++++++++++++++++ go.mod | 5 +- go.sum | 6 +- 6 files changed, 482 insertions(+), 5 deletions(-) create mode 100644 aws/table_aws_scheduler_schedule.go create mode 100644 docs/tables/aws_scheduler_schedule.md diff --git a/aws/plugin.go b/aws/plugin.go index c9ea62e0f..40ee12628 100644 --- a/aws/plugin.go +++ b/aws/plugin.go @@ -462,6 +462,7 @@ func Plugin(ctx context.Context) *plugin.Plugin { "aws_sagemaker_model": tableAwsSageMakerModel(ctx), "aws_sagemaker_notebook_instance": tableAwsSageMakerNotebookInstance(ctx), "aws_sagemaker_training_job": tableAwsSageMakerTrainingJob(ctx), + "aws_scheduler_schedule": tableAwsSchedulerSchedule(ctx), "aws_secretsmanager_secret": tableAwsSecretsManagerSecret(ctx), "aws_securityhub_action_target": tableAwsSecurityHubActionTarget(ctx), "aws_securityhub_finding": tableAwsSecurityHubFinding(ctx), diff --git a/aws/service.go b/aws/service.go index 040916daa..60aacb6a2 100644 --- a/aws/service.go +++ b/aws/service.go @@ -116,6 +116,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3control" "github.com/aws/aws-sdk-go-v2/service/sagemaker" + "github.com/aws/aws-sdk-go-v2/service/scheduler" "github.com/aws/aws-sdk-go-v2/service/secretsmanager" "github.com/aws/aws-sdk-go-v2/service/securityhub" "github.com/aws/aws-sdk-go-v2/service/securitylake" @@ -197,6 +198,7 @@ import ( resourceexplorer2Endpoint "github.com/aws/aws-sdk-go/service/resourceexplorer2" route53resolverEndpoint "github.com/aws/aws-sdk-go/service/route53resolver" sagemakerEndpoint "github.com/aws/aws-sdk-go/service/sagemaker" + schedulerEndpoint "github.com/aws/aws-sdk-go/service/scheduler" securityhubEndpoint "github.com/aws/aws-sdk-go/service/securityhub" securitylakeEndpoint "github.com/aws/aws-sdk-go/service/securitylake" serverlessrepoEndpoint "github.com/aws/aws-sdk-go/service/serverlessapplicationrepository" @@ -1381,6 +1383,17 @@ func SageMakerClient(ctx context.Context, d *plugin.QueryData) (*sagemaker.Clien return sagemaker.NewFromConfig(*cfg), nil } +func SchedulerClient(ctx context.Context, d *plugin.QueryData) (*scheduler.Client, error) { + cfg, err := getClientForQuerySupportedRegion(ctx, d, schedulerEndpoint.EndpointsID) + if err != nil { + return nil, err + } + if cfg == nil { + return nil, nil + } + return scheduler.NewFromConfig(*cfg), nil +} + func SecretsManagerClient(ctx context.Context, d *plugin.QueryData) (*secretsmanager.Client, error) { cfg, err := getClientForQueryRegion(ctx, d) if err != nil { @@ -1500,7 +1513,7 @@ func ShieldClient(ctx context.Context, d *plugin.QueryData) (*shield.Client, err if err != nil { return nil, err } - + if cfg == nil { return nil, nil } diff --git a/aws/table_aws_scheduler_schedule.go b/aws/table_aws_scheduler_schedule.go new file mode 100644 index 000000000..ab6544ef2 --- /dev/null +++ b/aws/table_aws_scheduler_schedule.go @@ -0,0 +1,238 @@ +package aws + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/scheduler" + "github.com/aws/aws-sdk-go-v2/service/scheduler/types" + + schedulerv1 "github.com/aws/aws-sdk-go/service/scheduler" + + "github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto" + "github.com/turbot/steampipe-plugin-sdk/v5/plugin" + "github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform" +) + +func tableAwsSchedulerSchedule(_ context.Context) *plugin.Table { + return &plugin.Table{ + Name: "aws_scheduler_schedule", + Description: "AWS EventBridge Scheduler Schedule", + Get: &plugin.GetConfig{ + KeyColumns: plugin.AllColumns([]string{"name", "group_name"}), + Hydrate: getAwsSchedulerSchedule, + IgnoreConfig: &plugin.IgnoreConfig{ + ShouldIgnoreErrorFunc: shouldIgnoreErrors([]string{"ResourceNotFoundException"}), + }, + Tags: map[string]string{"service": "scheduler", "action": "GetSchedule"}, + }, + List: &plugin.ListConfig{ + Hydrate: listAwsSchedulerSchedules, + IgnoreConfig: &plugin.IgnoreConfig{ + ShouldIgnoreErrorFunc: shouldIgnoreErrors([]string{"ResourceNotFoundException"}), + }, + KeyColumns: []*plugin.KeyColumn{ + {Name: "group_name", Require: plugin.Optional}, + {Name: "state", Require: plugin.Optional}, + }, + Tags: map[string]string{"service": "scheduler", "action": "ListSchedules"}, + }, + GetMatrixItemFunc: SupportedRegionMatrix(schedulerv1.EndpointsID), + Columns: awsRegionalColumns([]*plugin.Column{ + { + Name: "name", + Description: "The name of the schedule.", + Type: proto.ColumnType_STRING, + }, + { + Name: "arn", + Description: "The Amazon Resource Name (ARN) of the schedule.", + Type: proto.ColumnType_STRING, + }, + { + Name: "state", + Description: "Specifies whether the schedule is enabled or disabled.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("State"), + }, + { + Name: "description", + Description: "The description of the schedule.", + Type: proto.ColumnType_STRING, + Hydrate: getAwsSchedulerSchedule, + }, + { + Name: "schedule_expression", + Description: "The expression that defines when the schedule runs.", + Type: proto.ColumnType_STRING, + Hydrate: getAwsSchedulerSchedule, + }, + { + Name: "schedule_expression_timezone", + Description: "The timezone in which the scheduling expression is evaluated.", + Type: proto.ColumnType_STRING, + Hydrate: getAwsSchedulerSchedule, + }, + { + Name: "creation_date", + Description: "The time at which the schedule was created.", + Type: proto.ColumnType_TIMESTAMP, + }, + { + Name: "last_modification_date", + Description: "The time at which the schedule was last modified.", + Type: proto.ColumnType_TIMESTAMP, + }, + { + Name: "start_date", + Description: "The date, in UTC, after which the schedule can begin invoking its target.", + Type: proto.ColumnType_TIMESTAMP, + Hydrate: getAwsSchedulerSchedule, + }, + { + Name: "end_date", + Description: "The date, in UTC, before which the schedule can invoke its target.", + Type: proto.ColumnType_TIMESTAMP, + Hydrate: getAwsSchedulerSchedule, + }, + { + Name: "group_name", + Description: "The name of the schedule group associated with this schedule.", + Type: proto.ColumnType_STRING, + }, + { + Name: "kms_key_arn", + Description: "The ARN for a customer managed KMS Key used for encryption.", + Type: proto.ColumnType_STRING, + Hydrate: getAwsSchedulerSchedule, + }, + { + Name: "action_after_completion", + Description: "Indicates the action that EventBridge Scheduler applies after completion.", + Type: proto.ColumnType_STRING, + Hydrate: getAwsSchedulerSchedule, + Transform: transform.FromField("ActionAfterCompletion"), + }, + { + Name: "flexible_time_window", + Description: "Allows you to configure a time window during which the schedule invokes the target.", + Type: proto.ColumnType_JSON, + Hydrate: getAwsSchedulerSchedule, + }, + { + Name: "target", + Description: "The schedule target.", + Type: proto.ColumnType_JSON, + Hydrate: getAwsSchedulerSchedule, + }, + + // Standard columns + { + Name: "title", + Description: resourceInterfaceDescription("title"), + Type: proto.ColumnType_STRING, + Transform: transform.FromField("Name"), + }, + { + Name: "akas", + Description: resourceInterfaceDescription("akas"), + Type: proto.ColumnType_JSON, + Transform: transform.FromField("Arn").Transform(transform.EnsureStringArray), + }, + }), + } +} + +//// LIST FUNCTION + +func listAwsSchedulerSchedules(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + // Create session + svc, err := SchedulerClient(ctx, d) + if err != nil { + plugin.Logger(ctx).Error("aws_scheduler_schedule.listAwsSchedulerSchedules", "client_error", err) + return nil, err + } + + // Limiting the results + maxLimit := int32(100) + if d.QueryContext.Limit != nil { + limit := int32(*d.QueryContext.Limit) + if limit < maxLimit { + maxLimit = limit + } + } + + // Build params + params := &scheduler.ListSchedulesInput{} + if d.EqualsQuals["group_name"] != nil { + params.GroupName = aws.String(d.EqualsQuals["group_name"].GetStringValue()) + } + + paginator := scheduler.NewListSchedulesPaginator(svc, params, func(o *scheduler.ListSchedulesPaginatorOptions) { + o.Limit = maxLimit + o.StopOnDuplicateToken = true + }) + + // Iterate and stream results + for paginator.HasMorePages() { + // apply rate limiting + d.WaitForListRateLimit(ctx) + + page, err := paginator.NextPage(ctx) + if err != nil { + plugin.Logger(ctx).Error("aws_scheduler_schedule.listAwsSchedulerSchedules", "api_error", err) + return nil, err + } + + for _, schedule := range page.Schedules { + d.StreamListItem(ctx, schedule) + + // Context can be cancelled due to manual cancellation or the limit has been hit + if d.RowsRemaining(ctx) == 0 { + return nil, nil + } + } + } + + return nil, nil +} + +//// GET FUNCTION + +func getAwsSchedulerSchedule(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + name := d.EqualsQuals["name"].GetStringValue() + groupName := d.EqualsQuals["group_name"].GetStringValue() + + if h.Item != nil { + scheduler := h.Item.(types.ScheduleSummary) + name = *scheduler.Name + groupName = *scheduler.GroupName + } + + // Empty Check + if name == "" || groupName == "" { + return nil, nil + } + + // Create session + svc, err := SchedulerClient(ctx, d) + if err != nil { + plugin.Logger(ctx).Error("aws_scheduler_schedule.getAwsSchedulerSchedule", "client_error", err) + return nil, err + } + + // Build params + params := &scheduler.GetScheduleInput{ + Name: aws.String(name), + GroupName: aws.String(groupName), + } + + // Call API + result, err := svc.GetSchedule(ctx, params) + if err != nil { + plugin.Logger(ctx).Error("aws_scheduler_schedule.getAwsSchedulerSchedule", "api_error", err) + return nil, err + } + + return result, nil +} diff --git a/docs/tables/aws_scheduler_schedule.md b/docs/tables/aws_scheduler_schedule.md new file mode 100644 index 000000000..b070d2f2a --- /dev/null +++ b/docs/tables/aws_scheduler_schedule.md @@ -0,0 +1,222 @@ +--- +title: "Steampipe Table: aws_scheduler_schedule - Query AWS EventBridge Schedules using SQL" +description: "Allows users to query AWS EventBridge Scheduler schedules, including metadata, schedule expressions, target configurations, and other details." +--- + +# Table: aws_scheduler_schedule - Query AWS EventBridge Schedules using SQL + +The AWS EventBridge Scheduler is a fully managed service that enables automated task execution at specific times or intervals. It allows you to define schedules for running tasks, managing workflows, and automating processes across AWS services. Schedules can be one-time or recurring, using expressions such as `rate`, `cron`, or `at`. + +## Table Usage Guide + +The `aws_scheduler_schedule` table in Steampipe provides information about schedules configured in AWS EventBridge Scheduler. This table allows DevOps engineers, system administrators, and cloud professionals to query schedule-specific details, such as schedule expressions, start and end times, flexible time windows, target configurations, and states. + +The schema outlines key attributes of the schedule, including its name, ARN, associated group, creation date, schedule state, and metadata. You can use this table to monitor schedules, identify configuration details, and optimize automation workflows. + +## Examples + +### Basic info +Retrieve the name, ARN, group name, and creation date of all schedules in your AWS account. + +```sql+postgres +select + name, + arn, + group_name, + creation_date +from + aws_scheduler_schedule; +``` + +```sql+sqlite +select + name, + arn, + group_name, + creation_date +from + aws_scheduler_schedule; +``` + +### List schedules with a specific state (Enabled) +Identify all schedules that are currently enabled. + +```sql+postgres +select + name, + arn, + state, + schedule_expression +from + aws_scheduler_schedule +where + state = 'ENABLED'; +``` + +```sql+sqlite +select + name, + arn, + state, + schedule_expression +from + aws_scheduler_schedule +where + state = 'ENABLED'; +``` + +### List schedules with flexible time windows +Fetch schedules that have flexible time windows configured. + +```sql+postgres +select + name, + flexible_time_window, + schedule_expression +from + aws_scheduler_schedule +where + flexible_time_window is not null; +``` + +```sql+sqlite +select + name, + flexible_time_window, + schedule_expression +from + aws_scheduler_schedule +where + flexible_time_window is not null; +``` + +### Retrieve schedules with start and end dates +Identify schedules that have both start and end dates defined. + +```sql+postgres +select + name, + start_date, + end_date, + state +from + aws_scheduler_schedule +where + start_date is not null + and end_date is not null; +``` + +```sql+sqlite +select + name, + start_date, + end_date, + state +from + aws_scheduler_schedule +where + start_date is not null + and end_date is not null; +``` + +### List schedules grouped by their schedule group +Fetch schedules and group them based on their schedule group name. + +```sql+postgres +select + group_name, + count(*) as total_schedules +from + aws_scheduler_schedule +group by + group_name; +``` + +```sql+sqlite +select + group_name, + count(*) as total_schedules +from + aws_scheduler_schedule +group by + group_name; +``` + +### Analyze target configurations for specific schedules +Retrieve the target configurations, including the action and resource details, for specific schedules. + +```sql+postgres +select + name, + target, + action_after_completion +from + aws_scheduler_schedule +where + name = 'my-schedule'; +``` + +```sql+sqlite +select + name, + target, + action_after_completion +from + aws_scheduler_schedule +where + name = 'my-schedule'; +``` + +--- + +### Fetch schedules created in the last 30 days +Identify newly created schedules within the past month for auditing or review. + +```sql+postgres +select + name, + creation_date, + state, + group_name +from + aws_scheduler_schedule +where + creation_date > now() - interval '30 days'; +``` + +```sql+sqlite +select + name, + creation_date, + state, + group_name +from + aws_scheduler_schedule +where + creation_date > datetime('now','-30 days'); +``` + +### Retrieve schedules with specific time zones +Fetch schedules where the schedule expression is evaluated in a specific time zone. + +```sql+postgres +select + name, + schedule_expression, + schedule_expression_timezone +from + aws_scheduler_schedule +where + schedule_expression_timezone = 'UTC'; +``` + +```sql+sqlite +select + name, + schedule_expression, + schedule_expression_timezone +from + aws_scheduler_schedule +where + schedule_expression_timezone = 'UTC'; +``` \ No newline at end of file diff --git a/go.mod b/go.mod index 4f7ef1e83..f3f8a2c78 100644 --- a/go.mod +++ b/go.mod @@ -109,6 +109,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/s3 v1.54.3 github.com/aws/aws-sdk-go-v2/service/s3control v1.44.4 github.com/aws/aws-sdk-go-v2/service/sagemaker v1.135.0 + github.com/aws/aws-sdk-go-v2/service/scheduler v1.8.8 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.6 github.com/aws/aws-sdk-go-v2/service/securityhub v1.47.2 github.com/aws/aws-sdk-go-v2/service/securitylake v1.13.3 @@ -118,6 +119,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/servicequotas v1.21.4 github.com/aws/aws-sdk-go-v2/service/ses v1.22.4 github.com/aws/aws-sdk-go-v2/service/sfn v1.26.4 + github.com/aws/aws-sdk-go-v2/service/shield v1.25.7 github.com/aws/aws-sdk-go-v2/service/simspaceweaver v1.10.4 github.com/aws/aws-sdk-go-v2/service/sns v1.29.4 github.com/aws/aws-sdk-go-v2/service/sqs v1.31.4 @@ -132,7 +134,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/wafv2 v1.48.2 github.com/aws/aws-sdk-go-v2/service/wellarchitected v1.29.4 github.com/aws/aws-sdk-go-v2/service/workspaces v1.38.4 - github.com/aws/smithy-go v1.20.4 + github.com/aws/smithy-go v1.22.1 github.com/gocarina/gocsv v0.0.0-20201208093247-67c824bc04d4 github.com/goccy/go-yaml v1.11.3 github.com/golang/protobuf v1.5.4 @@ -147,7 +149,6 @@ require ( require golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect require ( - github.com/aws/aws-sdk-go-v2/service/shield v1.25.7 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect diff --git a/go.sum b/go.sum index db85cbafe..8c7c45843 100644 --- a/go.sum +++ b/go.sum @@ -433,6 +433,8 @@ github.com/aws/aws-sdk-go-v2/service/s3control v1.44.4 h1:9QdyZyzWTzZxr3uvVMVgN8 github.com/aws/aws-sdk-go-v2/service/s3control v1.44.4/go.mod h1:xywJi2/waU8+fglbs5ASVHKr5y7OAYsEBOyQwgQgTIc= github.com/aws/aws-sdk-go-v2/service/sagemaker v1.135.0 h1:xr65PMo/RO3CDaYz2/U4W+cadzWzB+2b7AW7em3OHos= github.com/aws/aws-sdk-go-v2/service/sagemaker v1.135.0/go.mod h1:7Tsdas9MqLqQ8xZM34242+vVz928Jdg8/HJnMXYoIUM= +github.com/aws/aws-sdk-go-v2/service/scheduler v1.8.8 h1:0JlMMgtgydlichQOArHBRgkAo/ycJ/aF3nMreMRxmv0= +github.com/aws/aws-sdk-go-v2/service/scheduler v1.8.8/go.mod h1:XIhMBVV65pl4sdT0SB6CnI/F3AUQ7yPNRdaCVG47ZHo= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.6 h1:TIOEjw0i2yyhmhRry3Oeu9YtiiHWISZ6j/irS1W3gX4= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.6/go.mod h1:3Ba++UwWd154xtP4FRX5pUK3Gt4up5sDHCve6kVfE+g= github.com/aws/aws-sdk-go-v2/service/securityhub v1.47.2 h1:Nl3VUaEtpoCkIL0BKc5xM2UmIAGvwSC+yPpPdbe5P/s= @@ -487,8 +489,8 @@ github.com/aws/aws-sdk-go-v2/service/wellarchitected v1.29.4 h1:OuFs453KXWTLBkem github.com/aws/aws-sdk-go-v2/service/wellarchitected v1.29.4/go.mod h1:MRT/P9Cwn+7xCCVpD1sTvUESiWMAc9hA+FooRsW5fe8= github.com/aws/aws-sdk-go-v2/service/workspaces v1.38.4 h1:SvHYikdxmnyptMebU3zFfXbfU96SHzdUX+KXqa6pjYE= github.com/aws/aws-sdk-go-v2/service/workspaces v1.38.4/go.mod h1:1XK49PATLHBd7mpKqO91GqRuV7bEsmyQ8Lslvn3fFj4= -github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4= -github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= +github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=