TerraformのS3 BackendをAWS CloudFormationで管理する方法とは?
2022.03.10
Terraformとは、クラウドサービスなどのインフラを宣言型構成ファイルで管理するためのオープンソースのInfrastructure as Code(IaC)ツールです。
本記事では、Terraformの状態(State)を保存するバックエンド(Backend)にS3 Backendを使用するための準備として、AWS CloudFormationを利用してS3 Backend用のAWSリソースを作成する方法をご紹介いたします。
はじめに
Infrastructure as Code(IaC)とは、コードを使用してインフラの管理を行うことを言います。 以下に示したDevOpsで必要となるデプロイパイプラインの基礎において、IaCは重要なプラクティスとなります。
クラウドサービスなどのインフラを宣言型構成ファイルで管理するためのIaCツールとしてTerraformが挙げられます。Terraformは管理対象のインフラとその構成に関する状態(State)を保存する必要があります。
Stateを保存するバックエンド(Backend)にS3 Backendを使用する場合、AWS上にS3 Backend用 のリソースを事前に作成する必要があります。そこで本記事では、S3 Backend用のリソース自体をIaCで管理するためにAWS CloudFormationを利用する方法をご紹介いたします。
まず、TerraformのStateとBackendについて簡単に説明し、その後CloudFormationによるS3 Backend用AWSリソースの作成とTerraformでのS3 Backendの使用方法について説明いたします。
Terraformの「State」と「Backend」とは?
「State」とは
先述のとおり、Terraformは管理対象のインフラとその構成に関する状態(State)を保存する必要があります。 Stateの主な目的は、実際のインフラと構成ファイルで宣言されたインフラのマッピングを格納することです。
Terraformは構成ファイルの変更を適用する際に、Stateに保存された状態と現在の構成ファイルを比較し、実際のインフラにどのような変更を加えるかの計画を作成します。
Stateはデフォルトで「terraform.tfstate」という名前のローカルファイルに保存されますが、リモートで保存することもできます。チーム開発でローカルファイルを使用する場合、各ユーザーがTerraformを実行する前に最新のStateを持っていることを確認し、他のユーザーが同時にTerraformを実行しないようにする必要があるため運用が複雑になります。
そのため、チーム開発ではStateをリモートに保存しすべてのチームメンバー間で共有するのがより適切な運用になります。 Stateをリモートに保存するためには構成ファイルでバックエンド(Backend)の設定を行う必要があります。
「Backend」とは
BackendはStateを保存し、Stateをロックする(オプション)役割を担います。 BackendはデフォルトのローカルBackend以外にリモートBackendとして各種クラウドサービスのオブジェクトストレージなどが選択できます。 利用可能なリモートBackendはこちらのサイト でご確認ください。
S3 Backend用 CloudFormation テンプレートとスタックの作成
実際にCloudFormationを使用してS3 Backend用のAWS リソースを作成してみます。
CloudFormationテンプレートの作成
以下に、S3 Backendに必要なAWS リソースを作成するためのテンプレートの例を示します。
AWSTemplateFormatVersion: 2010-09-09 Description: CloudFormation template for Terraform S3 backend Resources: S3Bucket: Type: AWS::S3::Bucket Properties: AccessControl: Private BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true VersioningConfiguration: Status: Enabled DynamodbTable: Type: AWS::DynamoDB::Table Properties: AttributeDefinitions: - AttributeName: LockID AttributeType: S BillingMode: PAY_PER_REQUEST KeySchema: - AttributeName: LockID KeyType: HASH TerraformS3BackendPolicy: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: s3:ListBucket Resource: !GetAtt S3Bucket.Arn - Effect: Allow Action: - s3:GetObject - s3:PutObject - s3:DeleteObject Resource: !Sub ${S3Bucket.Arn}/* - Effect: Allow Action: - dynamodb:GetItem - dynamodb:PutItem - dynamodb:DeleteItem Resource: !GetAtt DynamodbTable.Arn TerraformOperator: Type: AWS::IAM::User Properties: ManagedPolicyArns: - !Ref TerraformS3BackendPolicy TerraformOperatorAccessKey: Type: AWS::IAM::AccessKey Properties: UserName: !Ref TerraformOperator TerraformOperatorAccessKeySecret: Type: AWS::SecretsManager::Secret Properties: Name: !Sub ${TerraformOperator}_accessKeys SecretString: !Sub '{"accessKeyId":"${TerraformOperatorAccessKey}","secretAccessKey":"${TerraformOperatorAccessKey.SecretAccessKey}"}' Outputs: TerraformOperatorAccessKeySecret: Description: Secrets Manager secret of TerraformOperator access keys Value: !Ref TerraformOperatorAccessKeySecret S3Bucket: Value: !Ref S3Bucket DynamodbTable: Value: !Ref DynamodbTable
S3BucketはStateを保存するS3 Bucketです。各プロパティについては利用する環境に合わせて適宜修正してください。 DynamodbTableはStateの更新の競合を防ぐためのロックで使用します。Stateのロックが必要ない場合は作成する必要はありません。
作成する場合は以下のプロパティの指定が必須となります。
AttributeDefinitions: - AttributeName: LockID AttributeType: S KeySchema: - AttributeName: LockID KeyType: HASH
その他のプロパティについては利用する環境に合わせて適宜修正してください。 S3 Backendに必要なリソースはS3BucketとDynamodbTableのみですが、例ではTerraformで使用するための最小権限を持ったIAMユーザーのアクセスキーも作成しています。
また、Terraform側の設定で必要な情報を出力するためにOutputsを設定しています。スタックの作成
テンプレートを作成したら、そのテンプレートを使用してスタックを作成します。 スタックを作成することで、テンプレートで定義したAWSリソースを作成することができます。 以下はテンプレートを「tf-backend-s3.yaml」というファイルに保存してある前提で、スタックを作成するためのコマンド例です。
aws cloudformation create-stack \ --stack-name tf-s3-backend \ --template-body file://tf-backend-s3.yaml \ --capabilities CAPABILITY_IAM
テンプレート内でIAM関連のリソースを定義しているため「–capabilities CAPABILITY_IAM」オプションが必要となります。 また、適切な権限を持ったプリンシパルでコマンドを実行する必要があります。 コマンドを実行するとスタックが非同期で作成されます。
作成が完了したら、以下のコマンドでTerraform側の設定で必要な情報を確認できます。
aws cloudformation describe-stacks --stack-name tf-s3-backend
上記コマンドの実行結果の「Outputs」にTerraform側の設定で必要な情報が出力されています。 (出力されていない場合はコマンド実行結果の「StackStatus」が「CREATE_COMPLETE」になっていることを確認してください。)
"Outputs": [ { "OutputKey": "TerraformOperatorAccessKeySecret", "OutputValue": "arn:aws:secretsmanager:xxxxxx", "Description": "Secrets Manager secret of TerraformOperator access keys" }, { "OutputKey": "S3Bucket", "OutputValue": "tf-s3-backend-s3bucket-xxxxxx" }, { "OutputKey": "DynamodbTable", "OutputValue": "tf-s3-backend-DynamodbTable-xxxxxx" } ]
IAMユーザーのアクセスキーの情報を取得するには以下のコマンドを追加で実行します。
aws secretsmanager get-secret-value \ --secret-id arn:aws:secretsmanager:xxxxxx
以上、TerraformでS3 Backendを使用するためのAWS リソースの作成と、Terraform側の設定で必要な情報の取得が完了しました。
参考サイトhttps://www.terraform.io/language/settings/backends/s3
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/Welcome.html
TerraformでのS3 Backendの使用
S3 Backendを設定したルートモジュールの作成
Backendはルートモジュールで設定します。
以下は特にリソースは作成せずに「Hello S3 Backend!」という文字列だけをアウトプットとして出力するルートモジュールです。
terraform { backend "s3" { bucket = "tf-s3-backend-s3bucket-xxxxx" key = "terraform.tfstate" region = "ap-northeast-1" dynamodb_table = "tf-s3-backend-DynamodbTable-xxxxx" } } output "message" { value = "Hello S3 Backend!" }
「bucket」、「dynamodb_table」にはCloudFormationスタックから取得した情報を入力します。「region」はスタックを作成したリージョンを入力し、「key」は任意で変更してください。
動作確認
TerraformがS3 Backend用のAWSリソースにアクセスできるように認証情報を設定する必要があります。 Secrets Managerから取得した認証情報を使用して、こちらの資料を参考に認証情報を設定します。 認証情報を設定したら、「main.tf」というファイル名でルートモジュールを作成し、ルートモジュールがあるディレクトリで以下のコマンドを実行します。
terraform init
Terraformの初期化が実行され、backendの初期化も実行されます。 この時点でエラーが発生する場合は認証情報を正しく設定できていないので、認証情報の設定を確認してください。
次に以下のコマンドを実行します。
terraform apply
ルートモジュールで定義した情報を元に、どのような操作を行うかの計画が表示されます。 この時点では実際の操作は行われず、入力待ち状態となります。 まだ「yes」は入力せずに、Stateがロックされているか確認してみます。
別のターミナルで前述の「terraform apply」コマンドを実行します。 「Error: Error acquiring the state lock」が発生し、Stateがロックされていることが確認できます。 この時、DynamoDBのテーブルにはStateのロックのために1レコードが作成されており、以下のコマンドで確認できます。
aws dynamodb scan --table-name tf-s3-backend-DynamodbTable-xxxxxx
ロックの確認ができたので、元のターミナルでプロンプトに「yes」を入力して、Enterを押します。 正常に完了すると、S3 Bucketに「key」で指定した名前でStateが作成されます。 Stateの内容を確認するには以下のコマンドを実行します。
aws s3 cp s3://tf-s3-backend-s3bucket-xxxxx/terraform.tfstate -
ルートモジュールでアウトプットに設定したメッセージが出力されていることが確認できます。 以上、TerraformのStateがS3 Bucketに保存されていることとStateのロックにDynamoDB テーブルが使用されていることが確認できました。
まとめ
本記事では、CloudFormationを使用してTerraformのS3 Backend用のAWSリソースを作成する方法と、TerraformでS3 Backendをどのように使用するかをご紹介しました。 TerraformでS3 Backendを使用する際には参考にしてみてください。
なお、最後に宣伝にはなりますが、弊社はAWSの監視・運用代行サービスをご提供しております。 ご興味のある方は こちらをご覧ください!