Terraformを始めたばかりで、プロジェクトの状態を保存するためのバックエンドとしてAWS S3を使用できるようにしたいと考えています。
terraform {
backend "s3" {
bucket = "tfstate"
key = "app-state"
region = "us-east-1"
}
}
S3バケット、IAMグループ、Terraformを使用したバックエンドストレージインフラストラクチャのポリシーを設定するのが賢明だと思います。
最初のterraformインフラストラクチャを適用する前にバックエンドの状態を設定すると、バックエンドバケットがまだ作成されていないことを合理的に訴えます。だから、私の質問は、テラフォームで追跡されたバックエンドの状態を維持しながら、テラフォームでテラフォームのバックエンドをどのようにセットアップするのですか?入れ子人形の問題のようです。
たとえば、バケットが存在するか何らかの状態が設定されているかどうかを確認し、テラフォームをブートストラップし、最初の実行後にローカルファイルシステムからテラフォームtfstateをs3までコピーするなど、これを回避するスクリプトの作成方法について考えています。しかし、この骨の折れる道を進む前に、明らかな何かを見逃していないことを確認すると思いました。
Terraformリモート状態を使用してこれをセットアップするには、通常、devおよびprod terraformフォルダー内にremote-state
と呼ばれる別のフォルダーがあります。
次のmain.tf
ファイルは、投稿したもののリモート状態を設定します。
provider "aws" {
region = "us-east-1"
}
resource "aws_s3_bucket" "terraform_state" {
bucket = "tfstate"
versioning {
enabled = true
}
lifecycle {
prevent_destroy = true
}
}
resource "aws_dynamodb_table" "terraform_state_lock" {
name = "app-state"
read_capacity = 1
write_capacity = 1
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}
次に、cd remote-state
を使用してこのフォルダーに入り、terraform init && terraform apply
を実行します-これは一度だけ実行する必要があります。バケットとdynamodbのテーブル名に何かを追加して、異なる環境を分離できます。
オースティンデイビスの多大な貢献に基づいて、データ暗号化の要件を含む、私が使用するバリエーションを以下に示します。
provider "aws" {
region = "us-east-1"
}
resource "aws_s3_bucket" "terraform_state" {
bucket = "tfstate"
versioning {
enabled = true
}
lifecycle {
prevent_destroy = true
}
}
resource "aws_dynamodb_table" "terraform_state_lock" {
name = "app-state"
read_capacity = 1
write_capacity = 1
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}
resource "aws_s3_bucket_policy" "terraform_state" {
bucket = "${aws_s3_bucket.terraform_state.id}"
policy =<<EOF
{
"Version": "2012-10-17",
"Id": "RequireEncryption",
"Statement": [
{
"Sid": "RequireEncryptedTransport",
"Effect": "Deny",
"Action": ["s3:*"],
"Resource": ["arn:aws:s3:::${aws_s3_bucket.terraform_state.bucket}/*"],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
},
"Principal": "*"
},
{
"Sid": "RequireEncryptedStorage",
"Effect": "Deny",
"Action": ["s3:PutObject"],
"Resource": ["arn:aws:s3:::${aws_s3_bucket.terraform_state.bucket}/*"],
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "AES256"
}
},
"Principal": "*"
}
]
}
EOF
}
あなたが発見したように、そもそもテラフォームを使用してテラフォームに必要なコンポーネントを構築することはできません。
私はテラフォームを「すべてを追跡」する傾向を理解していますが、それは非常に困難であり、それが価値がある以上に頭痛の種です。
私は通常、単純なbootstrap=シェルスクリプトを作成することでこの状況を処理します。次のようなものを作成します。
これを(技術的に)1回だけ実行する必要があるはずですが、新しいシステムを開発しているときに、スピンを繰り返して物事を分解します。したがって、これらの手順を1つのスクリプトに含めると、それがはるかに簡単になります。
私は通常、べき等になるようにスクリプトを作成します。このように、重複するバケット、ユーザーなどを作成していることを心配せずに複数回実行できます
私が通常行うことは、あなたが言ったように、初期インフラストラクチャを作成するためのリモートバックエンドなしで開始することです。それができたら、バックエンド構成を追加し、terraform initを実行してS3に移行します。
それは最良のケースではありませんが、ほとんどの場合、毎日環境全体を再構築するわけではないため、この半自動化されたアプローチで十分です。また、インフラストラクチャの次の「レイヤー」(VPC、サブネット、IGW、NATなど))を異なる状態に分離します。
これを解決するために、いくつかのbootstrap=コマンド/命令でterraformモジュールを作成しました:
https://github.com/samstav/terraform-aws-backend
READMEには詳細な手順がありますが、要点は次のとおりです。
# conf.tf
module "backend" {
source = "github.com/samstav/terraform-aws-backend"
backend_bucket = "terraform-state-bucket"
}
次に、シェルで(terraform {}
まだブロック):
terraform get -update
terraform init -backend=false
terraform plan -out=backend.plan -target=module.backend
terraform apply backend.plan
terraform {}
ブロック:
# conf.tf
terraform {
backend "s3" {
bucket = "terraform-state-bucket"
key = "states/terraform.tfstate"
dynamodb_table = "terraform-lock"
}
}
そして、再初期化できます:
terraform init -reconfigure
この問題を克服する方法は、最初の初期化計画適用サイクルでプロジェクトのリモート状態を作成し、2番目の初期化計画適用サイクルでリモート状態を初期化することです。
# first init plan apply cycle
# Configure the AWS Provider
# https://www.terraform.io/docs/providers/aws/index.html
provider "aws" {
version = "~> 2.0"
region = "us-east-1"
}
resource "aws_s3_bucket" "terraform_remote_state" {
bucket = "terraform-remote-state"
acl = "private"
tags = {
Name = "terraform-remote-state"
Environment = "Dev"
}
}
# add this sniped and execute the
# the second init plan apply cycle
# https://www.terraform.io/docs/backends/types/s3.html
terraform {
backend "s3" {
bucket = "terraform-remote-state"
key = "path/to/my/key"
region = "us-east-1"
}
}