AWS

CodePipelineに承認アクションを設定する

スポンサーリンク
CodePipelineに承認アクションを設定する AWS
スポンサーリンク
スポンサーリンク

CodePipelineに承認アクションを設定する

Codepipelineのステージに承認(Approval)アクションを追加することができます。

パイプラインが承認ステージまで進むと、一度動作が停止し、手動による承認を待機するステータスとなります。

今回は承認ステージになりましたら、SNSでメール通知する構成を目指します。

構築する環境

Diagram of setup Approval Action in CodePipeline.

CodePipelineを構成し、4つのリソースを連携させます。

1つ目はCodeCommitです。
CodeCommitはCodePipelineのソースステージを担当します。
Gitリポジトリとして使用します。

2つ目はCodeBuildです。
CodeBuildはCodePipelineのビルドステージを担当します。
CodeCommitにプッシュされたコードから、Dockerイメージをビルドします。
ビルドしたイメージをECRにプッシュします。

3つ目はSNSです。
SNSはCodePipelineの承認ステージを担当します。
パイプラインが承認ステージになりましたら、一旦パイプラインが停止し、SNSでメール通知します。
メールによる通知を受けたユーザは、パイプラインのページにアクセスし、承認ボタンを押すとパイプラインが再開されます。

4つ目はLambda関数です。
この関数の働きは、ECS(Fargate)サービスのタスク希望数を変更することです。
具体的には希望数を0から1に変更します。

CodePipelineにデプロイステージを作成します。
後述のFargateにデプロイするように設定します。

SSMパラメータストアにDockerHubアカウント情報を保存します。
DockerBuildでイメージを生成する際に、DockerHubにサインインした上でベースイメージをプルするために、これらを使用します。

CodePipelineが開始されるきっかけですが、CodeCommitへのプッシュを条件とします。
具体的には、EventBridgeに上記を満たすルールを用意します。

プライベートサブネットに、FargateタイプのECSを作成します。

EC2インスタンスを作成します。
Fargate上に作成されたコンテナにアクセスするためのクライアントして使用します。

CloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。

https://github.com/awstut-an-r/awstut-fa/tree/main/080

テンプレートファイルのポイント解説

本ページはCodepipeline内に承認ステージを作成し、SNSでメール通知する方法を取り上げます。

CodePipelineに関する基本的な事項については、以下のページをご確認ください。

CodePipelineにデプロイステージを作成し、ECS(Fargate)にデプロイする方法については、以下のページをご確認ください。

CodePipeline内でLambda関数を呼び出すアクションを定義し、ECS(Fargate)サービスの希望数を変更する方法については、以下のページをご確認ください。

プライベートサブネットにFargateを構築する方法については、以下のページをご確認ください。

CloudFormationカスタムリソースを使用して、CloudFormationスタック削除時に、自動的にS3バケット内のオブジェクトと、ECRリポジトリ内のイメージを削除します。
詳細につきましては、以下のページをご確認ください。

SNS

Resources:
  Topic:
    Type: AWS::SNS::Topic
    Properties:
      Subscription: 
        - Endpoint: !Ref MailAddress
          Protocol: email
      TopicName: !Ref Prefix
Code language: YAML (yaml)

SNSトピックを作成します。
通知先にメールを指定します。

詳細については、以下のページをご確認ください。

CodePipeline

Resources:
  Pipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      ArtifactStore:
        Location: !Ref BucketName
        Type: S3
      Name: !Ref Prefix
      RoleArn: !GetAtt CodePipelineRole.Arn
      Stages:
        - Actions:
            - ActionTypeId: 
                Category: Source
                Owner: AWS
                Provider: CodeCommit
                Version: 1
              Configuration:
                BranchName: !Ref BranchName
                OutputArtifactFormat: CODE_ZIP
                PollForSourceChanges: false
                RepositoryName: !GetAtt CodeCommitRepository.Name
              Name: Source
              OutputArtifacts:
                - Name: !Ref PipelineSourceArtifact
              Region: !Ref AWS::Region
              RunOrder: 1
          Name: Source
        - Actions:
            - ActionTypeId:
                Category: Build
                Owner: AWS
                Provider: CodeBuild
                Version: 1
              Configuration:
                ProjectName: !Ref CodeBuildProject
              InputArtifacts:
                - Name: !Ref PipelineSourceArtifact
              Name: Build
              OutputArtifacts:
                - Name: !Ref PipelineBuildArtifact
              Region: !Ref AWS::Region
              RunOrder: 1
          Name: Build
        - Actions:
            - ActionTypeId:
                Category: Approval
                Owner: AWS
                Provider: Manual
                Version: 1
              Configuration:
                CustomData: hoge hoge hoge.
                ExternalEntityLink: http://example.com
                NotificationArn: !Ref TopicArn
              InputArtifacts: []
              Name: Approval
              OutputArtifacts: []
              Region: !Ref AWS::Region
              RunOrder: 1
          Name: Approval
        - Actions:
            - ActionTypeId:
                Category: Deploy
                Owner: AWS
                Provider: ECS
                Version: 1
              Configuration:
                ClusterName: !Ref ECSClusterName
                FileName: !Ref ImageDefinitionFileName
                ServiceName: !Ref ECSServiceName
              InputArtifacts:
                - Name: !Ref PipelineBuildArtifact
              Name: Deploy
              Region: !Ref AWS::Region
              RunOrder: 1
          Name: Deploy
        - Actions:
            - ActionTypeId:
                Category: Invoke
                Owner: AWS
                Provider: Lambda
                Version: 1
              Configuration:
                FunctionName: !Ref ECSFunctionName
              InputArtifacts: []
              Name: Invoke
              OutputArtifacts: []
              Region: !Ref AWS::Region
              RunOrder: 1
          Name: Invoke
Code language: YAML (yaml)

Stagesプロパティに承認ステージを定義します。
Configurationプロパティ内で、承認ステージの詳細設定を行います。特に重要なパラメータはNotificationArnプロパティです。本プロパティに先述のSNSトピックを指定します。
InputArtifactsおよびOutputArtifactsプロパティは空の配列を指定します。今回の挙動では、アーティファクトに対する読み書きは発生しないためです。

以下がCodePipeline用のIAMロールです。

Resources:
  CodePipelineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - codepipeline.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: PipelinePolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - lambda:invokeFunction
                Resource:
                  - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${ECSFunctionName}"
              - Effect: Allow
                Action:
                  - codecommit:CancelUploadArchive
                  - codecommit:GetBranch
                  - codecommit:GetCommit
                  - codecommit:GetRepository
                  - codecommit:GetUploadArchiveStatus
                  - codecommit:UploadArchive
                Resource:
                  - !GetAtt CodeCommitRepository.Arn
              - Effect: Allow
                Action:
                  - codebuild:BatchGetBuilds
                  - codebuild:StartBuild
                  - codebuild:BatchGetBuildBatches
                  - codebuild:StartBuildBatch
                Resource:
                  - !GetAtt CodeBuildProject.Arn
              - Effect: Allow
                Action:
                  - s3:PutObject
                  - s3:GetObject
                  - s3:GetObjectVersion
                  - s3:GetBucketAcl
                  - s3:GetBucketLocation
                Resource:
                  - !Sub "arn:aws:s3:::${BucketName}"
                  - !Sub "arn:aws:s3:::${BucketName}/*"
              - Effect: Allow
                Action:
                  - sns:Publish
                Resource:
                  - !Ref TopicArn
              - Effect: Allow
                Action:
                  - ecs:*
                Resource: "*"
              - Effect: Allow
                Action:
                  - iam:PassRole
                Resource: "*"
                Condition:
                  StringLike:
                    iam:PassedToService:
                      - ecs-tasks.amazonaws.com
Code language: YAML (yaml)

承認ステージ用の権限を設定します。
SNSトピックに対して、「sns:Publish」を許可します。

(参照)アプリコンテナ

Dockerfile

FROM amazonlinux

RUN yum update -y && yum install python3 python3-pip -y

RUN pip3 install bottle

COPY main.py ./

CMD ["python3", "main.py"]

EXPOSE 8080
Code language: Dockerfile (dockerfile)

アプリコンテナ用のイメージは、Amazon Linux 2をベースとして作成します。

Python製WebフレームワークのBottleを使用します。
ですからPythonおよびpipをインストール後、これをインストールします。

アプリロジックを記載したPythonスクリプト(main.py)をコピーし、これを実行するように設定します。

先述の通り、アプリはtcp/8080でHTTPリクエストを待ち受けますので、このポートを公開します。

main.py

from bottle import route, run

@route('/')
def hello():
  return 'Hello CodePipeline.'

if __name__ == '__main__':
  run(host='0.0.0.0', port=8080)
Code language: Python (python)

Bottleを使用して、簡易的なWebサーバを構築します。
8080/tcpでHTTPリクエストを待ち受け、「Hello CodePipeline.」を返すという単純な構成です。

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。

CloudFormationスタックを作成し、スタック内のリソースを確認する

CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。

各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。

  • ECR:fa-080
  • CodeCommit:fa-080
  • CodeBuild:fa-080
  • CodePipeline:fa-080
  • Lambda関数:fa-080-function-ecs
  • SNSトピック:fa-080

メールアドレスの認証

SNSトピックのサブスクライバーとしてメールアドレスを指定した場合、そのメールアドレスを認証する必要があります。

詳細につきましては、以下のページをご確認ください。

リソース確認

AWS Management Consoleから各リソースを確認します。
CodePipelineを確認します。

Detail of CodePipeline 1.

パイプラインの実行に失敗しています。
これはCloudFormationスタック作成時に、CodeCommitが作成されたことがきっかけとして、パイプラインが実行されたためです。
現時点ではCodeCommitにコードをプッシュしていないため、パイプライン実行の過程でエラーが発生しました。

作成されているステージに注目します。
Approvalという名前で、SNSトピックを呼び出すステージが用意されています。
パイプラインがこのステージまで到達すると、一旦停止し、承認を待つことになります。

動作確認

準備が整いましたので、CodeCommitにコードをプッシュします。

まずCodeCommitをプルします。

$ git clone https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/fa-080
Cloning into 'fa-080'...
warning: You appear to have cloned an empty repository.
Code language: Bash (bash)

空のリポジトリがプルされました。

リポジトリにDockerfileおよびmain.pyを加えます。

$ ls -al
total 8
drwxrwxr-x 3 ec2-user ec2-user  51 Aug 20 23:54 .
drwxrwxr-x 3 ec2-user ec2-user  20 Aug 20 23:53 ..
-rw-rw-r-- 1 ec2-user ec2-user 187 Aug 12 11:01 Dockerfile
drwxrwxr-x 7 ec2-user ec2-user 119 Aug 20 23:53 .git
-rw-rw-r-- 1 ec2-user ec2-user 691 Aug 20 11:25 main.py
Code language: Bash (bash)

2ファイルをCodeCommitにプッシュします。

$ git add .

$ git commit -m 'first commit'
[master (root-commit) 6bfe691] first commit
...
 2 files changed, 39 insertions(+)
 create mode 100644 Dockerfile
 create mode 100644 main.py

$ git push
...
 * [new branch]      master -> master
Code language: Bash (bash)

正常にプッシュできました。

しばらく待機した後、改めてCodePipelineを確認します。

Detail of CodePipeline 2.

パイプラインが開始されました。
SourceおよびBuildステージは正常に完了し、承認(Approval)ステージに到達しました。
ただしステータスは「Pending」とあり、「Waiting for approval」というメッセージが表示されてます。
つまり権限を持つユーザが承認するまで、この状況は継続されることになります。

SNSトピックから、以下の通りメールが届きます。

Detail of CodePipeline 6.

このようにメールによって、ユーザに承認を待っていることを知らせることができます。

パイプラインの承認に必要な権限につきましては、AWS公式ページでは、以下の通り示されています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "codepipeline:ListPipelines"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "codepipeline:GetPipeline",
                "codepipeline:GetPipelineState",
                "codepipeline:GetPipelineExecution"
            ],
            "Resource": "arn:aws:codepipeline:us-east-2:80398EXAMPLE:MyFirstPipeline"
        },
        {
            "Effect": "Allow",
            "Action": [
                "codepipeline:PutApprovalResult"
            ],
            "Resource": "arn:aws:codepipeline:us-east-2:80398EXAMPLE:MyFirstPipeline/MyApprovalStage/MyApprovalAction"
        }
    ]
}
Code language: JSON / JSON with Comments (json)
で IAM ユーザーに承認アクセス許可を付与する CodePipeline - AWS CodePipeline
IAM CodePipeline ユーザーに管理ポリシーを添付する方法について説明します。

上記の権限を満たすユーザで承認を進めます。

Detail of CodePipeline 3.

「Approval」を押下し、承認します。

Detail of CodePipeline 4.

「Approved」と表示されました。
つまり承認することによって、パイプラインが再開されたということです。
しばらく待機します。

Detail of CodePipeline 5.

パイプラインの全ステージが完了しました。

Fargateを確認します。

Detail of ECS 1.
Detail of ECS 2.

ECS(Fargate)のタスク希望数が1であり、そのタスクのIPアドレスが「10.0.2.4」であることがわかります。

実際にタスク内のコンテナにアクセスします。

% aws ssm start-session --target i-0c41c5926230b480c

Starting session with SessionId: root-0c76e08548a26fec6
sh-4.2$
Code language: Bash (bash)

SSM Session Managerの詳細につきましては、以下のページをご確認ください。

curlコマンドを使って、タスク内のコンテナにアクセスします。

sh-4.2$ curl http://10.0.2.4:8080/
Hello CodePipeline.
Code language: Bash (bash)

コンテナから応答がありました。
確かにBottleアプリで設定した文字列です。
このことから、確かにパイプラインが実行されたということがわかります。
このようにCodePipelineに承認ステージを作成することによって、パイプラインの一時停止、SNSによるメール通知、承認によるパイプラインの再開を実装することができました。

まとめ

CodePipelineに承認ステージを作成し、SNSでメール通知する方法を確認しました。

タイトルとURLをコピーしました