AWSで最初からCloudFormationでインフラを管理する方法を考えてみる

AWSで最初からCloudFormationを使ってインフラを管理していく方法について考えてみました。

個人でサービスを作ろうと思い、あまりお金もかけたくなかったのでサービス選定は

サービス名
クラウドサービス AWS
ソースコード管理 Bitbucket
CI/CD CircleCI

といった感じです。

参考にしたサイトです。

dev.classmethod.jp github.com

CloudFormation と CI の組み合わせをする上で必要な物

今回だとCircleCIからAWSへアクセスする為のIAMユーザーが必要になります。
また、CloudFormationのテンプレートを分割して管理する場合は、S3 Bucketも必要になります。

導入手順

先に今回の内容をコード化したものがコチラです。 github.com

導入手順としては

  1. 全サービスにアクセスできるIAMユーザーを作る
  2. 自分が作成したいサービス向けのIAMユーザーを作る

という流れにしようと思いました。

悩ましいのが、どうしても最初はWebのAWSコンソールからIAMユーザーとS3 bucketの作成が必要になることでした。

いろいろ考えて納得できる方法としては

  1. 手作業でAWSコンソールのCloudFormationから setup/cfn.template.yml を指定して awscli 用のIAMとCloudFormationのテンプレートを一時的に置くS3 bucket を作成します。
  2. 作成されたIAMユーザーのkey, secretを作成してAWS CLIにアカウントをセットアップします。
  3. AWS CLI でcloudformationのスタックを作成して新規作成するサービス向けのインフラを構築するための準備をします。

という手順です。

  1. の作成に使うテンプレートだけは別管理にした方がわかりやすいかなと思いました。

CloudFormationのスタックが2つに分かれてしまいますが、管理しやすくて良いかなと思いました。

  • スタック1(setupディレクトリ配下)
    • awscli用のIAMユーザーとテンプレートなどを保存するためのs3 bucketを管理するテンプレート
  • スタック2(srcディレクトリ配下)
    • 各サービスを管理するためのユーザーを管理するテンプレート

の形にCloudFormationのスタックを分けることでができるので良いかなと思いました。

理想としては1つのスタックで完結させたいです。
ディレクトリツリーで見るとこんな感じです。
要は全てのIAMを src/iam.template.yml で管理したいです。

├── src
│   ├── bucket.template.yml
│   ├── cfn.template.yml
│   └── iam.template.yml

ですが、 最初はWebのAWSコンソールからIAMユーザーとS3 bucketの作成 をしないといけないので

├── setup
│   └── cfn.template.yml
├── src
│   ├── bucket.template.yml
│   ├── cfn.template.yml
│   └── iam.template.yml

と分け

├── setup
│   └── cfn.template.yml

ここで、最初のユーザーを管理します。

AWS CLI からのスタック作成について

デプロイするテンプレートは管理しやすくするために分割してます。

├── src
│   ├── cfn.template.yml
│   └── iam.template.yml

実際のところは下の様に実行すればCloudFormationで実行可能なテンプレートを作成することができる。

$ aws cloudformation package \
    --template-file src/cfn.template.yml \
    --s3-bucket artifact \
    --output-template-file .cfn/packaged.yml \
    --s3-prefix template

このコマンドでしていることは、 --s3-bucket で指定したバケット--s3-prefix で指定したディレクトリ配下に src/cfn.template.yml 以外のテンプレートファイルをアップロードします。 --output-template-file .cfn/packaged.yml で指定している部分に出力されるファイルは、 src/cfn.template.yml の参照している他テンプレートファイルを指定するパスが追加されています。

TemplateURL: /path/to/bucket/fc2e4965584e9309b667bf9a6c1eb6f4.template

の様な物が追加されています。

.cfn/packaged.yml が作成されたら、create-stackupdate-stack を使って更新していきます。

ここまでやるとiam.template.ymlで作成したIAMユーザーをCircleCIに設定することで自動更新ができるようになります。

あとは、新しく作りたいサービスがでたらそのサービス向けに権限を絞ったIAMユーザーを作ることができます。 そうすることで、CIなどを使った際に誤操作で別サービスのデータを消してしまうなどが防げるのではないかと思いました。

課題

今回の構成で自動デプロイできるようにはなってますが、課題もあります。

  • update-stack 実行後のCloudFormationの状態を監視していないのでCircleCIでは Success になるが CloudFormation では失敗してロールバックする可能性がある
  • 連続でデプロイしたとき、現在のスタックの状態を確認していないため、スタックが更新中の場合不具合が起きる可能性がある

備考

テンプレートの検証に cfn-lint を使っていて気づいたけど、 Type: AWS::CloudFormation::Stack を使ってテンプレートを分けた場合は、子テンプレートまでチェックしてくれるわけではないみたい。 あくまで引数に渡したテンプレートファイル単体のテストをしてくれるツールみたい