Skip to main content
Latest Documentation
This is the latest documentation for the Cloud Posse Reference Architecture. To determine which version you're currently using, please see Version Identification.

Configure Custom AWS Config Rules

AWS Config rules evaluate resource configurations for compliance. While AWS provides many managed rules, you may need custom rules for organization-specific requirements. This tutorial covers creating and deploying custom Config rules.

Overview

AWS Config supports two types of rules:

TypeDescriptionUse Case
Managed RulesAWS-provided, ready to useStandard compliance checks
Custom RulesUser-defined using LambdaOrganization-specific requirements

Prerequisites

  • AWS Config deployed — Follow the AWS Config setup guide
  • Conformance packs configured — Organization-level Config enabled
  • Lambda permissions — Ability to deploy Lambda functions (for custom rules)

Using AWS Managed Rules

AWS provides 300+ managed rules for common compliance scenarios.

Add Managed Rules to Conformance Pack

1 Create Custom Conformance Pack File

Create a conformance pack YAML file in your component directory:

# components/terraform/aws-config/conformance-packs/custom-security-pack.yaml
Resources:
S3BucketPublicReadProhibited:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: s3-bucket-public-read-prohibited
Description: Checks that S3 buckets do not allow public read access
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_PUBLIC_READ_PROHIBITED
Scope:
ComplianceResourceTypes:
- AWS::S3::Bucket

S3BucketSSLRequestsOnly:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: s3-bucket-ssl-requests-only
Description: Checks that S3 bucket policies require SSL
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_SSL_REQUESTS_ONLY
Scope:
ComplianceResourceTypes:
- AWS::S3::Bucket

RDSEncryptionEnabled:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: rds-storage-encrypted
Description: Checks that RDS instances have encryption enabled
Source:
Owner: AWS
SourceIdentifier: RDS_STORAGE_ENCRYPTED
Scope:
ComplianceResourceTypes:
- AWS::RDS::DBInstance

IAMPasswordPolicy:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: iam-password-policy
Description: Checks that IAM password policy meets requirements
Source:
Owner: AWS
SourceIdentifier: IAM_PASSWORD_POLICY
InputParameters:
RequireUppercaseCharacters: "true"
RequireLowercaseCharacters: "true"
RequireSymbols: "true"
RequireNumbers: "true"
MinimumPasswordLength: "14"
PasswordReusePrevention: "24"
MaxPasswordAge: "90"

2 Reference in Stack Configuration

Add the custom conformance pack to your AWS Config stack:

# stacks/catalog/aws-config/organization.yaml
components:
terraform:
aws-config:
vars:
default_scope: organization
conformance_packs:
# AWS-managed CIS pack
- name: CIS-AWS-v1.4-Level2
conformance_pack: "https://raw.githubusercontent.com/awslabs/aws-config-rules/master/aws-config-conformance-packs/Operational-Best-Practices-for-CIS-AWS-v1.4-Level2.yaml"
parameter_overrides: {}
# Custom pack from local file
- name: Custom-Security-Pack
conformance_pack: "conformance-packs/custom-security-pack.yaml"
scope: organization
parameter_overrides: {}

3 Deploy the Configuration

Apply the AWS Config changes:

atmos terraform apply aws-config -s core-ue1-root

Creating Custom Lambda Rules

For requirements not covered by managed rules, create custom rules with Lambda.

Example: Require Specific Tags

1 Create Lambda Function

Create a Lambda function to check for required tags:

# lambda/config-rules/required-tags/handler.py
import json
import boto3

def lambda_handler(event, context):
"""Check that resources have required tags."""
config = boto3.client('config')

# Parse the invoking event
invoking_event = json.loads(event['invokingEvent'])
configuration_item = invoking_event['configurationItem']

# Get rule parameters
rule_parameters = json.loads(event.get('ruleParameters', '{}'))
required_tags = rule_parameters.get('requiredTags', 'Environment,Owner,CostCenter').split(',')

# Get resource tags
resource_tags = configuration_item.get('tags', {})
tag_keys = list(resource_tags.keys())

# Check for missing required tags
missing_tags = [tag for tag in required_tags if tag not in tag_keys]

if missing_tags:
compliance_type = 'NON_COMPLIANT'
annotation = f'Missing required tags: {", ".join(missing_tags)}'
else:
compliance_type = 'COMPLIANT'
annotation = 'All required tags present'

# Report evaluation result
config.put_evaluations(
Evaluations=[
{
'ComplianceResourceType': configuration_item['resourceType'],
'ComplianceResourceId': configuration_item['resourceId'],
'ComplianceType': compliance_type,
'Annotation': annotation,
'OrderingTimestamp': configuration_item['configurationItemCaptureTime']
},
],
ResultToken=event['resultToken']
)

return {
'compliance_type': compliance_type,
'annotation': annotation
}

2 Deploy Lambda Function

Deploy the Lambda function using Terraform:

# components/terraform/config-rules-lambda/main.tf
resource "aws_lambda_function" "required_tags" {
function_name = "${var.namespace}-config-required-tags"
role = aws_iam_role.config_rule_lambda.arn
handler = "handler.lambda_handler"
runtime = "python3.11"
timeout = 60

filename = data.archive_file.required_tags.output_path
source_code_hash = data.archive_file.required_tags.output_base64sha256
}

resource "aws_lambda_permission" "config" {
statement_id = "AllowConfigInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.required_tags.function_name
principal = "config.amazonaws.com"
}

3 Create Custom Config Rule

Add the custom rule to your conformance pack:

# conformance-packs/custom-tagging-pack.yaml
Resources:
RequiredTagsRule:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: required-tags-check
Description: Checks that resources have required tags
Source:
Owner: CUSTOM_LAMBDA
SourceIdentifier: !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${Namespace}-config-required-tags"
SourceDetails:
- EventSource: aws.config
MessageType: ConfigurationItemChangeNotification
Scope:
ComplianceResourceTypes:
- AWS::EC2::Instance
- AWS::RDS::DBInstance
- AWS::S3::Bucket
InputParameters:
requiredTags: "Environment,Owner,CostCenter"

Common Rule Patterns

Check S3 Encryption

S3BucketServerSideEncryptionEnabled:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: s3-bucket-server-side-encryption-enabled
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED

Check EBS Encryption

EBSEncryptionByDefault:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: ec2-ebs-encryption-by-default
Source:
Owner: AWS
SourceIdentifier: EC2_EBS_ENCRYPTION_BY_DEFAULT

Check VPC Flow Logs

VPCFlowLogsEnabled:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: vpc-flow-logs-enabled
Source:
Owner: AWS
SourceIdentifier: VPC_FLOW_LOGS_ENABLED
InputParameters:
trafficType: ALL

Check Root Account MFA

RootAccountMFAEnabled:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: root-account-mfa-enabled
Source:
Owner: AWS
SourceIdentifier: ROOT_ACCOUNT_MFA_ENABLED

Remediating Non-Compliant Resources

Automatic Remediation

Configure automatic remediation actions:

# Add to conformance pack
S3BucketPublicReadProhibited:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: s3-bucket-public-read-prohibited
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_PUBLIC_READ_PROHIBITED

S3PublicReadRemediation:
Type: AWS::Config::RemediationConfiguration
Properties:
ConfigRuleName: s3-bucket-public-read-prohibited
TargetType: SSM_DOCUMENT
TargetId: AWS-DisableS3BucketPublicReadWrite
Parameters:
S3BucketName:
ResourceValue:
Value: RESOURCE_ID
Automatic: true
MaximumAutomaticAttempts: 3
RetryAttemptSeconds: 60

Manual Remediation

Query non-compliant resources and remediate:

# List non-compliant resources
aws configservice get-compliance-details-by-config-rule \
--config-rule-name s3-bucket-public-read-prohibited \
--compliance-types NON_COMPLIANT \
--region us-east-1

# Get remediation status
aws configservice describe-remediation-execution-status \
--config-rule-name s3-bucket-public-read-prohibited \
--region us-east-1

Troubleshooting

Rule Not Evaluating

If rules aren't evaluating resources:

  1. Verify the resource type is in the rule's scope
  2. Check that Config recorder is enabled for the resource type
  3. Review Lambda function logs (for custom rules)
# Check recorder status
aws configservice describe-configuration-recorder-status --region us-east-1

# Check rule evaluation
aws configservice describe-config-rule-evaluation-status \
--config-rule-names s3-bucket-public-read-prohibited \
--region us-east-1

Lambda Errors

For custom Lambda rules, check CloudWatch Logs:

aws logs filter-log-events \
--log-group-name "/aws/lambda/<function-name>" \
--filter-pattern "ERROR" \
--region us-east-1

See Also

References