mcpField
C
cloudformation

cloudformation

github.com/bagelholeクラウド・DevOps1.2k311 回インストール23 週間前に更新
リポジトリ

cloudformation について

cloudformationは、ネイティブなCloudFormationテンプレートでAWSインフラをデプロイ、管理するスキルです。環境の再現、変更の安全な適用、ドリフト検出、ネストスタックによる構成を扱います。

主な機能

  1. 01テンプレートの検証とLint
  2. 02パラメーター付きスタックのデプロイ
  3. 03変更セットによる更新内容の確認
  4. 04構成ドリフトの検出と修正
  5. 05ネストスタックによるインフラ構成
  6. 06スタックイベントの確認と障害調査

活用シーン

  1. 01開発、ステージング、本番環境への同一インフラの展開
  2. 02スタック更新前の変更内容の確認
  3. 03構成ドリフトを起こす手動変更の特定
  4. 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 - テンプレート内のストレージリソース

コメント