現存する環境から複製するために部分的に書けたテンプレートを起こすというようなタスクがアレで個人的な覚書を。
・なんでこれで?
→ほかのあらゆるInfrastracture as a codeと同様に見える化と省力化とミス防止などいろいろメリットがある。当初の担当者が既にいないがブラックボックスな状況を多少は防げると思われるしわかる人どうしなら引き継ぎがしやすい。
強いて言うとクラウドプロバイダ謹製だから新サービス対応が早い
わかる人がみるとエクセル資料などをあさって確認せずとも環境を確認・俯瞰しやすいリアルな資料がわりに。
乖離してなければ。
・Terraformとなにが違うか
テスト結果の確認しやすさはTerraformが上かもしれないけども、CFでも変更検知は可能。
Terraformもマルチクラウドでできるといってもそれぞれのクラウドのことを知らずに使えるわけでもなく、
リソースの書き方はクラウドによって独特な感じなので、使う人々が使いやすい広めやすい方でよくてケースバイケースかな。
・一度始めたら手動対応するとめんどくさいことになる件
乖離してるときは手で変えた環境を手で戻してドリフトステータス再度確認する感じ。
そうしないとスタックが消せなくなる。スタックが消せないと中途半端に残った同名のリソースを作り直すとかできないことに。
作りかけとか消すのに失敗してるステータスだと問い合わせるかだいぶ待つかしないとロールバックしてくれなかったりも。
まあそれは他のツールでもあまり変わらないけどTerraformだとimportとかできるのはいい。それが良し悪しという意見もありそうだけど。
・動的パラメータが可能な種類が限られる件
ざっくりいうとRDBのパスワードは大体いけたりするがそれ以外は結構無理だったりなど。
パラメータストアとセキュリティマネージャは結構違い、どっちかにすぐできるという感じでもなくて
コードから変えないといけなかったりするため既存をある程度踏襲したいという場合には不向き。
これは書けるが
DBMasterUserPassword:
Description: from system manater parameter.
Default: "{{resolve:ssm-secure:stg-admin-DB_PASSWORD:1}}"
Type: String
NoEcho: true
これは無理だった。webhookのトークンとかも。
DatadogApikey:
Description: "datadog apikey from system manager parameterstore(manual, its support not yet)"
# Default: "{{resolve:ssm-secure:datadog_APIKEY:1}}"
Type: String
NoEcho: true
・アカウントIDやらリージョンやらはいきなり使える
Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/kinesisfirehose/${KinesisFirehoseName}:log-stream:*'
・パラメータからリストで取り出して番号指定して使う
何度もテストするのに既存にあるものから選ぶほうが運用しやすくてたぶん便利という動機からこんなかんじに。
Parameters:
EfsSecurityGroup:
Description: 'select sg-efs-xxx'
Type: "List<AWS::EC2::SecurityGroup::Id>"
Subnets:
Type: "List<AWS::EC2::Subnet::Id>"
Description: "The list of SubnetIds in your Virtual Private Cloud (VPC)"
Resources:
FileSystem:
Type: AWS::EFS::FileSystem
Properties:
PerformanceMode: generalPurpose
FileSystemTags:
- Key: Name
Value: efs-xxxx-stg
Encrypted: false
MountTarget1:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId:
Ref: "FileSystem"
SubnetId: !Select [0, !Ref Subnets]
SecurityGroups: !Ref EfsSecurityGroup
MountTarget2:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId:
Ref: "FileSystem"
SubnetId: !Select [1, !Ref Subnets]
SecurityGroups: !Ref EfsSecurityGroup
・文字列を結合して値とするにはSubとJoin
Policies:
- PolicyName: KinesisFirehosePolice
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/kinesisfirehose/${KinesisFirehoseName}:log-stream:*'
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
ResourceId: !Join ['', [service/, !Ref 'ECSCluster', /, !GetAtt [service, Name]]]
・パラメータはデフォルト値を定義したりDescriptionに書くと便利
固定の値ならデフォルト値書いとくと指定しなくていいので楽。TypeとDescriptionが必須。
InstanceType:
Description: "(Required)"
Default: "t3a.medium"
Type: "String"
パスワード的なものはNoEchoで定義したやつを見られないようにするのが定石っぽい
・OutputsとImportValueでテンプレート間のリレーション
OutPutしてImportすると親子的なリレーション依存関係のあるテンプレを作れる
便利なケースもそれだと取り回しづらいというケースもある
Outputs:
S3BucketName:
Description: Bucket used for CodePipeline temp files
Value: !Ref S3Bucket
Export:
Name: !Sub "${AWS::StackName}-S3BucketName"
CodePipeline:
Type: "AWS::CodePipeline::Pipeline"
DependsOn:
- CodeBuild
- CodePipelineRole
Properties:
Name: !Ref AWS::StackName
RoleArn: !GetAtt [ CodePipelineRole, Arn ]
ArtifactStore:
Location: !ImportValue pipeline-dependencies-stg-S3BucketName
Type: S3
・既存のリソースを使うときはARNを指定する書き方だと消して作り直されることはない
気を付ける点として、テンプレートとその設定した環境が乖離した状況になると
環境を手で戻して再度確認するなどの手順を踏まないと更新や削除が一切できなくなる。
なので、自由奔放な運用方法はできない。手動対応を規制する必要がある。
Parameterとして指定することで厳密に管理され固定的になるのを防ぐという手もなくはない。
ただパラメータにしてしまうと動的にとか複数環境に対応できるように抽象的にするということは難しいような気がする。たぶん。
・mappingで複数環境対応もできる
が、これも運用しやすいかどうかは対応する人によるので、誰がどうするかとか考えて書いたほうがいいかも
Mappings:
RDS:
stg:
ClusterName: rds-xxxxx-dbcluster
ClusterNameShort: rds-xxxxx
Instance1Name: rds-xxxxx-a
Instance2Name: rds-xxxxx-c
InstanceType: db.t2.medium
BackupRetentionPeriod: 5
PreferredBackupWindow: "15:07-15:37"
PreferredMaintenanceWindow: "sun:23:00-sun:23:30"
AvailabilityZone1: "ap-northeast-1a"
AvailabilityZone2: "ap-northeast-1c"
MonitoringInterval: 60
~~~
DBCluster:
Type: "AWS::RDS::DBCluster"
DeletionPolicy: Snapshot
Properties:
AvailabilityZones:
- !FindInMap [RDS, !Ref "EnvName", "AvailabilityZone1"]
- !FindInMap [RDS, !Ref "EnvName", "AvailabilityZone2"]
BackupRetentionPeriod: !FindInMap [RDS, !Ref "EnvName", "BackupRetentionPeriod"]
・Conditionで条件分岐する
あんまやりすぎると複雑になるので必要な分だけでいいかなあというか。
以下は、パラメータにスナップショットが指定さなかった場合に新規作成アトリビュートができて、
スナップショットは空に、ユーザとパスワードが設定されるがスナップショットがあったらそれが設定されて
ユーザとパスは無視される的な見本。
Conditions:
isBrandNewDB: !Equals [ !Ref DBSnapshotArn, "" ]
~~~
SnapshotIdentifier:
!If [isBrandNewDB, !Ref "AWS::NoValue", !Ref "DBSnapshotArn" ]
~~~
MasterUsername:
!If [isBrandNewDB, !Ref "DBMasterUserName", !Ref "AWS::NoValue" ]
MasterUserPassword:
!If [isBrandNewDB, !Ref "DBMasterUserPassword", !Ref "AWS::NoValue" ]
・対応しているリソースやロードマップを確認できる
リソースのTypeで指定できるかどうかという話を↑でみられる
こういうの↓
Type: "AWS::AutoScaling::LaunchConfiguration"
マニュアル検索するときに↑のTypeで検索する
・一回ブラックベルトよむとだいぶわかった気にはなりそう
・Jsonだったらデザイナーでyamlに置換しとくと楽
yamlの可読性がjsonに比べたら大変すばらしいし置換はデザイナーでできる。
・同様のツール
GCPだとCloud Deployment Manager、AzureだとAzure Resource Managerなど。(つかったことはない。使えと強制されたことがないのが大きいと思う。)