AWSリソースのデプロイ(CircleCI + CloudFormation + cfn-lint + awspec)
概要
GitHubのtestブランチへのコミットを契機にcfn-lintでCFnテンプレートの静的解析を実施します。
testブランチでの静的解析に成功したら、testブランチの変更をmainブランチにプルリクエストし、mainブランチにマージされたらcfn-lint → CFnのデプロイ → awspecでの単体テストを実施します。
認証には以下の記事で準備したOIDCを使用します。
IAMロールには上記の記事から以下の権限を追加しています。
- arn:aws:iam::aws:policy/AWSCloudFormationFullAccess
- arn:aws:iam::aws:policy/AmazonVPCFullAccess
ファイル構成
% tree -aFI .git
./
├── .circleci/
│ └── config.yml
├── CFn/
│ ├── Oidc.yml
│ └── Vpc.yml
└── awspec/
├── .rspec
├── Rakefile
└── spec/
├── .gitignore
├── spec_helper.rb
└── vpc_spec.rb
5 directories, 8 files
準備
CFnテンプレートの準備
cfn-lintとawspecの動作確認をしたいだけなので、VPCを作るだけのCFn/Vpc.ymlを用意しました。
AWSTemplateFormatVersion: 2010-09-09
Resources:
TestVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: TestVPC
.circleci/config.ymlの準備
config.ymlは長いので下に折りたたんでいます。
config.yml
version: 2.1
orbs:
aws-cli: circleci/aws-cli@4.0.0
jobs:
cfnlint:
docker:
- image: cimg/python:3.11.4
steps:
- checkout
- run:
name: Install cfn-lint
command: pip3 install cfn-lint==0.79.7
- run:
name: Execute cfn-lint
command: cfn-lint ./CFn/Vpc.yml
vpcstack:
executor: aws-cli/default
steps:
- checkout
- aws-cli/install
- aws-cli/assume_role_with_web_identity:
role_arn: $AWS_OIDC_ROLE_ARN
- run:
name: Deploy VPC
command: aws cloudformation deploy --template-file ./CFn/Vpc.yml --stack-name VpcStack --no-fail-on-empty-changeset --region ap-northeast-1
awspec:
docker:
- image: cimg/ruby:3.1.4
steps:
- checkout
- aws-cli/install
- aws-cli/assume_role_with_web_identity:
role_arn: $AWS_OIDC_ROLE_ARN
- run:
name: Install awspec
command: gem install awspec -v 1.29.2
- run:
name: execute awspec
command: |
export AWS_REGION=ap-northeast-1
cd ./awspec
rake spec
workflows:
lint:
jobs:
- cfnlint:
filters:
branches:
only:
- test
lint_deploy_test:
jobs:
- cfnlint:
filters:
branches:
only:
- main
- vpcstack:
requires:
- cfnlint
filters:
branches:
only:
- main
- awspec:
requires:
- vpcstack
filters:
branches:
only:
- main
awspecを実行するDockerイメージにはcimg/ruby:3.1.4を使用しています。
最初は3.2.2を使用しましたが、以下のエラーが発生したため3.1.4に変更しています。
Rubyの3.2からexistsがなくなったのが原因のようです。
NoMethodError:
undefined method `exists?' for File:Class
awspec:
docker:
- image: cimg/ruby:3.1.4
awspec実行時に実行リージョンの指定が必要だったため、AWS_REGION環境変数を指定しています。
- run:
name: execute awspec
command: |
export AWS_REGION=ap-northeast-1
cd ./awspec
rake spec
awspecの準備
awspec/spec/spec_helper.rbの編集
awspec initコマンドで作成されたspec_helper.rbは認証情報が書かれたファイルを検索します。
OIDC認証を使用する場合はファイルが存在せずエラーとなるので、Awsecrets.loadをコメントアウトしました。
require 'awspec'
#Awsecrets.load(secrets_path: File.expand_path('./secrets.yml', File.dirname(__FILE__)))
awspec/spec/vpc_spec.rbの作成
動作確認が目的なので、以下のコマンドでAWSに構築済みのVPCからvpc_spec.rbを作成しました。
$ awspec generate vpc vpc-052218b0ffdddd2c0
作成されたvpc_spec.rbはdescribe以下の行しか出力されないので、require 'spec_helper'をファイルの頭に追記しています。
require 'spec_helper'
describe vpc('TestVPC') do
it { should exist }
it { should be_available }
its(:vpc_id) { should eq 'vpc-052218b0ffdddd2c0' }
its(:cidr_block) { should eq '10.0.0.0/16' }
it { should have_route_table('rtb-062c5ebc2e422c701') }
it { should have_network_acl('acl-0da41742791c315c8') }
it { should have_vpc_attribute('enableDnsHostnames') }
it { should have_vpc_attribute('enableDnsSupport') }
end
動作確認
testブランチへのコミット
testブランチにコミットし、lintワークフローのcfnlintジョブが正常に終了することを確認できました。
mainブランチへのマージ
mainブランチでプルリクエストをマージし、lint_deploy_testワークフローが実行されました。
vpcstackジョブが25秒で終わっているのは、VPCを事前に構築済みのためです。
awspecも動作することを確認できました。
(実環境からテストを作成しているので成功するのは当然ですが。。。)
参考
Discussion