diff --git a/providers/aws/resources/aws.lr b/providers/aws/resources/aws.lr index dba983e108..66c7be9334 100644 --- a/providers/aws/resources/aws.lr +++ b/providers/aws/resources/aws.lr @@ -2513,6 +2513,8 @@ aws.config { recorders() []aws.config.recorder // List of AWS Config rules rules() []aws.config.rule + // List of delivery channels for each region in the account + deliveryChannels() []aws.config.deliverychannel } // AWS config rule @@ -2553,6 +2555,27 @@ private aws.config.recorder @defaults("name region") { resourceTypes []string } +// AWS config delivery channel +private aws.config.deliverychannel @defaults("name region") { + // Name of the delivery channel + name string + // S3 bucket name where configuration snapshots are delivered + s3BucketName string + // Prefix for the S3 bucket where configuration snapshots are delivered + s3KeyPrefix string + // ARN of the SNS topic that AWS Config delivers notifications to + snsTopicARN string + // Region for the delivery channel + region string +} + + + + + + + + // Amazon Elastic Kubernetes Service (EKS) aws.eks { // EKS clusters diff --git a/providers/aws/resources/aws.lr.go b/providers/aws/resources/aws.lr.go index 15ab8e4710..0afc767e32 100644 --- a/providers/aws/resources/aws.lr.go +++ b/providers/aws/resources/aws.lr.go @@ -630,6 +630,10 @@ func init() { // to override args, implement: initAwsConfigRecorder(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) Create: createAwsConfigRecorder, }, + "aws.config.deliverychannel": { + // to override args, implement: initAwsConfigDeliverychannel(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) + Create: createAwsConfigDeliverychannel, + }, "aws.eks": { // to override args, implement: initAwsEks(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) Create: createAwsEks, @@ -3547,6 +3551,9 @@ var getDataFields = map[string]func(r plugin.Resource) *plugin.DataRes{ "aws.config.rules": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlAwsConfig).GetRules()).ToDataRes(types.Array(types.Resource("aws.config.rule"))) }, + "aws.config.deliveryChannels": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlAwsConfig).GetDeliveryChannels()).ToDataRes(types.Array(types.Resource("aws.config.deliverychannel"))) + }, "aws.config.rule.arn": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlAwsConfigRule).GetArn()).ToDataRes(types.String) }, @@ -3592,6 +3599,21 @@ var getDataFields = map[string]func(r plugin.Resource) *plugin.DataRes{ "aws.config.recorder.resourceTypes": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlAwsConfigRecorder).GetResourceTypes()).ToDataRes(types.Array(types.String)) }, + "aws.config.deliverychannel.name": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlAwsConfigDeliverychannel).GetName()).ToDataRes(types.String) + }, + "aws.config.deliverychannel.s3BucketName": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlAwsConfigDeliverychannel).GetS3BucketName()).ToDataRes(types.String) + }, + "aws.config.deliverychannel.s3KeyPrefix": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlAwsConfigDeliverychannel).GetS3KeyPrefix()).ToDataRes(types.String) + }, + "aws.config.deliverychannel.snsTopicARN": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlAwsConfigDeliverychannel).GetSnsTopicARN()).ToDataRes(types.String) + }, + "aws.config.deliverychannel.region": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlAwsConfigDeliverychannel).GetRegion()).ToDataRes(types.String) + }, "aws.eks.clusters": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlAwsEks).GetClusters()).ToDataRes(types.Array(types.Resource("aws.eks.cluster"))) }, @@ -8038,6 +8060,10 @@ var setDataFields = map[string]func(r plugin.Resource, v *llx.RawData) bool { r.(*mqlAwsConfig).Rules, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) return }, + "aws.config.deliveryChannels": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsConfig).DeliveryChannels, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) + return + }, "aws.config.rule.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { r.(*mqlAwsConfigRule).__id, ok = v.Value.(string) return @@ -8106,6 +8132,30 @@ var setDataFields = map[string]func(r plugin.Resource, v *llx.RawData) bool { r.(*mqlAwsConfigRecorder).ResourceTypes, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) return }, + "aws.config.deliverychannel.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsConfigDeliverychannel).__id, ok = v.Value.(string) + return + }, + "aws.config.deliverychannel.name": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsConfigDeliverychannel).Name, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "aws.config.deliverychannel.s3BucketName": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsConfigDeliverychannel).S3BucketName, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "aws.config.deliverychannel.s3KeyPrefix": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsConfigDeliverychannel).S3KeyPrefix, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "aws.config.deliverychannel.snsTopicARN": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsConfigDeliverychannel).SnsTopicARN, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "aws.config.deliverychannel.region": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsConfigDeliverychannel).Region, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, "aws.eks.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { r.(*mqlAwsEks).__id, ok = v.Value.(string) return @@ -20886,6 +20936,7 @@ type mqlAwsConfig struct { // optional: if you define mqlAwsConfigInternal it will be used here Recorders plugin.TValue[[]interface{}] Rules plugin.TValue[[]interface{}] + DeliveryChannels plugin.TValue[[]interface{}] } // createAwsConfig creates a new instance of this resource @@ -20957,6 +21008,22 @@ func (c *mqlAwsConfig) GetRules() *plugin.TValue[[]interface{}] { }) } +func (c *mqlAwsConfig) GetDeliveryChannels() *plugin.TValue[[]interface{}] { + return plugin.GetOrCompute[[]interface{}](&c.DeliveryChannels, func() ([]interface{}, error) { + if c.MqlRuntime.HasRecording { + d, err := c.MqlRuntime.FieldResourceFromRecording("aws.config", c.__id, "deliveryChannels") + if err != nil { + return nil, err + } + if d != nil { + return d.Value.([]interface{}), nil + } + } + + return c.deliveryChannels() + }) +} + // mqlAwsConfigRule for the aws.config.rule resource type mqlAwsConfigRule struct { MqlRuntime *plugin.Runtime @@ -21120,6 +21187,70 @@ func (c *mqlAwsConfigRecorder) GetResourceTypes() *plugin.TValue[[]interface{}] return &c.ResourceTypes } +// mqlAwsConfigDeliverychannel for the aws.config.deliverychannel resource +type mqlAwsConfigDeliverychannel struct { + MqlRuntime *plugin.Runtime + __id string + // optional: if you define mqlAwsConfigDeliverychannelInternal it will be used here + Name plugin.TValue[string] + S3BucketName plugin.TValue[string] + S3KeyPrefix plugin.TValue[string] + SnsTopicARN plugin.TValue[string] + Region plugin.TValue[string] +} + +// createAwsConfigDeliverychannel creates a new instance of this resource +func createAwsConfigDeliverychannel(runtime *plugin.Runtime, args map[string]*llx.RawData) (plugin.Resource, error) { + res := &mqlAwsConfigDeliverychannel{ + MqlRuntime: runtime, + } + + err := SetAllData(res, args) + if err != nil { + return res, err + } + + // to override __id implement: id() (string, error) + + if runtime.HasRecording { + args, err = runtime.ResourceFromRecording("aws.config.deliverychannel", res.__id) + if err != nil || args == nil { + return res, err + } + return res, SetAllData(res, args) + } + + return res, nil +} + +func (c *mqlAwsConfigDeliverychannel) MqlName() string { + return "aws.config.deliverychannel" +} + +func (c *mqlAwsConfigDeliverychannel) MqlID() string { + return c.__id +} + +func (c *mqlAwsConfigDeliverychannel) GetName() *plugin.TValue[string] { + return &c.Name +} + +func (c *mqlAwsConfigDeliverychannel) GetS3BucketName() *plugin.TValue[string] { + return &c.S3BucketName +} + +func (c *mqlAwsConfigDeliverychannel) GetS3KeyPrefix() *plugin.TValue[string] { + return &c.S3KeyPrefix +} + +func (c *mqlAwsConfigDeliverychannel) GetSnsTopicARN() *plugin.TValue[string] { + return &c.SnsTopicARN +} + +func (c *mqlAwsConfigDeliverychannel) GetRegion() *plugin.TValue[string] { + return &c.Region +} + // mqlAwsEks for the aws.eks resource type mqlAwsEks struct { MqlRuntime *plugin.Runtime diff --git a/providers/aws/resources/aws.lr.manifest.yaml b/providers/aws/resources/aws.lr.manifest.yaml index 71caa872c4..c29e1138f0 100755 --- a/providers/aws/resources/aws.lr.manifest.yaml +++ b/providers/aws/resources/aws.lr.manifest.yaml @@ -679,6 +679,10 @@ resources: desc: | Use the `aws.config` resource to assess the configuration of the AWS Config service. The resource provides the `.recorders` field, which returns a list of `aws.config.recorder` resources representing all AWS Config recorders configured across all enabled regions, as well as the `.rules` field, which returns a list of `aws.config.rule` resources representing all AWS Config rules configured across all enabled regions in the account. fields: + deliveryChannels: + min_mondoo_version: latest + deliverychannels: + min_mondoo_version: latest recorders: {} rules: {} min_mondoo_version: 5.15.0 @@ -704,6 +708,22 @@ resources: recording == true && lastStatus == "SUCCESS" ) title: Ensure AWS Config is enabled in all regions + aws.config.deliverychannel: + docs: + desc: "The `aws.config.deliverychannel` resource provides fields representing + an individual AWS Config delivery channel configured within an account. For + usage, read the `aws.config` resource documentation. \n" + fields: + name: {} + region: {} + s3BucketName: {} + s3KeyPrefix: {} + snsTopicARN: {} + is_private: true + min_mondoo_version: 9.0.0 + platform: + name: + - aws aws.config.recorder: docs: desc: "The `aws.config.recorder` resource provides fields representing an individual diff --git a/providers/aws/resources/aws_config.go b/providers/aws/resources/aws_config.go index 70b7489c35..10b939f9d6 100644 --- a/providers/aws/resources/aws_config.go +++ b/providers/aws/resources/aws_config.go @@ -100,6 +100,66 @@ func (a *mqlAwsConfig) getRecorders(conn *connection.AwsConnection) []*jobpool.J return tasks } +func (a *mqlAwsConfig) deliveryChannels() ([]interface{}, error) { + conn := a.MqlRuntime.Connection.(*connection.AwsConnection) + res := []interface{}{} + poolOfJobs := jobpool.CreatePool(a.getDeliveryChannels(conn), 5) + poolOfJobs.Run() + + if poolOfJobs.HasErrors() { + return nil, poolOfJobs.GetErrors() + } + + for i := range poolOfJobs.Jobs { + res = append(res, poolOfJobs.Jobs[i].Result.([]interface{})...) + } + return res, nil +} + +func (a *mqlAwsConfig) getDeliveryChannels(conn *connection.AwsConnection) []*jobpool.Job { + tasks := make([]*jobpool.Job, 0) + regions, err := conn.Regions() + if err != nil { + return []*jobpool.Job{{Err: err}} + } + + for _, region := range regions { + regionVal := region + f := func() (jobpool.JobResult, error) { + log.Debug().Msgf("config>getDeliveryChannels>calling aws with region %s", regionVal) + + svc := conn.ConfigService(regionVal) + ctx := context.Background() + res := []interface{}{} + + deliveryChannelsParams := &configservice.DescribeDeliveryChannelsInput{} + deliveryChannels, err := svc.DescribeDeliveryChannels(ctx, deliveryChannelsParams) + if err != nil { + return nil, err + } + + for _, channel := range deliveryChannels.DeliveryChannels { + mqlDeliveryChannel, err := CreateResource(a.MqlRuntime, "aws.config.deliverychannel", + map[string]*llx.RawData{ + "name": llx.StringDataPtr(channel.Name), + "s3BucketName": llx.StringDataPtr(channel.S3BucketName), + "s3KeyPrefix": llx.StringDataPtr(channel.S3KeyPrefix), + "snsTopicARN": llx.StringDataPtr(channel.SnsTopicARN), + "region": llx.StringData(regionVal), + }) + if err != nil { + return nil, err + } + res = append(res, mqlDeliveryChannel) + } + + return jobpool.JobResult(res), nil + } + tasks = append(tasks, jobpool.NewJob(f)) + } + return tasks +} + func getName(name string, region string) string { return name + "/" + region }