複数環境デプロイ

複数環境デプロイとは

複数環境デプロイとは、アプリケーションを本番環境にリリースする前に、開発環境ステージング環境など複数の異なる環境に段階的にデプロイする手法です。

代表的な環境構成
環境用途
開発環境(Development)開発中の機能テスト、動作確認
ステージング環境(Staging)本番環境と同等の構成での統合テスト
本番環境(Production)エンドユーザーが利用する実稼働環境
なぜ複数環境が必要か
  • リスク低減:本番環境に影響を与える前に、バグや設定ミスを検出できる
  • 品質保証:本番相当環境で動作確認やパフォーマンステストを実施できる
  • 段階的なリリース:小さな変更から始めて、徐々に本番環境へ展開できる
  • 並行開発の実現:複数の機能を異なる環境で同時に開発・テストできる
CodePipelineでの実現方法

CodePipelineでは、パイプライン内に複数のデプロイステージを設定し、各環境への自動デプロイと承認フローを組み合わせることで、安全で効率的な複数環境デプロイを実現します。

環境分離戦略

複数環境を構築する際、環境をどのように分離するかは重要な設計判断です。主な分離戦略として、AWSアカウントレベルでの分離とリージョンレベルでの分離があります。

アカウント分離(推奨)

概要

開発環境、ステージング環境、本番環境をそれぞれ別のAWSアカウントに配置する方法です。

メリット
  • セキュリティの強化: 環境間で完全にIAM権限が分離される
  • 請求の分離: 環境ごとのコストを明確に追跡できる
  • リソース制限の回避: 各アカウントでサービスクォータが独立
  • 障害の影響範囲限定: 開発環境のミスが本番環境に影響しない
デメリット
  • 管理の複雑性: 複数アカウントの管理が必要
  • クロスアカウント設定: IAMロールやS3アクセスの設定が複雑
  • 初期コスト: 複数アカウントの初期セットアップに時間がかかる
適用ケース
  • エンタープライズ環境
  • 厳格なセキュリティ要件がある場合
  • 複数チームでの開発

リージョン分離

概要

同一AWSアカウント内で、異なるリージョンに環境を配置する方法です。

メリット
  • 管理がシンプル: 単一アカウントで完結
  • リソース共有が容易: IAMロールやセキュリティグループの共有が簡単
  • 低コスト: アカウント管理のオーバーヘッドが少ない
デメリット
  • セキュリティリスク: IAM権限の誤設定で環境間の影響が発生しうる
  • リソース競合: サービスクォータを環境間で共有
  • 請求の分離困難: タグベースでのコスト配分が必要
適用ケース
  • 小規模プロジェクト
  • 開発初期段階
  • セキュリティ要件が緩やかな場合

命名規則

リソース命名パターン

{プロジェクト名}-{環境名}-{リソースタイプ}-{用途}

具体例
リソース用途
myapp-dev-pipeline開発環境のパイプライン
myapp-stg-codebuild-apiステージング環境のCodeBuildプロジェクト(API用)
myapp-prd-ecs-cluster本番環境のECSクラスター
myapp-dev-s3-artifacts開発環境のアーティファクトS3バケット
環境名の標準化
環境略称フル名称
開発環境devdevelopment
ステージング環境stgstaging
本番環境prdproduction

タグ戦略

タグキー説明
Environment環境識別dev,
stg
prd
Projectプロジェクト名myapp
ManagedBy管理方法terraform
cloudformation
manual
CostCenterコストセンターengineering
marketing
タグ適用例
Resources:
  MyPipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: myapp-dev-pipeline
      Tags:
        - Key: Environment
          Value: dev
        - Key: Project
          Value: myapp
        - Key: ManagedBy
          Value: cloudformation
        - Key: CostCenter
          Value: engineering
タグによるコスト配分
# タグベースのコストレポート取得
aws ce get-cost-and-usage \
  --time-period Start=2024-01-01,End=2024-01-31 \
  --granularity MONTHLY \
  --metrics BlendedCost \
  --group-by Type=TAG,Key=Environment

パイプライン設計パターン

複数環境デプロイを実現するパイプラインの設計パターンには、主に2つのアプローチがあります。

単一パイプライン・複数環境

1つのパイプライン内に複数環境へのデプロイステージを順次配置する方法です。

メリット
  • 一貫性の保証: 同一のアーティファクトが全環境にデプロイされる
  • 管理がシンプル: パイプラインが1つで済む
  • 進捗の可視化: 全環境のデプロイ状況を一元管理できる
  • 自動的な段階デプロイ: 前段階が成功した場合のみ次の環境へ進む
デメリット
  • 柔軟性の低下: 特定環境のみの再デプロイが難しい
  • パイプラインの肥大化: 環境が増えるとステージ数が多くなる
  • 並行デプロイ不可: 複数環境への同時デプロイができない
適用ケース
  • 環境間で厳密な整合性が必要な場合
  • リリースプロセスが定型化されている場合
  • 小〜中規模プロジェクト
CloudFormation実装例
Resources:
  MultiEnvPipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: myapp-multi-env-pipeline
      RoleArn: !GetAtt CodePipelineServiceRole.Arn
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactStoreBucket
      Stages:
        # ソースステージ
        - Name: Source
          Actions:
            - Name: SourceAction
              ActionTypeId:
                Category: Source
                Owner: AWS
                Provider: CodeCommit
                Version: 1
              Configuration:
                RepositoryName: myapp-repo
                BranchName: main
              OutputArtifacts:
                - Name: SourceOutput

        # ビルドステージ
        - Name: Build
          Actions:
            - Name: BuildAction
              ActionTypeId:
                Category: Build
                Owner: AWS
                Provider: CodeBuild
                Version: 1
              Configuration:
                ProjectName: !Ref CodeBuildProject
              InputArtifacts:
                - Name: SourceOutput
              OutputArtifacts:
                - Name: BuildOutput

        # 開発環境デプロイ
        - Name: DeployToDev
          Actions:
            - Name: DeployDevAction
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Provider: CloudFormation
                Version: 1
              Configuration:
                ActionMode: CREATE_UPDATE
                StackName: myapp-dev-stack
                TemplatePath: BuildOutput::template.yaml
                ParameterOverrides: !Sub |
                  {
                    "Environment": "dev"
                  }
                Capabilities: CAPABILITY_IAM
                RoleArn: !GetAtt CloudFormationRole.Arn
              InputArtifacts:
                - Name: BuildOutput

        # ステージング環境デプロイ
        - Name: DeployToStaging
          Actions:
            - Name: DeployStagingAction
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Provider: CloudFormation
                Version: 1
              Configuration:
                ActionMode: CREATE_UPDATE
                StackName: myapp-stg-stack
                TemplatePath: BuildOutput::template.yaml
                ParameterOverrides: !Sub |
                  {
                    "Environment": "stg"
                  }
                Capabilities: CAPABILITY_IAM
                RoleArn: !GetAtt CloudFormationRole.Arn
              InputArtifacts:
                - Name: BuildOutput

        # 本番環境承認
        - Name: ApprovalForProduction
          Actions:
            - Name: ManualApproval
              ActionTypeId:
                Category: Approval
                Owner: AWS
                Provider: Manual
                Version: 1
              Configuration:
                CustomData: "本番環境へのデプロイを承認してください"

        # 本番環境デプロイ
        - Name: DeployToProduction
          Actions:
            - Name: DeployProductionAction
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Provider: CloudFormation
                Version: 1
              Configuration:
                ActionMode: CREATE_UPDATE
                StackName: myapp-prd-stack
                TemplatePath: BuildOutput::template.yaml
                ParameterOverrides: !Sub |
                  {
                    "Environment": "prd"
                  }
                Capabilities: CAPABILITY_IAM
                RoleArn: !GetAtt CloudFormationRole.Arn
              InputArtifacts:
                - Name: BuildOutput

環境ごとの個別パイプライン

各環境に対して独立したパイプラインを構築する方法です。

メリット
  • 柔軟性が高い: 環境ごとに異なる設定やプロセスを適用可能
  • 並行実行: 複数環境への同時デプロイが可能
  • 独立性: 特定環境のパイプライン変更が他環境に影響しない
  • ブランチ戦略との統合: 環境ごとに異なるブランチをソースにできる
デメリット
  • 管理の複雑性: 複数のパイプラインを個別に管理する必要がある
  • 一貫性の課題: 異なるアーティファクトが各環境にデプロイされる可能性
  • 設定の重複: 共通設定を複数パイプラインで維持する必要がある
適用ケース
  • 大規模プロジェクト
  • 環境ごとに異なるデプロイプロセスが必要な場合
  • ブランチ戦略(Git Flow等)と統合する場合
  • 複数チームでの開発
CloudFormation実装例(開発環境パイプライン)
Resources:
  DevPipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: myapp-dev-pipeline
      RoleArn: !GetAtt CodePipelineServiceRole.Arn
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactStoreBucket
      Stages:
        - Name: Source
          Actions:
            - Name: SourceAction
              ActionTypeId:
                Category: Source
                Owner: AWS
                Provider: CodeCommit
                Version: 1
              Configuration:
                RepositoryName: myapp-repo
                BranchName: develop  # 開発ブランチ
              OutputArtifacts:
                - Name: SourceOutput

        - Name: Build
          Actions:
            - Name: BuildAction
              ActionTypeId:
                Category: Build
                Owner: AWS
                Provider: CodeBuild
                Version: 1
              Configuration:
                ProjectName: !Ref DevCodeBuildProject
                EnvironmentVariables: !Sub |
                  [
                    {"name": "ENVIRONMENT", "value": "dev"}
                  ]
              InputArtifacts:
                - Name: SourceOutput
              OutputArtifacts:
                - Name: BuildOutput

        - Name: Deploy
          Actions:
            - Name: DeployAction
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Provider: ECS
                Version: 1
              Configuration:
                ClusterName: myapp-dev-cluster
                ServiceName: myapp-dev-service
                FileName: imagedefinitions.json
              InputArtifacts:
                - Name: BuildOutput
CloudFormation実装例(本番環境パイプライン)
Resources:
  ProductionPipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: myapp-prd-pipeline
      RoleArn: !GetAtt CodePipelineServiceRole.Arn
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactStoreBucket
      Stages:
        - Name: Source
          Actions:
            - Name: SourceAction
              ActionTypeId:
                Category: Source
                Owner: AWS
                Provider: CodeCommit
                Version: 1
              Configuration:
                RepositoryName: myapp-repo
                BranchName: main  # 本番ブランチ
              OutputArtifacts:
                - Name: SourceOutput

        - Name: Build
          Actions:
            - Name: BuildAction
              ActionTypeId:
                Category: Build
                Owner: AWS
                Provider: CodeBuild
                Version: 1
              Configuration:
                ProjectName: !Ref PrdCodeBuildProject
                EnvironmentVariables: !Sub |
                  [
                    {"name": "ENVIRONMENT", "value": "prd"}
                  ]
              InputArtifacts:
                - Name: SourceOutput
              OutputArtifacts:
                - Name: BuildOutput

        - Name: Approval
          Actions:
            - Name: ManualApproval
              ActionTypeId:
                Category: Approval
                Owner: AWS
                Provider: Manual
                Version: 1
              Configuration:
                CustomData: "本番環境へのデプロイを承認してください"
                NotificationArn: !Ref ApprovalSNSTopic

        - Name: Deploy
          Actions:
            - Name: DeployAction
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Provider: ECS
                Version: 1
              Configuration:
                ClusterName: myapp-prd-cluster
                ServiceName: myapp-prd-service
                FileName: imagedefinitions.json
              InputArtifacts:
                - Name: BuildOutput

承認フローの実装

本番環境へのデプロイ前に人間による承認を必要とすることで、意図しないリリースを防ぎます。

Manual Approval の設定

CodePipelineのManual Approvalアクションを使用して、パイプラインの実行を一時停止し、承認者による手動承認を待ちます。

CloudFormation実装例
Resources:
  ApprovalStage:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Stages:
        - Name: ApprovalStage
          Actions:
            - Name: ManualApprovalAction
              ActionTypeId:
                Category: Approval
                Owner: AWS
                Provider: Manual
                Version: 1
              Configuration:
                # 承認者へのメッセージ
                CustomData: |
                  本番環境へのデプロイを承認してください。
                  変更内容: アプリケーションバージョン v2.3.1
                  デプロイ予定時刻: 2024-01-15 18:00 JST
                
                # SNSトピックARN(承認通知用)
                NotificationArn: !Ref ApprovalNotificationTopic
                
                # 外部リンク(レビュー資料など)
                ExternalEntityLink: https://example.com/release-notes/v2.3.1
AWS CLI実装例
# パイプラインの承認待ち状態を確認
aws codepipeline get-pipeline-state \
  --name myapp-prd-pipeline

# 承認を実行
aws codepipeline put-approval-result \
  --pipeline-name myapp-prd-pipeline \
  --stage-name ApprovalStage \
  --action-name ManualApprovalAction \
  --result summary="承認します",status=Approved \
  --token 

# 却下を実行
aws codepipeline put-approval-result \
  --pipeline-name myapp-prd-pipeline \
  --stage-name ApprovalStage \
  --action-name ManualApprovalAction \
  --result summary="変更内容に問題があるため却下",status=Rejected \
  --token 
承認タイムアウト設定

Manual Approvalアクションには、デフォルトで7日間のタイムアウトがあります。この期間内に承認または却下されない場合、アクションは自動的に失敗します。

  • タイムアウト期間は変更できません(固定で7日間)
  • タイムアウト後は、パイプラインを再実行する必要があります
  • 長期休暇などを考慮してリリース計画を立てる
承認権限の設定

承認を実行できるユーザーを制限するには、IAMポリシーを使用します。

# IAMポリシー例

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "codepipeline:GetPipelineState",
        "codepipeline:PutApprovalResult"
      ],
      "Resource": "arn:aws:codepipeline:ap-northeast-1:123456789012:myapp-prd-pipeline"
    }
  ]
}

SNS通知の設定

承認が必要になったタイミングで、関係者に自動通知を送ります。

CloudFormation実装例
# SNSトピックの作成例

Resources:
  ApprovalNotificationTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: myapp-approval-notifications
      DisplayName: "MyApp 本番デプロイ承認通知"
      Subscriptions:
        - Protocol: email
          Endpoint: devops-team@example.com
        - Protocol: email
          Endpoint: release-manager@example.com

  ApprovalNotificationTopicPolicy:
    Type: AWS::SNS::TopicPolicy
    Properties:
      Topics:
        - !Ref ApprovalNotificationTopic
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: codepipeline.amazonaws.com
            Action:
              - SNS:Publish
            Resource: !Ref ApprovalNotificationTopic
通知メール内容
件名: APPROVAL NEEDED: myapp-prd-pipeline

本文:
Pipeline myapp-prd-pipeline requires approval.
Stage: ApprovalStage
Action: ManualApprovalAction

Custom Data:
本番環境へのデプロイを承認してください。
変更内容: アプリケーションバージョン v2.3.1
デプロイ予定時刻: 2024-01-15 18:00 JST

Approve or reject: https://console.aws.amazon.com/codesuite/codepipeline/pipelines/myapp-prd-pipeline/view

パラメータ管理

複数環境デプロイでは、環境ごとに異なるパラメータ(データベース接続先、APIエンドポイント、リソース設定など)を管理する必要があります。

Parameter Store の活用

環境固有の設定値を安全に保存・管理するためのサービスです。

パラメータの階層構造

環境ごとにパラメータを階層化して管理します。

/myapp/dev/db/host         → dev-db.example.com
/myapp/dev/db/port         → 3306
/myapp/dev/api/endpoint    → https://api-dev.example.com

/myapp/stg/db/host         → stg-db.example.com
/myapp/stg/db/port         → 3306
/myapp/stg/api/endpoint    → https://api-stg.example.com

/myapp/prd/db/host         → prd-db.example.com
/myapp/prd/db/port         → 3306
/myapp/prd/api/endpoint    → https://api.example.com
パラメータの作成
# 開発環境のパラメータ
aws ssm put-parameter \
  --name "/myapp/dev/db/host" \
  --value "dev-db.example.com" \
  --type String \
  --tags "Key=Environment,Value=dev" "Key=Project,Value=myapp"

# 本番環境のパラメータ(SecureString)
aws ssm put-parameter \
  --name "/myapp/prd/db/password" \
  --value "SecurePassword123!" \
  --type SecureString \
  --key-id alias/aws/ssm \
  --tags "Key=Environment,Value=prd" "Key=Project,Value=myapp"
CloudFormation実装例
Resources:
  DevDatabaseHost:
    Type: AWS::SSM::Parameter
    Properties:
      Name: /myapp/dev/db/host
      Type: String
      Value: dev-db.example.com
      Tags:
        Environment: dev
        Project: myapp

  DevDatabasePassword:
    Type: AWS::SSM::Parameter
    Properties:
      Name: /myapp/dev/db/password
      Type: SecureString
      Value: !Ref DevDBPassword  # CloudFormationパラメータから取得
      Tags:
        Environment: dev
        Project: myapp
パラメータの取得
# 単一パラメータの取得
aws ssm get-parameter \
  --name "/myapp/dev/db/host" \
  --query "Parameter.Value" \
  --output text

# SecureStringの取得(復号化)
aws ssm get-parameter \
  --name "/myapp/prd/db/password" \
  --with-decryption \
  --query "Parameter.Value" \
  --output text

# パスベースで複数パラメータを一括取得
aws ssm get-parameters-by-path \
  --path "/myapp/dev/" \
  --recursive \
  --with-decryption
アプリケーションからの取得(Python Boto3)
import boto3

ssm = boto3.client('ssm', region_name='ap-northeast-1')

# 単一パラメータ取得
response = ssm.get_parameter(
    Name='/myapp/dev/db/host'
)
db_host = response['Parameter']['Value']

# SecureString取得
response = ssm.get_parameter(
    Name='/myapp/dev/db/password',
    WithDecryption=True
)
db_password = response['Parameter']['Value']

# 複数パラメータ一括取得
response = ssm.get_parameters_by_path(
    Path='/myapp/dev/',
    Recursive=True,
    WithDecryption=True
)

params = {p['Name']: p['Value'] for p in response['Parameters']}
パラメータアクセスのIAMポリシー
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ssm:GetParameter",
        "ssm:GetParameters",
        "ssm:GetParametersByPath"
      ],
      "Resource": [
        "arn:aws:ssm:ap-northeast-1:123456789012:parameter/myapp/dev/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "kms:Decrypt"
      ],
      "Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/your-kms-key-id"
    }
  ]
}

環境変数の注入

Parameter Storeの値を、デプロイプロセスやアプリケーション実行時に環境変数として注入します。

CodeBuildでの環境変数注入
# buildspec.ymlでの実装

version: 0.2

env:
  parameter-store:
    DB_HOST: /myapp/${ENVIRONMENT}/db/host
    DB_PORT: /myapp/${ENVIRONMENT}/db/port
    DB_NAME: /myapp/${ENVIRONMENT}/db/name
    DB_PASSWORD: /myapp/${ENVIRONMENT}/db/password
    API_ENDPOINT: /myapp/${ENVIRONMENT}/api/endpoint

phases:
  pre_build:
    commands:
      - echo "環境: $ENVIRONMENT"
      - echo "データベースホスト: $DB_HOST"
      
  build:
    commands:
      - echo "アプリケーションをビルド"
      - docker build -t myapp:latest \
          --build-arg DB_HOST=$DB_HOST \
          --build-arg DB_PORT=$DB_PORT \
          --build-arg DB_NAME=$DB_NAME \
          --build-arg API_ENDPOINT=$API_ENDPOINT \
          .
CodeBuildプロジェクトでの環境変数設定
Resources:
  CodeBuildProject:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: myapp-build
      Environment:
        Type: LINUX_CONTAINER
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/standard:7.0
        EnvironmentVariables:
          - Name: ENVIRONMENT
            Value: dev
            Type: PLAINTEXT
      ServiceRole: !GetAtt CodeBuildRole.Arn
CodePipelineからの環境変数上書き
- Name: Build
  Actions:
    - Name: BuildAction
      ActionTypeId:
        Category: Build
        Owner: AWS
        Provider: CodeBuild
        Version: 1
      Configuration:
        ProjectName: !Ref CodeBuildProject
        EnvironmentVariables: !Sub |
          [
            {
              "name": "ENVIRONMENT",
              "value": "prd",
              "type": "PLAINTEXT"
            }
          ]
      InputArtifacts:
        - Name: SourceOutput
      OutputArtifacts:
        - Name: BuildOutput
CloudFormationでの環境変数注入
Parameters:
  Environment:
    Type: String
    AllowedValues:
      - dev
      - stg
      - prd

Resources:
  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub myapp-${Environment}-function
      Environment:
        Variables:
          DB_HOST: !Sub '{{resolve:ssm:/myapp/${Environment}/db/host}}'
          DB_PORT: !Sub '{{resolve:ssm:/myapp/${Environment}/db/port}}'
          DB_PASSWORD: !Sub '{{resolve:ssm-secure:/myapp/${Environment}/db/password}}'
          API_ENDPOINT: !Sub '{{resolve:ssm:/myapp/${Environment}/api/endpoint}}'
動的参照の種類
参照タイプ構文用途
ssm'{{resolve:ssm:parameter-name}}'String型パラメータ
ssm-secure'{{resolve:ssm-secure:parameter-name}}'SecureString型パラメータ
secretsmanager'{{resolve:secretsmanager:secret-id}}`Secrets Managerのシークレット
ECSタスク定義での環境変数注入
# タスク定義(CloudFormation)

Resources:
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: !Sub myapp-${Environment}
      ContainerDefinitions:
        - Name: app
          Image: !Sub ${AWS::AccountId}.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:latest
          Environment:
            - Name: ENVIRONMENT
              Value: !Ref Environment
          Secrets:
            - Name: DB_HOST
              ValueFrom: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/myapp/${Environment}/db/host
            - Name: DB_PASSWORD
              ValueFrom: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/myapp/${Environment}/db/password
            - Name: API_ENDPOINT
              ValueFrom: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/myapp/${Environment}/api/endpoint
必要なIAMポリシー(ECSタスクロール)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ssm:GetParameters",
        "secretsmanager:GetSecretValue"
      ],
      "Resource": [
        "arn:aws:ssm:ap-northeast-1:123456789012:parameter/myapp/*",
        "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:myapp/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "kms:Decrypt",
      "Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/your-kms-key-id"
    }
  ]
}

クロスアカウントデプロイ

IAM ロール設定

デプロイ先アカウントでのロール作成

各環境アカウント(開発/ステージング/本番)で、ツールアカウントから AssumeRole できるロールを作成します。

Parameters:
  ToolAccountId:
    Type: String
    Description: ツールアカウントのAWSアカウントID
    Default: "123456789012"

Resources:
  # クロスアカウントデプロイ用ロール
  CrossAccountDeployRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: CrossAccountDeployRole
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub arn:aws:iam::${ToolAccountId}:root
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/PowerUserAccess
      Policies:
        - PolicyName: DeployPermissions
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              # CloudFormationの権限
              - Effect: Allow
                Action:
                  - cloudformation:*
                Resource: "*"
              
              # ECSの権限
              - Effect: Allow
                Action:
                  - ecs:*
                  - ecr:*
                Resource: "*"
              
              # S3アーティファクトアクセス
              - Effect: Allow
                Action:
                  - s3:GetObject
                  - s3:GetObjectVersion
                Resource:
                  - !Sub arn:aws:s3:::tool-account-artifacts/*
              
              # KMS復号化権限
              - Effect: Allow
                Action:
                  - kms:Decrypt
                  - kms:DescribeKey
                Resource: !Sub arn:aws:kms:ap-northeast-1:${ToolAccountId}:key/*

  # CloudFormation実行用ロール(CrossAccountDeployRoleから利用)
  CloudFormationExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: CloudFormationExecutionRole
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: cloudformation.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/PowerUserAccess

Outputs:
  CrossAccountDeployRoleArn:
    Value: !GetAtt CrossAccountDeployRole.Arn
    Export:
      Name: CrossAccountDeployRoleArn
ツールアカウントでのパイプライン設定
# CloudFormation実装例(ツールアカウント)

Parameters:
  DevAccountId:
    Type: String
    Default: "111111111111"
  StagingAccountId:
    Type: String
    Default: "222222222222"
  ProductionAccountId:
    Type: String
    Default: "333333333333"

Resources:
  # CodePipelineサービスロール
  CodePipelineServiceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: codepipeline.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: CodePipelinePolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              # クロスアカウントロールのAssumeRole権限
              - Effect: Allow
                Action: sts:AssumeRole
                Resource:
                  - !Sub arn:aws:iam::${DevAccountId}:role/CrossAccountDeployRole
                  - !Sub arn:aws:iam::${StagingAccountId}:role/CrossAccountDeployRole
                  - !Sub arn:aws:iam::${ProductionAccountId}:role/CrossAccountDeployRole
              
              # S3アーティファクトアクセス
              - Effect: Allow
                Action:
                  - s3:GetObject
                  - s3:PutObject
                Resource:
                  - !Sub ${ArtifactBucket.Arn}/*
              
              # その他のCodePipeline権限
              - Effect: Allow
                Action:
                  - codecommit:GetBranch
                  - codecommit:GetCommit
                  - codebuild:StartBuild
                  - codebuild:BatchGetBuilds
                Resource: "*"

  # パイプライン定義
  CrossAccountPipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: cross-account-pipeline
      RoleArn: !GetAtt CodePipelineServiceRole.Arn
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactBucket
        EncryptionKey:
          Id: !GetAtt ArtifactKMSKey.Arn
          Type: KMS
      Stages:
        # ソースステージ
        - Name: Source
          Actions:
            - Name: SourceAction
              ActionTypeId:
                Category: Source
                Owner: AWS
                Provider: CodeCommit
                Version: 1
              Configuration:
                RepositoryName: myapp-repo
                BranchName: main
              OutputArtifacts:
                - Name: SourceOutput

        # ビルドステージ
        - Name: Build
          Actions:
            - Name: BuildAction
              ActionTypeId:
                Category: Build
                Owner: AWS
                Provider: CodeBuild
                Version: 1
              Configuration:
                ProjectName: !Ref CodeBuildProject
              InputArtifacts:
                - Name: SourceOutput
              OutputArtifacts:
                - Name: BuildOutput

        # 開発環境デプロイ(クロスアカウント)
        - Name: DeployToDev
          Actions:
            - Name: DeployDevAction
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Provider: CloudFormation
                Version: 1
              Configuration:
                ActionMode: CREATE_UPDATE
                StackName: myapp-dev-stack
                TemplatePath: BuildOutput::template.yaml
                RoleArn: !Sub arn:aws:iam::${DevAccountId}:role/CloudFormationExecutionRole
                Capabilities: CAPABILITY_IAM
              InputArtifacts:
                - Name: BuildOutput
              RoleArn: !Sub arn:aws:iam::${DevAccountId}:role/CrossAccountDeployRole

        # 本番環境デプロイ(クロスアカウント + 承認)
        - Name: ApprovalForProduction
          Actions:
            - Name: ManualApproval
              ActionTypeId:
                Category: Approval
                Owner: AWS
                Provider: Manual
                Version: 1

        - Name: DeployToProduction
          Actions:
            - Name: DeployProductionAction
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Provider: CloudFormation
                Version: 1
              Configuration:
                ActionMode: CREATE_UPDATE
                StackName: myapp-prd-stack
                TemplatePath: BuildOutput::template.yaml
                RoleArn: !Sub arn:aws:iam::${ProductionAccountId}:role/CloudFormationExecutionRole
                Capabilities: CAPABILITY_IAM
              InputArtifacts:
                - Name: BuildOutput
              RoleArn: !Sub arn:aws:iam::${ProductionAccountId}:role/CrossAccountDeployRole

S3/KMS アクセス設定

クロスアカウントデプロイでは、ツールアカウントのS3バケット(アーティファクト格納先)に他アカウントからアクセスする必要があります。

S3バケットポリシー
# CloudFormation実装例

Resources:
  ArtifactBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: myapp-cross-account-artifacts
      VersioningConfiguration:
        Status: Enabled
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: aws:kms
              KMSMasterKeyID: !GetAtt ArtifactKMSKey.Arn

  ArtifactBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref ArtifactBucket
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          # デプロイ先アカウントからの読み取り許可
          - Sid: AllowCrossAccountRead
            Effect: Allow
            Principal:
              AWS:
                - !Sub arn:aws:iam::${DevAccountId}:role/CrossAccountDeployRole
                - !Sub arn:aws:iam::${StagingAccountId}:role/CrossAccountDeployRole
                - !Sub arn:aws:iam::${ProductionAccountId}:role/CrossAccountDeployRole
            Action:
              - s3:GetObject
              - s3:GetObjectVersion
            Resource: !Sub ${ArtifactBucket.Arn}/*
          
          # CodePipelineからの読み書き許可
          - Sid: AllowCodePipeline
            Effect: Allow
            Principal:
              AWS: !GetAtt CodePipelineServiceRole.Arn
            Action:
              - s3:PutObject
              - s3:GetObject
            Resource: !Sub ${ArtifactBucket.Arn}/*
KMSキーポリシー

S3バケットが暗号化されている場合、デプロイ先アカウントがKMSキーにアクセスできる必要があります。

# CloudFormation実装例

Resources:
  ArtifactKMSKey:
    Type: AWS::KMS::Key
    Properties:
      Description: KMS key for cross-account artifact encryption
      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: "*"
          
          # CodePipelineの暗号化・復号化権限
          - Sid: Allow CodePipeline to use the key
            Effect: Allow
            Principal:
              AWS: !GetAtt CodePipelineServiceRole.Arn
            Action:
              - kms:Decrypt
              - kms:Encrypt
              - kms:GenerateDataKey
            Resource: "*"
          
          # デプロイ先アカウントの復号化権限
          - Sid: Allow cross-account decrypt
            Effect: Allow
            Principal:
              AWS:
                - !Sub arn:aws:iam::${DevAccountId}:role/CrossAccountDeployRole
                - !Sub arn:aws:iam::${StagingAccountId}:role/CrossAccountDeployRole
                - !Sub arn:aws:iam::${ProductionAccountId}:role/CrossAccountDeployRole
            Action:
              - kms:Decrypt
              - kms:DescribeKey
            Resource: "*"

  ArtifactKMSKeyAlias:
    Type: AWS::KMS::Alias
    Properties:
      AliasName: alias/artifact-bucket-key
      TargetKeyId: !Ref ArtifactKMSKey