コラム
こんにちは、クラウドCoEの熊谷です。
今回は「Lambdaのマルチアカウントデプロイ」と題し、テスト環境と本番環境とでAWSアカウントを分けるような運用環境において、テスト環境で構築したLambdaを本番環境にデプロイする方法をご紹介します。
構築編となる今回は、利用するCloudFormation(以下、CFn)テンプレートの解説をしつつ、どんどん構築していこうと思います。
構築の流れ
今回は4つのCFnテンプレートを使い、テスト環境と本番環境に対して交互に実行していきます。
前工程で作成したAWSリソースを後の工程で参照しているため、必ずこの順序でテンプレートを実行する必要があります。
なお本コラムではCloud9でLambdaを新規作成する手順やCodeCommitとCloud9の連携については説明を省略いたしますのでご了承ください。
[作業1]テスト環境にCodePipelineを構築!
[作業1]テンプレート 1-stg
AWSTemplateFormatVersion: 2010-09-09 Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: STG Environment Parameter Parameters: - RepositoryName - BuildProjectName - STGPipelineName - BucketName - Label: default: Prod Environment Parameter Parameters: - ProdPipelineName - ProdAccountID Parameters: ProdAccountID: Type : String RepositoryName: Type : String BuildProjectName: Type : String STGPipelineName: Type : String BucketName: Type: String ProdPipelineName: Type : String Resources: # ------------------------------------------------------------# # EventBridge IAM Role/IAM Policy # ------------------------------------------------------------# IAMPolicyForEventBridge: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: !Sub STGPolicyForEventBridge-${STGPipelineName} Roles: - !Ref IAMRoleForEventBridge PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - codepipeline:StartPipelineExecution Resource: - !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${STGPipelineName} IAMRoleForEventBridge: Type: AWS::IAM::Role Properties: RoleName: STGRoleForEventBridgeTriggerdBySTGBranchEvnet Path: /service-role/ AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: events.amazonaws.com # ------------------------------------------------------------# # EventBridgeTriggerdByMasterBranchEvnet IAM Role/IAM Policy # ------------------------------------------------------------# IAMPolicyForEventBridgeTriggerdByMasterBranchEvnet: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: !Sub STGPolicyForEventBridge-${ProdPipelineName} Roles: - !Ref IAMRoleForEventBridgeTriggerdByMasterBranchEvnet PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - events:PutEvents Resource: - !Sub arn:aws:events:${AWS::Region}:${ProdAccountID}:event-bus/default IAMRoleForEventBridgeTriggerdByMasterBranchEvnet: Type: AWS::IAM::Role Properties: RoleName: STGRoleForEventBridgeTriggerdByMasterBranchEvnet Path: /service-role/ AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: events.amazonaws.com # ------------------------------------------------------------# # CodeBuild IAM Role/IAM Policy # ref. https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/auth-and-access-control-permissions-reference.html # ------------------------------------------------------------# IAMPolicyForCodeBuild: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: !Sub STGPolicyForCodeBuild-${STGPipelineName} Roles: - !Ref IAMRoleForCodeBuild PolicyDocument: Version: '2012-10-17' Statement: - Sid: CloudWatchLogsPolicy Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${CodeBuildProject} - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${CodeBuildProject}:* - Sid: CodeCommitPull Effect: Allow Action: - codecommit:GitPull Resource: !Sub arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${RepositoryName} - Sid: BuildStageArtifactPushPolicy Effect: Allow Action: - s3:PutObject Resource: - !Join ['', ['arn:aws:s3:::', !Ref ArtifactBucket, /*]] - Sid: SourceStageArtifactGetPolicy Effect: Allow Action: - s3:GetBucketAcl - s3:GetBucketLocation Resource: - !GetAtt ArtifactBucket.Arn - Sid: GetArtifactPolicy Effect: Allow Action: - s3:GetObject - s3:GetObjectVersion Resource: - !Join ['', ['arn:aws:s3:::', !Ref ArtifactBucket, /*]] - Sid: AllowUseCMK Effect: Allow Action: - kms:GenerateDataKey - kms:GenerateDataKeyWithoutPlaintext - kms:DescribeKey - kms:Encrypt - kms:Decrypt Resource: - !GetAtt Key.Arn IAMRoleForCodeBuild: Type: AWS::IAM::Role Properties: RoleName: !Sub STGRoleForCodeBuild-${STGPipelineName} Path: /service-role/ AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: codebuild.amazonaws.com # ------------------------------------------------------------# # Deploy Stage IAM Role/IAM Policy # ------------------------------------------------------------# IAMPolicyForDeployStage: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: !Sub STGPolicyForDeploy-${STGPipelineName} Roles: - !Ref IAMRoleForDeployStage PolicyDocument: Version: '2012-10-17' Statement: - Sid: LambdaDeployPolicy Effect: Allow Action: - lambda:* Resource: "*" - Sid: APIGatewayPolicy Effect: Allow Action: - apigateway:* Resource: "*" - Sid: CloudFormationCreateChangeSetPolicy Effect: Allow Action: - cloudformation:CreateChangeSet Resource: "*" - Sid: IAMPolicy Effect: Allow Action: - iam:GetRole - iam:CreateRole - iam:DeleteRole - iam:PutRolePolicy - iam:AttachRolePolicy - iam:DeleteRolePolicy - iam:DetachRolePolicy - iam:PassRole Resource: "*" - Sid: GetArtifactStorePolicy Effect: Allow Action: - s3:GetBucketLocation - s3:GetBucketVersioning - s3:GetBucketAcl Resource: - !GetAtt ArtifactBucket.Arn - Sid: GetArtifactPolicy Effect: Allow Action: - s3:GetObject - s3:GetObjectVersion Resource: - !Join ['', ['arn:aws:s3:::', !Ref ArtifactBucket, /*]] IAMRoleForDeployStage: Type: AWS::IAM::Role Properties: RoleName: !Sub STGRoleForDeploy-${STGPipelineName} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: cloudformation.amazonaws.com Path: / # IAMPolicy for CodePipeline IAMPolicyForCodePipeline: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: !Sub STGPolicyForCodePipeline-${STGPipelineName} Roles: - !Ref IAMRoleFoCodePipeline PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - iam:PassRole Resource: "*" Condition: StringEqualsIfExists: iam:PassedToService: - cloudformation.amazonaws.com - Sid: CodeCommitPolicy Effect: Allow Action: - codecommit:CancelUploadArchive - codecommit:GetBranch - codecommit:GetCommit - codecommit:GetUploadArchiveStatus - codecommit:UploadArchive Resource: - !Sub arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${RepositoryName} - Sid: CodeBuildPolicy Effect: Allow Action: - codebuild:BatchGetBuilds - codebuild:StartBuild - codebuild:StopBuild - codebuild:RetryBuild Resource: - !Sub arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:project/${CodeBuildProject} - Sid: LambdaPolicy Effect: Allow Action: - lambda:InvokeFunction - lambda:ListFunctions Resource: "*" - Sid: CloudFormationDeployPolicy Effect: Allow Action: - cloudformation:CreateStack - cloudformation:DeleteStack - cloudformation:DescribeStacks - cloudformation:UpdateStack - cloudformation:CreateChangeSet - cloudformation:DeleteChangeSet - cloudformation:DescribeChangeSet - cloudformation:ExecuteChangeSet - cloudformation:SetStackPolicy - cloudformation:ValidateTemplate Resource: "*" - Sid: SourceStageArtifactPushPolicy Effect: Allow Action: - s3:PutObject Resource: - !Join ['', ['arn:aws:s3:::', !Ref ArtifactBucket, /*]] - Sid: SourceStageArtifactGetPolicy Effect: Allow Action: - s3:GetBucketAcl - s3:GetBucketLocation Resource: - !GetAtt ArtifactBucket.Arn - Sid: GetArtifactStorePolicy Effect: Allow Action: - s3:GetObject - s3:GetObjectVersion Resource: - !Join ['', ['arn:aws:s3:::', !Ref ArtifactBucket, /*]] IAMRoleFoCodePipeline: Type: AWS::IAM::Role Properties: RoleName: !Sub STGRoleForCodePipeline-${STGPipelineName} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: codepipeline.amazonaws.com # ------------------------------------------------------------# # CodeCommit # ------------------------------------------------------------# CodeCommitRepository: Type: AWS::CodeCommit::Repository Properties: RepositoryName: !Ref RepositoryName RepositoryDescription: !Sub store ${RepositoryName} source # ------------------------------------------------------------# # CodeBuild # ------------------------------------------------------------# CodeBuildProject: Type: AWS::CodeBuild::Project Properties: Name: !Ref BuildProjectName Source: BuildSpec: buildspec.yml Type: CODEPIPELINE Artifacts: Type: CODEPIPELINE EncryptionKey: !GetAtt Key.Arn Environment: ComputeType: BUILD_GENERAL1_SMALL Image: aws/codebuild/standard:4.0 Type: LINUX_CONTAINER EnvironmentVariables: - Name: BUCKET_NAME Type: PLAINTEXT Value: !Ref BucketName ServiceRole: !GetAtt IAMRoleForCodeBuild.Arn # ------------------------------------------------------------# # CpdePipeline # ------------------------------------------------------------# CodePipelineForLambda: Type: AWS::CodePipeline::Pipeline Properties: ArtifactStore: Location: !Ref ArtifactBucket Type: S3 EncryptionKey: Id: !GetAtt Key.Arn Type: KMS Name: !Ref STGPipelineName RoleArn: !GetAtt IAMRoleFoCodePipeline.Arn Stages: - Name: Source Actions: - Name: Commit ActionTypeId: Category: Source Owner: AWS Version: 1 Provider: CodeCommit Configuration: RepositoryName: !Ref RepositoryName BranchName: stg RunOrder: 1 OutputArtifacts: - Name: SourceArtifact - Name: Build Actions: - Name: Build ActionTypeId: Category: Build Owner: AWS Version: 1 Provider: CodeBuild RunOrder: 1 Configuration: ProjectName: !Ref CodeBuildProject InputArtifacts: - Name: SourceArtifact # SourceステージのOutputArtifactsの名称と揃える OutputArtifacts: - Name: BuildArtifact - Name: Deploy Actions: - Name: Deploy InputArtifacts: - Name: BuildArtifact # BuildステージのOutputArtifactsの名称と揃える ActionTypeId: Category: Deploy Owner: AWS Version: 1 Provider: CloudFormation Configuration: ActionMode: CHANGE_SET_REPLACE Capabilities: CAPABILITY_IAM,CAPABILITY_AUTO_EXPAND,CAPABILITY_NAMED_IAM ChangeSetName: lambda-pipeline-changeset StackName: lambda-pipeline-changeset TemplatePath: 'BuildArtifact::packaged-template.yaml' # CodeBuildのbuildspec.ymlで出力した成果物の名称 RoleArn: !GetAtt IAMRoleForDeployStage.Arn RunOrder: 1 # ------------------------------------------------------------# # EventBridge # ref. https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/pipelines-trigger-source-repo-changes-cfn.html # ------------------------------------------------------------# EventBridgeTriggerdByCodeCommitSTGBranchMerge: Type: AWS::Events::Rule Properties: Description: !Sub Invoking STG CodePipeline ${RepositoryName} Name: !Sub EventBridgeRule-STG-CodePipeline-${RepositoryName} EventPattern: source: - aws.codecommit resources: - !Sub arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${RepositoryName} detail: event: - referenceCreated - referenceUpdated referenceType: - branch referenceName: - stg detail-type: - CodeCommit Repository State Change Targets: - Id: codepipeline RoleArn: !GetAtt IAMRoleForEventBridge.Arn Arn: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${STGPipelineName} EventBridgeTriggerdByCodeCommitProdBranchMerge: Type: AWS::Events::Rule Properties: Description: !Sub Invoking Prod CodePipeline ${RepositoryName} Name: !Sub EventBridgeRule-Prod-CodePipeline-${RepositoryName} EventPattern: source: - aws.codecommit resources: - !Sub arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${RepositoryName} detail: event: - referenceCreated - referenceUpdated referenceType: - branch referenceName: - master detail-type: - CodeCommit Repository State Change Targets: - Id: prod-event-bus RoleArn: !GetAtt IAMRoleForEventBridgeTriggerdByMasterBranchEvnet.Arn Arn: !Sub arn:aws:events:${AWS::Region}:${ProdAccountID}:event-bus/default # ------------------------------------------------------------# # KMS # ------------------------------------------------------------# Key: Type: AWS::KMS::Key Properties: Description: CodePipeline CMK KeyPolicy: Version: 2012-10-17 Id: key-default-1 Statement: - Sid: Enable IAM User Permissions Effect: Allow Principal: AWS: - !Sub arn:aws:iam::${AWS::AccountId}:root Action: kms:* Resource: "*" - Sid: Allow administration of the key Effect: Allow Principal: AWS: - !Sub arn:aws:iam::${AWS::AccountId}:root Action: - kms:Create* - kms:Describe* - kms:Enable* - kms:List* - kms:Put* - kms:Update* - kms:Revoke* - kms:Disable* - kms:Get* - kms:Delete* - kms:ScheduleKeyDeletion - kms:CancelKeyDeletion Resource: "*" - Sid: Allow use of the key Effect: Allow Principal: AWS: - !GetAtt IAMRoleFoCodePipeline.Arn - !GetAtt IAMRoleForCodeBuild.Arn - !GetAtt IAMRoleForDeployStage.Arn Action: - kms:DescribeKey - kms:Encrypt - kms:Decrypt - kms:ReEncrypt* - kms:GenerateDataKey - kms:GenerateDataKeyWithoutPlaintext Resource: "*" - Sid: Allow attachment of persistent resources Effect: Allow Principal: AWS: - !GetAtt IAMRoleFoCodePipeline.Arn - !GetAtt IAMRoleForCodeBuild.Arn - !GetAtt IAMRoleForDeployStage.Arn Action: - kms:CreateGrant - kms:ListGrants - kms:RevokeGrant Resource: "*" Condition: Bool: kms:GrantIsForAWSResource: true Alias: Type: AWS::KMS::Alias Properties: AliasName: alias/CodePipelineArtifact TargetKeyId: Ref: Key # ------------------------------------------------------------# # S3Bucket # ------------------------------------------------------------# ArtifactBucket: Type: AWS::S3::Bucket Properties: BucketName: !Ref BucketName BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 LifecycleConfiguration: Rules: - ExpirationInDays: 90 Id: !Sub ${BucketName}-LifeCycle Status: Enabled S3ArtifactBucketPolicy: Type: AWS::S3::BucketPolicy Description: Amazon S3 bucket policy for CMK Properties: Bucket: !Ref ArtifactBucket PolicyDocument: Statement: - Sid: DenyAccessWithoutUsingCMK Action: - s3:PutObject Effect: Deny Principal: "*" Resource: - !Sub arn:aws:s3:::${ArtifactBucket}/* Condition: StringNotLikeIfExists: {"s3:x-amz-server-side-encryption-aws-kms-key-id": !GetAtt Key.Arn} - Sid: DenyHTTPAccessPolicy Action: - s3:* Effect: Deny Principal: "*" Resource: - !Sub arn:aws:s3:::${ArtifactBucket}/* Condition: Bool: aws:SecureTransport: false - Sid: S3GetPutPolicy Action: - s3:Get* - s3:Put* Effect: Allow Principal: AWS: - !Sub arn:aws:iam::${AWS::AccountId}:root Resource: - !Sub arn:aws:s3:::${ArtifactBucket}/* - Sid: S3ListPolicy Action: - s3:ListBucket Effect: Allow Principal: AWS: - !Sub arn:aws:iam::${AWS::AccountId}:root Resource: - !Sub arn:aws:s3:::${ArtifactBucket} Version: 2012-10-17
[作業1]テンプレートの補足 2つのEventBridge
1-stgテンプレートでは2つのEventBridgeを構築します。
- stgブランチのイベントを拾うEventBridge
- masterブランチのイベントを拾うEventBridge
1つ目のEventBridgeはstgブランチへのマージイベントを拾ってテスト環境のCodePipelineを動かします。
2つ目のEventBridgeはmasterブランチのイベントを拾い、本番CodePipelineを起動するためのものです。
両者のテンプレート上の記述の違いはイベントパターンのreferenceNameとターゲットです。
stgブランチのイベントを拾うEventBridgeの記述例
EventBridgeTriggerdByCodeCommitSTGBranchMerge: Type: AWS::Events::Rule Properties: ###省略### EventPattern: source: - aws.codecommit resources: - !Sub arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${RepositoryName} detail: event: - referenceCreated - referenceUpdated referenceType: - branch referenceName: - stg detail-type: - CodeCommit Repository State Change Targets: - Id: codepipeline RoleArn: !GetAtt IAMRoleForEventBridge.Arn Arn: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${STGPipelineName} #テスト環境のCodePipeline
本番環境のCodePipelineを起動させたいとしても、異なるAWSアカウントのCodePipelineを直接ターゲットにしたところで動いてくれません。
よって本番環境のEventBridgeを動かしてくれるEventBusをターゲットに設定します。
masterブランチのイベントを拾うEventBridgeの記述例
EventBridgeTriggerdByCodeCommitProdBranchMerge: Type: AWS::Events::Rule Properties: ###省略### EventPattern: source: - aws.codecommit resources: - !Sub arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${RepositoryName} detail: event: - referenceCreated - referenceUpdated referenceType: - branch referenceName: - master detail-type: - CodeCommit Repository State Change Targets: - Id: prod-event-bus RoleArn: !GetAtt IAMRoleForEventBridgeTriggerdByMasterBranchEvnet.Arn Arn: !Sub arn:aws:events:${AWS::Region}:${ProdAccountID}:event-bus/default #本番環境のEvnet Bus
masterブランチイベント用のEventBridgeのサービスロールでは、本番環境のEvent Busを動かすための権限が必要です。
master用EventBridgeのサービスロールにアタッチするIAMポリシー
IAMPolicyForEventBridgeTriggerdByMasterBranchEvnet: Type: AWS::IAM::ManagedPolicy Properties: ###省略### PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - events:PutEvents Resource: - !Sub arn:aws:events:${AWS::Region}:${ProdAccountID}:event-bus/default
[作業1]テンプレートの補足 DeployStageのサービスロール
今回はLambdaとAPI Gatewayをデプロイするため、サービスロールに両サービスの権限を付与しています。
DeployStageのサービスロール
IAMPolicyForDeployStage: Type: AWS::IAM::ManagedPolicy Properties: ###省略### PolicyDocument: Version: '2012-10-17' Statement: - Sid: LambdaDeployPolicy Effect: Allow Action: - lambda:* Resource: "*" - Sid: APIGatewayPolicy Effect: Allow Action: - apigateway:* Resource: "*"
SAMではEventBridge RuleやSNS、Step Functionもデプロイ出来ますので、利用したいサービスに応じてDeploy Stageの権限を変更しましょう。
SAMでデプロイ可能なAWSサービスに関しては以下をご参照ください。
[作業1]テンプレートのパラメータ
Pipeline名やBuildプロジェクト名を自由に設定できるようにパラメータにしました。
スタックが正常に作成されるとCodePipelineが起動しますが、まだCodeCommitにブランチが作成されていない状態ですのでこけます。
気にせず次の工程に進みましょう。
[作業2]本番環境でIAMロール・CMK・S3バケットを作成!
作業2では本番環境のCodePipelineやCodeBuildのサービスロールや、成果物暗号化用のCMK、成果物格納用S3バケットを作成します。
これらのAWSサービスのARNが作業3で必要になります。
[作業2]テンプレート 2-prod
AWSTemplateFormatVersion: 2010-09-09 Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: STG Environment Parameter Parameters: - STGAccountId - Label: default: Prod Environment Parameter Parameters: - BuildProjectName - PipelineName - BucketName Parameters: STGAccountId: Description: Repository Account ID MaxLength: 12 MinLength: 12 Type: String BucketName: Type: String BuildProjectName: Type : String PipelineName: Type : String Resources: # ------------------------------------------------------------# # CodeBuild IAM Role/IAM Policy # ref. https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/auth-and-access-control-permissions-reference.html # ------------------------------------------------------------# IAMPolicyForCodeBuild: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: !Sub ProdPolicyForCodeBuild-${PipelineName} Roles: - !Ref IAMRoleForCodeBuild PolicyDocument: Version: '2012-10-17' Statement: - Sid: CloudWatchLogsPolicy Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${BuildProjectName} - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${BuildProjectName}:* - Sid: BuildStageArtifactPushPolicy Effect: Allow Action: - s3:PutObject Resource: - !Join ['', ['arn:aws:s3:::', !Ref ArtifactBucket, /*]] - Sid: SourceStageArtifactGetPolicy Effect: Allow Action: - s3:GetBucketAcl - s3:GetBucketLocation Resource: - !GetAtt ArtifactBucket.Arn - Sid: GetArtifactPolicy Effect: Allow Action: - s3:GetObject - s3:GetObjectVersion Resource: - !Join ['', ['arn:aws:s3:::', !Ref ArtifactBucket, /*]] IAMRoleForCodeBuild: Type: AWS::IAM::Role Properties: RoleName: !Sub ProdRoleForCodeBuild-${PipelineName} Path: /service-role/ AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: codebuild.amazonaws.com # ------------------------------------------------------------# # Deploy Stage IAM Role/IAM Policy # ------------------------------------------------------------# IAMPolicyForDeployStage: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: !Sub ProdPolicyForDeploy-${PipelineName} Roles: - !Ref IAMRoleForDeployStage PolicyDocument: Version: '2012-10-17' Statement: - Sid: LambdaDeployPolicy Effect: Allow Action: - lambda:* Resource: "*" - Sid: APIGatewayPolicy Effect: Allow Action: - apigateway:* Resource: "*" - Sid: CloudFormationCreateChangeSetPolicy Effect: Allow Action: - cloudformation:CreateChangeSet Resource: "*" - Sid: IAMPolicy Effect: Allow Action: - iam:GetRole - iam:CreateRole - iam:DeleteRole - iam:PutRolePolicy - iam:AttachRolePolicy - iam:DeleteRolePolicy - iam:DetachRolePolicy - iam:PassRole Resource: "*" - Sid: GetArtifactStorePolicy Effect: Allow Action: - s3:GetBucketLocation - s3:GetBucketVersioning - s3:GetBucketAcl Resource: - !GetAtt ArtifactBucket.Arn - Sid: GetArtifactPolicy Effect: Allow Action: - s3:GetObject - s3:GetObjectVersion Resource: - !Join ['', ['arn:aws:s3:::', !Ref ArtifactBucket, /*]] IAMRoleForDeployStage: Type: AWS::IAM::Role Properties: RoleName: !Sub ProdRoleForDeploy-${PipelineName} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: cloudformation.amazonaws.com Path: / # IAMPolicy for CodePipeline IAMPolicyForCodePipeline: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: !Sub ProdPolicyForCodePipeline-${PipelineName} Roles: - !Ref IAMRoleFoCodePipeline PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - iam:PassRole Resource: "*" Condition: StringEqualsIfExists: iam:PassedToService: - cloudformation.amazonaws.com - Sid: CodeBuildPolicy Effect: Allow Action: - codebuild:BatchGetBuilds - codebuild:StartBuild - codebuild:StopBuild - codebuild:RetryBuild Resource: - !Sub arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:project/${BuildProjectName} - Sid: LambdaPolicy Effect: Allow Action: - lambda:InvokeFunction - lambda:ListFunctions Resource: "*" - Sid: CloudFormationDeployPolicy Effect: Allow Action: - cloudformation:CreateStack - cloudformation:DeleteStack - cloudformation:DescribeStacks - cloudformation:UpdateStack - cloudformation:CreateChangeSet - cloudformation:DeleteChangeSet - cloudformation:DescribeChangeSet - cloudformation:ExecuteChangeSet - cloudformation:SetStackPolicy - cloudformation:ValidateTemplate Resource: "*" - Sid: SourceStageArtifactPushPolicy Effect: Allow Action: - s3:PutObject Resource: - !Join ['', ['arn:aws:s3:::', !Ref ArtifactBucket, /*]] - Sid: SourceStageArtifactGetPolicy Effect: Allow Action: - s3:GetBucketAcl - s3:GetBucketLocation Resource: - !GetAtt ArtifactBucket.Arn - Sid: STGAccountAssumeRole Effect: Allow Action: - sts:AssumeRole Resource: - !Sub arn:aws:iam::${STGAccountId}:role/* - Sid: GetArtifactStorePolicy Effect: Allow Action: - s3:GetObject - s3:GetObjectVersion Resource: - !Join ['', ['arn:aws:s3:::', !Ref ArtifactBucket, /*]] IAMRoleFoCodePipeline: Type: AWS::IAM::Role Properties: RoleName: !Sub ProdRoleForCodePipeline-${PipelineName} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: codepipeline.amazonaws.com # ------------------------------------------------------------# # KMS # ref. https://docs.aws.amazon.com/ja_jp/kms/latest/developerguide/key-policy-modifying-external-accounts.html # ------------------------------------------------------------# Key: Type: AWS::KMS::Key Properties: Description: CodePipeline CMK KeyPolicy: Version: 2012-10-17 Id: key-default-1 Statement: - Sid: Enable IAM User Permissions Effect: Allow Principal: AWS: - !Sub arn:aws:iam::${AWS::AccountId}:root Action: kms:* Resource: "*" - Sid: Allow administration of the key Effect: Allow Principal: AWS: - !Sub arn:aws:iam::${AWS::AccountId}:root - !Sub arn:aws:iam::${STGAccountId}:root Action: - kms:Create* - kms:Describe* - kms:Enable* - kms:List* - kms:Put* - kms:Update* - kms:Revoke* - kms:Disable* - kms:Get* - kms:Delete* - kms:ScheduleKeyDeletion - kms:CancelKeyDeletion Resource: "*" - Sid: Allow use of the key Effect: Allow Principal: AWS: - !Sub arn:aws:iam::${STGAccountId}:root - !GetAtt IAMRoleFoCodePipeline.Arn - !GetAtt IAMRoleForCodeBuild.Arn - !GetAtt IAMRoleForDeployStage.Arn Action: - kms:DescribeKey - kms:Encrypt - kms:Decrypt - kms:ReEncrypt* - kms:GenerateDataKey - kms:GenerateDataKeyWithoutPlaintext Resource: "*" - Sid: Allow attachment of persistent resources Effect: Allow Principal: AWS: - !Sub arn:aws:iam::${STGAccountId}:root - !GetAtt IAMRoleFoCodePipeline.Arn - !GetAtt IAMRoleForCodeBuild.Arn - !GetAtt IAMRoleForDeployStage.Arn Action: - kms:CreateGrant - kms:ListGrants - kms:RevokeGrant Resource: "*" Condition: Bool: kms:GrantIsForAWSResource: true Alias: Type: AWS::KMS::Alias Properties: AliasName: alias/CodePipelineArtifact TargetKeyId: Ref: Key # ------------------------------------------------------------# # S3Bucket # ------------------------------------------------------------# ArtifactBucket: Description: Amazon S3 bucket for AWS CodePipeline artifacts Type: AWS::S3::Bucket Properties: BucketName: !Ref BucketName BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 LifecycleConfiguration: Rules: - ExpirationInDays: 90 Id: !Sub ${BucketName}-LifeCycle Status: Enabled S3ArtifactBucketPolicy: Type: AWS::S3::BucketPolicy Description: Amazon S3 bucket policy for Cross Account access Properties: Bucket: !Ref ArtifactBucket PolicyDocument: Statement: - Sid: DenyAccessWithoutUsingCMK Action: - s3:PutObject Effect: Deny Principal: "*" Resource: - !Sub arn:aws:s3:::${ArtifactBucket}/* Condition: StringNotLikeIfExists: {"s3:x-amz-server-side-encryption-aws-kms-key-id": !GetAtt Key.Arn} - Sid: DenyHTTPAccessPolicy Action: - s3:* Effect: Deny Principal: "*" Resource: - !Sub arn:aws:s3:::${ArtifactBucket}/* Condition: Bool: aws:SecureTransport: false - Sid: CrossAccountS3GetPutPolicy Action: - s3:Get* - s3:Put* Effect: Allow Principal: AWS: - !Sub arn:aws:iam::${STGAccountId}:root Resource: - !Sub arn:aws:s3:::${ArtifactBucket}/* - Sid: CrossAccountS3ListPolicy Action: - s3:ListBucket Effect: Allow Principal: AWS: - !Sub arn:aws:iam::${STGAccountId}:root Resource: - !Sub arn:aws:s3:::${ArtifactBucket} Version: 2012-10-17 Outputs: KMS: Description: Prod KMS Export: Name: PipelineKey Value: !GetAtt Key.Arn ArtifactBucketName: Description: Prod S3Bucket Export: Name: ProdBucket Value: !Ref ArtifactBucket ArtifactBucketARN: Description: Prod S3Bucket ARN Export: Name: ProdBucketARN Value: !GetAtt ArtifactBucket.Arn IAMRoleForCodeBuildARN: Description: CodeBuild IAM Role ARN Export: Name: IAMRoleForCodeBuildARN Value: !GetAtt IAMRoleForCodeBuild.Arn IAMRoleForDeployStageARN: Description: DeployStage IAM Role ARN Export: Name: IAMRoleForDeployStageARN Value: !GetAtt IAMRoleForDeployStage.Arn IAMRoleFoCodePipelineARN: Description: CodePipeline IAM Role ARN Export: Name: IAMRoleFoCodePipelineARN Value: !GetAtt IAMRoleFoCodePipeline.Arn
[作業2]テンプレートの補足 CodePipelineのサービスロール
作業2で作成するCode系のサービスのIAMロールの設定は、作業1でテスト環境用に作成したものとほぼ同じです。
違いは、本番環境CodePipelineのサービスロールにCodeCommitのpull権限等がないことと、テスト環境のIAMロールへのAssumeRole権限が設定されていることの2点になります。
「Lambdaのマルチアカウントデプロイ 設計編」でも触れましたが、テスト環境のCodeCommitのリポジトリからpullするためにはテスト環境のIAMロールを利用します。
<再掲>本番環境のCodePipelineがテスト環境のCodeCommitリポジトリにアクセスするイメージ
よって本番環境のCodePipelineにはテスト環境のActionロールを利用できる権限が必要です。
CodePipelineのサービスロールにアタッチする権限
IAMPolicyForCodePipeline: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: !Sub ProdPolicyForCodePipeline-${PipelineName} Roles: - !Ref IAMRoleFoCodePipeline PolicyDocument: Version: '2012-10-17' Statement: ###省略### - Sid: STGAccountAssumeRole Effect: Allow Action: - sts:AssumeRole Resource: - !Sub arn:aws:iam::${STGAccountId}:role/*
[作業2]テンプレートのパラメータ
[作業3]テスト環境にActionロールを作成!
ではテスト環境に本番環境CodePipelineが利用するActionロールを作りましょう。
[作業3]テンプレート 3-stg
Actionロールには以下の権限が必要です。
- 本番環境のS3バケットにCodeCommitのソースをPut
- 本番環境のCMKを使って成果物を暗号化
- テスト環境のリポジトリの変更情報を取得
AWSTemplateFormatVersion: 2010-09-09 Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: STG Environment Parameter Parameters: - RepositoryName - Label: default: Prod Environment Parameter Parameters: - ProductionAccountId - ProductionS3BucketARN - ProductionCmkArn Parameters: ProductionAccountId: Description: Production Account ID MaxLength: 12 MinLength: 12 Type: String ProductionS3BucketARN: Description: Production Account S3 Bucket ARN for Artifact Type: String ProductionCmkArn: Description: Production Account CMK ARN Type: String RepositoryName: Description: STG CodeCommit RepositoryName Type: String Resources: # ------------------------------------------------------------# # IAM Role # ------------------------------------------------------------# MultiAccountCodeCommitRole: Type: AWS::IAM::Role Properties: RoleName: ProdCodePipelineActionRole AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: AWS: - !Sub arn:aws:iam::${ProductionAccountId}:root Action: - sts:AssumeRole Path: / Policies: - PolicyName: source PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - s3:PutObject - s3:PutObjectAcl Resource: - !Sub ${ProductionS3BucketARN}/* - Effect: Allow Action: - kms:DescribeKey - kms:GenerateDataKey* - kms:Encrypt - kms:ReEncrypt* - kms:Decrypt Resource: - !Ref ProductionCmkArn - Effect: Allow Action: - codecommit:GetBranch - codecommit:GetCommit - codecommit:UploadArchive - codecommit:GetUploadArchiveStatus - codecommit:CancelUploadArchive Resource: - !Sub arn:aws:codecommit:ap-northeast-1:${AWS::AccountId}:${RepositoryName}
[作業3]テンプレートのパラメータ
[作業4]本番環境にCodePipelineを構築!
ではいよいよ本番環境のCodePipelineを構築します。CFnでの作業はこれが最後になります。
[作業4]テンプレート 4-prod
AWSTemplateFormatVersion: 2010-09-09 Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: STG Environment Parameter Parameters: - STGCodeCommitIAMRole - STGRepositoryName - STGAccountId - Label: default: Prod Environment Parameter Parameters: - BuildProjectName - PipelineName Parameters: BuildProjectName: Type : String PipelineName: Type : String STGCodeCommitIAMRole: Type : String STGRepositoryName: Type : String STGAccountId: Description: Repository Account ID MaxLength: 12 MinLength: 12 Type: String Resources: # ------------------------------------------------------------# # EventBridge IAM Role/IAM Policy # ------------------------------------------------------------# IAMPolicyForEventBridge: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: !Sub ProdPolicyForEventBridge-${PipelineName} Roles: - !Ref IAMRoleForEventBridge PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - codepipeline:StartPipelineExecution Resource: - !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${PipelineName} IAMRoleForEventBridge: Type: AWS::IAM::Role Properties: RoleName: ProdRoleForEventBridge Path: /service-role/ AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: events.amazonaws.com # ------------------------------------------------------------# # CodeBuild # ------------------------------------------------------------# CodeBuildProject: Type: AWS::CodeBuild::Project Properties: Artifacts: Type: CODEPIPELINE EncryptionKey: !ImportValue PipelineKey Name: !Ref BuildProjectName Source: BuildSpec: buildspec.yml Type: CODEPIPELINE Environment: ComputeType: BUILD_GENERAL1_SMALL Image: aws/codebuild/standard:4.0 Type: LINUX_CONTAINER EnvironmentVariables: - Name: BUCKET_NAME Type: PLAINTEXT Value: !ImportValue ProdBucket ServiceRole: !ImportValue IAMRoleForCodeBuildARN # ------------------------------------------------------------# # CodePipeline # ------------------------------------------------------------# CodePipelineForLambda: Type: AWS::CodePipeline::Pipeline Properties: ArtifactStore: Location: !ImportValue ProdBucket Type: S3 EncryptionKey: Id: !ImportValue PipelineKey Type: KMS Name: !Ref PipelineName RoleArn: !ImportValue IAMRoleFoCodePipelineARN Stages: - Name: Source Actions: - Name: Commit InputArtifacts: [] OutputArtifacts: - Name: SourceArtifact ActionTypeId: Category: Source Owner: AWS Version: 1 Provider: CodeCommit RoleArn: !Ref STGCodeCommitIAMRole Configuration: RepositoryName: !Ref STGRepositoryName BranchName: master PollForSourceChanges: false RunOrder: 1 - Name: Build Actions: - Name: Build ActionTypeId: Category: Build Owner: AWS Version: 1 Provider: CodeBuild RunOrder: 1 Configuration: ProjectName: !Ref CodeBuildProject InputArtifacts: - Name: SourceArtifact OutputArtifacts: - Name: BuildArtifact - Name: Deploy Actions: - Name: Deploy InputArtifacts: - Name: BuildArtifact ActionTypeId: Category: Deploy Owner: AWS Version: 1 Provider: CloudFormation Configuration: ActionMode: CHANGE_SET_REPLACE Capabilities: CAPABILITY_IAM,CAPABILITY_AUTO_EXPAND,CAPABILITY_NAMED_IAM ChangeSetName: lambda-pipeline-changeset StackName: lambda-pipeline-changeset TemplatePath: 'BuildArtifact::packaged-template.yaml' # CodeBuildのbuildspec.ymlで出力した成果物の名称 RoleArn: !ImportValue IAMRoleForDeployStageARN RunOrder: 3 # ------------------------------------------------------------# # EventBridge # ref. https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/pipelines-trigger-source-repo-changes-cfn.html # ------------------------------------------------------------# EventBridgeTriggerdByCodeCommitMerge: Type: AWS::Events::Rule Properties: Description: !Sub Invoking CodePipeline prod ${STGRepositoryName} Name: !Sub EventBridgeRule-CodePipeline-Prod${STGRepositoryName} EventPattern: source: - aws.codecommit resources: - !Sub arn:aws:codecommit:${AWS::Region}:${STGAccountId}:${STGRepositoryName} detail: event: - referenceCreated - referenceUpdated referenceType: - branch referenceName: - master detail-type: - CodeCommit Repository State Change Targets: - Id: codepipeline RoleArn: !GetAtt IAMRoleForEventBridge.Arn Arn: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${PipelineName}
[作業4]テンプレートの補足 SourceステージのIAMロール
CodePipeline自体に設定するサービスロールと、Sourceステージに設定するIAMロールを区別することが出来ます。
Sourceステージに作業3で作成したIAMのActionロールを設定することで、テスト環境のCodeCommitリポジトリを操作することが出来ます。
CodePipeline(Sourceステージのみ抜粋)
CodePipelineForLambda: Type: AWS::CodePipeline::Pipeline Properties: ###省略### RoleArn: !ImportValue IAMRoleFoCodePipelineARN Stages: - Name: Source Actions: - Name: Commit ###省略### RoleArn: !Ref STGCodeCommitIAMRole Configuration: RepositoryName: !Ref STGRepositoryName BranchName: master PollForSourceChanges: false RunOrder: 1
[作業4]テンプレートのパラメータ
以上でCFnでの作業は全て完了しました。あと少しです。
[作業5]本番環境のEventBusのアクセス許可設定変更
この時点で、masterブランチへのマージ作業から本番環境のLambdaをデプロイする為の一連の流れはほぼ完成しています。
[一連の流れ]
1.テスト環境のEventBridge発動
2.★本番環境のEventBus発動
3.本番環境のEventBridge発動
4.本番環境のCodePipeline発動
5.本番環境へLambdaデプロイ
現時点では上記の★の部分が実行できません。
作業5ではテスト環境のEventBridgeが本番環境のEventBusを発動させるためのアクセス許可を設定します。
本番環境のdefaultのEventBusを開き、リソースベースのポリシーに以下を加えます。
{ "Version": "2012-10-17", "Statement": [{ "Sid": "AllowCodeCommitEvents", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::{テスト環境}:root" }, "Action": "*", "Resource": "arn:aws:events:ap-northeast-1:{本番環境}:event-bus/default" }] }
操作対象がdefaultのEventBusであることもポイントです。
EventBusはdefaultのもの以外に自分で作成できるものもありますが、AWSサービス間の連携には必ずdefaultのEventBusを使わなくてはなりません。
※2021年3月時点の情報です。
詳しくはEventBridgeのBlackBeltの「EventBridgeのアーキテクチャ」をご参照ください。
AWS Black Belt Online Seminar Amazon EventBridge
[作業6]本番環境へLambdaをデプロイ!
以上で準備が全て完了しました。
少し補足を入れながら、masterブランチにstgをマージして本番環境にLambdaをデプロイしてみます。
補足事項
- buildspec.ymlの構成と配置場所
- SAMテンプレート
- CodeCommitのブランチ
buildspec.ymlの構成
buildspec.ymlのsam packageコマンドでは必ずcmkを指定して成果物を暗号化した上で作成します。
template-fileのパスはbuildspec.yml自身の配置場所によって変わるので注意してください。
version: 0.2 phases: install: runtime-versions: python: 3.8 build: commands: | sam package --template-file ./Test-Function/template.yaml --s3-bucket ${BUCKET_NAME} --kms-key-id ${CODEBUILD_KMS_KEY_ID} --output-template-file packaged-template.yaml artifacts: type: zip files: - packaged-template.yaml
“–kms-key-id ${CODEBUILD_KMS_KEY_ID}”を指定しないと、バケットポリシーによりPutObjectがはじかれます。
buildspec.ymlの配置場所
今回はbuildspec.ymlをlambdaリポジトリの直下に配置しましたが、リポジトリ内に配置されていれば必ずしも直下に置く必要はありません。
配置場所を変更する場合は、CFnのCodeBuildProjectのBuildSpecのパスとbuildspec.yml内のコマンドのパスを変えてください。
BuildSpecのパス
CodeBuildProject: Type: AWS::CodeBuild::Project Properties: ###省略### Source: BuildSpec: buildspec.yml #--★ Type: CODEPIPELINE
SAMテンプレート
今回はCloud9の機能を使い、Cloud9上でLambdaを新規作成しました。
よってSAMのテンプレートもデフォルトのものを使っております。Lambdaの名前だけ「Test-Function」となるよう追加しました。
デフォルトではLambda Functionの直下に配置されていましたのでこのまま利用しております。
CodeCommitのブランチ
今回の構成ではCodeCommitのブランチに少なくともmasterとstgの2つのブランチが必要です。
時流に乗ってprodブランチにしようかとも思ったのですが今回はCodeCommitのデフォルトに従いました。
それではテスト環境のCodeCommitで、stgブランチをmasterへマージしてみます。
pullリクエストの作成
マージ
マージした直後、本番環境のCodePipelineが動き始めます。
CodePipelineが正常に終了した場合、CloudFormationのスタック「lambda-pipeline-changeset」が作成されます。※スタック名はCFnテンプレートで指定可能
「REVIEW_IN_PROGRESS」というステータスで作成されますので、変更セットを実行します。
少し待ったら、Lambdaがデプロイされました。
以上です!この構成であれば、複数の本番AWSアカウントに対してまとめてデプロイすることが可能になります。
ぜひご活用ください。
-
PICK UP
ピックアップ
-
ピックアップコンテンツがありません
-
RANKING
人気の記事
-
-
1
【SSM】SSM Inventory vol.1 …
【SSM】SSM Inventory vol.1 マルチアカウント構成でのSSM…
2022/10/31
-
2
DevOps、その遙かなる道程(第1回)
DevOps、その遙かなる道程(第1回)
2019/08/01
-
3
DevOps、その遙かなる道程(第2回)
DevOps、その遙かなる道程(第2回)
2019/08/30
-
4
62人分のIAMユーザの発行と通知をLambdaと…
62人分のIAMユーザの発行と通知をLambdaとSESで自動化してみた
2023/04/05
-
5
公共システムのコスト見直しのアプローチと調達仕様書…
公共システムのコスト見直しのアプローチと調達仕様書への記載事項(AWS Comp…
2023/12/21
-
-
ARCHIVE
アーカイブ
-
- July 2024 (1)
- January 2024 (1)
- December 2023 (2)
- June 2023 (2)
- May 2023 (1)
- April 2023 (1)
- March 2023 (2)
- February 2023 (2)
- January 2023 (1)
- December 2022 (2)
- October 2022 (2)
- September 2022 (2)