Terraform yerel olarak Terraform ile çalışırken, durumunu .terraform.tfstate dosyasında saklamak için varsayılan yerel arka ucu kullanır. Durum dosyası, gizli bilgileri açığa çıkarabileceği için sürüm kontrol sisteminde kaydedilmemelidir. Bazı Terraform komutları çalıştığında, bu dosya değiştirilir. Bu nedenle, sistemdeki değişiklikler mühendislere derhal sunulmalıdır. Ancak durum dosyası yerel olarak yönetiliyorsa bu mümkün değildir. Bu sorunların üstesinden gelmek için, durum dosyasını depolamak için AWS S3'ü ve kilitleme bilgileri için DynamoDB'yi kullanacağız. Kilitleme mekanizması, aynı anda değiştirildiğinde durum dosyasının bozulmasını önlemek için önemli bir özelliktir. Terraform, durumu aynı anda birden fazla kullanıcı tarafından yazılmasını önlemek için bir durum kilidi edinir.


S3 klasörünü ve DynamoDB'yi kurun


Bu adımları AWS konsolunda manuel olarak gerçekleştirmelisiniz, ancak şimdilik bunu halletmek için Terraform'u kullanacağım. Bu arada, kullanıcı/grubunuz en fazla iam:*, ec2:*, s3:*, dynamodb:* izinlerine ihtiyaç duyar, ancak en az ayrıcalıklı izinleri kullanmayı tercih edin.


├── dynamodb.tf
├── local.tf
├── main.tf
└── s3.tf

local.tf


locals {
terraform_state_bucket_name = "inanzzz-development-terraform"
terraform_state_bucket_key = "terraform.tfstate"
terraform_state_dynamodb_table = "terraform_tfstate"
}

main.tf


terraform {
required_version = "~> 1.4.4"

required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.30.0"
}
}
}

provider "aws" {
profile = "development"
region = "eu-west-1"
}

s3.tf


resource "aws_s3_bucket" "terraform_state" {
bucket = local.terraform_state_bucket_name
force_destroy = true
}

resource "aws_s3_bucket_versioning" "versioning_example" {
bucket = aws_s3_bucket.terraform_state.id

versioning_configuration {
status = "Enabled"
}
}

resource "aws_s3_bucket_public_access_block" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id

block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

dynamodb.tf


resource "aws_dynamodb_table" "terraform_state" {
name = local.terraform_state_dynamodb_table
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"

attribute {
name = "LockID"
type = "S"
}
}

Kurulum


terraform init ve terraform apply komutlarını çalıştırın. Bu, AWS konsolunda S3 kovası ve DynamoDB tablosu oluşturacaktır. Yerel ortamda .terraform klasörü ile birlikte .terraform.lock.hcl ve .terraform.tfstate dosyaları oluşturulacaktır.


S3 arka ucu oluştur


├── group.tf
└── main.tf

main.tf


terraform {
required_version = "~> 1.4.4"

required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.30.0"
}
}
}

provider "aws" {
profile = "development"
region = "eu-west-1"
}

terraform {
backend "s3" {
profile = "development"
region = "eu-west-1"
encrypt = true
bucket = "inanzzz-development-terraform"
key = "terraform.tfstate"
dynamodb_table = "terraform_tfstate"
}
}

group.tf


Bu isteğe bağlıdır, ancak durum dosyasının yerel ortamda nasıl depolanmadığını göstermek için kullanacağız.


# resource "aws_iam_group" "dummy_team" {
# name = "dummy-team"
# }

Kurulum


terraform init komutunu çalıştırın. Bu herhangi bir kaynak oluşturmaz. Ancak, .terraform klasörü ve .terraform.lock.hcl dosyası oluşturacak, ancak .terraform.tfstate dosyasını oluşturmayacaktır.


aws_iam_group bloğunu aktif hale getirin ve terraform apply komutunu çalıştırın. Bu, S3 klasöründe .terraform.tfstate dosyası oluşturacak ve kilitleme mekanizması için DynamoDB tablosunda LockID özelliğini ayarlayacaktır.


Bundan sonra, terraform apply komutlarını her çalıştırdığınızda, yerel .terraform.tfstate dosyası içinde listelenen hiçbir kaynağa sahip olmayacak. Bunun yerine S3 kovasındaki .terraform.tfstate dosyasında görünecekler. İçeriğini indirip kontrol edebilirsiniz. Ayrıca DynamoDB tablosundaki LockID özelliği güncellenecektir.


Kilitlemeyi test etme


Yeni bir kaynak (lock_team) eklemek için bir terminal açın ve uygulamayı çalıştırın, ancak istemi yanıtlamayın.


$ terraform apply
2023/05/25 21:26:26 Enabling CSM
2023/05/25 21:26:27 Enabling CSM
aws_iam_group.dummy_team: Refreshing state... [id=dummy-team]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create

Terraform will perform the following actions:

# aws_iam_group.lock_team will be created
+ resource "aws_iam_group" "lock_team" {
+ arn = (known after apply)
+ id = (known after apply)
+ name = "lock-team"
+ path = "/"
+ unique_id = (known after apply)
}

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.

Enter a value:

Başka bir terminal açın ve plan komutunu çalıştırın. Kilitleme hatası göreceksiniz.


$ terraform plan
2023/05/25 21:26:39 Enabling CSM
2023/05/25 21:26:39 Enabling CSM

│ Error: Error acquiring the state lock

│ Error message: ConditionalCheckFailedException: The conditional request failed
│ Lock Info:
│ ID: 12345-qwer-asdf-34e5-1234567890
│ Path: inanzzz-development-terraform/terraform.tfstate
│ Operation: OperationTypeApply
│ Who: me@home
│ Version: 1.4.4
│ Created: 2023-05-25 20:26:29.120347 +0000 UTC
│ Info:


│ Terraform acquires a state lock to protect the state from being written
│ by multiple users at the same time. Please resolve the issue above and try
│ again. For most commands, you can disable locking with the "-lock=false"
│ flag, but this is not recommended.


Not


Normal kullanıcılar için, S3 klasörü için aşağıdaki politikayı kullanabilirsiniz.


data "aws_iam_policy_document" "terraform_state" {
version = "2012-10-17"

statement {
actions = [
"s3:ListBucket",
]
resources = [
"arn:aws:s3:::inanzzz-development-terraform"
]
}

statement {
actions = [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
]
resources = [
"arn:aws:s3:::inanzzz-development-terraform/terraform.tfstate"
]
}
}

resource "aws_s3_bucket_policy" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
policy = data.aws_iam_policy_document.terraform_state.json
}