Implementing Secure CloudFormation Patterns
Implementing Secure CloudFormation Patterns
Parameterization and dynamic references eliminate hardcoded values while maintaining template flexibility. CloudFormation parameters allow users to provide values at stack creation time, while dynamic references retrieve values from AWS Systems Manager Parameter Store or AWS Secrets Manager. This approach keeps sensitive data out of templates while enabling secure, automated deployments.
# Secure CloudFormation template with proper patterns
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Secure template demonstrating best practices'
Parameters:
Environment:
Type: String
AllowedValues: [development, staging, production]
Description: Deployment environment
VPCId:
Type: AWS::EC2::VPC::Id
Description: VPC for resource deployment
SubnetIds:
Type: List<AWS::EC2::Subnet::Id>
Description: Private subnets for resources
Resources:
# SECURE: S3 bucket with encryption and private access
SecureBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub '${AWS::StackName}-secure-data-${AWS::AccountId}'
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: Enabled
LifecycleConfiguration:
Rules:
- Id: DeleteOldVersions
NoncurrentVersionExpirationInDays: 30
Status: Enabled
# SECURE: RDS instance with proper security controls
DatabaseInstance:
Type: AWS::RDS::DBInstance
Properties:
DBInstanceIdentifier: !Sub '${AWS::StackName}-db'
Engine: mysql
MasterUsername: !Sub '{{resolve:secretsmanager:${DatabaseSecret}:SecretString:username}}'
MasterUserPassword: !Sub '{{resolve:secretsmanager:${DatabaseSecret}:SecretString:password}}'
DBInstanceClass: db.t3.micro
AllocatedStorage: 20
StorageEncrypted: true
KmsKeyId: !Ref DatabaseKMSKey
PubliclyAccessible: false
VPCSecurityGroups:
- !Ref DatabaseSecurityGroup
DBSubnetGroupName: !Ref DatabaseSubnetGroup
BackupRetentionPeriod: 7
PreferredBackupWindow: "03:00-04:00"
PreferredMaintenanceWindow: "sun:04:00-sun:05:00"
# SECURE: Properly configured security group
DatabaseSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for RDS instance
VpcId: !Ref VPCId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
SourceSecurityGroupId: !Ref ApplicationSecurityGroup
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-db-sg'
# Secret storage for database credentials
DatabaseSecret:
Type: AWS::SecretsManager::Secret
Properties:
Description: RDS master credentials
GenerateSecretString:
SecretStringTemplate: '{"username": "admin"}'
GenerateStringKey: password
PasswordLength: 32
RequireEachIncludedType: true
# KMS key for encryption
DatabaseKMSKey:
Type: AWS::KMS::Key
Properties:
Description: Encryption key for RDS
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 RDS to use the key
Effect: Allow
Principal:
Service: rds.amazonaws.com
Action:
- 'kms:Decrypt'
- 'kms:GenerateDataKey'
Resource: '*'
Conditional resource creation based on environment parameters prevents accidental production changes and enforces environment-specific security controls. Development environments might allow more permissive configurations for testing, while production environments enforce strict security requirements. CloudFormation conditions enable single templates to support multiple environments safely.