cloudformation について
cloudformationは、ネイティブなCloudFormationテンプレートでAWSインフラをデプロイ、管理するスキルです。環境の再現、変更の安全な適用、ドリフト検出、ネストスタックによる構成を扱います。
主な機能
- 01テンプレートの検証とLint
- 02パラメーター付きスタックのデプロイ
- 03変更セットによる更新内容の確認
- 04構成ドリフトの検出と修正
- 05ネストスタックによるインフラ構成
- 06スタックイベントの確認と障害調査
活用シーン
- 01開発、ステージング、本番環境への同一インフラの展開
- 02スタック更新前の変更内容の確認
- 03構成ドリフトを起こす手動変更の特定
- 04大規模なインフラ定義のネストスタック分割
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ bagelhole/ devops-security-agent-skills --skill cloudformationインストール後、Claude Code で/cloudformationを入力して読み込まれたか確認します。
同梱ファイルreferences/cfn-syntax.md
詳細
CloudFormation
CloudFormation テンプレート、変更セット、ネストされたスタック、ドリフト検出を使用して AWS インフラストラクチャをデプロイします。
このスキルを使用する場面
- AWS ネイティブの Infrastructure as Code を使用して AWS リソースをデプロイする
- 再利用可能でパラメーター化されたインフラストラクチャテンプレートを作成する
- 同じテンプレートで複数の環境(dev、staging、prod)へのデプロイを管理する
- 変更セットとロールバック保護を使用して安全なデプロイを実施する
- 設定ドリフトを検出し、修正する
- 大規模なインフラストラクチャをネストされたスタックに整理する
- スタック間で値をエクスポート/インポートする
前提条件
- AWS CLI v2 がインストールされ、設定済みであること
- IAM 権限:
cloudformation:*と、テンプレート内のすべてのリソースに対する権限 - (任意)テンプレートの検証用に
cfn-lintがインストールされていること(pip install cfn-lint) - 51,200 バイトを超えるテンプレートを保存するための S3 バケット
テンプレートの構造
AWSTemplateFormatVersion: '2010-09-09'
Description: Production web application infrastructure
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label: { default: "Environment" }
Parameters: [Environment, InstanceType]
- Label: { default: "Network" }
Parameters: [VpcId, SubnetIds]
Parameters:
Environment:
Type: String
AllowedValues: [dev, staging, prod]
Default: dev
InstanceType:
Type: String
Default: t3.micro
AllowedValues: [t3.micro, t3.small, t3.medium, t3.large]
VpcId:
Type: AWS::EC2::VPC::Id
Description: VPC to deploy into
SubnetIds:
Type: List<AWS::EC2::Subnet::Id>
Description: Subnets for the application
Conditions:
IsProd: !Equals [!Ref Environment, prod]
CreateReadReplica: !Equals [!Ref Environment, prod]
Mappings:
RegionAMI:
us-east-1:
AL2023: ami-0abcdef1234567890
us-west-2:
AL2023: ami-0fedcba9876543210
Resources:
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: !Sub '${Environment}-web-sg'
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: !Sub '${Environment}-web-sg'
LaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateName: !Sub '${Environment}-web'
LaunchTemplateData:
ImageId: !FindInMap [RegionAMI, !Ref 'AWS::Region', AL2023]
InstanceType: !If [IsProd, t3.large, !Ref InstanceType]
MetadataOptions:
HttpTokens: required
SecurityGroupIds:
- !Ref SecurityGroup
AutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
AutoScalingGroupName: !Sub '${Environment}-web-asg'
LaunchTemplate:
LaunchTemplateId: !Ref LaunchTemplate
Version: !GetAtt LaunchTemplate.LatestVersionNumber
MinSize: !If [IsProd, 2, 1]
MaxSize: !If [IsProd, 10, 3]
DesiredCapacity: !If [IsProd, 4, 1]
VPCZoneIdentifier: !Ref SubnetIds
TargetGroupARNs:
- !Ref TargetGroup
HealthCheckType: ELB
HealthCheckGracePeriod: 300
Tags:
- Key: Name
Value: !Sub '${Environment}-web'
PropagateAtLaunch: true
UpdatePolicy:
AutoScalingRollingUpdate:
MinInstancesInService: !If [IsProd, 2, 0]
MaxBatchSize: 1
PauseTime: PT5M
WaitOnResourceSignals: true
SuspendProcesses:
- HealthCheck
- ReplaceUnhealthy
- AZRebalance
- AlarmNotification
- ScheduledActions
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Sub '${Environment}-web-tg'
Port: 8080
Protocol: HTTP
VpcId: !Ref VpcId
TargetType: instance
HealthCheckPath: /health
HealthCheckIntervalSeconds: 30
HealthyThresholdCount: 2
UnhealthyThresholdCount: 3
Outputs:
SecurityGroupId:
Description: Web security group ID
Value: !Ref SecurityGroup
Export:
Name: !Sub '${Environment}-WebSecurityGroup'
AutoScalingGroupName:
Description: ASG name
Value: !Ref AutoScalingGroup
Export:
Name: !Sub '${Environment}-WebASG'
スタック操作
# Validate a template
aws cloudformation validate-template --template-body file://template.yaml
# Lint with cfn-lint (catches more issues)
cfn-lint template.yaml
# Create a stack
aws cloudformation create-stack \
--stack-name production-web \
--template-body file://template.yaml \
--parameters \
ParameterKey=Environment,ParameterValue=prod \
ParameterKey=VpcId,ParameterValue=vpc-abc123 \
ParameterKey=SubnetIds,ParameterValue="subnet-aaa\\,subnet-bbb" \
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
--tags Key=Environment,Value=production Key=Team,Value=platform \
--enable-termination-protection \
--on-failure ROLLBACK
# Wait for stack creation
aws cloudformation wait stack-create-complete --stack-name production-web
# Describe stack status and outputs
aws cloudformation describe-stacks \
--stack-name production-web \
--query "Stacks[0].{Status:StackStatus,Outputs:Outputs}" \
--output table
# List stack resources
aws cloudformation list-stack-resources --stack-name production-web \
--query "StackResourceSummaries[].{Logical:LogicalResourceId,Physical:PhysicalResourceId,Type:ResourceType,Status:ResourceStatus}" \
--output table
# Delete a stack
aws cloudformation delete-stack --stack-name dev-web
aws cloudformation wait stack-delete-complete --stack-name dev-web
変更セット(安全な更新)
# Create a change set to preview changes before applying
aws cloudformation create-change-set \
--stack-name production-web \
--change-set-name update-instance-type \
--template-body file://template.yaml \
--parameters \
ParameterKey=Environment,ParameterValue=prod \
ParameterKey=InstanceType,ParameterValue=t3.large \
ParameterKey=VpcId,UsePreviousValue=true \
ParameterKey=SubnetIds,UsePreviousValue=true \
--capabilities CAPABILITY_IAM
# Describe the change set to review planned changes
aws cloudformation describe-change-set \
--stack-name production-web \
--change-set-name update-instance-type \
--query "Changes[].{Action:ResourceChange.Action,Resource:ResourceChange.LogicalResourceId,Type:ResourceChange.ResourceType,Replacement:ResourceChange.Replacement}" \
--output table
# Execute the change set (apply changes)
aws cloudformation execute-change-set \
--stack-name production-web \
--change-set-name update-instance-type
# Wait for update
aws cloudformation wait stack-update-complete --stack-name production-web
# Delete a change set without applying
aws cloudformation delete-change-set \
--stack-name production-web \
--change-set-name update-instance-type
ドリフト検出
# Start drift detection
DRIFT_ID=$(aws cloudformation detect-stack-drift \
--stack-name production-web \
--query 'StackDriftDetectionId' --output text)
# Check drift detection status
aws cloudformation describe-stack-drift-detection-status \
--stack-drift-detection-id $DRIFT_ID
# View drifted resources
aws cloudformation describe-stack-resource-drifts \
--stack-name production-web \
--stack-resource-drift-status-filters MODIFIED DELETED \
--query "StackResourceDrifts[].{Resource:LogicalResourceId,Status:StackResourceDriftStatus,Differences:PropertyDifferences}" \
--output table
# Detect drift on a specific resource
aws cloudformation detect-stack-resource-drift \
--stack-name production-web \
--logical-resource-id SecurityGroup
ネストされたスタック
親テンプレート:
AWSTemplateFormatVersion: '2010-09-09'
Description: Parent stack - full application
Parameters:
Environment:
Type: String
AllowedValues: [dev, staging, prod]
Resources:
NetworkStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/my-cfn-templates/network.yaml
Parameters:
Environment: !Ref Environment
VpcCidr: "10.0.0.0/16"
Tags:
- Key: Environment
Value: !Ref Environment
DatabaseStack:
Type: AWS::CloudFormation::Stack
DependsOn: NetworkStack
Properties:
TemplateURL: https://s3.amazonaws.com/my-cfn-templates/database.yaml
Parameters:
Environment: !Ref Environment
VpcId: !GetAtt NetworkStack.Outputs.VpcId
SubnetIds: !GetAtt NetworkStack.Outputs.PrivateSubnetIds
AppStack:
Type: AWS::CloudFormation::Stack
DependsOn: [NetworkStack, DatabaseStack]
Properties:
TemplateURL: https://s3.amazonaws.com/my-cfn-templates/app.yaml
Parameters:
Environment: !Ref Environment
VpcId: !GetAtt NetworkStack.Outputs.VpcId
SubnetIds: !GetAtt NetworkStack.Outputs.PrivateSubnetIds
DbEndpoint: !GetAtt DatabaseStack.Outputs.Endpoint
Outputs:
VpcId:
Value: !GetAtt NetworkStack.Outputs.VpcId
AppUrl:
Value: !GetAtt AppStack.Outputs.LoadBalancerDNS
# Package nested templates (uploads local references to S3)
aws cloudformation package \
--template-file parent.yaml \
--s3-bucket my-cfn-templates \
--output-template-file packaged.yaml
# Deploy the packaged template
aws cloudformation deploy \
--template-file packaged.yaml \
--stack-name production-app \
--parameter-overrides Environment=prod \
--capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND \
--tags Environment=production
組み込み関数のリファレンス
# Ref - reference a parameter or resource
SecurityGroupId: !Ref SecurityGroup
# GetAtt - get an attribute of a resource
SecurityGroupArn: !GetAtt SecurityGroup.GroupId
# Sub - string substitution
BucketName: !Sub '${Environment}-${AWS::AccountId}-data'
# Join - concatenate strings
PolicyArn: !Join ['', ['arn:aws:iam::', !Ref 'AWS::AccountId', ':policy/MyPolicy']]
# Select - pick from a list
FirstSubnet: !Select [0, !Ref SubnetIds]
# Split - split a string
FirstPart: !Select [0, !Split ['-', !Ref 'AWS::StackName']]
# If - conditional value
InstanceSize: !If [IsProd, t3.large, t3.micro]
# Equals - condition definition
Conditions:
IsProd: !Equals [!Ref Environment, prod]
# ImportValue - cross-stack reference
VpcId: !ImportValue production-VpcId
# Cidr - generate CIDR blocks
Subnets: !Cidr [!GetAtt VPC.CidrBlock, 6, 8]
# GetAZs - list availability zones
AZ: !Select [0, !GetAZs '']
スタックポリシー(意図しない置換の防止)
# Apply a stack policy that prevents replacement of the database
aws cloudformation set-stack-policy \
--stack-name production-web \
--stack-policy-body '{
"Statement": [
{
"Effect": "Allow",
"Action": "Update:*",
"Principal": "*",
"Resource": "*"
},
{
"Effect": "Deny",
"Action": "Update:Replace",
"Principal": "*",
"Resource": "LogicalResourceId/Database"
},
{
"Effect": "Deny",
"Action": "Update:Delete",
"Principal": "*",
"Resource": "LogicalResourceId/Database"
}
]
}'
スタックイベントとデバッグ
# View stack events (most recent first)
aws cloudformation describe-stack-events \
--stack-name production-web \
--query "StackEvents[?ResourceStatus=='CREATE_FAILED' || ResourceStatus=='UPDATE_FAILED'].{Time:Timestamp,Resource:LogicalResourceId,Status:ResourceStatus,Reason:ResourceStatusReason}" \
--output table
# Continue a rollback that is stuck
aws cloudformation continue-update-rollback \
--stack-name production-web \
--resources-to-skip SecurityGroup
# Cancel an in-progress update
aws cloudformation cancel-update-stack --stack-name production-web
# Get template from an existing stack
aws cloudformation get-template \
--stack-name production-web \
--template-stage Processed \
--query TemplateBody \
--output text > current-template.yaml
トラブルシューティング
| 問題 | 原因 | 解決方法 |
|---|---|---|
| IAM リソースで CREATE_FAILED が発生する | CAPABILITY_IAM が指定されていない | --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM を追加します |
| スタックが UPDATE_ROLLBACK_FAILED の状態から進まない | リソースをロールバックできない | --resources-to-skip を指定して continue-update-rollback を使用します |
| ネストされたスタックが失敗する | テンプレート URL が誤っている、または S3 へのアクセスが拒否されている | aws cloudformation package を使用してアップロードし、バケットポリシーを確認します |
| 循環依存エラーが発生する | 2 つのリソースが相互に参照している | 3 つ目のリソースで循環を解消するか、DependsOn を使用します |
| ドリフトが検出される | CloudFormation の外部で手動変更が行われた | テンプレートを再適用するか、現在の状態に合わせてテンプレートを更新します |
| 変更セットに変更が表示されない | テンプレートとパラメーターが同一である | 差分を確認し、パラメーターのみの変更かどうかを確認します |
| テンプレートの検証エラーが発生する | YAML の構文エラー、または無効なリソースプロパティ | cfn-lint を実行し、ドキュメントでプロパティ名を確認します |
| エクスポート名がすでに存在する | 別のスタックが同じエクスポート名を使用している | !Sub '${AWS::StackName}-Name' を使って一意のエクスポート名を指定します |
| 削除に失敗する(リソースが使用中) | スタック外に依存するリソースがある | 先に依存関係を削除し、SG の参照を確認します |
関連スキル
- terraform-aws - Terraform を使用する別の IaC
- aws-iam - テンプレート内の IAM リソース
- aws-vpc - ネットワークインフラストラクチャのテンプレート
- aws-ec2 - テンプレート内のコンピューティングリソース
- aws-s3 - テンプレート内のストレージリソース
コメント
Markdown 対応