AWS CloudFormation

DevOpsInfrastructure as CodeIaCAWSJSONYAMLテンプレートクラウドネイティブ

DevOpsツール

AWS CloudFormation

概要

AWS CloudFormationは、AWSが提供するネイティブのInfrastructure as Code(IaC)サービスです。JSON/YAMLテンプレートを使用してAWSリソースを宣言的に定義・管理し、AWSサービスとの密接な統合によりスタック単位での組織的なリソース管理を実現します。

詳細

AWS CloudFormationは2011年にAWSによって開始された最も歴史のあるIaCサービスの一つです。AWSネイティブサービスとして、全てのAWSリソースを網羅的にサポートし、新しいAWSサービスがリリースされると比較的早期にCloudFormationでも利用可能になります。

CloudFormationの主要な特徴は「スタック」という概念によるリソース管理です。関連するリソースをスタック単位でグループ化し、作成・更新・削除を一括して行えます。また、リソース間の依存関係を自動的に解決し、適切な順序で作成・削除を実行します。

変更セット機能により、実際に変更を適用する前に「何が変更されるか」をプレビューでき、安全なインフラ運用が可能です。また、Drift Detection機能により、テンプレートと実際のリソース状態の差分を検出できます。

最近では、CDK(Cloud Development Kit)により、プログラミング言語(TypeScript、Python、Java、C#、Go)でCloudFormationテンプレートを生成することも可能になっています。

メリット・デメリット

メリット

  • AWSネイティブ: AWSサービスとの完全統合、追加コスト不要
  • 包括的サポート: 全AWSリソースを網羅的にサポート
  • スタック管理: 関連リソースを組織的に管理
  • 変更セット: 変更内容の事前プレビューで安全なデプロイ
  • ロールバック機能: 失敗時の自動ロールバック

デメリット

  • AWS限定: AWS以外のクラウドプロバイダーは非対応
  • JSON/YAML複雑性: 大規模テンプレートが複雑になりがち
  • 学習コスト: AWS固有の記法とパラメータの習得が必要
  • 実行速度: 大規模スタックでは作成・更新に時間がかかる
  • 状態管理: スタック外で作成されたリソースとの連携が困難

参考ページ

公式リソース

学習リソース

書き方の例

基本的なテンプレート構造(YAML)

# basic-template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Basic CloudFormation template example'

# パラメータ定義
Parameters:
  Environment:
    Type: String
    Default: development
    AllowedValues:
      - development
      - staging
      - production
    Description: Environment name
  
  InstanceType:
    Type: String
    Default: t3.micro
    AllowedValues:
      - t3.micro
      - t3.small
      - t3.medium
    Description: EC2 instance type

# マッピング(環境別設定)
Mappings:
  EnvironmentConfig:
    development:
      InstanceType: t3.micro
      MinSize: 1
      MaxSize: 2
    staging:
      InstanceType: t3.small
      MinSize: 2
      MaxSize: 4
    production:
      InstanceType: t3.medium
      MinSize: 3
      MaxSize: 10

# 条件分岐
Conditions:
  IsProduction: !Equals [!Ref Environment, production]
  CreateMultipleInstances: !Not [!Equals [!Ref Environment, development]]

# リソース定義
Resources:
  # 後述のサンプルを参照

# 出力
Outputs:
  EnvironmentName:
    Description: Environment name
    Value: !Ref Environment
    Export:
      Name: !Sub '${AWS::StackName}-Environment'

VPCとサブネット作成

# vpc-infrastructure.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'VPC and networking infrastructure'

Parameters:
  VpcCidr:
    Type: String
    Default: '10.0.0.0/16'
    Description: CIDR block for VPC

Resources:
  # VPC
  MainVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCidr
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-vpc'

  # インターネットゲートウェイ
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-igw'

  # IGWをVPCにアタッチ
  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref MainVPC

  # パブリックサブネット(AZ-a)
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MainVPC
      AvailabilityZone: !Select [0, !GetAZs '']
      CidrBlock: !Select [0, !Cidr [!Ref VpcCidr, 6, 8]]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-public-subnet-1'

  # パブリックサブネット(AZ-c)
  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MainVPC
      AvailabilityZone: !Select [1, !GetAZs '']
      CidrBlock: !Select [1, !Cidr [!Ref VpcCidr, 6, 8]]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-public-subnet-2'

  # プライベートサブネット(AZ-a)
  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MainVPC
      AvailabilityZone: !Select [0, !GetAZs '']
      CidrBlock: !Select [2, !Cidr [!Ref VpcCidr, 6, 8]]
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-private-subnet-1'

  # プライベートサブネット(AZ-c)
  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MainVPC
      AvailabilityZone: !Select [1, !GetAZs '']
      CidrBlock: !Select [3, !Cidr [!Ref VpcCidr, 6, 8]]
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-private-subnet-2'

  # パブリックルートテーブル
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MainVPC
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-public-rt'

  # パブリックルート(IGW向け)
  DefaultPublicRoute:
    Type: AWS::EC2::Route
    DependsOn: InternetGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  # パブリックサブネットとルートテーブル関連付け
  PublicSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet1

  PublicSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet2

Outputs:
  VPC:
    Description: VPC ID
    Value: !Ref MainVPC
    Export:
      Name: !Sub '${AWS::StackName}-VPCID'

  PublicSubnets:
    Description: Public subnets
    Value: !Join [',', [!Ref PublicSubnet1, !Ref PublicSubnet2]]
    Export:
      Name: !Sub '${AWS::StackName}-PublicSubnets'

  PrivateSubnets:
    Description: Private subnets
    Value: !Join [',', [!Ref PrivateSubnet1, !Ref PrivateSubnet2]]
    Export:
      Name: !Sub '${AWS::StackName}-PrivateSubnets'

EC2インスタンスとセキュリティグループ

# ec2-infrastructure.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'EC2 instances and security groups'

Parameters:
  NetworkStackName:
    Type: String
    Description: Name of the network stack to import values from
  
  InstanceType:
    Type: String
    Default: t3.micro
    Description: EC2 instance type
  
  KeyName:
    Type: AWS::EC2::KeyPair::KeyName
    Description: Name of an existing EC2 KeyPair

Resources:
  # ウェブサーバー用セキュリティグループ
  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for web servers
      VpcId:
        Fn::ImportValue: !Sub '${NetworkStackName}-VPCID'
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-web-sg'

  # EC2インスタンス用IAMロール
  EC2Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-ec2-role'

  # インスタンスプロファイル
  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref EC2Role

  # ユーザーデータスクリプト
  UserDataScript:
    Type: AWS::CloudFormation::WaitConditionHandle

  # EC2インスタンス
  WebServer1:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Sub '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2}}'
      InstanceType: !Ref InstanceType
      KeyName: !Ref KeyName
      SecurityGroupIds:
        - !Ref WebServerSecurityGroup
      SubnetId: !Select
        - 0
        - !Split
          - ','
          - Fn::ImportValue: !Sub '${NetworkStackName}-PublicSubnets'
      IamInstanceProfile: !Ref EC2InstanceProfile
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          yum update -y
          yum install -y httpd
          systemctl start httpd
          systemctl enable httpd
          
          # Simple web page
          cat > /var/www/html/index.html << EOF
          <!DOCTYPE html>
          <html>
          <head>
              <title>CloudFormation Demo</title>
          </head>
          <body>
              <h1>Hello from CloudFormation!</h1>
              <p>Instance ID: $(curl -s http://169.254.169.254/latest/meta-data/instance-id)</p>
              <p>Region: ${AWS::Region}</p>
              <p>Stack: ${AWS::StackName}</p>
          </body>
          </html>
          EOF
          
          # Install CloudWatch agent
          yum install -y amazon-cloudwatch-agent
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-web-server-1'

  WebServer2:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Sub '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2}}'
      InstanceType: !Ref InstanceType
      KeyName: !Ref KeyName
      SecurityGroupIds:
        - !Ref WebServerSecurityGroup
      SubnetId: !Select
        - 1
        - !Split
          - ','
          - Fn::ImportValue: !Sub '${NetworkStackName}-PublicSubnets'
      IamInstanceProfile: !Ref EC2InstanceProfile
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          yum update -y
          yum install -y httpd
          systemctl start httpd
          systemctl enable httpd
          
          cat > /var/www/html/index.html << EOF
          <!DOCTYPE html>
          <html>
          <head>
              <title>CloudFormation Demo</title>
          </head>
          <body>
              <h1>Hello from CloudFormation! (Server 2)</h1>
              <p>Instance ID: $(curl -s http://169.254.169.254/latest/meta-data/instance-id)</p>
              <p>Region: ${AWS::Region}</p>
              <p>Stack: ${AWS::StackName}</p>
          </body>
          </html>
          EOF
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-web-server-2'

Outputs:
  WebServer1PublicIp:
    Description: Web Server 1 Public IP
    Value: !GetAtt WebServer1.PublicIp

  WebServer2PublicIp:
    Description: Web Server 2 Public IP
    Value: !GetAtt WebServer2.PublicIp

  WebServer1URL:
    Description: Web Server 1 URL
    Value: !Sub 'http://${WebServer1.PublicIp}'

  WebServer2URL:
    Description: Web Server 2 URL
    Value: !Sub 'http://${WebServer2.PublicIp}'

ALB(Application Load Balancer)構成

# alb-infrastructure.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Application Load Balancer infrastructure'

Parameters:
  NetworkStackName:
    Type: String
    Description: Name of the network stack to import values from

  ComputeStackName:
    Type: String
    Description: Name of the compute stack to import values from

Resources:
  # ALB用セキュリティグループ
  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for Application Load Balancer
      VpcId:
        Fn::ImportValue: !Sub '${NetworkStackName}-VPCID'
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-alb-sg'

  # Application Load Balancer
  ApplicationLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Sub '${AWS::StackName}-alb'
      Scheme: internet-facing
      Type: application
      SecurityGroups:
        - !Ref ALBSecurityGroup
      Subnets: !Split
        - ','
        - Fn::ImportValue: !Sub '${NetworkStackName}-PublicSubnets'
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-alb'

  # ターゲットグループ
  WebServerTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub '${AWS::StackName}-web-tg'
      Port: 80
      Protocol: HTTP
      VpcId:
        Fn::ImportValue: !Sub '${NetworkStackName}-VPCID'
      HealthCheckIntervalSeconds: 30
      HealthCheckPath: /
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 3
      TargetType: instance
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-web-tg'

  # ALBリスナー
  ALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref WebServerTargetGroup
      LoadBalancerArn: !Ref ApplicationLoadBalancer
      Port: 80
      Protocol: HTTP

Outputs:
  LoadBalancerURL:
    Description: Application Load Balancer URL
    Value: !Sub 'http://${ApplicationLoadBalancer.DNSName}'

  LoadBalancerDNSName:
    Description: Application Load Balancer DNS Name
    Value: !GetAtt ApplicationLoadBalancer.DNSName

  TargetGroup:
    Description: Target Group ARN
    Value: !Ref WebServerTargetGroup
    Export:
      Name: !Sub '${AWS::StackName}-TargetGroup'

S3バケットとCloudFront配信

# s3-cloudfront.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'S3 bucket with CloudFront distribution for static website'

Parameters:
  DomainName:
    Type: String
    Description: Domain name for the website (optional)
    Default: ''

Conditions:
  HasDomainName: !Not [!Equals [!Ref DomainName, '']]

Resources:
  # S3バケット(ウェブサイトホスティング用)
  WebsiteBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${AWS::StackName}-website-${AWS::AccountId}'
      WebsiteConfiguration:
        IndexDocument: index.html
        ErrorDocument: error.html
      PublicAccessBlockConfiguration:
        BlockPublicAcls: false
        BlockPublicPolicy: false
        IgnorePublicAcls: false
        RestrictPublicBuckets: false
      VersioningConfiguration:
        Status: Enabled
      LifecycleConfiguration:
        Rules:
          - Id: DeleteOldVersions
            Status: Enabled
            NoncurrentVersionExpirationInDays: 30

  # S3バケットポリシー(CloudFrontからのアクセス許可)
  WebsiteBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref WebsiteBucket
      PolicyDocument:
        Statement:
          - Sid: PublicReadGetObject
            Effect: Allow
            Principal:
              Service: cloudfront.amazonaws.com
            Action: s3:GetObject
            Resource: !Sub '${WebsiteBucket}/*'
            Condition:
              StringEquals:
                'AWS:SourceArn': !Sub 'arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}'

  # CloudFront Origin Access Control
  OriginAccessControl:
    Type: AWS::CloudFront::OriginAccessControl
    Properties:
      OriginAccessControlConfig:
        Name: !Sub '${AWS::StackName}-oac'
        OriginAccessControlOriginType: s3
        SigningBehavior: always
        SigningProtocol: sigv4

  # CloudFront Distribution
  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Origins:
          - DomainName: !GetAtt WebsiteBucket.RegionalDomainName
            Id: S3Origin
            S3OriginConfig:
              OriginAccessIdentity: ''
            OriginAccessControlId: !Ref OriginAccessControl
        Enabled: true
        DefaultRootObject: index.html
        DefaultCacheBehavior:
          TargetOriginId: S3Origin
          ViewerProtocolPolicy: redirect-to-https
          CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # CachingOptimized
          OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf # CORS-S3Origin
        PriceClass: PriceClass_100
        CustomErrorResponses:
          - ErrorCode: 404
            ResponseCode: 200
            ResponsePagePath: /index.html
          - ErrorCode: 403
            ResponseCode: 200
            ResponsePagePath: /index.html
        Aliases: !If
          - HasDomainName
          - [!Ref DomainName]
          - []
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-cloudfront'

Outputs:
  BucketName:
    Description: S3 Bucket Name
    Value: !Ref WebsiteBucket

  BucketWebsiteURL:
    Description: S3 Website URL
    Value: !GetAtt WebsiteBucket.WebsiteURL

  CloudFrontDistributionId:
    Description: CloudFront Distribution ID
    Value: !Ref CloudFrontDistribution

  CloudFrontDomainName:
    Description: CloudFront Domain Name
    Value: !GetAtt CloudFrontDistribution.DomainName

  WebsiteURL:
    Description: Website URL
    Value: !Sub 'https://${CloudFrontDistribution.DomainName}'

CloudFormation基本コマンド

# スタック作成
aws cloudformation create-stack \
  --stack-name my-infrastructure \
  --template-body file://template.yaml \
  --parameters ParameterKey=Environment,ParameterValue=development \
  --capabilities CAPABILITY_IAM

# スタック更新
aws cloudformation update-stack \
  --stack-name my-infrastructure \
  --template-body file://template.yaml \
  --parameters ParameterKey=Environment,ParameterValue=staging

# 変更セット作成・確認
aws cloudformation create-change-set \
  --stack-name my-infrastructure \
  --template-body file://template.yaml \
  --change-set-name my-changes \
  --parameters ParameterKey=Environment,ParameterValue=production

aws cloudformation describe-change-set \
  --stack-name my-infrastructure \
  --change-set-name my-changes

# 変更セット実行
aws cloudformation execute-change-set \
  --stack-name my-infrastructure \
  --change-set-name my-changes

# スタック削除
aws cloudformation delete-stack \
  --stack-name my-infrastructure

# スタック一覧
aws cloudformation list-stacks \
  --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE

# スタック詳細表示
aws cloudformation describe-stacks \
  --stack-name my-infrastructure

# スタックリソース一覧
aws cloudformation list-stack-resources \
  --stack-name my-infrastructure

# スタックイベント確認
aws cloudformation describe-stack-events \
  --stack-name my-infrastructure

# テンプレート検証
aws cloudformation validate-template \
  --template-body file://template.yaml

# ドリフト検出
aws cloudformation detect-stack-drift \
  --stack-name my-infrastructure

aws cloudformation describe-stack-drift-detection-status \
  --stack-drift-detection-id <detection-id>

ネストされたスタック

# master-stack.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Master stack orchestrating nested stacks'

Parameters:
  Environment:
    Type: String
    Default: development

Resources:
  # ネットワークスタック
  NetworkStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: https://my-templates-bucket.s3.amazonaws.com/vpc-infrastructure.yaml
      Parameters:
        VpcCidr: '10.0.0.0/16'
      Tags:
        - Key: Environment
          Value: !Ref Environment

  # コンピュートスタック
  ComputeStack:
    Type: AWS::CloudFormation::Stack
    DependsOn: NetworkStack
    Properties:
      TemplateURL: https://my-templates-bucket.s3.amazonaws.com/ec2-infrastructure.yaml
      Parameters:
        NetworkStackName: !GetAtt NetworkStack.Outputs.StackName
        InstanceType: t3.micro
        KeyName: my-key-pair
      Tags:
        - Key: Environment
          Value: !Ref Environment

  # ロードバランサースタック
  LoadBalancerStack:
    Type: AWS::CloudFormation::Stack
    DependsOn: [NetworkStack, ComputeStack]
    Properties:
      TemplateURL: https://my-templates-bucket.s3.amazonaws.com/alb-infrastructure.yaml
      Parameters:
        NetworkStackName: !GetAtt NetworkStack.Outputs.StackName
        ComputeStackName: !GetAtt ComputeStack.Outputs.StackName
      Tags:
        - Key: Environment
          Value: !Ref Environment

Outputs:
  WebsiteURL:
    Description: Website URL
    Value: !GetAtt LoadBalancerStack.Outputs.LoadBalancerURL

開発ワークフロー

1. テンプレート作成

# テンプレート作成
vim my-infrastructure.yaml

# 検証
aws cloudformation validate-template \
  --template-body file://my-infrastructure.yaml

2. 開発・テストサイクル

# 開発環境デプロイ
aws cloudformation create-stack \
  --stack-name dev-infrastructure \
  --template-body file://my-infrastructure.yaml \
  --parameters ParameterKey=Environment,ParameterValue=development

# 変更時は変更セットで確認
aws cloudformation create-change-set \
  --stack-name dev-infrastructure \
  --template-body file://my-infrastructure.yaml \
  --change-set-name update-$(date +%Y%m%d-%H%M%S)

# 実行
aws cloudformation execute-change-set \
  --change-set-name update-$(date +%Y%m%d-%H%M%S) \
  --stack-name dev-infrastructure

3. CI/CD統合

# .github/workflows/cloudformation.yml
name: CloudFormation Deployment
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1
          
      - name: Validate CloudFormation templates
        run: |
          for template in templates/*.yaml; do
            aws cloudformation validate-template --template-body file://$template
          done

  deploy-dev:
    needs: validate
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1
          
      - name: Deploy to development
        run: |
          aws cloudformation deploy \
            --template-file templates/main.yaml \
            --stack-name dev-infrastructure \
            --parameter-overrides Environment=development \
            --capabilities CAPABILITY_IAM \
            --no-fail-on-empty-changeset