question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Support setting up ConfigurationRecorder and DeliveryChannel in AWS Config

See original GitHub issue

I’m using CDK v0.36, but the current version (v1.2) doesn’t appear to different substantively.

Currently, the CDK does not provide a convenient Construct to get a working AWS Config ConfigurationRecorder up and running. This forces users to create a number of resources manually, even though every customer will define the same resources. Given one cannot use AWS Config without a ConfigurationRecorder, this seems like opportunity to add a new construct to do this for our customers.

Additionally, there are no L2 constructs for some resources.

Consider the following example code. The IAM permissions that AWS Config must have in order to function correctly are documented more here, and the bucket configuration is documented here.

import config = require('@aws-cdk/aws-config');
import iam = require('@aws-cdk/aws-iam');
import s3 = require('@aws-cdk/aws-s3');

const awsConfigBucket = new s3.Bucket(this, 'BucketAwsConfig', {versioned: true});

const policyStatement1 = new iam.PolicyStatement({
    actions: ['s3:*'],
    principals: [new iam.AnyPrincipal()],
    resources: [`${awsConfigBucket.bucketArn}/*`],
    conditions: {'Bool': {'aws:SecureTransport': false}}
});
policyStatement1.effect = iam.Effect.DENY;
awsConfigBucket.addToResourcePolicy(policyStatement1);

const policyStatement2 = new iam.PolicyStatement({
    actions: ['s3:PutObject'],
    principals: [new iam.ServicePrincipal('config.amazonaws.com')],
    resources: [`${awsConfigBucket.bucketArn}/*`],
    conditions: {'StringEquals': { "s3:x-amz-acl": "bucket-owner-full-control" }}
});
policyStatement2.effect = iam.Effect.ALLOW;
awsConfigBucket.addToResourcePolicy(policyStatement2);

const policyStatement3 = new iam.PolicyStatement({
    actions: ['s3:GetBucketAcl'],
    principals: [new iam.ServicePrincipal('config.amazonaws.com')],
    resources: [awsConfigBucket.bucketArn]
});
policyStatement3.effect = iam.Effect.ALLOW;
awsConfigBucket.addToResourcePolicy(policyStatement3);

const awsConfigRole = new iam.Role(this, 'RoleAwsConfig', {
    assumedBy: new iam.ServicePrincipal('config.amazonaws.com')
});
awsConfigRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSConfigRole'))

new config.CfnDeliveryChannel(this, "DeliveryChannel", {s3BucketName: awsConfigBucket.bucketName});
new config.CfnConfigurationRecorder(this, "Recorder", {
    name: "default", 
    roleArn: awsConfigRole.roleArn
});

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:6
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
tvbcommented, Apr 12, 2021

I can confirm the following works (assume we’ve created the needed role and a bucket outside of this stack):

        ## CONFIG CONFIGURATION RECORDER
        config_configuration_recorder = config.CfnConfigurationRecorder(self, "ConfigConfigurationRecorder",
            role_arn=config_iam_role.role_arn,
            recording_group=config.CfnConfigurationRecorder.RecordingGroupProperty(
                all_supported=True,
                include_global_resource_types=True
            )
        )

        ## CONFIG DELIVERY CHANNEL
        config_delivery_channel = config.CfnDeliveryChannel(self, "ConfigDeliveryChannel",
            config_snapshot_delivery_properties=config.CfnDeliveryChannel.ConfigSnapshotDeliveryPropertiesProperty(
                delivery_frequency="TwentyFour_Hours"
            ),
            s3_bucket_name="my_domain-config",
        )

Adding a add_depends_on for the config_recorder to depend on the delivery_channel stalls the CloudFormation deployment. Without it it deployed fine.

2reactions
mark-schaalcommented, Mar 6, 2020

For what it is worth, I found that you can use the following CfnResource constructs to workaround this edge. Note that you would likely need to check if a Service Role already exists for Config in the target region, because those operations are not idempotent. I believe the same goes for the Delivery Channel and Recorder resources.

Also, there is not currently a clean way to return an Arn value for the CfnServiceLinkedRole construct when using .getAtt(). It will not return Arn or roleArn in the ref, so for our purposes we hardcoded the Arn string. This is admittedly a bit ugly, but it does function correctly when deploying the CFT.

When deployed, the Configuration Recorder resource will wait for the Delivery Channel resource to become available before finalizing creation. Be warned: if you have any errors in your Delivery Channel configuration, it may take the full timeout duration for the operation to fail (1hr default).

/**
 * The core network stack responsible for initializing
 * all integrated configuration management and tracking
 * resources attached to the AWS landing zone.
 *
 * @export
 * @class CMRecorderStack
 * @extends {cdk.Stack}
 */
export class CMRecorderStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props: CommonStackProps) {
    super(scope, id, props);

    // Provides consistent bucket formatting from props.
    const formatBucketName = (name: string) => {
      const { env: { region }, bucket: { prefix, delimiter } } = props;
      return [prefix, region, name].join(delimiter);
    }

    // Defines the Service Linked Role.
    const slr = new CfnServiceLinkedRole(this, 'ServiceLinkedRole', {
      awsServiceName: 'config.amazonaws.com',
    });

    // Defines the Configuration Recorder.
    const recorder = new CfnConfigurationRecorder(this, 'ConfigRecorder', {
      roleArn: `arn:aws:iam::${Stack.of(this).account}:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig`,
      name: 'ConfigRecorder',
      recordingGroup: {
        allSupported: true
      }
    });

    // Defines the Configuration Delivery Channel SNS Topic.
    const deliverySnsTopic = new Topic(this, 'ConfigSNSTopicDelivery', {});

    deliverySnsTopic.addToResourcePolicy(new PolicyStatement({
      effect: Effect.ALLOW,
      actions: ['SNS:Publish'],
      principals: [
        new AccountPrincipal(Stack.of(this).account),
        new ServicePrincipal('config.amazonaws.com')
      ],
      resources: ['*']
    }));

    // Defines the Config Delivery Channel S3 Bucket.
    const deliveryChannelBucket = new Bucket(this, 'S3Config', {
      bucketName: formatBucketName('config'),
      removalPolicy: RemovalPolicy.DESTROY,
      versioned: false,
      lifecycleRules: [
        {
          id: 'Transition90dRetain7y',
          enabled: true,
          expiration: Duration.days(2555),
          transitions: [{
            storageClass: StorageClass.GLACIER,
            transitionAfter: Duration.days(90),
          }]
        }
      ],
    });

    // Attaches the AWSConfigBucketPermissionsCheck policy statement.
    deliveryChannelBucket.addToResourcePolicy(new PolicyStatement({
      effect: Effect.ALLOW,
      principals: [new ServicePrincipal('config.amazonaws.com')],
      resources: [deliveryChannelBucket.bucketArn],
      actions: ['s3:ListBucket', 's3:GetBucketAcl'],
    }));

    // Attaches the AWSConfigBucketDelivery policy statement.
    deliveryChannelBucket.addToResourcePolicy(new PolicyStatement({
      effect: Effect.ALLOW,
      principals: [new ServicePrincipal('config.amazonaws.com')],
      resources: [deliveryChannelBucket.arnForObjects(`AWSLogs/${Stack.of(this).account}/Config/*`)],
      actions: ['s3:PutObject'],
      conditions: {
        StringEquals: {
          's3:x-amz-acl': 'bucket-owner-full-control',
        }
      }
    }));

    // Defines the Configuration Delivery Channel.
    const deliveryChannel = new CfnDeliveryChannel(this, 'ConfigDeliveryChannel', {
      s3BucketName: deliveryChannelBucket.bucketName,
      name: 'DeliveryChannel',
      snsTopicArn: deliverySnsTopic.topicArn,
      configSnapshotDeliveryProperties: {
        deliveryFrequency: 'One_Hour'
      }
    });

  }
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

Managing the Delivery Channel - AWS Config
As AWS Config continually records the changes that occur to your AWS resources, it sends notifications and updated configuration states through the delivery...
Read more >
How can I recreate an AWS Config delivery channel?
1. Open the IAM console. 2. Choose Roles, and then choose Create role. 3. In Select type of trusted entity, choose AWS service...
Read more >
AWS::Config::DeliveryChannel - AWS CloudFormation
Specifies a delivery channel object to deliver configuration information to an Amazon S3 bucket and Amazon SNS topic. Before you can create a...
Read more >
Managing the Configuration Recorder - AWS Config
AWS Config uses the configuration recorder to detect changes in your resource configurations and capture these changes as configuration items.
Read more >
DeliveryChannel - AWS Config
By default, AWS Config assigns the name "default" when creating the delivery channel. To change the delivery channel name, you must use the ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found