From b7d15c74b910cc80628298d1bd88fb02be4880f9 Mon Sep 17 00:00:00 2001 From: ParthaI Date: Wed, 6 Dec 2023 22:21:01 +0530 Subject: [PATCH 1/5] Add table aws_iot_greengrass_* Closes #1994 --- aws/plugin.go | 3 + aws/service.go | 13 + aws/table_aws_iot_greengrass_component.go | 172 +++++++++++++ aws/table_aws_iot_greengrass_core_device.go | 183 ++++++++++++++ aws/table_aws_iot_greengrass_deployment.go | 226 ++++++++++++++++++ docs/tables/aws_iot_greengrass_component.md | 45 ++++ docs/tables/aws_iot_greengrass_core_device.md | 46 ++++ docs/tables/aws_iot_greengrass_deployment.md | 98 ++++++++ go.mod | 3 +- go.sum | 12 +- 10 files changed, 792 insertions(+), 9 deletions(-) create mode 100644 aws/table_aws_iot_greengrass_component.go create mode 100644 aws/table_aws_iot_greengrass_core_device.go create mode 100644 aws/table_aws_iot_greengrass_deployment.go create mode 100644 docs/tables/aws_iot_greengrass_component.md create mode 100644 docs/tables/aws_iot_greengrass_core_device.md create mode 100644 docs/tables/aws_iot_greengrass_deployment.md diff --git a/aws/plugin.go b/aws/plugin.go index d04aa2d0c..fbcda157b 100644 --- a/aws/plugin.go +++ b/aws/plugin.go @@ -301,6 +301,9 @@ func Plugin(ctx context.Context) *plugin.Plugin { "aws_inspector_assessment_template": tableAwsInspectorAssessmentTemplate(ctx), "aws_inspector_exclusion": tableAwsInspectorExclusion(ctx), "aws_inspector_finding": tableAwsInspectorFinding(ctx), + "aws_iot_greengrass_component": tableAwsIotGreengrassComponent(ctx), + "aws_iot_greengrass_core_device": tableAwsIotGreengrassCoreDevice(ctx), + "aws_iot_greengrass_deployment": tableAwsIotGreengrassDeployment(ctx), "aws_kinesis_consumer": tableAwsKinesisConsumer(ctx), "aws_kinesis_firehose_delivery_stream": tableAwsKinesisFirehoseDeliveryStream(ctx), "aws_kinesis_stream": tableAwsKinesisStream(ctx), diff --git a/aws/service.go b/aws/service.go index 6c770f0d4..d36391301 100644 --- a/aws/service.go +++ b/aws/service.go @@ -69,6 +69,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/glacier" "github.com/aws/aws-sdk-go-v2/service/globalaccelerator" "github.com/aws/aws-sdk-go-v2/service/glue" + "github.com/aws/aws-sdk-go-v2/service/greengrassv2" "github.com/aws/aws-sdk-go-v2/service/guardduty" "github.com/aws/aws-sdk-go-v2/service/health" "github.com/aws/aws-sdk-go-v2/service/iam" @@ -154,6 +155,7 @@ import ( glacierEndpoint "github.com/aws/aws-sdk-go/service/glacier" inspectorEndpoint "github.com/aws/aws-sdk-go/service/inspector" inspector2Endpoint "github.com/aws/aws-sdk-go/service/inspector2" + greengrassEndpoint "github.com/aws/aws-sdk-go/service/greengrassv2" kafkaEndpoint "github.com/aws/aws-sdk-go/service/kafka" kinesisanalyticsv2Endpoint "github.com/aws/aws-sdk-go/service/kinesisanalyticsv2" kinesisvideoEndpoint "github.com/aws/aws-sdk-go/service/kinesisvideo" @@ -832,6 +834,17 @@ func Inspector2Client(ctx context.Context, d *plugin.QueryData) (*inspector2.Cli return inspector2.NewFromConfig(*cfg), nil } +func IoTGreengrassClient(ctx context.Context, d *plugin.QueryData) (*greengrassv2.Client, error) { +cfg, err := getClientForQuerySupportedRegion(ctx, d, greengrassEndpoint.EndpointsID) + if err != nil { + return nil, err + } + if cfg == nil { + return nil, nil + } + return greengrassv2.NewFromConfig(*cfg), nil +} + func KafkaClient(ctx context.Context, d *plugin.QueryData) (*kafka.Client, error) { cfg, err := getClientForQuerySupportedRegion(ctx, d, kafkaEndpoint.EndpointsID) if err != nil { diff --git a/aws/table_aws_iot_greengrass_component.go b/aws/table_aws_iot_greengrass_component.go new file mode 100644 index 000000000..f9f62c5f6 --- /dev/null +++ b/aws/table_aws_iot_greengrass_component.go @@ -0,0 +1,172 @@ +package aws + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/greengrassv2" + "github.com/aws/aws-sdk-go-v2/service/greengrassv2/types" + + greengrassv1 "github.com/aws/aws-sdk-go/service/greengrassv2" + + "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" +) + +//// TABLE DEFINITION + +func tableAwsIotGreengrassComponent(_ context.Context) *plugin.Table { + return &plugin.Table{ + Name: "aws_iot_greengrass_component", + Description: "AWS IoT Greengrass Component", + List: &plugin.ListConfig{ + Hydrate: listIotGreengrassComponents, + Tags: map[string]string{"service": "greengrassv2", "action": "ListComponents"}, + }, + HydrateConfig: []plugin.HydrateConfig{ + { + Func: getIotGreengrassComponent, + Tags: map[string]string{"service": "greengrassv2", "action": "GetComponent"}, + IgnoreConfig: &plugin.IgnoreConfig{ + ShouldIgnoreErrorFunc: shouldIgnoreErrors([]string{"ResourceNotFoundException"}), + }, + }, + }, + GetMatrixItemFunc: SupportedRegionMatrix(greengrassv1.EndpointsID), + Columns: awsRegionalColumns([]*plugin.Column{ + { + Name: "component_name", + Description: "The name of the component.", + Type: proto.ColumnType_STRING, + }, + { + Name: "arn", + Description: "The ARN of the component version.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ARN"), + }, + { + Name: "recipe", + Description: "The recipe of the component version.", + Type: proto.ColumnType_STRING, + Hydrate: getIotGreengrassComponent, + }, + { + Name: "recipe_output_format", + Description: "The format of the recipe.", + Type: proto.ColumnType_STRING, + Hydrate: getIotGreengrassComponent, + }, + + // JSON columns + { + Name: "latest_version", + Description: "The latest version of the component and its details.", + Type: proto.ColumnType_JSON, + }, + + // Steampipe standard columns + { + Name: "title", + Description: resourceInterfaceDescription("title"), + Type: proto.ColumnType_STRING, + Transform: transform.FromField("CoreDeviceThingName"), + }, + { + Name: "tags", + Description: resourceInterfaceDescription("tags"), + Type: proto.ColumnType_JSON, + Hydrate: getIotGreengrassComponent, + }, + { + Name: "akas", + Description: resourceInterfaceDescription("akas"), + Type: proto.ColumnType_JSON, + Transform: transform.FromField("ARN").Transform(transform.EnsureStringArray), + }, + }), + } +} + +//// LIST FUNCTION + +func listIotGreengrassComponents(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { + // Create Session + svc, err := IoTGreengrassClient(ctx, d) + if err != nil { + plugin.Logger(ctx).Error("aws_iot_greengrass_component.listIotGreengrassComponents", "connection_error", err) + return nil, err + } + if svc == nil { + // Unsupported region, return no data + return nil, nil + } + + // Limiting the results + maxLimit := int32(250) + if d.QueryContext.Limit != nil { + limit := int32(*d.QueryContext.Limit) + if limit < maxLimit { + maxLimit = limit + } + } + + input := &greengrassv2.ListComponentsInput{ + MaxResults: aws.Int32(maxLimit), + } + + paginator := greengrassv2.NewListComponentsPaginator(svc, input, func(o *greengrassv2.ListComponentsPaginatorOptions) { + o.Limit = maxLimit + o.StopOnDuplicateToken = true + }) + + // List call + for paginator.HasMorePages() { + // apply rate limiting + d.WaitForListRateLimit(ctx) + + output, err := paginator.NextPage(ctx) + if err != nil { + plugin.Logger(ctx).Error("aws_iot_greengrass_component.listIotGreengrassComponents", "api_error", err) + return nil, err + } + + for _, item := range output.Components { + d.StreamListItem(ctx, item) + + // Context can be cancelled due to manual cancellation or the limit has been hit + if d.RowsRemaining(ctx) == 0 { + return nil, nil + } + } + } + + return nil, err +} + +//// HYDRATE FUNCTIONS + +func getIotGreengrassComponent(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + t := h.Item.(types.Component) + arn := *t.Arn + + // Create service + svc, err := IoTGreengrassClient(ctx, d) + if err != nil { + plugin.Logger(ctx).Error("aws_iot_greengrass_component.getIotGreengrassComponent", "connection_error", err) + return nil, err + } + + params := &greengrassv2.GetComponentInput{ + Arn: aws.String(arn), + } + + resp, err := svc.GetComponent(ctx, params) + if err != nil { + plugin.Logger(ctx).Error("aws_iot_greengrass_component.getIotGreengrassComponent", "api_error", err) + return nil, err + } + + return resp, nil +} diff --git a/aws/table_aws_iot_greengrass_core_device.go b/aws/table_aws_iot_greengrass_core_device.go new file mode 100644 index 000000000..1d804bd89 --- /dev/null +++ b/aws/table_aws_iot_greengrass_core_device.go @@ -0,0 +1,183 @@ +package aws + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/greengrassv2" + "github.com/aws/aws-sdk-go-v2/service/greengrassv2/types" + + greengrassv1 "github.com/aws/aws-sdk-go/service/greengrassv2" + + "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" +) + +//// TABLE DEFINITION + +func tableAwsIotGreengrassCoreDevice(_ context.Context) *plugin.Table { + return &plugin.Table{ + Name: "aws_iot_greengrass_core_device", + Description: "AWS IoT Greengrass Core Device", + Get: &plugin.GetConfig{ + KeyColumns: plugin.SingleColumn("core_device_thing_name"), + Hydrate: getIotGreengrassCoreDevice, + Tags: map[string]string{"service": "greengrassv2", "action": "GetCoreDevice"}, + IgnoreConfig: &plugin.IgnoreConfig{ + ShouldIgnoreErrorFunc: shouldIgnoreErrors([]string{"ResourceNotFoundException"}), + }, + }, + List: &plugin.ListConfig{ + Hydrate: listIotGreengrassCoreDevices, + Tags: map[string]string{"service": "greengrassv2", "action": "ListCoreDevices"}, + KeyColumns: plugin.KeyColumnSlice{ + {Name: "status", Require: plugin.Optional}, + }, + }, + GetMatrixItemFunc: SupportedRegionMatrix(greengrassv1.EndpointsID), + Columns: awsRegionalColumns([]*plugin.Column{ + { + Name: "core_device_thing_name", + Description: "The name of the core device. This is also the name of the IoT thing.", + Type: proto.ColumnType_STRING, + }, + { + Name: "architecture", + Description: "The computer architecture of the core device.", + Type: proto.ColumnType_STRING, + Hydrate: getIotGreengrassCoreDevice, + }, + { + Name: "core_version", + Description: "The version of the IoT Greengrass Core software that the core device runs.", + Type: proto.ColumnType_STRING, + Hydrate: getIotGreengrassCoreDevice, + }, + { + Name: "platform", + Description: "The operating system platform that the core device runs.", + Type: proto.ColumnType_STRING, + Hydrate: getIotGreengrassCoreDevice, + }, + { + Name: "last_status_update_timestamp", + Description: "The time at which the core device's status last updated, expressed in ISO 8601 format.", + Type: proto.ColumnType_TIMESTAMP, + }, + { + Name: "status", + Description: "The status of the core device. Core devices can have the following statuses HEALTHY, UNHEALTHY.", + Type: proto.ColumnType_STRING, + }, + + // Steampipe standard columns + { + Name: "title", + Description: resourceInterfaceDescription("title"), + Type: proto.ColumnType_STRING, + Transform: transform.FromField("CoreDeviceThingName"), + }, + { + Name: "tags", + Description: resourceInterfaceDescription("tags"), + Type: proto.ColumnType_JSON, + Hydrate: getIotGreengrassCoreDevice, + }, + }), + } +} + +//// LIST FUNCTION + +func listIotGreengrassCoreDevices(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { + // Create Session + svc, err := IoTGreengrassClient(ctx, d) + if err != nil { + plugin.Logger(ctx).Error("aws_iot_greengrass_core_device.listIotGreengrassCoreDevices", "connection_error", err) + return nil, err + } + if svc == nil { + // Unsupported region, return no data + return nil, nil + } + + // Limiting the results + maxLimit := int32(100) + if d.QueryContext.Limit != nil { + limit := int32(*d.QueryContext.Limit) + if limit < maxLimit { + maxLimit = limit + } + } + + input := &greengrassv2.ListCoreDevicesInput{ + MaxResults: aws.Int32(maxLimit), + } + if d.EqualsQualString("status") != "" { + input.Status = types.CoreDeviceStatus(d.EqualsQualString("status")) + } + + paginator := greengrassv2.NewListCoreDevicesPaginator(svc, input, func(o *greengrassv2.ListCoreDevicesPaginatorOptions) { + o.Limit = maxLimit + o.StopOnDuplicateToken = true + }) + + // List call + for paginator.HasMorePages() { + // apply rate limiting + d.WaitForListRateLimit(ctx) + + output, err := paginator.NextPage(ctx) + if err != nil { + plugin.Logger(ctx).Error("aws_iot_greengrass_core_device.listIotGreengrassCoreDevices", "api_error", err) + return nil, err + } + + for _, item := range output.CoreDevices { + d.StreamListItem(ctx, item) + + // Context can be cancelled due to manual cancellation or the limit has been hit + if d.RowsRemaining(ctx) == 0 { + return nil, nil + } + } + } + + return nil, err +} + +//// HYDRATE FUNCTIONS + +func getIotGreengrassCoreDevice(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + coreDeviceThingName := "" + if h.Item != nil { + t := h.Item.(types.CoreDevice) + coreDeviceThingName = *t.CoreDeviceThingName + } else { + coreDeviceThingName = d.EqualsQualString("core_device_thing_name") + } + + if coreDeviceThingName == "" { + return nil, nil + } + + // Create service + svc, err := IoTGreengrassClient(ctx, d) + if err != nil { + plugin.Logger(ctx).Error("aws_iot_greengrass_core_device.getIotGreengrassCoreDevice", "connection_error", err) + return nil, err + } + + params := &greengrassv2.GetCoreDeviceInput{ + CoreDeviceThingName: aws.String(coreDeviceThingName), + } + + resp, err := svc.GetCoreDevice(ctx, params) + if err != nil { + plugin.Logger(ctx).Error("aws_iot_greengrass_core_device.getIotGreengrassCoreDevice", "api_error", err) + return nil, err + } + + return resp, nil +} diff --git a/aws/table_aws_iot_greengrass_deployment.go b/aws/table_aws_iot_greengrass_deployment.go new file mode 100644 index 000000000..945d5184e --- /dev/null +++ b/aws/table_aws_iot_greengrass_deployment.go @@ -0,0 +1,226 @@ +package aws + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/greengrassv2" + "github.com/aws/aws-sdk-go-v2/service/greengrassv2/types" + + greengrassv1 "github.com/aws/aws-sdk-go/service/greengrassv2" + + "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" +) + +//// TABLE DEFINITION + +func tableAwsIotGreengrassDeployment(_ context.Context) *plugin.Table { + return &plugin.Table{ + Name: "aws_iot_greengrass_deployment", + Description: "AWS IoT Greengrass Deployment", + Get: &plugin.GetConfig{ + KeyColumns: plugin.SingleColumn("deployment_id"), + Hydrate: getIotGreengrassDeployment, + Tags: map[string]string{"service": "greengrassv2", "action": "GetDeployment"}, + IgnoreConfig: &plugin.IgnoreConfig{ + ShouldIgnoreErrorFunc: shouldIgnoreErrors([]string{"ResourceNotFoundException"}), + }, + }, + List: &plugin.ListConfig{ + Hydrate: listIotGreengrassDeployments, + Tags: map[string]string{"service": "greengrassv2", "action": "ListDeployments"}, + KeyColumns: plugin.KeyColumnSlice{ + {Name: "parent_target_arn", Require: plugin.Optional}, + {Name: "target_arn", Require: plugin.Optional}, + }, + }, + GetMatrixItemFunc: SupportedRegionMatrix(greengrassv1.EndpointsID), + Columns: awsRegionalColumns([]*plugin.Column{ + { + Name: "deployment_name", + Description: "The name of the deployment.", + Type: proto.ColumnType_STRING, + }, + { + Name: "deployment_id", + Description: "The ID of the deployment.", + Type: proto.ColumnType_STRING, + }, + { + Name: "deployment_status", + Description: "The status of the deployment.", + Type: proto.ColumnType_STRING, + }, + { + Name: "is_latest_for_target", + Description: "Whether or not the deployment is the latest revision for its target.", + Type: proto.ColumnType_BOOL, + }, + { + Name: "creation_timestamp", + Description: "The time at which the deployment was created, expressed in ISO 8601 format.", + Type: proto.ColumnType_TIMESTAMP, + }, + { + Name: "parent_target_arn", + Description: "The parent deployment's target ARN within a subdeployment.", + Type: proto.ColumnType_STRING, + }, + { + Name: "revision_id", + Description: "The revision number of the deployment.", + Type: proto.ColumnType_STRING, + }, + { + Name: "target_arn", + Description: "The ARN of the target IoT thing or thing group. When creating a subdeployment, the targetARN can only be a thing group.", + Type: proto.ColumnType_STRING, + }, + { + Name: "iot_job_arn", + Description: "The ARN of the IoT job that applies the deployment to target devices.", + Type: proto.ColumnType_STRING, + Hydrate: getIotGreengrassDeployment, + }, + { + Name: "iot_job_id", + Description: "The ID of the IoT job that applies the deployment to target devices.", + Type: proto.ColumnType_STRING, + Hydrate: getIotGreengrassDeployment, + }, + + // JSON fields + { + Name: "components", + Description: "The components to deploy. This is a dictionary, where each key is the name of a component, and each key's value is the version and configuration to deploy for that component.", + Type: proto.ColumnType_JSON, + Hydrate: getIotGreengrassDeployment, + }, + { + Name: "deployment_policies", + Description: "The deployment policies for the deployment. These policies define how the deployment updates components and handles failure.", + Type: proto.ColumnType_JSON, + Hydrate: getIotGreengrassDeployment, + }, + { + Name: "iot_job_configuration", + Description: " The job configuration for the deployment configuration. The job configuration specifies the rollout, timeout, and stop configurations for the deployment configuration.", + Type: proto.ColumnType_JSON, + Hydrate: getIotGreengrassDeployment, + }, + + // Steampipe standard columns + { + Name: "title", + Description: resourceInterfaceDescription("title"), + Type: proto.ColumnType_STRING, + Transform: transform.FromField("DeploymentName"), + }, + { + Name: "tags", + Description: resourceInterfaceDescription("tags"), + Type: proto.ColumnType_JSON, + Hydrate: getIotGreengrassDeployment, + }, + }), + } +} + +//// LIST FUNCTION + +func listIotGreengrassDeployments(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { + // Create Session + svc, err := IoTGreengrassClient(ctx, d) + if err != nil { + plugin.Logger(ctx).Error("aws_iot_greengrass_deployment.listIotGreengrassDeployments", "connection_error", err) + return nil, err + } + if svc == nil { + // Unsupported region, return no data + return nil, nil + } + + // Limiting the results + maxLimit := int32(100) + if d.QueryContext.Limit != nil { + limit := int32(*d.QueryContext.Limit) + if limit < maxLimit { + maxLimit = limit + } + } + + input := &greengrassv2.ListDeploymentsInput{ + MaxResults: aws.Int32(maxLimit), + } + if d.EqualsQualString("target_arn") != "" { + input.TargetArn = aws.String(d.EqualsQualString("target_arn")) + } + if d.EqualsQualString("parent_target_arn") != "" { + input.ParentTargetArn = aws.String(d.EqualsQualString("parent_target_arn")) + } + + paginator := greengrassv2.NewListDeploymentsPaginator(svc, input, func(o *greengrassv2.ListDeploymentsPaginatorOptions) { + o.Limit = maxLimit + o.StopOnDuplicateToken = true + }) + + // List call + for paginator.HasMorePages() { + // apply rate limiting + d.WaitForListRateLimit(ctx) + + output, err := paginator.NextPage(ctx) + if err != nil { + plugin.Logger(ctx).Error("aws_iot_greengrass_deployment.listIotGreengrassDeployments", "api_error", err) + return nil, err + } + + for _, item := range output.Deployments { + d.StreamListItem(ctx, item) + + // Context can be cancelled due to manual cancellation or the limit has been hit + if d.RowsRemaining(ctx) == 0 { + return nil, nil + } + } + } + + return nil, err +} + +//// HYDRATE FUNCTIONS + +func getIotGreengrassDeployment(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + deploymentId := "" + if h.Item != nil { + t := h.Item.(types.Deployment) + deploymentId = *t.DeploymentId + } else { + deploymentId = d.EqualsQualString("deployment_id") + } + + if deploymentId == "" { + return nil, nil + } + + // Create service + svc, err := IoTGreengrassClient(ctx, d) + if err != nil { + plugin.Logger(ctx).Error("aws_iot_greengrass_deployment.getIotGreengrassDeployment", "connection_error", err) + return nil, err + } + + params := &greengrassv2.GetDeploymentInput{ + DeploymentId: aws.String(deploymentId), + } + + resp, err := svc.GetDeployment(ctx, params) + if err != nil { + plugin.Logger(ctx).Error("aws_iot_greengrass_deployment.getIotGreengrassDeployment", "api_error", err) + return nil, err + } + + return resp, nil +} diff --git a/docs/tables/aws_iot_greengrass_component.md b/docs/tables/aws_iot_greengrass_component.md new file mode 100644 index 000000000..9328dc03a --- /dev/null +++ b/docs/tables/aws_iot_greengrass_component.md @@ -0,0 +1,45 @@ +# Table: aws_iot_greengrass_component + +AWS IoT Greengrass components are software modules that you deploy to Greengrass core devices. Components can represent applications, runtime installers, libraries, or any code that you would run on a device. You can define components that depend on other components. For example, you might define a component that installs Python, and then define that component as a dependency of your components that run Python applications. When you deploy your components to your fleets of devices, Greengrass deploys only the software modules that your devices require. + +## Examples + +### Basic info + +```sql +select + component_name, + arn, + recipe, + recipe_output_format +from + aws_iot_greengrass_component; +``` + +### Get the latest version details of components + +```sql +select + component_name, + latest_version ->> 'Arn' as latest_version_arn, + latest_version ->> 'ComponentVersion' as component_version, + latest_version ->> 'Description' as description, + latest_version ->> 'CreationTimestamp' as creation_timestamp, + latest_version -> 'Platforms' as platforms, + latest_version ->> 'Publisher' as publisher +from + aws_iot_greengrass_component; +``` + +### Filter the components by tag + +```sql +select + component_name, + arn, + recipe +from + aws_iot_greengrass_component +where + tags -> 'owner' is null; +``` diff --git a/docs/tables/aws_iot_greengrass_core_device.md b/docs/tables/aws_iot_greengrass_core_device.md new file mode 100644 index 000000000..385a2da93 --- /dev/null +++ b/docs/tables/aws_iot_greengrass_core_device.md @@ -0,0 +1,46 @@ +# Table: aws_iot_greengrass_core_device + +A Greengrass core is a device that runs the AWS IoT Greengrass Core software, which allows it to communicate directly with AWS IoT Core and the AWS IoT Greengrass service. A core has its own device certificate used for authenticating with AWS IoT Core. It has a device shadow and an entry in the AWS IoT Core registry. Greengrass cores run a local Lambda runtime, deployment agent, and IP address tracker that sends IP address information to the AWS IoT Greengrass service to allow client devices to automatically discover their group and core connection information. + +## Examples + +### Basic info + +```sql +select + core_device_thing_name, + architecture, + core_version, + platform +from + aws_iot_greengrass_core_device; +``` + +### List devices that are unhealthy + +```sql +select + core_device_thing_name, + architecture, + core_version + last_status_update_timestamp, + status +from + aws_iot_greengrass_core_device +where + status = 'UNHEALTHY'; +``` + +### List devices by platform + +```sql +select + core_device_thing_name, + architecture, + core_version + platform +from + aws_iot_greengrass_core_device +where + platform ILIKE 'linux'; +``` diff --git a/docs/tables/aws_iot_greengrass_deployment.md b/docs/tables/aws_iot_greengrass_deployment.md new file mode 100644 index 000000000..1cc865e3b --- /dev/null +++ b/docs/tables/aws_iot_greengrass_deployment.md @@ -0,0 +1,98 @@ +# Table: aws_iot_greengrass_deployment + +Each core device runs the components of the deployments for that device. A new deployment to the same target overwrites the previous deployment to the target. When you create a deployment, you define the components and configurations to apply to the core device's existing software. + +When you revise a deployment for a target, you replace the components from the previous revision with the components in the new revision. For example, you deploy the Log manager and Secret manager components to the thing group TestGroup. Then you create another deployment for TestGroup that specifies only the secret manager component. As a result, the core devices in that group no longer run the log manager. + +## Examples + +### Basic info + +```sql +select + deployment_name, + deployment_id, + is_latest_for_target, + creation_timestamp, + parent_target_arn, + target_arn +from + aws_iot_greengrass_deployment; +``` + +### List deployments created in the last 30 days + +```sql +select + deployment_name, + deployment_id, + is_latest_for_target, + creation_timestamp, + target_arn +from + aws_iot_greengrass_deployment +where + creation_timestamp >= now() - interval '30' day; +``` + +### List deployments that are the latest revision for its target + +```sql +select + deployment_name, + deployment_id, + is_latest_for_target, + revision_id, + iot_job_arn, + iot_job_id +from + aws_iot_greengrass_deployment +where + is_latest_for_target; +``` + +### Get deployment policy details of the deployments + +```sql +select + deployment_name, + deployment_policies -> 'ComponentUpdatePolicy' as component_update_policy, + deployment_policies -> 'ConfigurationValidationPolicy' as configuration_validation_policy, + deployment_policies -> 'FailureHandlingPolicy' as failure_handling_policy +from + aws_iot_greengrass_deployment +where + is_latest_for_target; +``` + +### Get IoT job configuration details of deployments + +```sql +select + deployment_name, + iot_job_configuration -> 'AbortConfig' as abort_config, + iot_job_configuration -> 'JobExecutionsRolloutConfig' as job_executions_rollout_config, + iot_job_configuration -> 'TimeoutConfig' as timeout_config +from + aws_iot_greengrass_deployment +where + is_latest_for_target +``` + +### Get IoT thing group details of deployments + +```sql +select + d.deployment_name, + d.target_arn as thing_group_arn, + d.parent_target_arn, + g.group_name, + g.status as thing_group_status, + g.query_string, + g.parent_group_name +from + aws_iot_greengrass_deployment as d, + aws_iot_thing_group as g +where + g.arn = d.target_arn; +``` \ No newline at end of file diff --git a/go.mod b/go.mod index 4d25d0459..715a936cf 100644 --- a/go.mod +++ b/go.mod @@ -62,6 +62,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/glacier v1.14.0 github.com/aws/aws-sdk-go-v2/service/globalaccelerator v1.16.0 github.com/aws/aws-sdk-go-v2/service/glue v1.40.0 + github.com/aws/aws-sdk-go-v2/service/greengrassv2 v1.24.0 github.com/aws/aws-sdk-go-v2/service/guardduty v1.18.0 github.com/aws/aws-sdk-go-v2/service/health v1.16.0 github.com/aws/aws-sdk-go-v2/service/iam v1.19.0 @@ -119,7 +120,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/wafv2 v1.24.2 github.com/aws/aws-sdk-go-v2/service/wellarchitected v1.20.1 github.com/aws/aws-sdk-go-v2/service/workspaces v1.28.0 - github.com/aws/smithy-go v1.14.2 + github.com/aws/smithy-go v1.18.1 github.com/gocarina/gocsv v0.0.0-20201208093247-67c824bc04d4 github.com/golang/protobuf v1.5.3 github.com/turbot/go-kit v0.8.0-rc.0 diff --git a/go.sum b/go.sum index 8e1b3b1d6..9a88e5b23 100644 --- a/go.sum +++ b/go.sum @@ -212,7 +212,6 @@ github.com/aws/aws-sdk-go-v2 v1.17.5/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3eP github.com/aws/aws-sdk-go-v2 v1.17.6/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.18.1/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2 v1.19.0 h1:klAT+y3pGFBU/qVf1uzwttpBbiuozJYWzNLHioyDJ+k= github.com/aws/aws-sdk-go-v2 v1.19.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.21.0 h1:gMT0IW+03wtYJhRqTVYn0wLzwdnK9sRMcxmtfGzRdJc= github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= @@ -229,7 +228,6 @@ github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29/go.mod h1:Dip3sIGv48 github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30/go.mod h1:LUBAO3zNXQjoONBKn/kR1y0Q4cj/D02Ts0uHYjcCQLM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34/go.mod h1:wZpTEecJe0Btj3IYnDx/VlUzor9wm3fJHyvLpQF0VwY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 h1:hMUCiE3Zi5AHrRNGf5j985u0WyqI6r2NULhUfo0N/No= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35/go.mod h1:ipR5PvpSPqIqL5Mi82BxLnfMkHVbmco8kUwO2xrCi0M= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas= @@ -238,7 +236,6 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23/go.mod h1:mr6c4cHC+S/ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24/go.mod h1:gAuCezX/gob6BSMbItsSlMb6WZGV7K2+fWOvk8xBSto= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28/go.mod h1:7VRpKQQedkfIEXb4k52I7swUnZP0wohVajJMRn3vsUw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 h1:yOpYx+FTBdpk/g+sBU6Cb1H0U/TLEcYYp66mYqsPpcc= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29/go.mod h1:M/eUABlDbw2uVrdAn+UsI6M727qp2fxkp8K0ejcBDUY= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw= @@ -356,6 +353,8 @@ github.com/aws/aws-sdk-go-v2/service/globalaccelerator v1.16.0 h1:ql/fiMSSB4Khjq github.com/aws/aws-sdk-go-v2/service/globalaccelerator v1.16.0/go.mod h1:DtC1mhXEIaOXYLdHNHXu4BD4r3L2AX1DLipIm+rWjNI= github.com/aws/aws-sdk-go-v2/service/glue v1.40.0 h1:2BRxT2dD1/lh1GjPqijYIWpDqlBbNe7tW6Bt8uAeA+k= github.com/aws/aws-sdk-go-v2/service/glue v1.40.0/go.mod h1:kXio9ACrVjRndIledemYxonQhqmfDFbgDH3/AIBx3Hk= +github.com/aws/aws-sdk-go-v2/service/greengrassv2 v1.24.0 h1:4owXy7W+fwSCy4SiI/k+9nGT0KCikAwy6jMQQ0pXXCo= +github.com/aws/aws-sdk-go-v2/service/greengrassv2 v1.24.0/go.mod h1:jX5Jy9hR+qVLNkQmz8/aDdZeO44jOV05h7MmrGwyFCA= github.com/aws/aws-sdk-go-v2/service/guardduty v1.18.0 h1:7y+pLGYzArJ2dfN7xDXvcrP57B/QmxL5UwMsEhmkdZ8= github.com/aws/aws-sdk-go-v2/service/guardduty v1.18.0/go.mod h1:OGpwaCNgEABe9oLMLKKejMXS1I2umXzJmJIvzE57e1A= github.com/aws/aws-sdk-go-v2/service/health v1.16.0 h1:667XFqUFGBHA2tYlZlgb3nAvM+K+NKT6dZR5sTS1bBE= @@ -485,12 +484,9 @@ github.com/aws/aws-sdk-go-v2/service/wellarchitected v1.20.1/go.mod h1:Hb9paYQ+u github.com/aws/aws-sdk-go-v2/service/workspaces v1.28.0 h1:sjfoG4ahd0yE77sKiBokTlZHd9pk1k+vPGNJo6ct2QQ= github.com/aws/aws-sdk-go-v2/service/workspaces v1.28.0/go.mod h1:oudvE1/KOdqMDrq9PG1QjLpvQb8S5R3y2Fci+Vuc6To= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.14.1 h1:EFKMUmH/iHMqLiwoEDx2rRjRQpI1YCn5jTysoaDujFs= -github.com/aws/smithy-go v1.14.1/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ= github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/aws/smithy-go v1.18.1 h1:pOdBTUfXNazOlxLrgeYalVnuTpKreACHtc62xLwIB3c= +github.com/aws/smithy-go v1.18.1/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= 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= From 76bc24d0a8acfbba8e392746d65d6fab401121e2 Mon Sep 17 00:00:00 2001 From: ParthaI Date: Tue, 30 Jan 2024 20:07:01 +0530 Subject: [PATCH 2/5] Updated the doc for the tables to match with latest doc format and added the missing rate limit configuration --- aws/table_aws_iot_greengrass_core_device.go | 6 + docs/tables/aws_iot_greengrass_component.md | 54 ++++++++- docs/tables/aws_iot_greengrass_core_device.md | 61 ++++++++-- docs/tables/aws_iot_greengrass_deployment.md | 111 ++++++++++++++++-- 4 files changed, 212 insertions(+), 20 deletions(-) diff --git a/aws/table_aws_iot_greengrass_core_device.go b/aws/table_aws_iot_greengrass_core_device.go index 1d804bd89..c67e3dc15 100644 --- a/aws/table_aws_iot_greengrass_core_device.go +++ b/aws/table_aws_iot_greengrass_core_device.go @@ -35,6 +35,12 @@ func tableAwsIotGreengrassCoreDevice(_ context.Context) *plugin.Table { {Name: "status", Require: plugin.Optional}, }, }, + HydrateConfig: []plugin.HydrateConfig{ + { + Func: getIotGreengrassCoreDevice, + Tags: map[string]string{"service": "greengrassv2", "action": "GetCoreDevice"}, + }, + }, GetMatrixItemFunc: SupportedRegionMatrix(greengrassv1.EndpointsID), Columns: awsRegionalColumns([]*plugin.Column{ { diff --git a/docs/tables/aws_iot_greengrass_component.md b/docs/tables/aws_iot_greengrass_component.md index 9328dc03a..7b88bbee0 100644 --- a/docs/tables/aws_iot_greengrass_component.md +++ b/docs/tables/aws_iot_greengrass_component.md @@ -1,12 +1,32 @@ -# Table: aws_iot_greengrass_component +--- +title: "Steampipe Table: aws_iot_greengrass_component - Query AWS IoT Greengrass Components using SQL" +description: "Allows users to query AWS IoT Greengrass Components. This table provides information about Greengrass Components, which are software modules deployed to Greengrass core devices. Components can represent applications, runtime installers, libraries, or any code that runs on a device. Users can define components with dependencies, and Greengrass deploys only the required software modules when deploying to devices." +--- + +# Table: aws_iot_greengrass_component - Query AWS IoT Greengrass Components using SQL AWS IoT Greengrass components are software modules that you deploy to Greengrass core devices. Components can represent applications, runtime installers, libraries, or any code that you would run on a device. You can define components that depend on other components. For example, you might define a component that installs Python, and then define that component as a dependency of your components that run Python applications. When you deploy your components to your fleets of devices, Greengrass deploys only the software modules that your devices require. +## Table Usage Guide + +The `aws_iot_greengrass_component` table in Steampipe enables users to query information about AWS IoT Greengrass Components. This includes details such as component name, ARN, recipe, and recipe output format. Users can also retrieve the latest version details of components, filter components by tags, and gain insights into component dependencies. + ## Examples ### Basic info +Retrieve basic information about Greengrass Components, such as their names, ARNs, recipes, and recipe output formats. This query provides an overview of the components in your environment. + +```sql+postgres +select + component_name, + arn, + recipe, + recipe_output_format +from + aws_iot_greengrass_component; +``` -```sql +```sql+sqlite select component_name, arn, @@ -17,8 +37,9 @@ from ``` ### Get the latest version details of components +Retrieve the latest version details of Greengrass Components, including the ARN, component version, description, creation timestamp, supported platforms, and publisher. This query helps you stay up-to-date with the latest component versions. -```sql +```sql+postgres select component_name, latest_version ->> 'Arn' as latest_version_arn, @@ -31,9 +52,23 @@ from aws_iot_greengrass_component; ``` +```sql+sqlite +select + component_name, + json_extract(latest_version, '$.Arn') as latest_version_arn, + json_extract(latest_version, '$.ComponentVersion') as component_version, + json_extract(latest_version, '$.Description') as description, + json_extract(latest_version, '$.CreationTimestamp') as creation_timestamp, + json_extract(latest_version, '$.Platforms') as platforms, + json_extract(latest_version, '$.Publisher') as publisher +from + aws_iot_greengrass_component; +``` + ### Filter the components by tag +Filter Greengrass Components based on their tags. In this example, components with a missing 'owner' tag are retrieved. This query allows you to categorize and manage components effectively. -```sql +```sql+postgres select component_name, arn, @@ -43,3 +78,14 @@ from where tags -> 'owner' is null; ``` + +```sql+sqlite +select + component_name, + arn, + recipe +from + aws_iot_greengrass_component +where + json_extract(tags, '$.owner') is null; +``` diff --git a/docs/tables/aws_iot_greengrass_core_device.md b/docs/tables/aws_iot_greengrass_core_device.md index 385a2da93..c6628d441 100644 --- a/docs/tables/aws_iot_greengrass_core_device.md +++ b/docs/tables/aws_iot_greengrass_core_device.md @@ -1,12 +1,32 @@ -# Table: aws_iot_greengrass_core_device +--- +title: "Steampipe Table: aws_iot_greengrass_core_device - Query AWS IoT Greengrass Core Devices using SQL" +description: "Allows users to query AWS IoT Greengrass Core Devices. This table provides information about Greengrass Core Devices within AWS IoT Greengrass, enabling users to gather insights on core devices, including their thing name, architecture, core version, and platform." +--- -A Greengrass core is a device that runs the AWS IoT Greengrass Core software, which allows it to communicate directly with AWS IoT Core and the AWS IoT Greengrass service. A core has its own device certificate used for authenticating with AWS IoT Core. It has a device shadow and an entry in the AWS IoT Core registry. Greengrass cores run a local Lambda runtime, deployment agent, and IP address tracker that sends IP address information to the AWS IoT Greengrass service to allow client devices to automatically discover their group and core connection information. +# Table: aws_iot_greengrass_core_device - Query AWS IoT Greengrass Core Devices using SQL + +A Greengrass core device is a fundamental component that runs the AWS IoT Greengrass Core software. This software allows the core device to establish direct communication with AWS IoT Core and the AWS IoT Greengrass service. Each core device possesses its unique device certificate for authentication with AWS IoT Core. Additionally, core devices have a device shadow and are registered in the AWS IoT Core registry. Greengrass core devices operate a local Lambda runtime, deployment agent, and IP address tracker. The IP address tracker sends IP address information to the AWS IoT Greengrass service, enabling client devices to automatically discover their group and core connection information. + +## Table Usage Guide + +The `aws_iot_greengrass_core_device` table in Steampipe provides IoT engineers and DevOps professionals with the ability to query specific details about AWS IoT Greengrass Core Devices. This includes essential information like core device thing name, architecture, core version, and platform. You can use this table to monitor the health and status of Greengrass core devices, identify unhealthy devices, and filter devices by their platform for efficient management. ## Examples ### Basic info +Retrieve basic information about Greengrass Core Devices, such as their thing names, architecture, core versions, and platforms. This query provides an overview of the core devices in your environment. + +```sql+postgres +select + core_device_thing_name, + architecture, + core_version, + platform +from + aws_iot_greengrass_core_device; +``` -```sql +```sql+sqlite select core_device_thing_name, architecture, @@ -17,12 +37,26 @@ from ``` ### List devices that are unhealthy +Identify Greengrass Core Devices that are marked as 'UNHEALTHY' in their status. This helps you monitor and manage devices with potential issues. + +```sql+postgres +select + core_device_thing_name, + architecture, + core_version, + last_status_update_timestamp, + status +from + aws_iot_greengrass_core_device +where + status = 'UNHEALTHY'; +``` -```sql +```sql+sqlite select core_device_thing_name, architecture, - core_version + core_version, last_status_update_timestamp, status from @@ -32,15 +66,28 @@ where ``` ### List devices by platform +Filter Greengrass Core Devices by platform, focusing on devices with a platform that matches 'linux.' This query allows you to group and manage devices based on their platform type. -```sql +```sql+postgres select core_device_thing_name, architecture, - core_version + core_version, platform from aws_iot_greengrass_core_device where platform ILIKE 'linux'; ``` + +```sql+sqlite +select + core_device_thing_name, + architecture, + core_version, + platform +from + aws_iot_greengrass_core_device +where + platform LIKE 'linux'; +``` diff --git a/docs/tables/aws_iot_greengrass_deployment.md b/docs/tables/aws_iot_greengrass_deployment.md index 1cc865e3b..d304b7e12 100644 --- a/docs/tables/aws_iot_greengrass_deployment.md +++ b/docs/tables/aws_iot_greengrass_deployment.md @@ -1,14 +1,24 @@ -# Table: aws_iot_greengrass_deployment +--- +title: "Steampipe Table: aws_iot_greengrass_deployment - Query AWS IoT Greengrass Deployments using SQL" +description: "Allows users to query AWS IoT Greengrass Deployments. This table provides information about Greengrass Deployments within AWS IoT Greengrass, enabling users to gather insights on deployments such as deployment name, ID, creation timestamp, and more." +--- -Each core device runs the components of the deployments for that device. A new deployment to the same target overwrites the previous deployment to the target. When you create a deployment, you define the components and configurations to apply to the core device's existing software. +# Table: aws_iot_greengrass_deployment - Query AWS IoT Greengrass Deployments using SQL -When you revise a deployment for a target, you replace the components from the previous revision with the components in the new revision. For example, you deploy the Log manager and Secret manager components to the thing group TestGroup. Then you create another deployment for TestGroup that specifies only the secret manager component. As a result, the core devices in that group no longer run the log manager. +The AWS IoT Greengrass Deployment is a feature within the AWS IoT Greengrass service that manages the deployment of software to IoT devices. Each core device runs the components of the deployments for that device. A new deployment to the same target overwrites the previous deployment to the target. + +When revising a deployment for a target, it replaces the components from the previous revision with the components in the new revision. For instance, if you initially deploy certain components to a group and later create another deployment for the same group with different components, the core devices in that group will run the new set of components. + +## Table Usage Guide + +The `aws_iot_greengrass_deployment` table in Steampipe enables you, as an IoT engineer or DevOps professional, to query deployment-specific details within AWS IoT Greengrass. This includes information like deployment name, ID, whether it's the latest for the target, creation timestamp, and associated target ARNs. You can use this table to monitor and manage deployments, ensuring that your IoT devices are running the desired software configurations and versions. ## Examples ### Basic info +Query basic information about Greengrass deployments, such as their names, IDs, and associated target ARNs. This is useful for a quick overview of the deployments in your environment. -```sql +```sql+postgres select deployment_name, deployment_id, @@ -20,9 +30,22 @@ from aws_iot_greengrass_deployment; ``` +```sql+sqlite +select + deployment_name, + deployment_id, + is_latest_for_target, + creation_timestamp, + parent_target_arn, + target_arn +from + aws_iot_greengrass_deployment; +`` + ### List deployments created in the last 30 days +Identify recent deployments to manage and monitor new changes in your IoT environment. -```sql +```sql+postgres select deployment_name, deployment_id, @@ -35,9 +58,37 @@ where creation_timestamp >= now() - interval '30' day; ``` +```sql+sqlite +select + deployment_name, + deployment_id, + is_latest_for_target, + creation_timestamp, + target_arn +from + aws_iot_greengrass_deployment +where + creation_timestamp >= datetime('now', '-30 day'); +``` + ### List deployments that are the latest revision for its target +Focus on the most current deployments for each target to ensure up-to-date configuration and software. + +```sql+postgres +select + deployment_name, + deployment_id, + is_latest_for_target, + revision_id, + iot_job_arn, + iot_job_id +from + aws_iot_greengrass_deployment +where + is_latest_for_target; +``` -```sql +```sql+sqlite select deployment_name, deployment_id, @@ -52,8 +103,9 @@ where ``` ### Get deployment policy details of the deployments +Examine the specific policies associated with each deployment to understand how they are configured and managed. -```sql +```sql+postgres select deployment_name, deployment_policies -> 'ComponentUpdatePolicy' as component_update_policy, @@ -65,9 +117,22 @@ where is_latest_for_target; ``` +```sql+sqlite +select + deployment_name, + json_extract(deployment_policies, '$.ComponentUpdatePolicy') as component_update_policy, + json_extract(deployment_policies, '$.ConfigurationValidationPolicy') as configuration_validation_policy, + json_extract(deployment_policies, '$.FailureHandlingPolicy') as failure_handling_policy +from + aws_iot_greengrass_deployment +where + is_latest_for_target; +``` + ### Get IoT job configuration details of deployments +Review the configurations related to IoT jobs associated with the deployments for detailed insight into their execution and management. -```sql +```sql+postgres select deployment_name, iot_job_configuration -> 'AbortConfig' as abort_config, @@ -79,9 +144,22 @@ where is_latest_for_target ``` +```sql+sqlite +select + deployment_name, + json_extract(iot_job_configuration, '$.AbortConfig') as abort_config, + json_extract(iot_job_configuration, '$.JobExecutionsRolloutConfig') as job_executions_rollout_config, + json_extract(iot_job_configuration, '$.TimeoutConfig') as timeout_config +from + aws_iot_greengrass_deployment +where + is_latest_for_target; +``` + ### Get IoT thing group details of deployments +Link deployments to specific IoT thing groups for a comprehensive understanding of which groups are affected by which deployments. -```sql +```sql+postgres select d.deployment_name, d.target_arn as thing_group_arn, @@ -95,4 +173,19 @@ from aws_iot_thing_group as g where g.arn = d.target_arn; +``` + +```sql+sqlite +select + d.deployment_name, + d.target_arn as thing_group_arn, + d.parent_target_arn, + g.group_name, + g.status as thing_group_status, + g.query_string, + g.parent_group_name +from + aws_iot_greengrass_deployment as d +join + aws_iot_thing_group as g on g.arn = d.target_arn; ``` \ No newline at end of file From fc62324b53dc3e5b2cfa44802dc3831a0226490e Mon Sep 17 00:00:00 2001 From: Khushboo <46913995+khushboo9024@users.noreply.github.com> Date: Mon, 1 Apr 2024 14:45:18 +0530 Subject: [PATCH 3/5] Update aws_iot_greengrass_deployment.md --- docs/tables/aws_iot_greengrass_deployment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tables/aws_iot_greengrass_deployment.md b/docs/tables/aws_iot_greengrass_deployment.md index d304b7e12..87ea18eb8 100644 --- a/docs/tables/aws_iot_greengrass_deployment.md +++ b/docs/tables/aws_iot_greengrass_deployment.md @@ -40,7 +40,7 @@ select target_arn from aws_iot_greengrass_deployment; -`` +``` ### List deployments created in the last 30 days Identify recent deployments to manage and monitor new changes in your IoT environment. @@ -188,4 +188,4 @@ from aws_iot_greengrass_deployment as d join aws_iot_thing_group as g on g.arn = d.target_arn; -``` \ No newline at end of file +``` From c0fa800f085e874ff0bb3e395b718c0eeff78f53 Mon Sep 17 00:00:00 2001 From: ParthaI Date: Mon, 1 Apr 2024 17:33:24 +0530 Subject: [PATCH 4/5] Made changes as per the review comments and fixed the invalid parameter exception. --- aws/table_aws_iot_greengrass_component.go | 59 +++++++++++++++------ aws/table_aws_iot_greengrass_core_device.go | 26 ++++----- aws/table_aws_iot_greengrass_deployment.go | 34 +++++++----- 3 files changed, 75 insertions(+), 44 deletions(-) diff --git a/aws/table_aws_iot_greengrass_component.go b/aws/table_aws_iot_greengrass_component.go index f9f62c5f6..14fb0a405 100644 --- a/aws/table_aws_iot_greengrass_component.go +++ b/aws/table_aws_iot_greengrass_component.go @@ -21,12 +21,16 @@ func tableAwsIotGreengrassComponent(_ context.Context) *plugin.Table { Name: "aws_iot_greengrass_component", Description: "AWS IoT Greengrass Component", List: &plugin.ListConfig{ - Hydrate: listIotGreengrassComponents, + Hydrate: listIoTGreengrassComponents, Tags: map[string]string{"service": "greengrassv2", "action": "ListComponents"}, }, + // The `get config` is not included here for several reasons: + // 1. The API only returns the recipe and recipeOutputFormat, without any additional information. + // 2. A specific version is required to make the API call, but we obtain the latest version through the list call. + // 3. Utilizing the GetComponent API as a hydrated call is a more effective approach. HydrateConfig: []plugin.HydrateConfig{ { - Func: getIotGreengrassComponent, + Func: getIoTGreengrassComponent, Tags: map[string]string{"service": "greengrassv2", "action": "GetComponent"}, IgnoreConfig: &plugin.IgnoreConfig{ ShouldIgnoreErrorFunc: shouldIgnoreErrors([]string{"ResourceNotFoundException"}), @@ -42,21 +46,32 @@ func tableAwsIotGreengrassComponent(_ context.Context) *plugin.Table { }, { Name: "arn", + Description: "The ARN of the component.", + Type: proto.ColumnType_STRING, + }, + { + Name: "component_version_arn", Description: "The ARN of the component version.", Type: proto.ColumnType_STRING, - Transform: transform.FromField("ARN"), + Transform: transform.FromField("LatestVersion.Arn"), + }, + { + Name: "component_version", + Description: "The version of the component.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("LatestVersion.ComponentVersion"), }, { Name: "recipe", Description: "The recipe of the component version.", Type: proto.ColumnType_STRING, - Hydrate: getIotGreengrassComponent, + Hydrate: getIoTGreengrassComponent, }, { Name: "recipe_output_format", Description: "The format of the recipe.", Type: proto.ColumnType_STRING, - Hydrate: getIotGreengrassComponent, + Hydrate: getIoTGreengrassComponent, }, // JSON columns @@ -71,19 +86,19 @@ func tableAwsIotGreengrassComponent(_ context.Context) *plugin.Table { Name: "title", Description: resourceInterfaceDescription("title"), Type: proto.ColumnType_STRING, - Transform: transform.FromField("CoreDeviceThingName"), + Transform: transform.FromField("ComponentName"), }, { Name: "tags", Description: resourceInterfaceDescription("tags"), Type: proto.ColumnType_JSON, - Hydrate: getIotGreengrassComponent, + Hydrate: getIoTGreengrassComponent, }, { Name: "akas", Description: resourceInterfaceDescription("akas"), Type: proto.ColumnType_JSON, - Transform: transform.FromField("ARN").Transform(transform.EnsureStringArray), + Transform: transform.FromField("Arn").Transform(transform.EnsureStringArray), }, }), } @@ -91,11 +106,11 @@ func tableAwsIotGreengrassComponent(_ context.Context) *plugin.Table { //// LIST FUNCTION -func listIotGreengrassComponents(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { +func listIoTGreengrassComponents(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { // Create Session svc, err := IoTGreengrassClient(ctx, d) if err != nil { - plugin.Logger(ctx).Error("aws_iot_greengrass_component.listIotGreengrassComponents", "connection_error", err) + plugin.Logger(ctx).Error("aws_iot_greengrass_component.listIoTGreengrassComponents", "connection_error", err) return nil, err } if svc == nil { @@ -128,7 +143,7 @@ func listIotGreengrassComponents(ctx context.Context, d *plugin.QueryData, _ *pl output, err := paginator.NextPage(ctx) if err != nil { - plugin.Logger(ctx).Error("aws_iot_greengrass_component.listIotGreengrassComponents", "api_error", err) + plugin.Logger(ctx).Error("aws_iot_greengrass_component.listIoTGreengrassComponents", "api_error", err) return nil, err } @@ -147,24 +162,34 @@ func listIotGreengrassComponents(ctx context.Context, d *plugin.QueryData, _ *pl //// HYDRATE FUNCTIONS -func getIotGreengrassComponent(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { - t := h.Item.(types.Component) - arn := *t.Arn +func getIoTGreengrassComponent(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + arn := "" + componentVersion := "" + if h.Item != nil { + item := h.Item.(types.Component) + arn = *item.Arn + componentVersion = *item.LatestVersion.ComponentVersion + } + + // Empty check + if arn == "" || componentVersion == "" { + return nil, nil + } // Create service svc, err := IoTGreengrassClient(ctx, d) if err != nil { - plugin.Logger(ctx).Error("aws_iot_greengrass_component.getIotGreengrassComponent", "connection_error", err) + plugin.Logger(ctx).Error("aws_iot_greengrass_component.getIoTGreengrassComponent", "connection_error", err) return nil, err } params := &greengrassv2.GetComponentInput{ - Arn: aws.String(arn), + Arn: aws.String(arn + ":versions:" + componentVersion), } resp, err := svc.GetComponent(ctx, params) if err != nil { - plugin.Logger(ctx).Error("aws_iot_greengrass_component.getIotGreengrassComponent", "api_error", err) + plugin.Logger(ctx).Error("aws_iot_greengrass_component.getIoTGreengrassComponent", "api_error", err) return nil, err } diff --git a/aws/table_aws_iot_greengrass_core_device.go b/aws/table_aws_iot_greengrass_core_device.go index c67e3dc15..a5108ebc7 100644 --- a/aws/table_aws_iot_greengrass_core_device.go +++ b/aws/table_aws_iot_greengrass_core_device.go @@ -22,14 +22,14 @@ func tableAwsIotGreengrassCoreDevice(_ context.Context) *plugin.Table { Description: "AWS IoT Greengrass Core Device", Get: &plugin.GetConfig{ KeyColumns: plugin.SingleColumn("core_device_thing_name"), - Hydrate: getIotGreengrassCoreDevice, + Hydrate: getIoTGreengrassCoreDevice, Tags: map[string]string{"service": "greengrassv2", "action": "GetCoreDevice"}, IgnoreConfig: &plugin.IgnoreConfig{ ShouldIgnoreErrorFunc: shouldIgnoreErrors([]string{"ResourceNotFoundException"}), }, }, List: &plugin.ListConfig{ - Hydrate: listIotGreengrassCoreDevices, + Hydrate: listIoTGreengrassCoreDevices, Tags: map[string]string{"service": "greengrassv2", "action": "ListCoreDevices"}, KeyColumns: plugin.KeyColumnSlice{ {Name: "status", Require: plugin.Optional}, @@ -37,7 +37,7 @@ func tableAwsIotGreengrassCoreDevice(_ context.Context) *plugin.Table { }, HydrateConfig: []plugin.HydrateConfig{ { - Func: getIotGreengrassCoreDevice, + Func: getIoTGreengrassCoreDevice, Tags: map[string]string{"service": "greengrassv2", "action": "GetCoreDevice"}, }, }, @@ -52,19 +52,19 @@ func tableAwsIotGreengrassCoreDevice(_ context.Context) *plugin.Table { Name: "architecture", Description: "The computer architecture of the core device.", Type: proto.ColumnType_STRING, - Hydrate: getIotGreengrassCoreDevice, + Hydrate: getIoTGreengrassCoreDevice, }, { Name: "core_version", Description: "The version of the IoT Greengrass Core software that the core device runs.", Type: proto.ColumnType_STRING, - Hydrate: getIotGreengrassCoreDevice, + Hydrate: getIoTGreengrassCoreDevice, }, { Name: "platform", Description: "The operating system platform that the core device runs.", Type: proto.ColumnType_STRING, - Hydrate: getIotGreengrassCoreDevice, + Hydrate: getIoTGreengrassCoreDevice, }, { Name: "last_status_update_timestamp", @@ -88,7 +88,7 @@ func tableAwsIotGreengrassCoreDevice(_ context.Context) *plugin.Table { Name: "tags", Description: resourceInterfaceDescription("tags"), Type: proto.ColumnType_JSON, - Hydrate: getIotGreengrassCoreDevice, + Hydrate: getIoTGreengrassCoreDevice, }, }), } @@ -96,11 +96,11 @@ func tableAwsIotGreengrassCoreDevice(_ context.Context) *plugin.Table { //// LIST FUNCTION -func listIotGreengrassCoreDevices(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { +func listIoTGreengrassCoreDevices(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { // Create Session svc, err := IoTGreengrassClient(ctx, d) if err != nil { - plugin.Logger(ctx).Error("aws_iot_greengrass_core_device.listIotGreengrassCoreDevices", "connection_error", err) + plugin.Logger(ctx).Error("aws_iot_greengrass_core_device.listIoTGreengrassCoreDevices", "connection_error", err) return nil, err } if svc == nil { @@ -136,7 +136,7 @@ func listIotGreengrassCoreDevices(ctx context.Context, d *plugin.QueryData, _ *p output, err := paginator.NextPage(ctx) if err != nil { - plugin.Logger(ctx).Error("aws_iot_greengrass_core_device.listIotGreengrassCoreDevices", "api_error", err) + plugin.Logger(ctx).Error("aws_iot_greengrass_core_device.listIoTGreengrassCoreDevices", "api_error", err) return nil, err } @@ -155,7 +155,7 @@ func listIotGreengrassCoreDevices(ctx context.Context, d *plugin.QueryData, _ *p //// HYDRATE FUNCTIONS -func getIotGreengrassCoreDevice(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { +func getIoTGreengrassCoreDevice(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { coreDeviceThingName := "" if h.Item != nil { t := h.Item.(types.CoreDevice) @@ -171,7 +171,7 @@ func getIotGreengrassCoreDevice(ctx context.Context, d *plugin.QueryData, h *plu // Create service svc, err := IoTGreengrassClient(ctx, d) if err != nil { - plugin.Logger(ctx).Error("aws_iot_greengrass_core_device.getIotGreengrassCoreDevice", "connection_error", err) + plugin.Logger(ctx).Error("aws_iot_greengrass_core_device.getIoTGreengrassCoreDevice", "connection_error", err) return nil, err } @@ -181,7 +181,7 @@ func getIotGreengrassCoreDevice(ctx context.Context, d *plugin.QueryData, h *plu resp, err := svc.GetCoreDevice(ctx, params) if err != nil { - plugin.Logger(ctx).Error("aws_iot_greengrass_core_device.getIotGreengrassCoreDevice", "api_error", err) + plugin.Logger(ctx).Error("aws_iot_greengrass_core_device.getIoTGreengrassCoreDevice", "api_error", err) return nil, err } diff --git a/aws/table_aws_iot_greengrass_deployment.go b/aws/table_aws_iot_greengrass_deployment.go index 945d5184e..ef8562159 100644 --- a/aws/table_aws_iot_greengrass_deployment.go +++ b/aws/table_aws_iot_greengrass_deployment.go @@ -22,20 +22,26 @@ func tableAwsIotGreengrassDeployment(_ context.Context) *plugin.Table { Description: "AWS IoT Greengrass Deployment", Get: &plugin.GetConfig{ KeyColumns: plugin.SingleColumn("deployment_id"), - Hydrate: getIotGreengrassDeployment, + Hydrate: getIoTGreengrassDeployment, Tags: map[string]string{"service": "greengrassv2", "action": "GetDeployment"}, IgnoreConfig: &plugin.IgnoreConfig{ ShouldIgnoreErrorFunc: shouldIgnoreErrors([]string{"ResourceNotFoundException"}), }, }, List: &plugin.ListConfig{ - Hydrate: listIotGreengrassDeployments, + Hydrate: listIoTGreengrassDeployments, Tags: map[string]string{"service": "greengrassv2", "action": "ListDeployments"}, KeyColumns: plugin.KeyColumnSlice{ {Name: "parent_target_arn", Require: plugin.Optional}, {Name: "target_arn", Require: plugin.Optional}, }, }, + HydrateConfig: []plugin.HydrateConfig{ + { + Func: getIoTGreengrassDeployment, + Tags: map[string]string{"service": "greengrassv2", "action": "GetDeployment"}, + }, + }, GetMatrixItemFunc: SupportedRegionMatrix(greengrassv1.EndpointsID), Columns: awsRegionalColumns([]*plugin.Column{ { @@ -82,13 +88,13 @@ func tableAwsIotGreengrassDeployment(_ context.Context) *plugin.Table { Name: "iot_job_arn", Description: "The ARN of the IoT job that applies the deployment to target devices.", Type: proto.ColumnType_STRING, - Hydrate: getIotGreengrassDeployment, + Hydrate: getIoTGreengrassDeployment, }, { Name: "iot_job_id", Description: "The ID of the IoT job that applies the deployment to target devices.", Type: proto.ColumnType_STRING, - Hydrate: getIotGreengrassDeployment, + Hydrate: getIoTGreengrassDeployment, }, // JSON fields @@ -96,19 +102,19 @@ func tableAwsIotGreengrassDeployment(_ context.Context) *plugin.Table { Name: "components", Description: "The components to deploy. This is a dictionary, where each key is the name of a component, and each key's value is the version and configuration to deploy for that component.", Type: proto.ColumnType_JSON, - Hydrate: getIotGreengrassDeployment, + Hydrate: getIoTGreengrassDeployment, }, { Name: "deployment_policies", Description: "The deployment policies for the deployment. These policies define how the deployment updates components and handles failure.", Type: proto.ColumnType_JSON, - Hydrate: getIotGreengrassDeployment, + Hydrate: getIoTGreengrassDeployment, }, { Name: "iot_job_configuration", Description: " The job configuration for the deployment configuration. The job configuration specifies the rollout, timeout, and stop configurations for the deployment configuration.", Type: proto.ColumnType_JSON, - Hydrate: getIotGreengrassDeployment, + Hydrate: getIoTGreengrassDeployment, }, // Steampipe standard columns @@ -122,7 +128,7 @@ func tableAwsIotGreengrassDeployment(_ context.Context) *plugin.Table { Name: "tags", Description: resourceInterfaceDescription("tags"), Type: proto.ColumnType_JSON, - Hydrate: getIotGreengrassDeployment, + Hydrate: getIoTGreengrassDeployment, }, }), } @@ -130,11 +136,11 @@ func tableAwsIotGreengrassDeployment(_ context.Context) *plugin.Table { //// LIST FUNCTION -func listIotGreengrassDeployments(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { +func listIoTGreengrassDeployments(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { // Create Session svc, err := IoTGreengrassClient(ctx, d) if err != nil { - plugin.Logger(ctx).Error("aws_iot_greengrass_deployment.listIotGreengrassDeployments", "connection_error", err) + plugin.Logger(ctx).Error("aws_iot_greengrass_deployment.listIoTGreengrassDeployments", "connection_error", err) return nil, err } if svc == nil { @@ -173,7 +179,7 @@ func listIotGreengrassDeployments(ctx context.Context, d *plugin.QueryData, _ *p output, err := paginator.NextPage(ctx) if err != nil { - plugin.Logger(ctx).Error("aws_iot_greengrass_deployment.listIotGreengrassDeployments", "api_error", err) + plugin.Logger(ctx).Error("aws_iot_greengrass_deployment.listIoTGreengrassDeployments", "api_error", err) return nil, err } @@ -192,7 +198,7 @@ func listIotGreengrassDeployments(ctx context.Context, d *plugin.QueryData, _ *p //// HYDRATE FUNCTIONS -func getIotGreengrassDeployment(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { +func getIoTGreengrassDeployment(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { deploymentId := "" if h.Item != nil { t := h.Item.(types.Deployment) @@ -208,7 +214,7 @@ func getIotGreengrassDeployment(ctx context.Context, d *plugin.QueryData, h *plu // Create service svc, err := IoTGreengrassClient(ctx, d) if err != nil { - plugin.Logger(ctx).Error("aws_iot_greengrass_deployment.getIotGreengrassDeployment", "connection_error", err) + plugin.Logger(ctx).Error("aws_iot_greengrass_deployment.getIoTGreengrassDeployment", "connection_error", err) return nil, err } @@ -218,7 +224,7 @@ func getIotGreengrassDeployment(ctx context.Context, d *plugin.QueryData, h *plu resp, err := svc.GetDeployment(ctx, params) if err != nil { - plugin.Logger(ctx).Error("aws_iot_greengrass_deployment.getIotGreengrassDeployment", "api_error", err) + plugin.Logger(ctx).Error("aws_iot_greengrass_deployment.getIoTGreengrassDeployment", "api_error", err) return nil, err } From e2d9cdfcf7e013c97255ec62bd4afeabef3b64d2 Mon Sep 17 00:00:00 2001 From: ParthaI Date: Mon, 3 Jun 2024 09:55:16 +0530 Subject: [PATCH 5/5] Added greengrassv2 Go package --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index c2d4528ce..acce8e7ca 100644 --- a/go.mod +++ b/go.mod @@ -143,6 +143,7 @@ require golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect require ( github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.21 // indirect + github.com/aws/aws-sdk-go-v2/service/greengrassv2 v1.31.3 // 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 75475d0a7..29f450185 100644 --- a/go.sum +++ b/go.sum @@ -353,6 +353,8 @@ github.com/aws/aws-sdk-go-v2/service/globalaccelerator v1.23.1 h1:E48tPAIKptyIb8 github.com/aws/aws-sdk-go-v2/service/globalaccelerator v1.23.1/go.mod h1:6morRSCgJD400qAu5DCEtvoaAC1owS5t6oq8ddLLwxw= github.com/aws/aws-sdk-go-v2/service/glue v1.78.0 h1:B7NIez2lCPjP9F/ucgjJSZ9JWHBO9KIeLAxn39G8mLA= github.com/aws/aws-sdk-go-v2/service/glue v1.78.0/go.mod h1:maQT+ebL6UAFXYp8fJlK2Dv/s42LZuggi2l6pVeE2B4= +github.com/aws/aws-sdk-go-v2/service/greengrassv2 v1.31.3 h1:qWI4VrTRdKSmM8mo8tE4mOwRagKeuA1EW0GKnOTt8Cg= +github.com/aws/aws-sdk-go-v2/service/greengrassv2 v1.31.3/go.mod h1:V3fvC4aMH1y21pfystBRewJVaQLUmroX/fQWBG26mqY= github.com/aws/aws-sdk-go-v2/service/guardduty v1.41.1 h1:HbecqrH+phcfa2XVmFlJEjEFM2FXJFhAtqLBq+k0q1I= github.com/aws/aws-sdk-go-v2/service/guardduty v1.41.1/go.mod h1:qXyWkjk60YMVbYEBkQBYqk7d4WJTEPnQzxbWWQ5d6pI= github.com/aws/aws-sdk-go-v2/service/health v1.24.4 h1:5QROeJylnNdBQxxYn4BPpbgoo3nXT+SMG3KvFd71O4s=