概要
CloudFormationよりTerraform派なので、理解しているようできちんと理解していなかったCloudFormationについて、最近ServerlessFrameworkの利用機会が増えてきて(ServerlessFrameworkはCloudFormationがベースとなっているので)、改めて勉強したので、忘れないようにアウトプットしていく。
参考にした資料:
- 【AWS Black Belt Online Seminar】AWS CloudFormation
- 【AWS Hands-on for Beginners】AWS 環境のコード管理 AWS CloudFormationで Web システムを構築する
CloudFormationとは
AWS公式の構成管理ツール(AWSサービス)でAWSにおけるIaCの手段の一つ。
クラウドインフラのIaCツールは他にもあるが、CloudFormation利用のメリットとして、以下がある。
- AWSサービスのため、ポリシーによる細かい制御やOrganizationsによる複数アカウントの一元管理が可能
- AWSサービスのため、利用している組織が多い(チームや企業が変わっても変わらず利用できる可能性が高い)
- SAMやServerlessFrameworkのベースになっているため、これらに応用できる。
AWSリソースの定義をJSONやYAML(JSONはYAMLのサブセットなので当たり前だが)で記述しておき、構成の変更や追加、さらに異なるリージョンやアカウントでの再構築を容易にできる。
CloudFormation利用の流れ
定義ファイルを記述し、S3に配置。
その定義ファイルを元に実際にリソースの作成や変更を実行という流れ。
もちろんこれらの操作(アップロード以降)はCLIでもマネジメントコンソールでも実施できる。
テンプレートとスタック
CloudFormationの理解に欠かせない2つの要素が「テンプレート」と「スタック」
これらについては少し深く書いていく。
テンプレート
CloudFormationの管理対象とするAWSリソースの定義。
YAML/JSONで記述したテキストドキュメント。
テンプレートに記載する主な要素
その他にもあるが基本はこのあたり。
その他の要素については下記を参照のこと。
公式リファレンス:https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-reference.html
Resources
管理対象とするAWSリソースの情報を記述する。
リソースタイプごとに必須とオプションのプロパティがあるので、指定可能なリソースタイプやリソースごとのプロパティについては公式リファレンスを見ながら、必要なものを記述していく。
Terraformのresource
と似ているが、こちらはResources
属性の中にまとめて記述する。
AWSTemplateFormatVersion & Description
AWSTemplateFormatVersion
: テンプレートの構文バージョン。IAMポリシーのVersion
のようなもの、現状一度もアップデートされていないので2010-09-09
しか存在しない。指定無しでもデフォルトで最新バージョンとなるが、今後バージョンが上がって互換性がなくなる可能性も考え、ときのために書いておいたほうがいい。(とBlackBeltセミナーで言っていた)
Description
: このテンプレートの対象が何なのか説明を書くことができる。動作に影響はなく、必須ではないが、保守観点で書いておいたほうがいい。
Parameters
スタック構築時にユーザに指定させる値を定義。デフォルト値やデータ型(String
やList
など)の指定が必須。
正規表現や長さなどを指定し、入力値のチェックをさせることもできる。
Terraformのvariable
に似ているが、こちらはParameters
属性の中にまとめて記述する。
Outputs
スタック構築後に表示させる情報、自動で付与されたインスタンスIDなど表示させて、後で利用したりできる。
Terraformのoutput
に似ているが、こちらはOutputs
属性の中にまとめて記述する。
Conditions
環境ごとなどで異なる定義でリソースを構築したい時に利用する。
下記例の場合、パラメータの値により構築するインスタンスタイプを切り替えている。
Env
パラメータにprod
が指定された場合c1.xlarge
、dev
の場合m1.large
、その他の場合m1.small
となる。
↓の記述例では組み込み関数も出てくるので、後述の組み込み関数と合わせて読むといい。
Parameters:
EnvType:
Description: Environment type.
Default: test
Type: String
AllowedValues: [prod, dev, test]
Conditions:
CreateProdResources: !Equals [!Ref EnvType, "prod"]
CreateDevResources: !Equals [!Ref EnvType, "dev"]
Resources:
EC2Instance:
Type: "AWS::EC2::Instance"
Properties:
ImageId: "ami-06cd52961ce9f0d85"
InstanceType: !If [CreateProdResources, c1.xlarge, !If [CreateDevResources, m1.large, m1.small]]
MountPoint:
Type: "AWS::EC2::VolumeAttachment"
Condition: CreateProdResources
Properties:
InstanceId: !Ref EC2Instance
VolumeId: !Ref NewVolume
Device: /dev/sdh
NewVolume:
Type: "AWS::EC2::Volume"
Condition: CreateProdResources
Properties:
Size: 100
AvailabilityZone: !GetAtt EC2Instance.AvailabilityZone
組み込み関数
テンプレート内では事前定義されたいくつかの組み込み関数を利用することができる。
詳細は下記公式リファレンスを参照のこと。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html
Fn
Fn::~
の利用例
組み込み関数を利用することで、リソースの名前やOutputs
で出力させる文字列を動的に生成できる。
基本は属性名
が関数名
で値
として引数
を指定する。引数が複数の場合配列
で指定する。
短縮形の書き方もあり、Fn::
の代わりに!
を書き、関数名
のあとにスペースを挟んで引数
を指定する。
短縮形の場合も引数が複数の場合、配列
で指定する。
基本は短縮形だけ覚えておけば困らなそう。
例えばFn::Sub
は単純に文字列の中に変数を展開する関数。
下記の例ではParameters
で受け取ったEnv
文字列とCloudFormationを実行するリージョンに応じたバケット名をつけている。
Parameters:
Env:
Type: String
Resources:
DataBucket:
DeletionPolicy: Retain
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::Sub:
src-${AWS::Region}-${Env}
↑のProperties
以下を短縮形にすると↓になる。
Properties:
BucketName: !Sub src-${AWS::Region}-${Env}
もう一つの例としてGetAtt
はResources
で定義したAWSリソースから様々な属性値を取得できる。
下記例の場合、myELB
のソースセキュリティグループ名を取得しています。
Resources:
myELB:
Type: AWS::ElasticLoadBalancing::LoadBalancer
Properties:
AvailabilityZones:
- eu-west-1a
Listeners:
- LoadBalancerPort: '80'
InstancePort: '80'
Protocol: HTTP
myELBIngressGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ELB ingress group
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupName:
GetAtt:
- myELB
- SourceSecurityGroup.GroupName
↑のSourceSecurityGroupName
以下を短縮すると↓になる。
SourceSecurityGroupName: !GetAtt myELB.SourceSecurityGroup.GroupName
その他にも配列から添え字で値を取り出したり、文字列を結合したりできる。(配列から値を取り出すぐらい関数とか使わずかけるようにしてほしいが、しょうがない。)
Fn::Sub
なんかもTerraformなら関数無しで変数展開してくれるのに、と思ってしまった。
Ref
パラメータまたはリソースの論理名(Resource定義の初めに書く名前)からID(インスタンスIDなど)を取得できる
MyEIP:
Type: "AWS::EC2::EIP"
Properties:
InstanceId:
Ref:
MyEC2Instance
↓こちらも短縮できる
MyEIP:
Type: "AWS::EC2::EIP"
Properties:
InstanceId: !Ref MyEC2Instance
Ref
とFn
を組み合わせる事もできる
↓アカウントIDを取得してIAMロールのARNの文字列を作る例
Fn::Join:
- :
-
- arn:aws:iam:
- !Ref AWS::AccountId
- role/xxxRole
↓短縮
!Join
- :
-
- arn:aws:iam:
- !Ref AWS::AccountId
- role/xxxRole
↓ワンライン
!Join [:, [arn:aws:iam:, !Ref AWS::AccountId, role/xxxRole]]
疑似パラメータ
テンプレート内では事前定義されたいくつかの変数を利用することができる。
リージョンやアカウントIDなどにこの変数を指定することで複数リージョン間や複数アカウント間でのテンプレートの共有、共通環境の構築ができる。
詳細は下記公式リファレンスを参照のこと。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/pseudo-parameter-reference.html
Outputs:
MyStacksRegion:
Value: !Ref "AWS::Region"
MyStackAccount:
Value: !Ref "AWS::AccountId"
Dynamic References
環境ごとに異なるが複数のサービスから共通で使うような変数や、パスワードなどの定義ファイルに記載したくない値はSSMやシークレットマネージャーに登録しておき、CloudFormationの実行時に取得させることができる。
例えば、下記の例ではRDSのユーザ名やユーザパスワードなどのセキュアな情報はテンプレートに直接記載したくないので、シークレットマネージャーに登録しておきDynamic References
で動的に設定している。
MyRDSInstance:
Type: 'AWS::RDS::DBInstance'
Properties:
DBName: MyRDSInstance
AllocatedStorage: '20'
DBInstanceClass: db.t2.micro
Engine: mysql
MasterUsername: '{{resolve:secretsmanager:MyRDSSecret:SecretString:username}}'
MasterUserPassword: '{{resolve:secretsmanager:MyRDSSecret:SecretString:password}}'
スタック
CloudFormationでの管理単位、同一スタック内での構築順序はスタック内のリソース同士の依存関係から自動的に決定される。
Terraformで言うとディレクトリやworkspace
の単位にあたる。
リソースのインポート
いままでCloudFormationなど利用せずに手動や他のツールで構築したAWSリソースがあり、これからCloudFormationの管理対象にしたい場合はインポートすることもできる。
スタック分割について
AWSリソース管理単位の分割については、はじめにある程度うまく分割しておかないと、後々困る。
とはいえ、分割したリソース間でも参照したい場合があるので、これを実現する「クロススタック参照」という機能もある。
スタック分割については最初に紹介した資料(【AWS Black Belt Online Seminar】AWS CloudFormation)でさらに詳しく説明されているので是非視聴してもらいたい。
スタックセット
CloudFormationではテンプレートを使用して、複数のリージョンのAWSアカウントにスタックを作成できる。
この機能をスタックセットという。
同一Organizations内のアカウントでない場合、対象の各アカウントからスタック作成を許可するIAMロールを引き受ける必要がある。
同一のOrganizations内で複数アカウントに対してリソースを作成する場合、IAMロールは必要ない。
参考:https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/stacksets-concepts.html
スタックとリソースの保護
作成後のスタックやリソースを変更や削除から保護する方法は複数ある。
スタックポリシー
スタック作成時や作成後にスタック単位で変更/削除に対するポリシーを付与できる。
(作成後はCLIでしかポリシーの付与ができない。)
DeletionPolicy
スタックの削除時に一部リソースのみ保持させたい場合や、バックアップを取得させたい場合に利用する。