Skip to content
Back to blog AWS Config Rules with Auto Remediation - Enforce Compliance Automatically

AWS Config Rules with Auto Remediation - Enforce Compliance Automatically

AWSSecurity

AWS Config Rules with Auto Remediation - Enforce Compliance Automatically

Someone creates an S3 bucket without encryption. Someone launches an EC2 instance with a public IP. Someone disables versioning on a critical bucket. By the time you notice, it’s been running non-compliant for weeks.

AWS Config changes this from reactive firefighting to proactive enforcement. It continuously evaluates your resources against rules, detects violations, and - with auto remediation - fixes them automatically.

This post covers how to set up Config Rules with automatic remediation, common compliance use cases, and complete Terraform examples.

TL;DR

  • AWS Config continuously evaluates resource configurations against rules
  • Managed rules cover common compliance scenarios (encryption, public access, tagging)
  • Custom rules use Lambda or Guard policy-as-code for specific requirements
  • Auto remediation uses SSM Automation documents to fix violations
  • IAM permissions are the tricky part - remediation role needs specific actions

Code Repository: All code from this post is available at github.com/moabukar/blog-code/aws-config-auto-remediation


How AWS Config Works

AWS Config records configuration changes to your AWS resources and evaluates them against rules:

┌─────────────────────────────────────────────────────────────────┐
│                     AWS Config Flow                             │
└─────────────────────────────────────────────────────────────────┘

  Resource Change          Config Rule            Remediation
       │                       │                      │
       ▼                       ▼                      ▼
┌─────────────┐         ┌─────────────┐        ┌─────────────┐
│ S3 Bucket   │ ──────► │  Evaluate   │ ──────►│   SSM       │
│ Created     │         │  Against    │        │ Automation  │
│             │         │  Rules      │        │  Document   │
└─────────────┘         └─────────────┘        └─────────────┘
                               │                      │
                               ▼                      ▼
                        ┌─────────────┐        ┌─────────────┐
                        │ COMPLIANT   │        │   Fixed!    │
                        │     or      │        │ Encryption  │
                        │NON_COMPLIANT│        │  Enabled    │
                        └─────────────┘        └─────────────┘

The flow is:

  1. Recording - Config records resource configurations and changes
  2. Evaluation - Rules evaluate resources (on change or periodic)
  3. Compliance - Resources are marked COMPLIANT or NON_COMPLIANT
  4. Remediation - Optional: auto-fix non-compliant resources

Enabling AWS Config

Before using Config Rules, you need a Config Recorder and Delivery Channel:

# S3 bucket for Config recordings
resource "aws_s3_bucket" "config" {
  bucket = "my-config-recordings-${data.aws_caller_identity.current.account_id}"
}

resource "aws_s3_bucket_versioning" "config" {
  bucket = aws_s3_bucket.config.id
  versioning_configuration {
    status = "Enabled"
  }
}

# IAM role for Config
resource "aws_iam_role" "config" {
  name = "aws-config-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Principal = {
        Service = "config.amazonaws.com"
      }
    }]
  })
}

resource "aws_iam_role_policy_attachment" "config" {
  role       = aws_iam_role.config.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWS_ConfigRole"
}

resource "aws_iam_role_policy" "config_s3" {
  name = "config-s3-policy"
  role = aws_iam_role.config.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "s3:PutObject",
          "s3:PutObjectAcl"
        ]
        Resource = "${aws_s3_bucket.config.arn}/*"
        Condition = {
          StringLike = {
            "s3:x-amz-acl" = "bucket-owner-full-control"
          }
        }
      },
      {
        Effect   = "Allow"
        Action   = "s3:GetBucketAcl"
        Resource = aws_s3_bucket.config.arn
      }
    ]
  })
}

# Config Recorder
resource "aws_config_configuration_recorder" "main" {
  name     = "default"
  role_arn = aws_iam_role.config.arn

  recording_group {
    all_supported                 = true
    include_global_resource_types = true
  }
}

# Delivery Channel
resource "aws_config_delivery_channel" "main" {
  name           = "default"
  s3_bucket_name = aws_s3_bucket.config.id

  depends_on = [aws_config_configuration_recorder.main]
}

# Start the recorder
resource "aws_config_configuration_recorder_status" "main" {
  name       = aws_config_configuration_recorder.main.name
  is_enabled = true

  depends_on = [aws_config_delivery_channel.main]
}

Managed Rules

AWS provides 300+ managed rules covering common compliance requirements. No Lambda required - just reference them:

S3 Bucket Encryption

resource "aws_config_config_rule" "s3_encryption" {
  name = "s3-bucket-server-side-encryption-enabled"

  source {
    owner             = "AWS"
    source_identifier = "S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED"
  }

  depends_on = [aws_config_configuration_recorder.main]
}

S3 Public Access Block

resource "aws_config_config_rule" "s3_public_access" {
  name = "s3-bucket-public-read-prohibited"

  source {
    owner             = "AWS"
    source_identifier = "S3_BUCKET_PUBLIC_READ_PROHIBITED"
  }

  depends_on = [aws_config_configuration_recorder.main]
}

EBS Encryption

resource "aws_config_config_rule" "ebs_encryption" {
  name = "encrypted-volumes"

  source {
    owner             = "AWS"
    source_identifier = "ENCRYPTED_VOLUMES"
  }

  depends_on = [aws_config_configuration_recorder.main]
}

Required Tags

resource "aws_config_config_rule" "required_tags" {
  name = "required-tags"

  source {
    owner             = "AWS"
    source_identifier = "REQUIRED_TAGS"
  }

  input_parameters = jsonencode({
    tag1Key   = "Environment"
    tag2Key   = "Owner"
    tag3Key   = "CostCenter"
  })

  scope {
    compliance_resource_types = [
      "AWS::EC2::Instance",
      "AWS::S3::Bucket",
      "AWS::RDS::DBInstance"
    ]
  }

  depends_on = [aws_config_configuration_recorder.main]
}

IAM Password Policy

resource "aws_config_config_rule" "iam_password_policy" {
  name = "iam-password-policy"

  source {
    owner             = "AWS"
    source_identifier = "IAM_PASSWORD_POLICY"
  }

  input_parameters = jsonencode({
    RequireUppercaseCharacters = "true"
    RequireLowercaseCharacters = "true"
    RequireSymbols             = "true"
    RequireNumbers             = "true"
    MinimumPasswordLength      = "14"
    PasswordReusePrevention    = "24"
    MaxPasswordAge             = "90"
  })

  depends_on = [aws_config_configuration_recorder.main]
}

RDS Encryption

resource "aws_config_config_rule" "rds_encryption" {
  name = "rds-storage-encrypted"

  source {
    owner             = "AWS"
    source_identifier = "RDS_STORAGE_ENCRYPTED"
  }

  depends_on = [aws_config_configuration_recorder.main]
}

Custom Rules with Guard

For requirements not covered by managed rules, use Guard (policy-as-code) or Lambda.

Guard is simpler for most use cases:

resource "aws_config_config_rule" "ec2_instance_type" {
  name = "ec2-approved-instance-types"

  source {
    owner = "CUSTOM_POLICY"

    source_detail {
      message_type = "ConfigurationItemChangeNotification"
    }

    custom_policy_details {
      policy_runtime = "guard-2.x.x"
      policy_text    = <<-POLICY
        rule ec2_approved_instance_types when resourceType == "AWS::EC2::Instance" {
          configuration.instanceType IN ["t3.micro", "t3.small", "t3.medium", "t3.large"]
        }
      POLICY
    }
  }

  depends_on = [aws_config_configuration_recorder.main]
}

Guard syntax is declarative and readable:

# Ensure RDS instances use approved engine versions
rule rds_approved_versions when resourceType == "AWS::RDS::DBInstance" {
  configuration.engineVersion IN ["14.7", "14.8", "15.2", "15.3"]
}

# Ensure EC2 instances don't have public IPs
rule ec2_no_public_ip when resourceType == "AWS::EC2::Instance" {
  configuration.publicIpAddress NOT EXISTS OR
  configuration.publicIpAddress == null
}

# Ensure S3 buckets have logging enabled
rule s3_logging_enabled when resourceType == "AWS::S3::Bucket" {
  supplementaryConfiguration.BucketLoggingConfiguration.destinationBucketName EXISTS
}

Auto Remediation

The magic happens when you connect Config Rules to SSM Automation documents. Non-compliant resources get fixed automatically.

Remediation Architecture

┌──────────────────────────────────────────────────────────────────┐
│                    Auto Remediation Flow                         │
└──────────────────────────────────────────────────────────────────┘

     Config Rule              Remediation              SSM Document
         │                      Config                      │
         ▼                         │                        ▼
┌────────────────┐          ┌─────────────┐         ┌─────────────┐
│ NON_COMPLIANT  │ ──────── │ Trigger     │ ──────► │ AWS-        │
│ S3 Bucket      │          │ Remediation │         │ EnableS3    │
│ (no encryption)│          │ Action      │         │ BucketEnc   │
└────────────────┘          └─────────────┘         └─────────────┘


                                                    ┌─────────────┐
                                                    │ S3 Bucket   │
                                                    │ Now Has     │
                                                    │ Encryption! │
                                                    └─────────────┘

IAM Role for Remediation

This is where most people get stuck. The remediation role needs permissions for both SSM and the actions being performed:

# Remediation role
resource "aws_iam_role" "config_remediation" {
  name = "config-remediation-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Principal = {
        Service = "ssm.amazonaws.com"
      }
    }]
  })
}

# SSM Automation permissions
resource "aws_iam_role_policy" "remediation_ssm" {
  name = "remediation-ssm"
  role = aws_iam_role.config_remediation.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "ssm:StartAutomationExecution",
          "ssm:GetAutomationExecution"
        ]
        Resource = "*"
      }
    ]
  })
}

# S3 remediation permissions
resource "aws_iam_role_policy" "remediation_s3" {
  name = "remediation-s3"
  role = aws_iam_role.config_remediation.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "s3:PutBucketEncryption",
          "s3:PutBucketPublicAccessBlock",
          "s3:PutBucketVersioning",
          "s3:PutBucketLogging"
        ]
        Resource = "arn:aws:s3:::*"
      }
    ]
  })
}

# EC2 remediation permissions
resource "aws_iam_role_policy" "remediation_ec2" {
  name = "remediation-ec2"
  role = aws_iam_role.config_remediation.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "ec2:ModifyInstanceAttribute",
          "ec2:StopInstances",
          "ec2:StartInstances",
          "ec2:TerminateInstances"
        ]
        Resource = "*"
      }
    ]
  })
}

S3 Encryption Remediation

resource "aws_config_remediation_configuration" "s3_encryption" {
  config_rule_name = aws_config_config_rule.s3_encryption.name
  target_type      = "SSM_DOCUMENT"
  target_id        = "AWS-EnableS3BucketEncryption"
  target_version   = "1"

  parameter {
    name         = "BucketName"
    resource_value = "RESOURCE_ID"
  }

  parameter {
    name         = "SSEAlgorithm"
    static_value = "AES256"
  }

  parameter {
    name         = "AutomationAssumeRole"
    static_value = aws_iam_role.config_remediation.arn
  }

  automatic                  = true
  maximum_automatic_attempts = 5
  retry_attempt_seconds      = 60

  execution_controls {
    ssm_controls {
      concurrent_execution_rate_percentage = 25
      error_percentage                     = 25
    }
  }
}

S3 Public Access Block Remediation

resource "aws_config_remediation_configuration" "s3_public_access" {
  config_rule_name = aws_config_config_rule.s3_public_access.name
  target_type      = "SSM_DOCUMENT"
  target_id        = "AWS-DisableS3BucketPublicReadWrite"
  target_version   = "1"

  parameter {
    name         = "S3BucketName"
    resource_value = "RESOURCE_ID"
  }

  parameter {
    name         = "AutomationAssumeRole"
    static_value = aws_iam_role.config_remediation.arn
  }

  automatic                  = true
  maximum_automatic_attempts = 5
  retry_attempt_seconds      = 60
}

Custom SSM Automation Documents

When managed SSM documents don’t fit your needs, create custom ones:

Enable S3 Versioning

resource "aws_ssm_document" "enable_s3_versioning" {
  name            = "Custom-EnableS3BucketVersioning"
  document_type   = "Automation"
  document_format = "YAML"

  content = <<-DOC
    description: Enable versioning on S3 bucket
    schemaVersion: '0.3'
    assumeRole: '{{ AutomationAssumeRole }}'
    parameters:
      BucketName:
        type: String
        description: Name of the S3 bucket
      AutomationAssumeRole:
        type: String
        description: IAM role for automation
    mainSteps:
      - name: EnableVersioning
        action: aws:executeAwsApi
        inputs:
          Service: s3
          Api: PutBucketVersioning
          Bucket: '{{ BucketName }}'
          VersioningConfiguration:
            Status: Enabled
        isEnd: true
  DOC
}

resource "aws_config_remediation_configuration" "s3_versioning" {
  config_rule_name = aws_config_config_rule.s3_versioning.name
  target_type      = "SSM_DOCUMENT"
  target_id        = aws_ssm_document.enable_s3_versioning.name

  parameter {
    name         = "BucketName"
    resource_value = "RESOURCE_ID"
  }

  parameter {
    name         = "AutomationAssumeRole"
    static_value = aws_iam_role.config_remediation.arn
  }

  automatic                  = true
  maximum_automatic_attempts = 3
  retry_attempt_seconds      = 60
}

Stop Non-Compliant EC2 Instances

For serious violations, you might want to stop instances:

resource "aws_ssm_document" "stop_ec2_instance" {
  name            = "Custom-StopNonCompliantEC2"
  document_type   = "Automation"
  document_format = "YAML"

  content = <<-DOC
    description: Stop non-compliant EC2 instance
    schemaVersion: '0.3'
    assumeRole: '{{ AutomationAssumeRole }}'
    parameters:
      InstanceId:
        type: String
        description: EC2 Instance ID
      AutomationAssumeRole:
        type: String
        description: IAM role for automation
    mainSteps:
      - name: StopInstance
        action: aws:executeAwsApi
        inputs:
          Service: ec2
          Api: StopInstances
          InstanceIds:
            - '{{ InstanceId }}'
      - name: WaitForStop
        action: aws:waitForAwsResourceProperty
        inputs:
          Service: ec2
          Api: DescribeInstances
          InstanceIds:
            - '{{ InstanceId }}'
          PropertySelector: '$.Reservations[0].Instances[0].State.Name'
          DesiredValues:
            - stopped
        isEnd: true
  DOC
}

Tag Non-Compliant Resources

Instead of fixing, tag resources for review:

resource "aws_ssm_document" "tag_non_compliant" {
  name            = "Custom-TagNonCompliantResource"
  document_type   = "Automation"
  document_format = "YAML"

  content = <<-DOC
    description: Tag resource as non-compliant
    schemaVersion: '0.3'
    assumeRole: '{{ AutomationAssumeRole }}'
    parameters:
      ResourceArn:
        type: String
        description: ARN of the resource
      ViolationType:
        type: String
        description: Type of compliance violation
      AutomationAssumeRole:
        type: String
        description: IAM role for automation
    mainSteps:
      - name: TagResource
        action: aws:executeAwsApi
        inputs:
          Service: resourcegroupstaggingapi
          Api: TagResources
          ResourceARNList:
            - '{{ ResourceArn }}'
          Tags:
            compliance-status: non-compliant
            violation-type: '{{ ViolationType }}'
            detected-at: '{{ global:DATE_TIME }}'
        isEnd: true
  DOC
}

Common Remediation Patterns

Pattern 1: Encryption Everywhere

locals {
  encryption_rules = {
    s3 = {
      rule_identifier = "S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED"
      remediation_doc = "AWS-EnableS3BucketEncryption"
      parameters = {
        BucketName   = "RESOURCE_ID"
        SSEAlgorithm = "aws:kms"
        KMSMasterKey = data.aws_kms_key.default.arn
      }
    }
    ebs = {
      rule_identifier = "ENCRYPTED_VOLUMES"
      remediation_doc = null  # Can't encrypt existing volumes - alert only
      parameters      = {}
    }
    rds = {
      rule_identifier = "RDS_STORAGE_ENCRYPTED"
      remediation_doc = null  # Can't encrypt existing RDS - alert only
      parameters      = {}
    }
  }
}

resource "aws_config_config_rule" "encryption" {
  for_each = local.encryption_rules

  name = "${each.key}-encryption-enabled"

  source {
    owner             = "AWS"
    source_identifier = each.value.rule_identifier
  }

  depends_on = [aws_config_configuration_recorder.main]
}

resource "aws_config_remediation_configuration" "encryption" {
  for_each = { for k, v in local.encryption_rules : k => v if v.remediation_doc != null }

  config_rule_name = aws_config_config_rule.encryption[each.key].name
  target_type      = "SSM_DOCUMENT"
  target_id        = each.value.remediation_doc

  dynamic "parameter" {
    for_each = each.value.parameters
    content {
      name           = parameter.key
      static_value   = parameter.value != "RESOURCE_ID" ? parameter.value : null
      resource_value = parameter.value == "RESOURCE_ID" ? "RESOURCE_ID" : null
    }
  }

  parameter {
    name         = "AutomationAssumeRole"
    static_value = aws_iam_role.config_remediation.arn
  }

  automatic                  = true
  maximum_automatic_attempts = 5
  retry_attempt_seconds      = 60
}

Pattern 2: Security Baseline

locals {
  security_baseline = {
    "s3-public-read"       = "S3_BUCKET_PUBLIC_READ_PROHIBITED"
    "s3-public-write"      = "S3_BUCKET_PUBLIC_WRITE_PROHIBITED"
    "ec2-imdsv2"           = "EC2_IMDSV2_CHECK"
    "iam-root-mfa"         = "ROOT_ACCOUNT_MFA_ENABLED"
    "iam-user-mfa"         = "IAM_USER_MFA_ENABLED"
    "rds-public"           = "RDS_INSTANCE_PUBLIC_ACCESS_CHECK"
    "sg-ssh-restricted"    = "INCOMING_SSH_DISABLED"
    "cloudtrail-enabled"   = "CLOUDTRAIL_ENABLED"
    "vpc-flow-logs"        = "VPC_FLOW_LOGS_ENABLED"
    "guardduty-enabled"    = "GUARDDUTY_ENABLED_CENTRALIZED"
  }
}

resource "aws_config_config_rule" "security_baseline" {
  for_each = local.security_baseline

  name = each.key

  source {
    owner             = "AWS"
    source_identifier = each.value
  }

  depends_on = [aws_config_configuration_recorder.main]
}

Pattern 3: Alerting Without Remediation

For sensitive resources where auto-remediation is risky:

resource "aws_config_config_rule" "production_changes" {
  name = "production-resource-changes"

  source {
    owner = "CUSTOM_POLICY"

    source_detail {
      message_type = "ConfigurationItemChangeNotification"
    }

    custom_policy_details {
      policy_runtime = "guard-2.x.x"
      policy_text    = <<-POLICY
        # Alert on any change to production-tagged resources
        rule production_change_alert {
          tags.Environment == "production"
        }
      POLICY
    }
  }
}

# SNS notification instead of remediation
resource "aws_sns_topic" "config_alerts" {
  name = "config-compliance-alerts"
}

resource "aws_config_config_rule" "notify_sns" {
  # ... rule config ...
}

# CloudWatch Event to trigger SNS
resource "aws_cloudwatch_event_rule" "config_compliance" {
  name = "config-compliance-change"

  event_pattern = jsonencode({
    source      = ["aws.config"]
    detail-type = ["Config Rules Compliance Change"]
    detail = {
      messageType         = ["ComplianceChangeNotification"]
      newEvaluationResult = {
        complianceType = ["NON_COMPLIANT"]
      }
    }
  })
}

resource "aws_cloudwatch_event_target" "sns" {
  rule      = aws_cloudwatch_event_rule.config_compliance.name
  target_id = "SendToSNS"
  arn       = aws_sns_topic.config_alerts.arn
}

Multi-Account Setup with AWS Organizations

For organisation-wide compliance, use AWS Config Aggregator:

resource "aws_config_configuration_aggregator" "organisation" {
  name = "organisation-aggregator"

  organization_aggregation_source {
    all_regions = true
    role_arn    = aws_iam_role.config_aggregator.arn
  }
}

resource "aws_iam_role" "config_aggregator" {
  name = "config-aggregator-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Principal = {
        Service = "config.amazonaws.com"
      }
    }]
  })
}

resource "aws_iam_role_policy_attachment" "config_aggregator" {
  role       = aws_iam_role.config_aggregator.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSConfigRoleForOrganizations"
}

Deploy rules across all accounts using Organisation Config Rules:

resource "aws_config_organization_managed_rule" "s3_encryption" {
  name            = "org-s3-encryption"
  rule_identifier = "S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED"

  excluded_accounts = [
    "123456789012",  # Security account - managed separately
  ]
}

Troubleshooting

Remediation Not Triggering

Check the remediation execution status:

aws configservice describe-remediation-execution-status \
  --config-rule-name s3-bucket-server-side-encryption-enabled

Common issues:

  • Missing IAM permissions - Remediation role lacks required actions
  • SSM document not found - Check document name and region
  • Rate limiting - Adjust concurrent_execution_rate_percentage

Finding Available SSM Documents

List AWS-provided remediation documents:

aws ssm list-documents \
  --filters "Key=Owner,Values=Amazon" \
  --query "DocumentIdentifiers[?contains(Name, 'AWS-')].Name" \
  --output table

Debugging Custom Documents

Test SSM documents manually before attaching to Config:

aws ssm start-automation-execution \
  --document-name "Custom-EnableS3BucketVersioning" \
  --parameters "BucketName=my-test-bucket,AutomationAssumeRole=arn:aws:iam::123456789012:role/config-remediation-role"

Check execution status:

aws ssm get-automation-execution \
  --automation-execution-id <execution-id>

Best Practices

1. Start with Detection, Then Add Remediation

Don’t enable auto-remediation immediately. First:

  • Deploy rules in detection-only mode
  • Review compliance reports
  • Understand the scope of violations
  • Test remediation manually
  • Then enable automatic remediation

2. Exclude Sensitive Resources

Some resources shouldn’t be auto-remediated:

resource "aws_config_config_rule" "s3_encryption" {
  name = "s3-bucket-encryption"

  source {
    owner             = "AWS"
    source_identifier = "S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED"
  }

  # Exclude specific buckets
  scope {
    compliance_resource_id = "my-special-bucket"  # Exclude this
    tag_key                = "AutoRemediate"
    tag_value              = "false"              # Or exclude by tag
  }
}

3. Rate Limit Remediation

Prevent remediation storms:

execution_controls {
  ssm_controls {
    concurrent_execution_rate_percentage = 10  # Only 10% at a time
    error_percentage                     = 10  # Stop if >10% fail
  }
}

4. Log Everything

Enable CloudTrail logging for Config and SSM:

resource "aws_cloudtrail" "config_audit" {
  name           = "config-audit-trail"
  s3_bucket_name = aws_s3_bucket.audit_logs.id

  event_selector {
    read_write_type           = "All"
    include_management_events = true

    data_resource {
      type   = "AWS::S3::Object"
      values = ["arn:aws:s3:::"]
    }
  }
}

5. Use Conformance Packs

For standards like CIS or PCI-DSS, use conformance packs:

resource "aws_config_conformance_pack" "cis" {
  name = "cis-aws-foundations-benchmark"

  template_body = file("${path.module}/conformance-packs/cis-benchmark.yaml")

  input_parameter {
    parameter_name  = "AccessKeysRotatedParameterMaxAccessKeyAge"
    parameter_value = "90"
  }
}

Cost Considerations

AWS Config pricing:

  • Configuration items recorded: $0.003 per item
  • Config rule evaluations: $0.001 per evaluation
  • Conformance pack evaluations: $0.001 per evaluation per rule

For a medium account (1000 resources, 20 rules):

  • Monthly cost: ~$50-100
  • Cost per remediation: Free (you pay for SSM, which is also free for most use cases)

Conclusion

AWS Config with auto remediation transforms compliance from a manual audit exercise into continuous enforcement. Resources that violate policies get fixed automatically, reducing the window of non-compliance from weeks to minutes.

Start with managed rules for common scenarios, then add custom Guard policies for specific requirements. Always test remediation manually before enabling automatic mode, and be careful with remediation on production resources.

The combination of Config Rules + SSM Automation is powerful - use it wisely.


References

Found this helpful?

Comments