In my previous blog I showed you how you can write your own config rules. But it will only bring you value if you deploy the rule in your AWS Accounts. In this blog we will dive into the distribution of these config rules.
What are my options?
There are many ways you can deploy these rules in your member accounts. In this blog I will only focus on the 2 real options:
- Deploy the rules using CloudFormation StackSets in each account.
- Use conformance packs.
Using CloudFormation StackSets
When you look at what CloudFormation StackSets does, you will see the reason why this would be a good fit:
You need to have a delegated administrator. This admin has the ability to deploy the template in other accounts. You define all the account ids and regions you want to deploy the config rules in.
If you combine this with a landingzone you most likely already have this in place.
Using Conformance Packs
A conformance pack is nothing more than a bunch of config rules grouped together. But having this bundle gives you some more benefits. For example, you can see the compliance score of your bundle in your account. You can compare it to the compliance scores that you can see in Security Hub. Assume you have 10 config rules. Now one has resources that are non-compliant you will have a compliance score of 90%.
But this alone would not address the need to distribute the rules across all your accounts. But the conformance packs come in 2 flavors, regular and organizational conformance packs. Regular rules deploy the conformance pack in a single account. The organizational conformance pack will deploy the rule in all accounts.
Note, You can deploy the regular config rules using an organization config rule. This would not give you the grouping of all your rules. For this reason I tend to use conformance packs.
Show me an example
It’s always better to use an example, this snippet will only work when you follow the prerequisites. When you deploy this template in the account that is setup as the delegated administrator. All member accounts will have the config rules available.
Resources:
ConformancePackBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
BucketName: !Sub awsconfigconforms-${AWS::AccountId}-${AWS::Region}
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
ConformancePackBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref ConformancePackBucket
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: AWSConfigConformsCheckAcls
Action: s3:GetBucketAcl
Effect: Allow
Resource: !GetAtt ConformancePackBucket.Arn
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:role/aws-service-role/config-conforms.amazonaws.com/AWSServiceRoleForConfigConforms
Condition:
StringEquals:
aws:PrincipalOrgID: ${aws:PrincipalOrgID}
Bool:
aws:SecureTransport: true
- Sid: AWSConfigConformsReadWriteBucket
Action:
- s3:PutObject
- s3:GetObject
Effect: Allow
Resource: !Sub ${ConformancePackBucket.Arn}/*
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:role/aws-service-role/config-conforms.amazonaws.com/AWSServiceRoleForConfigConforms
Condition:
StringEquals:
aws:PrincipalOrgID: ${aws:PrincipalOrgID}
Bool:
aws:SecureTransport: true
OrganizationConformancePack:
Type: AWS::Config::OrganizationConformancePack
Properties:
OrganizationConformancePackName: lz-framework
DeliveryS3Bucket: !Ref ConformancePackBucket
ExcludedAccounts: [ "000000000000" ] # AccountID to exclude
TemplateBody: |-
Resources:
S3AccessLogging:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: lz-my-custom-rule
Description: My custom rule
EvaluationModes:
- Mode: DETECTIVE
Scope:
ComplianceResourceTypes:
- AWS::S3::Bucket
Source:
Owner: CUSTOM_POLICY
SourceDetails:
- EventSource: aws.config
MessageType: ConfigurationItemChangeNotification
- EventSource: aws.config
MessageType: OversizedConfigurationItemChangeNotification
CustomPolicyDetails:
EnableDebugLogDelivery: 'true'
PolicyRuntime: guard-2.x.x
PolicyText: |-
# The rule definition goes here
The template only includes 3 main resources, but 2 actual logical components. A bucket with a policy and the conformance pack definition. Let’s go over all the components in the example.
S3 Bucket and the bucket policy
This bucket needs to start with awsconfigconforms
. The config-conforms.amazonaws.com
service will use this bucket to upload the template. The template comes from the OrganizationConformancePack
resource. The policy allows the service to put and get objects as long as it is within the organization. This allows the service to deploy the template in all member accounts.
Organization Conformance Pack
The organization conformance pack definition looks big but it’s quite simple. If you have a closer look you will notice it has only 4 properties:
- OrganizationConformancePackName
- DeliveryS3Bucket
- ExcludedAccounts
- TemplateBody
Now the first 3 options are pretty straight forward. The template itself is a bit more complicated. In my example I used an inline template, I did this for the sake of this blog. But you can also reference an existing object on S3. This way you can use linting tools like cfn-lint on your conformance pack. This will reduce errors during deployment as you can catch them before you commit and push your code.
If you need help with setting up the conformance pack template, have a look at these samples.
Conclusion
When you combine custom rules in a conformance pack. It will allow you to simplify the distribution of these rules. You don’t need to maintain any pipelines or stack sets. And you outsource the distribution of your rules to AWS.
This removes all the maintenance on the infrastructure to do this distribution yourself. And you don’t need to think about scenarios like, adding/removing accounts.
Photo by ELEVATE