はじめに
CI/CDの勉強を行うにあたって、以下のハンズオンを行いました。
ハンズオンではコンソール操作で作成していましたが、CFnで作成してみようと思いました。
ハンズオンは以下の二つがあります。
- S3 をデプロイ先とした CI/CD 環境の構築【AWS CodeCommit, AWS CodePipeline を利用】
- EC2 インスタンスをデプロイ先とした CI/CD 環境の構築【AWS CodeCommit, AWS CodeBuild, AWS CodeDeploy, AWS CodePipeline を利用】
今回CFnで作成した対象はS3 をデプロイ先とした CI/CD 環境の構築【AWS CodeCommit, AWS CodePipeline を利用】のみです。
結果として、見えないところで色々なリソースが作られていることがわかりました。
参考
構成図
思っていたの
公式では以下の図を用いていました。(上の方のパイプラインになります)
実際
さらに
ハンズオン(コンソールの操作)で作成されるパイプラインは、以下の様になるようです。
やったこと
環境
コンソールからの操作のみになります。
事前準備
初期ソースの用意
- 任意のS3にZip圧縮したソースをアップ
- CodeCommit作成時に、ブランチ名とS3のオブジェクトを指定しておくと、そのファイルがコミットされた状態で作成されます。
対象のソース
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>S3 Static Web Hosting</title>
</head>
<body>
Hello, AWS World!!
</body>
</html>
バケットの用意
任意のバケットでよいです。
Zip圧縮したHTMLファイルのアップロード
HTMLファイルをZipで固めて、先のバケットにアップロードしておきます。
パイプラインの作成(CodeCommitの変更を直接検知)
以下のCFnを実行するだけです。
クリックで表示
AWSTemplateFormatVersion: 2010-09-09
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Parameters:
- RepositoryName
- TargetBrranch
- MySourceCodeBucket
- MyKey
- AllowedIps
- PipelineName
Parameters:
RepositoryName:
Type: String
Default: "repo20220926"
TargetBrranch:
Type: String
Default: "main"
MySourceCodeBucket:
Type: String
MyKey:
Type: String
Default: "index.zip"
AllowedIps:
Description: IP list to allow access.
Type: CommaDelimitedList
Default: "1.1.1.1/32, 2.2.2.2/32"
PipelineName:
Type: String
Default: "pipeline20220926"
Resources:
TargetRepository:
Type: AWS::CodeCommit::Repository
Properties:
RepositoryName: !Ref RepositoryName
Code:
BranchName: !Ref TargetBrranch
S3:
Bucket: !Ref MySourceCodeBucket
Key: !Ref MyKey
TargetBucket:
Type: AWS::S3::Bucket
Properties:
WebsiteConfiguration:
IndexDocument: index.html
TargetBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref TargetBucket
PolicyDocument:
Statement:
- Effect: Allow
Principal: '*'
Action:
- 's3:GetObject'
Resource: !Sub arn:aws:s3:::${TargetBucket}/*
Condition:
IpAddress:
'aws:SourceIp': !Ref AllowedIps
# CodePipeline resoure
## Bucket for Artifactstore
ArtifactstoreBucket:
Type: AWS::S3::Bucket
Properties:
PublicAccessBlockConfiguration:
BlockPublicAcls: TRUE
BlockPublicPolicy: TRUE
IgnorePublicAcls: TRUE
RestrictPublicBuckets: TRUE
## IAM ROLE FOR Codepipeline
PipelineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- codepipeline.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: !Sub AWSCodePipelineServiceRole-${AWS::Region}-${PipelineName}
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:*Object
- codecommit:UploadArchive
- codecommit:GetCommit
- codecommit:GetUploadArchiveStatus
- codecommit:GetRepository
- codecommit:GetBranch
- codecommit:CancelUploadArchive
Resource:
- !GetAtt TargetRepository.Arn
- !Sub arn:aws:s3:::${TargetBucket}/*
- !Sub arn:aws:s3:::${ArtifactstoreBucket}/*
## Codepipeline
ContentsPipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
RoleArn: !GetAtt PipelineRole.Arn
ArtifactStore:
Type: S3
Location: !Ref ArtifactstoreBucket
Stages:
- Name: Source
Actions:
- Name: Source
ActionTypeId:
Category: Source
Owner: AWS
Version: 1
Provider: CodeCommit
Configuration:
RepositoryName: !Ref RepositoryName
BranchName: !Ref TargetBrranch
PollForSourceChanges: true
RunOrder: 1
OutputArtifacts:
- Name: SourceArtifact
- Name: Deploy
Actions:
- Name: Deploy
ActionTypeId:
Category: Deploy
Owner: AWS
Version: 1
Provider: S3
InputArtifacts:
- Name: SourceArtifact
Configuration:
BucketName: !Ref TargetBucket
Extract: true
RunOrder: 1
Outputs:
TargetURL:
Value: !Sub http://${TargetBucket}.s3-website-${AWS::Region}.amazonaws.com
パラメータとして以下を指定します。
- 作成するCodeCommitのリポジトリ名
- 対象のブランチ
- 最初にPUSHする(Zip圧縮された)コードファイルが置いてあるバケット名
- ファイルのパス(Key)
- 静的ホスティングサイトとして許可するIP群
- パイプラインの名称
解説
コードパイプラインのところに癖があったので説明します
パイプライン起動のトリガー
Configuration:
RepositoryName: !Ref RepositoryName
BranchName: !Ref TargetBrranch
PollForSourceChanges: true
このPollForSourceChanges
は以下の様に動作する模様です。
- true:対象のブランチにPUSHしたら、パイプラインが起動
- false:PUSHしても、起動しない
- 別途、EventBridge等から起動させる場合に使う模様
- ハンズオンにて作成される(=コンソールで作成する)環境は、falseになっており、EventBridgeから起動している
- 別途、EventBridge等から起動させる場合に使う模様
詳細は公式にあります。(Default settings for the PollForSourceChanges parameterの箇所です)
S3にデプロイする際に、展開する指定
Configuration:
BucketName: !Ref TargetBucket
Extract: true
このExtract
はfalseだと圧縮されたままアップロードされるようです。詳細は公式に。
ページ確認
CloudFormationのOutputsにURLを出力するよう設定したので、CFnの出力タブにあるURLで確認できます。
編集
CodeCommitであれば、コンソールから編集できます。
リポジトリから、index.htmlを選ぶと、編集できます。
以下の様に変更すると、パイプラインが動き、ページに反映されます。
実行履歴を見ると、ソースの変更がトリガーになっていることがわかります。
パイプラインの作成(CodeCommitの変更をEventBridgeが検知)
上記を作成した際に、ハンズオンで作成した環境と異なることに気が付きました。
そのためそれらを比較したところ、EventBridgeがトリガーとなり動いており、そちらもCFnで作成してみました。
クリックで表示
AWSTemplateFormatVersion: 2010-09-09
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Parameters:
- RepositoryName
- TargetBrranch
- MySourceCodeBucket
- MyKey
- AllowedIps
- PipelineName
Parameters:
RepositoryName:
Type: String
Default: "repo20220928"
TargetBrranch:
Type: String
Default: "main"
MySourceCodeBucket:
Type: String
MyKey:
Type: String
Default: "index.zip"
AllowedIps:
Description: IP list to allow access.
Type: CommaDelimitedList
Default: "1.1.1.1/32, 2.2.2.2/32"
PipelineName:
Type: String
Default: "hoge20220928"
Resources:
TargetRepository:
Type: AWS::CodeCommit::Repository
Properties:
RepositoryName: !Ref RepositoryName
Code:
BranchName: !Ref TargetBrranch
S3:
Bucket: !Ref MySourceCodeBucket
Key: !Ref MyKey
TargetBucket:
Type: AWS::S3::Bucket
Properties:
WebsiteConfiguration:
IndexDocument: index.html
TargetBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref TargetBucket
PolicyDocument:
Statement:
- Effect: Allow
Principal: '*'
Action:
- 's3:GetObject'
Resource: !Sub arn:aws:s3:::${TargetBucket}/*
Condition:
IpAddress:
'aws:SourceIp': !Ref AllowedIps
# CodePipeline resoure
## Bucket for Artifactstore
ArtifactstoreBucket:
Type: AWS::S3::Bucket
Properties:
PublicAccessBlockConfiguration:
BlockPublicAcls: TRUE
BlockPublicPolicy: TRUE
IgnorePublicAcls: TRUE
RestrictPublicBuckets: TRUE
## IAM ROLE FOR Codepipeline
PipelineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- codepipeline.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: !Sub AWSCodePipelineServiceRole-${AWS::Region}-${PipelineName}
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:*Object
- codecommit:UploadArchive
- codecommit:GetCommit
- codecommit:GetUploadArchiveStatus
- codecommit:GetRepository
- codecommit:GetBranch
- codecommit:CancelUploadArchive
Resource:
- !GetAtt TargetRepository.Arn
- !Sub arn:aws:s3:::${TargetBucket}/*
- !Sub arn:aws:s3:::${ArtifactstoreBucket}/*
## Codepipeline
ContentsPipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
RoleArn: !GetAtt PipelineRole.Arn
ArtifactStore:
Type: S3
Location: !Ref ArtifactstoreBucket
Stages:
- Name: Source
Actions:
- Name: Source
ActionTypeId:
Category: Source
Owner: AWS
Version: 1
Provider: CodeCommit
Configuration:
RepositoryName: !Ref RepositoryName
BranchName: !Ref TargetBrranch
PollForSourceChanges: false
RunOrder: 1
OutputArtifacts:
- Name: SourceArtifact
- Name: Deploy
Actions:
- Name: Deploy
ActionTypeId:
Category: Deploy
Owner: AWS
Version: 1
Provider: S3
InputArtifacts:
- Name: SourceArtifact
Configuration:
BucketName: !Ref TargetBucket
Extract: true
RunOrder: 1
## IAM ROLE FOR EventBridge
EventBridgeRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: !Sub AWSCodePipelineServiceRole-${AWS::Region}-${PipelineName}
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- codepipeline:StartPipelineExecution
Resource:
- !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${ContentsPipeline}
EventRuleCodeCommit:
Type: AWS::Events::Rule
Properties:
EventBusName: default
State: ENABLED
EventPattern:
source:
- aws.codecommit
detail-type:
- "CodeCommit Repository State Change"
resources:
- !GetAtt TargetRepository.Arn
detail:
event:
- "referenceCreated"
- "referenceUpdated"
referenceType:
- "branch"
referenceName:
- !Ref TargetBrranch
Targets:
- Arn: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${ContentsPipeline}
Id: !Ref AWS::StackName
RoleArn: !GetAtt EventBridgeRole.Arn
Outputs:
TargetURL:
Value: !Sub http://${TargetBucket}.s3-website-${AWS::Region}.amazonaws.com
パラメータは、先のものと一緒です。
違いは、パイプライン起動のトリガーが、CodePipeline内では無効化されている点です。
Configuration:
RepositoryName: !Ref RepositoryName
BranchName: !Ref TargetBrranch
- PollForSourceChanges: true
+ PollForSourceChanges: false
EventBridgeについては、以下の公式等を参考にしました。
先程と同様に、コードを変更させると反映されます。
履歴にはCloudWatchEvent(EventBridgeの昔の名前?)で動いたことがわかります。
片づけ
両方ともCFnで作成したので、削除するのは簡単です。
- バケットを空にする。
- 公開用バケット
- Artifact用バケット
- 対象のスタックを削除する。
おわりに
最も簡単(と思われる)なCI/CDパイプライン環境をCFnから作成してみました。
CI/CDの超基礎を勉強しつつ、同時にコンソールがどれだけ良しなに構築しているのか、大変参考になりました。
実業務で使うには簡単すぎる構成なので、もう少し複雑な環境も勉強していけたらと考えています。