일시적인 쓰기 전용(write-only) 인자 사용하기

일시적인 쓰기 전용(write-only) 인자 사용하기

쓰기 전용 인자를 사용하면 작업 중에 임시 값을 Terraform의 관리 리소스에 안전하게 전달하면서 그 값을 상태나 계획 파일에 영속화하지 않을 수 있어요. 비밀번호, API 토큰, 기타 비밀 값 같은 민감한 데이터를 처리할 때 쓰기 전용 인자를 사용하세요.

출처: 문서

본문

배경

쓰기 전용 인자는 Terraform의 다른 임시(ephemeral) 값을 보완해서, 민감한 데이터를 Terraform의 산출물에 전혀 저장하지 않고 구성 전체에 안전하게 전달할 수 있게 해줘요. 예를 들어 ephemeral 리소스를 사용해 임의의 비밀번호를 생성한 다음, 다른 resource 블록의 쓰기 전용 인자에 전달할 수 있어요. 프로바이더는 쓰기 전용 인자 값을 사용해 리소스를 구성하고, Terraform은 그 값을 저장하지 않고 버려요.

실습: Upgrade RDS major version 튜토리얼에서 쓰기 전용 인자를 선언해 보세요.

임시 리소스나 변수 같은 Terraform의 다른 임시 구조와 달리, 쓰기 전용 인자는 임시 값과 비임시 값 모두를 받아들여요.

요구 사항

쓰기 전용 인자를 사용하려면 Terraform v1.11 이상을 사용해야 하고, 쓰기 전용 인자를 지원하는 리소스를 사용해야 해요.

쓰기 전용 인자 선언하기

프로바이더는 Terraform 레지스트리에서 인자가 쓰기 전용인지 여부를 나타내요. 예를 들어 aws 프로바이더의 aws_db_instance 리소스에는 쓰기 전용 password_wo 인자가 있어요. password_wo 인자는 데이터베이스 비밀번호로 사용할 값을 받아요:

resource "aws_db_instance" "test" {
  instance_class      = "db.t3.micro"
  allocated_storage   = "5"
  engine              = "postgres"
  username            = "example"
  skip_final_snapshot = true
  password_wo         =
  password_wo_version = 1
}

쓰기 전용 인자는 임시 값과 비임시 값 모두를 받아들여요. 예를 들어 문자열을 쓰기 전용 인자의 값으로 사용할 수도 있어요:

resource "aws_db_instance" "test" {
  instance_class      = "db.t3.micro"
  allocated_storage   = "5"
  engine              = "postgres"
  username            = "example"
  skip_final_snapshot = true
  password_wo         = "my-password-here"
  password_wo_version = 1
}

그러나 리소스에 임시 값을 전달할 때 쓰기 전용 인자를 사용하는 것을 권장해요. 예를 들어 ephemeral 리소스를 사용해 임의의 비밀번호를 생성하고 password_wo 쓰기 전용 인자에 전달할 수 있어요:

ephemeral "random_password" "db_password" {
  length           = 16
  override_special = "!#$%&*()-_=+[]{}<>:?"
}

resource "aws_db_instance" "example" {
  instance_class      = "db.t3.micro"
  allocated_storage   = "5"
  engine              = "postgres"
  username            = "example"
  skip_final_snapshot = true
  password_wo         = ephemeral.random_password.db_password.result
  password_wo_version = 1
}

Terraform 작업 중에 프로바이더는 password_wo 값을 사용해 데이터베이스 인스턴스를 만들고, 그 후 Terraform은 그 값을 계획이나 상태 파일에 저장하지 않고 버려요.

Terraform은 password_wo의 생성 값을 저장하지 않지만, 다른 리소스나 출력에 캡처할 수 있어요. 임시 비밀번호를 생성, 저장, 검색하고 쓰기 전용 인자로 사용하는 예시는 예시를 참고하세요.

버전으로 쓰기 전용 인자 업데이트하기

Terraform은 쓰기 전용 인자를 상태 파일에 저장하지 않으므로, 쓰기 전용 인자 값이 변경됐는지 알 방법이 없어요. Terraform은 쓰기 전용 인자 값을 추적할 수 없기 때문에, 매 작업마다 쓰기 전용 인자를 프로바이더로 보내요.

Terraform은 또한 해당 값을 계획 파일에 저장하지 않으므로 쓰기 전용 인자에 대한 계획 diff를 만들 수 없어요. 하지만 프로바이더는 일반적으로 쓰기 전용 인자와 함께 버전 인자를 포함해요. Terraform은 버전 인자를 상태에 저장하고, 버전 인자가 변경되는지 추적할 수 있어요.

프로바이더는 실무자가 쓰기 전용 인자 값을 추적하고 프로바이더가 그 쓰기 전용 인자를 사용할 시점을 제어할 수 있도록 버전 인자를 구현해요. 쓰기 전용 인자와 그 버전 인자의 구현은 프로바이더별로 다르므로, 특정 프로바이더에 대한 자세한 내용은 레지스트리를 참고하세요.

예를 들어 aws_db_instance 리소스에는 password_wo 쓰기 전용 인자와 함께 그에 대응하는 password_wo_version 인자가 있어요:

resource "aws_db_instance" "test" {
  instance_class      = "db.t3.micro"
  allocated_storage   = "5"
  engine              = "postgres"
  username            = "example"
  skip_final_snapshot = true
  password_wo         = "old-password-here"
  password_wo_version = 1
}

프로바이더는 aws_db_instance 리소스를 만들 때 쓰기 전용 인자 값을 사용하고, Terraform은 password_wo_version 인자 값을 상태에 저장해요.

쓰기 전용 인자의 업데이트를 트리거하려면 구성에서 버전 인자 값을 증가시키세요:

resource "aws_db_instance" "main" {
  instance_class      = "db.t3.micro"
  allocated_storage   = "5"
  engine              = "postgres"
  username            = "example"
  password_wo         = "new-password-here"
  password_wo_version = 2
}

password_wo_version 인자를 증가시키면 Terraform은 계획에서 그 변경을 감지하고 aws 프로바이더에 알려요. 그러면 aws 프로바이더는 새 password_wo 값을 사용해 aws_db_instance 리소스를 업데이트해요.

예시

다음은 서로 다른 클라우드 프로바이더에서 쓰기 전용 인자를 사용하는 방법을 보여줘요.

AWS Secrets Manager에 임시 비밀번호 설정하고 저장하기

ephemeral 리소스를 사용해 임의의 비밀번호를 생성하고 AWS Secrets Manager에 저장한 다음, 다른 ephemeral 리소스를 사용해 검색할 수 있어요. 마지막으로 aws_db_instance 리소스의 password_wo 쓰기 전용 인자에 그 비밀번호를 전달할 수 있어요:

ephemeral "random_password" "db_password" {
  length           = 16
  override_special = "!#$%&*()-_=+[]{}<>:?"
}

resource "aws_secretsmanager_secret" "db_password" {
  name = "db_password"
}

resource "aws_secretsmanager_secret_version" "db_password" {
  secret_id                = aws_secretsmanager_secret.db_password.id
  secret_string_wo         = ephemeral.random_password.db_password.result
  secret_string_wo_version = 1
}

ephemeral "aws_secretsmanager_secret_version" "db_password" {
  secret_id = aws_secretsmanager_secret_version.db_password.secret_id
}

resource "aws_db_instance" "example" {
  instance_class      = "db.t3.micro"
  allocated_storage   = "5"
  engine              = "postgres"
  username            = "example"
  skip_final_snapshot = true
  password_wo         = ephemeral.aws_secretsmanager_secret_version.db_password.secret_string
  password_wo_version = aws_secretsmanager_secret_version.db_password.secret_string_wo_version
}

위 예시에서 임시 리소스 aws_secretsmanager_secret_version은 Terraform이 처음에 알지 못하는 인자를 참조해요. Terraform은 aws_secretsmanager_secret_version을 적용 단계까지 지연해서, 필요한 정보를 얻은 후에 리소스를 평가하도록 해요. Terraform은 먼저 임시 random_password를 사용해 AWS Secrets Manager에 비밀을 생성한 다음, 임시 aws_secretsmanager_secret_version 리소스로 검색하고, 마지막으로 aws_db_instance 리소스의 쓰기 전용 password_wo 인자에 비밀번호를 기록해요.

Azure Key Vault에 임시 비밀번호 설정하고 저장하기

쓰기 전용 인자를 사용해 Azure의 Key Vault에 비밀번호를 저장한 다음, 그 비밀번호를 사용해 Azure에서 MySQL 데이터베이스를 만들 수 있어요. 다음 예시에서 Terraform은 ephemeral 리소스를 사용해 비밀번호를 생성하고, 그 비밀번호를 azurerm_key_vault_secret에 저장한 다음, azurerm_mysql_flexible_server 리소스에서 검색해요:

provider "azurerm" {
  features {}
}

ephemeral "random_password" "db_password" {
  length           = 16
  override_special = "!#$%&*()-_=+[]{}<>:?"
}

locals {
 db_password_version = 1
}

resource "azurerm_resource_group" "example" {
  name     = "example-resource-group"
  location = "westeurope"
}

data "azurerm_client_config" "current" {}

resource "azurerm_key_vault" "example" {
  name                       = "example-key-vault"
  location                   = azurerm_resource_group.example.location
  resource_group_name        = azurerm_resource_group.example.name
  tenant_id                  = data.azurerm_client_config.current.tenant_id
  sku_name                   = "standard"
  soft_delete_retention_days = 7

  access_policy {
    tenant_id = data.azurerm_client_config.current.tenant_id
    object_id = data.azurerm_client_config.current.object_id

    key_permissions = [
      "Get",
    ]

    secret_permissions = [
      "Get",
      "Delete",
      "List",
      "Purge",
      "Recover",
      "Set",
    ]
  }
}

resource "azurerm_key_vault_secret" "example" {
  name             = "example-secret"
  value_wo         = ephemeral.random_password.db_password.result
  value_wo_version = local.db_password_version
  key_vault_id     = azurerm_key_vault.example.id
}

ephemeral "azurerm_key_vault_secret" "db_password" {
  name         = azurerm_key_vault_secret.example.name
  key_vault_id = azurerm_key_vault.example.id
}

resource "azurerm_mysql_flexible_server" "example" {
  name                 = "example-mysql-flexible-server"
  resource_group_name  = azurerm_resource_group.example.name
  location             = azurerm_resource_group.example.location
  sku_name             = "B_Standard_B1s"

  administrator_login               = "newuser"
  administrator_password_wo         = ephemeral.azurerm_key_vault_secret.db_password.value
  administrator_password_wo_version = local.db_password_version
}

위 구성은 비밀번호를 Azure의 Key Vault에 저장하고, 그 비밀번호를 Terraform 산출물에 전혀 저장하지 않고 Azure에서 데이터베이스를 만드는 데 사용해요.

더 알아보기 (Learn more)