Repository: turbot/steampipe-plugin-aws (language: go)

Issue:
Add table aws_eventbridge_scheduler
**References**
> https://docs.aws.amazon.com/scheduler/latest/APIReference/Welcome.html



Discussion / hints:
Thanks @ibaraki-douji for raising the request 👍 !

If anyone would like to attempt to develop this table, the API mentioned by @ibaraki-douji is an excellent place to start.

Please let us know if there are questions 👍!
Why did you change it to not planned ?
@misraved could i have an response ? schedulers are the "new" way in AWS to create events instead of rules. So could i have a why you closed it ?
Apologies for closing this issue earlier, @ibaraki-douji. I had initially hoped for community contributions, but I’ve now decided to take it upon myself. However, it may take some time as I work through it. Thank you for your patience and understanding!

Fix patch:
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..4eba7fefd
--- /dev/null
+++ b/aws/table_aws_scheduler_schedule.go
@@ -0,0 +1,241 @@
+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())
+	}
+	if d.EqualsQuals["state"] != nil {
+		params.State = types.ScheduleState(d.EqualsQuals["state"].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..8113e95fa
--- /dev/null
+++ b/docs/tables/aws_scheduler_schedule.md
@@ -0,0 +1,220 @@
+---
+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';
+```
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=


Test patch: