Patterns you’ll see on SAP-C02 topics. Treat these as templates: swap in your ARNs, account IDs, VPC IDs, and CIDRs. Confirm syntax and behavior in current AWS docs and against your org’s security rules.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListBucketScope",
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": "arn:aws:s3:::my-app-data-prod",
"Condition": {
"StringLike": {
"s3:prefix": ["tenant-a/*"]
}
}
},
{
"Sid": "ObjectReadScope",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:GetObjectVersion"],
"Resource": "arn:aws:s3:::my-app-data-prod/tenant-a/*"
}
]
}Prefer IAM roles for workloads; narrow prefixes and KMS grants when buckets use SSE-KMS.
Key policies are resource-based; the CMK must allow both administrators and data-plane users.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow application role to use the key",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/AppDataProcessorRole"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*"
}
]
}{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyInsecureTransport",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::my-app-data-prod",
"arn:aws:s3:::my-app-data-prod/*"
],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
},
{
"Sid": "DenyUnencryptedObjectUploads",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::my-app-data-prod/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "aws:kms"
}
}
}
]
}Match the encryption condition to SSE-S3 vs SSE-KMS requirements.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::222222222222:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "unique-per-integration-id"
}
}
}
]
}Use ExternalId for third-party or cross-org access to reduce confused deputy risk.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyAllOutsideEU",
"Effect": "Deny",
"NotAction": [
"iam:*",
"organizations:*",
"support:*",
"account:*",
"billing:*",
"payments:*",
"tax:*",
"invoice:*",
"ce:*",
"cur:*",
"pricing:*",
"route53:*",
"cloudfront:*",
"globalaccelerator:*",
"wafv2:*",
"shield:*"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": ["eu-west-1", "eu-central-1"]
}
}
}
]
}SCP Deny lists need exceptions for global services and break-glass paths. Test in a sandbox OU first.
Single-AZ excerpt for study; production stacks use multiple AZs and NAT per AZ as needed.
AWSTemplateFormatVersion: '2010-09-09'
Description: Minimal VPC pattern for study (add Multi-AZ for production)
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: true
EnableDnsSupport: true
InternetGateway:
Type: AWS::EC2::InternetGateway
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.1.0/24
MapPublicIpOnLaunch: true
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.2.0/24
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
DefaultPublicRoute:
Type: AWS::EC2::Route
DependsOn: VPCGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTable
Outputs:
VpcId:
Value: !Ref VPC
PublicSubnetId:
Value: !Ref PublicSubnet
PrivateSubnetId:
Value: !Ref PrivateSubnetAdd NAT in public subnets, private route tables, and VPC endpoints for S3/DynamoDB if you want to trim NAT traffic.
# Illustrative only; real templates add IAM roles, artifacts, and source config
pipeline:
name: app-release
stages:
- name: Source
actions:
- provider: CodeCommit | GitHub | CodeStarConnections
- name: Build
actions:
- provider: CodeBuild
- name: DeployToStaging
actions:
- provider: CodeDeploy
deployment: BlueGreen
- name: Approval
actions:
- provider: Manual
- name: DeployToProduction
actions:
- provider: CodeDeployPair CodeDeploy with ALB target groups for blue/green on EC2 or ECS.
# Temporary credentials for a role in another account
aws sts assume-role \
--role-arn arn:aws:iam::111111111111:role/CrossAccountReadOnly \
--role-session-name local-audit \
--external-id unique-per-integration-id
# Export AccessKeyId, SecretAccessKey, SessionToken (syntax depends on shell)
# Then:
aws s3 ls s3://my-app-data-prod/ --region eu-west-1On Windows PowerShell, set $env:AWS_ACCESS_KEY_ID, $env:AWS_SECRET_ACCESS_KEY, $env:AWS_SESSION_TOKEN from the assume-role output.
{
"source": ["aws.s3"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventSource": ["s3.amazonaws.com"],
"eventName": ["DeleteBucket", "DeleteBucketPolicy"]
}
}Route to SNS, Lambda, or SSM Automation; put human approval on destructive automation.
DatabasePasswordParameter:
Type: AWS::SSM::Parameter
Properties:
Name: /app/prod/db/password
Type: String
Value: 'REPLACE_VIA_PIPELINE_OR_MANUAL'For production DB secrets, Secrets Manager with rotation and dynamic references usually fits better.
- Small lab: VPC, private EC2, SSM Session Manager (no SSH), S3 VPC endpoint, organization CloudTrail.
- Config rule plus remediation for an unencrypted EBS volume in a test account.
- Map each example back to a domain and a pillar in Domain 1.