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

Issue:
Add table aws_config_delivery_channels
We can currently list config recorders (https://hub.steampipe.io/plugins/turbot/aws/tables/aws_config_configuration_recorder) but cant query delivery channels associated with them.

https://docs.aws.amazon.com/config/latest/APIReference/API_DescribeDeliveryChannels.html
https://awscli.amazonaws.com/v2/documentation/api/latest/reference/configservice/describe-delivery-channels.html





Discussion / hints:

Fix patch:
diff --git a/aws/plugin.go b/aws/plugin.go
index fd72d1b01..c9ea62e0f 100644
--- a/aws/plugin.go
+++ b/aws/plugin.go
@@ -164,6 +164,7 @@ func Plugin(ctx context.Context) *plugin.Plugin {
 			"aws_config_aggregate_authorization":                           tableAwsConfigAggregateAuthorization(ctx),
 			"aws_config_configuration_recorder":                            tableAwsConfigConfigurationRecorder(ctx),
 			"aws_config_conformance_pack":                                  tableAwsConfigConformancePack(ctx),
+			"aws_config_delivery_channel":                                  tableAwsConfigDeliveryChannel(ctx),
 			"aws_config_retention_configuration":                           tableAwsConfigRetentionConfiguration(ctx),
 			"aws_config_rule":                                              tableAwsConfigRule(ctx),
 			"aws_cost_by_account_daily":                                    tableAwsCostByLinkedAccountDaily(ctx),
diff --git a/aws/table_aws_config_delivery_channel.go b/aws/table_aws_config_delivery_channel.go
new file mode 100644
index 000000000..21baa14d8
--- /dev/null
+++ b/aws/table_aws_config_delivery_channel.go
@@ -0,0 +1,179 @@
+package aws
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go-v2/service/configservice"
+	"github.com/aws/aws-sdk-go-v2/service/configservice/types"
+
+	configservicev1 "github.com/aws/aws-sdk-go/service/configservice"
+
+	"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 tableAwsConfigDeliveryChannel(_ context.Context) *plugin.Table {
+	return &plugin.Table{
+		Name:        "aws_config_delivery_channel",
+		Description: "AWS Config Delivery Channel",
+		List: &plugin.ListConfig{
+			Hydrate: listConfigDeliveryChannels,
+			KeyColumns: []*plugin.KeyColumn{
+				{
+					Name:    "name",
+					Require: plugin.Optional,
+				},
+			},		
+			IgnoreConfig: &plugin.IgnoreConfig{
+				ShouldIgnoreErrorFunc: shouldIgnoreErrors([]string{"NoSuchDeliveryChannelException"}),
+			},
+			Tags:    map[string]string{"service": "config", "action": "DescribeDeliveryChannels"},
+		},
+		GetMatrixItemFunc: SupportedRegionMatrix(configservicev1.EndpointsID),
+		Columns: awsRegionalColumns([]*plugin.Column{
+			{
+				Name:        "name",
+				Description: "The name of the delivery channel.",
+				Type:        proto.ColumnType_STRING,
+			},
+			{
+				Name:        "s3_bucket_name",
+				Description: "The name of the Amazon S3 bucket to which AWS Config delivers configuration snapshots and configuration history files.",
+				Type:        proto.ColumnType_STRING,
+				Transform:   transform.FromField("S3BucketName"),
+			},
+			{
+				Name:        "s3_key_prefix",
+				Description: "The prefix for the specified Amazon S3 bucket.",
+				Type:        proto.ColumnType_STRING,
+				Transform:   transform.FromField("S3KeyPrefix"),
+			},
+			{
+				Name:        "s3_kms_key_arn",
+				Description: "The Amazon Resource Name (ARN) of the KMS key.",
+				Type:        proto.ColumnType_STRING,
+				Transform:   transform.FromField("S3KmsKeyArn"),
+			},
+			{
+				Name:        "sns_topic_arn",
+				Description: "The Amazon Resource Name (ARN) of the Amazon SNS topic to which AWS Config sends notifications about configuration changes.",
+				Type:        proto.ColumnType_STRING,
+				Transform:   transform.FromField("SnsTopicARN"),
+			},
+			{
+				Name:        "delivery_frequency",
+				Description: "The frequency with which the AWS Config delivers configuration snapshots to the Amazon S3 bucket.",
+				Type:        proto.ColumnType_STRING,
+				Transform:   transform.FromField("ConfigSnapshotDeliveryProperties.DeliveryFrequency"),
+			},
+			{
+				Name:        "status",
+				Description: "The current status of the delivery channel.",
+				Type:        proto.ColumnType_JSON,
+				Hydrate:     getConfigDeliveryChannelStatus,
+				Transform:   transform.FromValue(),
+			},
+
+			// Steampipe standard columns
+			{
+				Name:        "akas",
+				Description: resourceInterfaceDescription("akas"),
+				Type:        proto.ColumnType_JSON,
+				Hydrate:     getAwsDeliveryChannelAkas,
+				Transform:   transform.FromValue().Transform(transform.EnsureStringArray),
+			},
+			{
+				Name:        "title",
+				Description: resourceInterfaceDescription("title"),
+				Type:        proto.ColumnType_STRING,
+				Transform:   transform.FromField("Name"),
+			},
+		}),
+	}
+}
+
+//// LIST FUNCTION
+
+func listConfigDeliveryChannels(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) {
+	// Create session
+	svc, err := ConfigClient(ctx, d)
+	if err != nil {
+		plugin.Logger(ctx).Error("aws_config_delivery_channel.listConfigDeliveryChannels", "get_client_error", err)
+		return nil, err
+	}
+
+	input := &configservice.DescribeDeliveryChannelsInput{}
+
+	// Additonal Filter
+	equalQuals := d.EqualsQuals
+	if equalQuals["name"] != nil {
+		input.DeliveryChannelNames = []string{equalQuals["name"].GetStringValue()}
+	}
+
+	op, err := svc.DescribeDeliveryChannels(ctx, input)
+	if err != nil {
+		plugin.Logger(ctx).Error("aws_config_delivery_channel.listConfigDeliveryChannels", "api_error", err)
+		return nil, err
+	}
+
+	if op.DeliveryChannels != nil {
+		for _, deliveryChannel := range op.DeliveryChannels {
+			d.StreamListItem(ctx, deliveryChannel)
+
+			// Context can be cancelled due to manual cancellation or limit being reached
+			if d.RowsRemaining(ctx) == 0 {
+				return nil, nil
+			}
+		}
+	}
+
+	return nil, nil
+}
+
+//// HYDRATE FUNCTIONS
+
+func getConfigDeliveryChannelStatus(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
+	deliveryChannel := h.Item.(types.DeliveryChannel)
+
+	// Create session
+	svc, err := ConfigClient(ctx, d)
+	if err != nil {
+		plugin.Logger(ctx).Error("aws_config_delivery_channel.getConfigDeliveryChannelStatus", "get_client_error", err)
+		return nil, err
+	}
+
+	params := &configservice.DescribeDeliveryChannelStatusInput{
+		DeliveryChannelNames: []string{*deliveryChannel.Name},
+	}
+
+	status, err := svc.DescribeDeliveryChannelStatus(ctx, params)
+	if err != nil {
+		plugin.Logger(ctx).Error("aws_config_delivery_channel.getConfigDeliveryChannelStatus", "api_error", err)
+		return nil, err
+	}
+
+	if len(status.DeliveryChannelsStatus) < 1 {
+		return nil, nil
+	}
+
+	return status.DeliveryChannelsStatus[0], nil
+}
+
+func getAwsDeliveryChannelAkas(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
+	region := d.EqualsQualString(matrixKeyRegion)
+
+	deliveryChannel := h.Item.(types.DeliveryChannel)
+
+	c, err := getCommonColumns(ctx, d, h)
+	if err != nil {
+		plugin.Logger(ctx).Error("aws_config_delivery_channel.getAwsDeliveryChannelAkas", "api_error", err)
+		return nil, err
+	}
+	commonColumnData := c.(*awsCommonColumnData)
+	arn := "arn:" + commonColumnData.Partition + ":config:" + region + ":" + commonColumnData.AccountId + ":delivery-channel" + "/" + *deliveryChannel.Name
+
+	return arn, nil
+}
diff --git a/docs/tables/aws_config_delivery_channel.md b/docs/tables/aws_config_delivery_channel.md
new file mode 100644
index 000000000..fe1218964
--- /dev/null
+++ b/docs/tables/aws_config_delivery_channel.md
@@ -0,0 +1,149 @@
+---
+title: "Steampipe Table: aws_config_delivery_channel - Query AWS Config Delivery Channels using SQL"
+description: "Allows users to query AWS Config Delivery Channels"
+---
+
+# Table: aws_config_delivery_channel - Query AWS Config Delivery Channels using SQL
+
+The AWS Config Delivery Channel is a feature that enables AWS Config to deliver configuration snapshots and configuration change notifications to specified destinations. It plays a key role in ensuring that your configuration data is stored securely and notifications are sent promptly for compliance or operational purposes.
+
+## Table Usage Guide
+
+The `aws_config_delivery_channel` table in Steampipe provides insights into the Delivery Channels associated with AWS Config. This table enables DevOps engineers, security analysts, and cloud administrators to query delivery channel details such as the destination S3 bucket, SNS topic for notifications, and delivery status. Use this table to ensure your configuration change data is being delivered correctly and troubleshoot delivery-related issues.
+
+## Examples
+
+### Retrieve basic delivery channel information
+Get a detailed view of your AWS Config Delivery Channels, including their destinations and notification settings.
+
+```sql+postgres
+select
+  name,
+  s3_bucket_name,
+  s3_key_prefix,
+  sns_topic_arn,
+  delivery_frequency,
+  status,
+  title,
+  akas
+from
+  aws_config_delivery_channel;
+```
+
+```sql+sqlite
+select
+  name,
+  s3_bucket_name,
+  s3_key_prefix,
+  sns_topic_arn,
+  delivery_frequency,
+  status,
+  title,
+  akas
+from
+  aws_config_delivery_channel;
+```
+
+### List delivery channels without SNS topic configured
+Identify delivery channels that do not have an SNS topic configured for notifications. This can help ensure you have proper alerting mechanisms in place.
+
+```sql+postgres
+select
+  name,
+  s3_bucket_name,
+  sns_topic_arn
+from
+  aws_config_delivery_channel
+where
+  sns_topic_arn is null;
+```
+
+```sql+sqlite
+select
+  name,
+  s3_bucket_name,
+  sns_topic_arn
+from
+  aws_config_delivery_channel
+where
+  sns_topic_arn is null;
+```
+
+### Check delivery channels with delivery failures
+Discover delivery channels with failed deliveries to address issues in your AWS Config setup.
+
+```sql+postgres
+select
+  name,
+  status ->> 'LastStatus' as last_status,
+  status ->> 'LastStatusChangeTime' as last_status_change_time,
+  status ->> 'LastErrorCode' as last_error_code,
+  status ->> 'LastErrorMessage' as last_error_message
+from
+  aws_config_delivery_channel
+where
+  (status ->> 'LastStatus') = 'FAILURE';
+```
+
+```sql+sqlite
+select
+  name,
+  json_extract(status, '$.LastStatus') as last_status,
+  json_extract(status, '$.LastStatusChangeTime') as last_status_change_time,
+  json_extract(status, '$.LastErrorCode') as last_error_code,
+  json_extract(status, '$.LastErrorMessage') as last_error_message
+from
+  aws_config_delivery_channel
+where
+  json_extract(status, '$.LastStatus') = 'FAILURE';
+```
+
+### List delivery channels sending to a specific S3 bucket
+Query the delivery channels that are configured to send data to a particular S3 bucket.
+
+```sql+postgres
+select
+  name,
+  s3_bucket_name,
+  sns_topic_arn,
+  delivery_frequency
+from
+  aws_config_delivery_channel
+where
+  s3_bucket_name = 'test-bucket-delivery-channel';
+```
+
+```sql+sqlite
+select
+  name,
+  s3_bucket_name,
+  sns_topic_arn,
+  delivery_frequency
+from
+  aws_config_delivery_channel
+where
+  s3_bucket_name = 'test-bucket-delivery-channel';
+```
+
+### Analyze delivery frequency of all channels
+Get an overview of how often your delivery channels send data, ensuring they align with organizational requirements.
+
+```sql+postgres
+select
+  name,
+  delivery_frequency,
+  s3_bucket_name,
+  sns_topic_arn
+from
+  aws_config_delivery_channel;
+```
+
+```sql+sqlite
+select
+  name,
+  delivery_frequency,
+  s3_bucket_name,
+  sns_topic_arn
+from
+  aws_config_delivery_channel;
+```


Test patch: