CloudWatch Log Group KMS Encryption
Overview
This check verifies that your CloudWatch Log Groups are encrypted using a customer-managed AWS KMS key. By default, CloudWatch Logs encrypts data at rest using AWS-managed keys. However, using your own KMS key gives you more control over who can access your log data and how long the encryption keys remain valid.
Risk
When CloudWatch Log Groups rely on service-managed encryption instead of customer-managed KMS keys:
- Reduced access control: You cannot define custom key policies to restrict who can decrypt your log data
- Limited key management: Service-managed keys do not support custom rotation schedules or rapid revocation
- Compliance gaps: Many regulatory frameworks (HIPAA, PCI-DSS, SOC 2) require customer-managed encryption keys for audit trails and sensitive data
- Incident response challenges: Without control over the encryption key, you cannot quickly revoke access to log data during a security incident
Remediation Steps
Prerequisites
- AWS account access with permissions to modify CloudWatch Log Groups and KMS keys
- An existing KMS key, or permission to create one
Required IAM permissions
To associate a KMS key with a log group, you need:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:AssociateKmsKey",
"logs:DescribeLogGroups"
],
"Resource": "arn:aws:logs:us-east-1:*:log-group:*"
},
{
"Effect": "Allow",
"Action": [
"kms:DescribeKey",
"kms:CreateGrant"
],
"Resource": "arn:aws:kms:us-east-1:*:key/*"
}
]
}
AWS Console Method
Step 1: Create or identify a KMS key
- Open the AWS KMS Console at https://console.aws.amazon.com/kms
- Ensure you are in the us-east-1 region (top-right corner)
- Click Customer managed keys in the left sidebar
- Either select an existing symmetric key, or click Create key:
- Key type: Symmetric
- Key usage: Encrypt and decrypt
- Give it a name like
cloudwatch-logs-key - Add the CloudWatch Logs service to the key policy (see below)
- Copy the Key ARN for later use
KMS key policy for CloudWatch Logs
Your KMS key policy must allow CloudWatch Logs to use the key. Add this statement to your key policy:
{
"Sid": "Allow CloudWatch Logs",
"Effect": "Allow",
"Principal": {
"Service": "logs.us-east-1.amazonaws.com"
},
"Action": [
"kms:Encrypt*",
"kms:Decrypt*",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:Describe*"
],
"Resource": "*",
"Condition": {
"ArnLike": {
"kms:EncryptionContext:aws:logs:arn": "arn:aws:logs:us-east-1:<YOUR_ACCOUNT_ID>:log-group:*"
}
}
}
Replace <YOUR_ACCOUNT_ID> with your 12-digit AWS account ID.
Step 2: Associate the KMS key with your Log Group
- Open the CloudWatch Console at https://console.aws.amazon.com/cloudwatch
- In the left sidebar, click Logs then Log groups
- Select the log group you want to encrypt
- Click Actions then Edit
- Scroll to the Encryption section
- Select Customer managed key
- Choose your KMS key from the dropdown or paste the Key ARN
- Click Save changes
Changes may take up to 5 minutes to take effect. New log data will be encrypted with your KMS key.
AWS CLI (optional)
Associate KMS key with an existing log group
aws logs associate-kms-key \
--log-group-name <YOUR_LOG_GROUP_NAME> \
--kms-key-id arn:aws:kms:us-east-1:<YOUR_ACCOUNT_ID>:key/<YOUR_KEY_ID> \
--region us-east-1
Replace:
<YOUR_LOG_GROUP_NAME>with your log group name (e.g.,/aws/lambda/my-function)<YOUR_ACCOUNT_ID>with your 12-digit AWS account ID<YOUR_KEY_ID>with your KMS key ID
Create a new encrypted log group
aws logs create-log-group \
--log-group-name <YOUR_LOG_GROUP_NAME> \
--kms-key-id arn:aws:kms:us-east-1:<YOUR_ACCOUNT_ID>:key/<YOUR_KEY_ID> \
--region us-east-1
Remove KMS encryption (if needed)
aws logs disassociate-kms-key \
--log-group-name <YOUR_LOG_GROUP_NAME> \
--region us-east-1
CloudFormation (optional)
This template creates a KMS key with the proper permissions and an encrypted CloudWatch Log Group.
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudWatch Log Group with KMS encryption
Parameters:
LogGroupName:
Type: String
Description: Name of the CloudWatch Log Group
Default: my-encrypted-log-group
RetentionInDays:
Type: Number
Description: Number of days to retain log events
Default: 30
AllowedValues: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1096, 1827, 2192, 2557, 2922, 3288, 3653]
Resources:
LogGroupKMSKey:
Type: AWS::KMS::Key
Properties:
Description: KMS key for CloudWatch Log Group encryption
EnableKeyRotation: true
KeyPolicy:
Version: '2012-10-17'
Statement:
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
Action: 'kms:*'
Resource: '*'
- Sid: Allow CloudWatch Logs
Effect: Allow
Principal:
Service: !Sub 'logs.${AWS::Region}.amazonaws.com'
Action:
- kms:Encrypt*
- kms:Decrypt*
- kms:ReEncrypt*
- kms:GenerateDataKey*
- kms:Describe*
Resource: '*'
Condition:
ArnLike:
'kms:EncryptionContext:aws:logs:arn': !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:*'
LogGroupKMSKeyAlias:
Type: AWS::KMS::Alias
Properties:
AliasName: alias/cloudwatch-logs-key
TargetKeyId: !Ref LogGroupKMSKey
EncryptedLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Ref LogGroupName
KmsKeyId: !GetAtt LogGroupKMSKey.Arn
RetentionInDays: !Ref RetentionInDays
Outputs:
LogGroupArn:
Description: ARN of the encrypted log group
Value: !GetAtt EncryptedLogGroup.Arn
KMSKeyArn:
Description: ARN of the KMS key
Value: !GetAtt LogGroupKMSKey.Arn
Deploy with:
aws cloudformation deploy \
--template-file template.yaml \
--stack-name encrypted-log-group \
--parameter-overrides LogGroupName=my-app-logs \
--region us-east-1
Terraform (optional)
variable "log_group_name" {
description = "Name of the CloudWatch Log Group"
type = string
default = "my-encrypted-log-group"
}
variable "retention_in_days" {
description = "Number of days to retain log events"
type = number
default = 30
}
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
resource "aws_kms_key" "cloudwatch_logs" {
description = "KMS key for CloudWatch Log Group encryption"
deletion_window_in_days = 30
enable_key_rotation = true
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "Enable IAM User Permissions"
Effect = "Allow"
Principal = { AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" }
Action = "kms:*"
Resource = "*"
},
{
Sid = "Allow CloudWatch Logs"
Effect = "Allow"
Principal = { Service = "logs.${data.aws_region.current.name}.amazonaws.com" }
Action = [
"kms:Encrypt*",
"kms:Decrypt*",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:Describe*"
]
Resource = "*"
Condition = {
ArnLike = {
"kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:*"
}
}
}
]
})
}
resource "aws_kms_alias" "cloudwatch_logs" {
name = "alias/cloudwatch-logs-key"
target_key_id = aws_kms_key.cloudwatch_logs.key_id
}
resource "aws_cloudwatch_log_group" "encrypted" {
name = var.log_group_name
kms_key_id = aws_kms_key.cloudwatch_logs.arn
retention_in_days = var.retention_in_days
}
output "log_group_arn" {
description = "ARN of the encrypted log group"
value = aws_cloudwatch_log_group.encrypted.arn
}
output "kms_key_arn" {
description = "ARN of the KMS key"
value = aws_kms_key.cloudwatch_logs.arn
}
Deploy with:
terraform init
terraform plan
terraform apply
Verification
After making changes, verify encryption is enabled:
- Open the CloudWatch Console at https://console.aws.amazon.com/cloudwatch
- Navigate to Logs > Log groups
- Click on your log group name
- In the Details section, confirm KMS key ARN shows your customer-managed key
Verify with AWS CLI
aws logs describe-log-groups \
--log-group-name-prefix <YOUR_LOG_GROUP_NAME> \
--region us-east-1 \
--query 'logGroups[*].{Name:logGroupName,KmsKeyId:kmsKeyId}'
A properly encrypted log group will show the KMS key ARN in the output:
[
{
"Name": "my-encrypted-log-group",
"KmsKeyId": "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}
]
If KmsKeyId is null or missing, the log group is not using customer-managed encryption.
Additional Resources
- AWS Documentation: Encrypt log data in CloudWatch Logs
- AWS CLI Reference: associate-kms-key
- Terraform: aws_cloudwatch_log_group
- AWS KMS Developer Guide
Notes
- Encryption applies to new data only: After associating a KMS key, only new log events are encrypted with it. Existing log data remains encrypted with the previous key (or service-managed key).
- Propagation delay: Changes may take up to 5 minutes to take effect.
- Key availability: If your KMS key is deleted or disabled, CloudWatch Logs cannot decrypt your log data. Ensure key policies allow necessary access.
- Cross-account keys: If using a KMS key from another account, additional key policy configuration is required.
- Symmetric keys only: CloudWatch Logs only supports symmetric KMS keys, not asymmetric keys.
- Cost considerations: Using customer-managed KMS keys incurs additional charges for key storage and API calls.