✅ 이 블로깅은 테라폼으로 시작하는 IaC 책을 기반으로 작성한다.

테라폼은 immutable한 언어이다.

immutable이란 인프라스트럭처의 상태를 변경할 때, 기존의 인프라스트럭처를 수정하거나 업데이트하는 것이 아니라 새로운 인프라스트럭처를 생성하여 이전 상태의 인프라스트럭처를 교체하는 방식을 의미한다고 한다.

이 책에서 대표적인 IaC 도구인 Terraform / Ansible / CloudFormation / ARM Template(Azure)를 비교하였다. 여기선 Ansible은 제외한 나머지 도구는 immutable하다고 되어 있다. 나머지 세 도구는 환경을 프로비저닝하기 위한 도구로 immutable인 성격을 띠는 반면, Ansible은 구성관리에 초점을 두고 있어 mutable한 성격을 띠고 있다고 이해했다.

참고 ) 관련 도구

아래는 테라폼 사용하면서 유용할 거 같은 도구를 추가로 적어봤다

1. 테라폼 주요 커맨드

init과 apply는 테라폼에서 필수 커맨드이며, 나머지 아래가 주로 쓰이는 커맨드다.

(1) terraform init

init은 테라폼 구성 파일이 있는 작업 디렉토리를 초기화하는 과정이다. init을 수행하게 되면 실행에 필요한 외부 플러그인을 로컬에 다운로드하는 과정을 거치게 된다.

그래서 만약 init 과정없이 plan 또는 apply를 진행하게 되면 아래와 같은 오류가 발생한다.

╷
│ Error: Inconsistent dependency lock file
│
│ The following dependency selections recorded in the lock file are inconsistent with the current configuration:
│   - provider registry.terraform.io/hashicorp/aws: required by this configuration but no version is selected   
│
│ To make the initial dependency selections that will initialize the dependency lock file, run:
│   terraform init
╵

따라서 테라폼 실행 시 init 과정은 절대적으로 필요하며, 추가로 프로바이더, 모듈, 백엔드 구성이 설정되고 변경되는 경우에도 init을 수행해야 한다!

init을 수행하면 아래와 같이 테라폼에서 친절하게 말해준다

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

+) 참고로, 모듈 버전을 업그레이드하고 싶다면 terraform init -upgrade를 수행해야 한다.

terraform init을 수행하면 tf 파일에 명시된 버전으로 플러그인을 설치하게 된다. 이후 다른 작업자가 init을 수행하게 되면 버전 dependency에 영향을 받을 수 있어, 0.14버전부터는 .terraform.lock.hcl 이 추가됐다고 한다. 이 파일이 있으면 해당 파일에 명시된 버전으로 init을 수행하게 된다.

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

따라서 .terraform.lock.hcl에 명시된 버전이 아닌 코드에 명시한 다른 버전으로 변경하려면 upgrade 옵션을 추가해서 init을 수행해야 한다!

(2) terraform apply

apply는 plan을 토대로 작업을 실행한다

terraform plan -out=[파일명]으로 바이너리 형태로 실행 계획 파일을 생성할 수 있는데 terraform apply [파일명]을 하게 되면 실행 계획을 따로 출력하지 않고 실행 계획 파일을 토대로 수행하게 된다.

terraform apply를 수행하면 승인 절차를 진행하게 되는데 이를 생략하려면 -auto-approve 옵션을 추가해 주면 된다!

(3) terraform plan

테라폼 사용에 있어 best practice다

테라폼으로 적용할 인프라의 변경 사항에 관한 실행 계획을 생성하고 출력한다. 이로 인해 사용자는 실제 적용하기 전에 미리 검토할 수 있다

$ terraform plan

	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:
	# 테라폼이 수행할 작업을 출력해 준다

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

(4) terraform validate

테라폼 구성 파일의 유효성을 검토한다. plan과 달리 API 작업은 발생하지 않고 문법, 종속성, 속성 이름이나 연결된 값의 정확성을 확인할 수 있다.

문법이 알맞으면 아래와 같은 메세지를 출력한다.

$ terraform validate 
Success! The configuration is valid.

그러나 맞지 않는다면 아래와 같이 출력해 준다. 어디가 잘못됐는지 알려주니 정말 유용할 듯하다

$ terraform validate
╷
│ Error: Unsupported argument
│
│   on main.tf line 2, in provider "aws":
│    2:   regions = "ap-northeast-2"
│
│ An argument named "regions" is not expected here. Did you mean "region"?

(5) terraform fmt

fmt는 indent 검사를 해 준다

format 또는 reformat 줄임 표시로, 테라폼 구성 파일을 표준 형식과 표준 스타일로 적용하여 코드 가독성 높일 수 있다.

2. HCL (HashiCorp Configuration Langauge)

테라폼은 HCL 언어로 구성되어 있다.

HCL은 하시코프사에서 개발한 언어로, 프로그래밍적인 언어이다.

주석이나 function, 조건문, 선언 블록도 사용 가능하다.

// 한줄 주석 방법1
# 한줄 주석 방법2

/*
라인
주석
*/

locals {
  key1     = "value1"     # = 를 기준으로 키와 값이 구분되며
  myStr    = "TF ♡ UTF-8" # UTF-8 문자를 지원한다.
  multiStr = <<EOF
  Multi
  Line
  String
  with anytext
EOF

  boolean1    = true   # boolean true
  boolean2    = false  # boolean false를 지원한다.
  deciaml     = 123    # 기본적으로 숫자는 10진수,
  octal       = 0123   # 0으로 시작하는 숫자는 8진수,
  hexadecimal = "0xD5" # 0x 값을 포함하는 스트링은 16진수,
  scientific  = 1e10   # 과학표기 법도 지원한다.

  # funtion 호출 예
  myprojectname = format("%s is myproject name", var.project)

  # 3항 연산자 조건문을 지원한다.
  credentials = var.credentials == "" ? file(var.credentials_file) : var.credentials
}

3. 테라폼 블록

테라폼 블록은 테라폼 구성을 명시하는 데에 사용된다.

테라폼 버전을 명시하지 않으면 latest 버전이 설치되는데 (Desired State + Immutable)을 위해 버전을 꼭 명시해주도록 하자!

테라폼 버전이나 프로바이더 버전과 같은 값들은 자동으로 설정되지만, 함께 작업할 때는 버전을 명시적으로 선언하고 필요한 조건을 입력하여 실행 오류를 최소화 할 것을 권장한다

백엔드 블록

여러 명의 사용자가 동일한 tfstate 파일을 참조하고 작업하기 위해 하나의 backend만 허용한다

기본적으로는 local 백엔드가 활성화된다. 작업자의 로컬 환경에 상태를 저장하고 관리하게 된다.

tfstate

테라폼은 state의 데이터를 사용해 코드로 관리된 리소스를 탐색하고 추적한다. 또한 state에는 외부로 노출되면 안되는 패스워드 또는 인증서 정보 같은 민감한 데이터들이 포함될 수 있으므로 state의 접근 제어에 대한 고려도 필요하다

local 외의 백엔드 구성은 여러 작업자가 공유할 수 있는 스토리지 개념이라, 동시 사용하지 못하게 .terraform.tfstate.lock.info 파일이 생성된다.

{
    "ID":"4ce57cb2-5eef-2ba6-de10-47038f1069df",
    "Operation":"OperationTypeApply",   # 어떤 동작으로 인해 해당 잠금 파일이 생성되었는지 명기
    "Info":"",
    "Who":"gain@DESKTOP-94ANG27",       # 작업자 정보
    "Version":"1.3.3",              # 실행한 테라폼 버전
    "Created":"2023-09-02T16:20:01.4045043Z",
    "Path":"terraform.tfstate"      # 잠긴 state 파일의 위치
}

원래는 다른 작업자가 terraform apply로 작업 중일 때 .terraform.tfstate.lock.info 파일이 생성되면 다른 작업자가 apply 명령어를 수행했을 때 아래와 같은 메세지가 발생해야 한다.

Error: Error acquiring the state lock

Error message: resource temporarily unavailable
Lock info:
~~

근데 두 명령창을 띄어놓고 테스트하는 데 나는 왜인지 둘다 apply가 되더라(?)

다시 테스트해 봐야 할 듯하다. 분명 .terraform.tfstate.lock.info 파일은 생성되었다..

백엔드 설정 변경

백엔드가 만약 local에서 외부 스토리지로 변경된다면 다시 init을 수행해 state의 위치를 재설정해야 한다.

기본 동작은 -migrate-state 옵션과 동일하고 옵션은 아래와 같이 두 가지이다.

  • 추가 옵션1 (이전 구성 유지) : -migrate-state는 terraform.tfstate의 이전 구성에서 최신의 state 스냅샷을 읽고 기록된 정보를 새 구성으로 전환한다.
  • 추가 옵션2 (새로 초기화) : -reconfigure는 init을 실행하기 전에 terraform.tfstate 파일을 삭제해 테라폼을 처음 사용할 때처럼 이 작업 공간(디렉터리)을 초기화 하는 동작이다.

❓ state/terraform.tfstate 파일을 강제로 삭제 후, plan 과 apply 실행 시 어떻게 될까요?

{
  "version": 4,
  "terraform_version": "1.3.3",
  "serial": 6,
  "lineage": "af7b536b-1e1a-ed47-c2fb-dedfcf295c4b",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "local_file",
      "name": "abc",
      "provider": "provider[\"registry.terraform.io/hashicorp/local\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "content": "123456789!",
            "content_base64": null,
            "content_base64sha256": "LAHAyRrrDM3mJCibz6g432UeYCzLsW0xXI4h3Opad7g=",
            "content_base64sha512": "6jq4hFRux4UoARXDRVdFUOyRgLpzDOAnFg63dJm1w5G9zzwL/tD9IyOCMKIQWE3R/HJflJcx/VWJ+WaOZifEqA==",
            "content_md5": "2045e3295067d5767de71e262776b581",
            "content_sha1": "efbc19993c089de75c87e4017f0c73e2fc9da863",
            "content_sha256": "2c01c0c91aeb0ccde624289bcfa838df651e602ccbb16d315c8e21dcea5a77b8",
            "content_sha512": "ea3ab884546ec785280115c345574550ec9180ba730ce027160eb77499b5c391bdcf3c0bfed0fd23238230a210584dd1fc725f949731fd5589f9668e6627c4a8",
            "directory_permission": "0777",
            "file_permission": "0777",
            "filename": "./abc.txt",
            "id": "efbc19993c089de75c87e4017f0c73e2fc9da863",
            "sensitive_content": null,
            "source": null
          },
          "sensitive_attributes": []
        }
      ]
    }
  ],
  "check_results": []
}

현재 terraform.tfstate 파일이다.

파일을 강제 삭제 후, apply를 다시 해 보니, serial 넘버만 바뀌었다. 6 → 2로!

  "serial": 2,

참고 ) lineage와 serial?

Differing lineage: The “lineage” is a unique ID assigned to a state when it is created. If a lineage is different, then it means the states were created at different times and its very likely you’re modifying a different state. Terraform will not allow this.

Higher serial: Every state has a monotonically increasing “serial” number. If the destination state has a higher serial, Terraform will not allow you to write it since it means that changes have occurred since the state you’re attempting to write.

lineage는 state가 생성될 때 할당되는 고유 번호이다.

serial은 state마다 증가한다. 변경 사항이 발생했음을 의미한다.

4. 리소스

resource "local_file" "abc" {
  content  = "123456!"
  filename = "${path.module}/abc.txt"
}

위처럼 backend 지정도 안했지만

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/local...
- Installing hashicorp/local v2.4.0...
- Installed hashicorp/local v2.4.0 (signed by HashiCorp)

init 수행 시 자동으로 local 프로바이더로 받는 걸 볼 수 있었다.

이처럼 프로바이더 선언 없이도 종속성을 가지게 되어, 리소스 추가로 자동 인식된 프로바이더를 설치하게 된다! (하지만 프로바이더 블록 별도 선언을 권장한다!)

리소스 구성

리소스 블록은 resource로 시작한다. 이후 리소스 블록이 생성할 ‘리소스 유형’을 정의한다.

resource "<리소스 유형>" "<이름>" {
  <인수> = <값>
}

resource "local_file" "abc" {
  content  = "123"
  filename = "${path.module}/abc.txt"
}

종속성

테라폼 종속성은 resource, module 선언으로 프로비저닝되는 각 요소의 생성 순서를 구분짓는다.

기본적으로 다른 리소스에서 값을 참조해 불러올 경우 생성 선후 관계에 따라 암시적 종속성을 갖게 되고, 강제로 리소스 간 명시적 종속성을 부여할 경우에는 메타인수인 depends_on을 활용한다.

(1) 동작 확인 1 - 종속성 X

resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}

resource "local_file" "def" {
  content  = "456!"
  filename = "${path.module}/def.txt"
}
$ terraform apply
	...중략...
	local_file.abc: Creating...
	local_file.def: Creating...
	local_file.def: Creation complete after 0s [id=b9fbde4d33ab9c450a7ce303fb4788c9d2db9aed]
	local_file.abc: Creation complete after 0s [id=5f30576af23a25b7f44fa7f5fdf70325ee389155]
	
	Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

두 리소스의 구성 요소는 선후 관계가 없는 동일한 수준으로, 병렬 실행 방식에 따라 동시에 수행한다.

$ terraform graph 
	digraph {
	        compound = "true"
	        newrank = "true"
	        subgraph "root" {
	                "[root] local_file.abc (expand)" [label = "local_file.abc", shape = "box"]
	                "[root] local_file.def (expand)" [label = "local_file.def", shape = "box"]
	                "[root] provider[\"registry.terraform.io/hashicorp/local\"]" [label = "provider[\"registry.terraform.io/hashicorp/local\"]", shape = "diamond"]
	                "[root] local_file.abc (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/local\"]"
	                "[root] local_file.def (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/local\"]"
	                "[root] provider[\"registry.terraform.io/hashicorp/local\"] (close)" -> "[root] local_file.abc (expand)"
	                "[root] provider[\"registry.terraform.io/hashicorp/local\"] (close)" -> "[root] local_file.def (expand)"
	                "[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/local\"] (close)"
	        }
	}

graph 명령어를 통해 종속성을 확인해 볼 수 있으며,

결과 값을 파일로 저장하여

terraform graph > graph-1.dot

Untitled

vscode에서 extension으로 설치한 Graphviz (dot) 도구로 시각화해서 볼 수 있다.

(2) 동작 확인 2 - 종속성 O

명시적 종속성 부여는 두 가지로 가능하다

resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}

resource "local_file" "def" {
  content  = local_file.abc.content    # 123! 
  filename = "${path.module}/def.txt"
}

또는

resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}

resource "local_file" "def" {
  depends_on = [
    local_file.abc
  ]

  content  = "456!"
  filename = "${path.module}/def.txt"
}
$ terraform apply
	...중략...
	local_file.abc: Creating...
	local_file.abc: Creation complete after 0s [id=5f30576af23a25b7f44fa7f5fdf70325ee389155]
	local_file.def: Creating...
	local_file.def: Creation complete after 0s [id=5f30576af23a25b7f44fa7f5fdf70325ee389155]
	
	Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

def가 abc에 종속성이 생겼기 때문에 abc 파일을 먼저 생성하고 def 파일을 생성하게 된다.

digraph {
        compound = "true"
        newrank = "true"
        subgraph "root" {
                "[root] local_file.abc (expand)" [label = "local_file.abc", shape = "box"]
                "[root] local_file.def (expand)" [label = "local_file.def", shape = "box"]
                "[root] provider[\"registry.terraform.io/hashicorp/local\"]" [label = "provider[\"registry.terraform.io/hashicorp/local\"]", shape = "diamond"]                
                "[root] local_file.abc (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/local\"]"
                "[root] local_file.def (expand)" -> "[root] local_file.abc (expand)"
                "[root] provider[\"registry.terraform.io/hashicorp/local\"] (close)" -> "[root] local_file.def (expand)"
                "[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/local\"] (close)"
        }
}

def 파일이 abc 파일을 참조하고 있는 걸 확인할 수 있다.

Untitled 1

(3) 결론

"[root] local_file.abc (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/local\"]"
"[root] local_file.def (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/local\"]"
"[root] local_file.abc (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/local\"]"
"[root] local_file.def (expand)" -> "[root] local_file.abc (expand)"

종속성의 유무를 파일 또는 그림으로 직관적으로 확인할 수 있다!

리소스 속성 참조

리소스 구성에서 인수와 속성은 참조 가능한 값이다.

  • 인수 : 리소스 생성 시 사용자가 선언하는 값
  • 속성 : 사용자가 설정하는 것은 불가능하지만 리소스 생성 이후 획득 가능한 리소스 고유 값
# Terraform Code
resource "<리소스 유형>" "<이름>" {
  <인수> = <값>
}

# 리소스 참조
<리소스 유형>.<이름>.<인수>
<리소스 유형>.<이름>.<속성>

생명주기

lifecycle은 리소스의 기본 수명주기를 작업자가 의도적으로 변경하는 메타인수다

  • create_before_destroy (bool): 리소스 수정 시 신규 리소스를 우선 생성하고 기존 리소스를 삭제
  • prevent_destroy (bool): 해당 리소스를 삭제 Destroy 하려 할 때 명시적으로 거부
  • ignore_changes (list): 리소스 요소에 선언된 인수의 변경 사항을 테라폼 실행 시 무시
  • precondition: 리소스 요소에 선언해 인수의 조건을 검증
  • postcondition: Plan과 Apply 이후의 결과를 속성 값으로 검증

(1) create_before_destroy (bool)

테라폼의 기본 생명주기는 삭제 후 생성인데 이미지 교체같은 작업인 경우 작업자가 리소스를 생성 후 삭제를 원할 때 사용된다.

resource "local_file" "abc" {
  content  = "lifecycle - step 2"   # 수정
  filename = "${path.module}/abc.txt"

  lifecycle {
    create_before_destroy = true    # 생성 후 삭제
  }
}

파일을 생성하고 주석단 부분만 수정해 주었다.

$ terraform apply -auto-approve
	local_file.abc: Refreshing state... [id=7e817d811209db576335597bcd326518fe13398b]
	
	Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
	+/- create replacement and then destroy
	
	Terraform will perform the following actions:
	
	  # local_file.abc must be replaced
	+/- resource "local_file" "abc" {
	      ~ content              = "lifecycle - step 1" -> "lifecycle - step 2" # forces replacement
	      ~ content_base64sha256 = "mWPFMXSOz+Q/uQKVbykSD/aZqtO8THFqRCcH2NaqkXc=" -> (known after apply)
	      ~ content_base64sha512 = "Wa0KB/Qm0YIFpVWUcDMFSiC42nNRLzGsB+MOox8EnJMWGwJ5gkfx4GdODN9BX9lPO+C1lWMAhUNTOLD6j1cBRg==" -> (known after apply)
	      ~ content_md5          = "973913f00967dad5c7f311448854eadf" -> (known after apply)
	      ~ content_sha1         = "7e817d811209db576335597bcd326518fe13398b" -> (known after apply)
	      ~ content_sha256       = "9963c531748ecfe43fb902956f29120ff699aad3bc4c716a442707d8d6aa9177" -> (known after apply)
	      ~ content_sha512       = "59ad0a07f426d18205a555947033054a20b8da73512f31ac07e30ea31f049c93161b02798247f1e0674e0cdf415fd94f3be0b595630085435338b0fa8f570146" -> (known after apply)
	      ~ id                   = "7e817d811209db576335597bcd326518fe13398b" -> (known after apply)
	        # (3 unchanged attributes hidden)
	    }
	
	Plan: 1 to add, 0 to change, 1 to destroy.
	local_file.abc: Creating...
	local_file.abc: Creation complete after 0s [id=43809e4e5139be51422bfdcb41cab0852741ec10]
	local_file.abc (deposed object 4458247f): Destroying... [id=7e817d811209db576335597bcd326518fe13398b]
	local_file.abc: Destruction complete after 0s
	
	Apply complete! Resources: 1 added, 0 changed, 1 destroyed.

동일한 파일명을 지정했으므로 내용은 수정되었지만 최종적으로는 파일이 삭제된다.

+/- create replacement and then destroy으로 출력된 걸 확인할 수 있다

(2) prevent_destroy (bool)

이는 삭제 방지를 위한 것이다.

resource "local_file" "abc" {
  content  = "lifecycle - step 33" # 수정
  filename = "${path.module}/abc.txt"

  lifecycle {
    prevent_destroy = true # 삭제 방지
  }
}
$ terraform apply -auto-approve
	local_file.abc: Refreshing state... [id=57b7d0c9d55b33110ce01703cb26078653551b59]
	╷
	│ Error: Instance cannot be destroyed
	│
	│   on main.tf line 1:
	│    1: resource "local_file" "abc" {
	│
	│ Resource local_file.abc has lifecycle.prevent_destroy set, but the plan calls for this resource to be destroyed. To avoid this error and continue with the   
	│ plan, either disable lifecycle.prevent_destroy or reduce the scope of the plan using the -target flag.
	╵

테라폼 기본 생명주기인 삭제 후 생성을 수행해야 하는데 해당 인수로 인해 삭제가 방지되어 apply가 실패됐다

(3) ignore_changes (list)

이는 변경사항이 반영되지 않게 하는 것이다.

resource "local_file" "abc" {
  content  = "lifecycle - step 5"   # 수정
  filename = "${path.module}/abc.txt"

  lifecycle {
    ignore_changes = [
      content
    ]
  }
}

content를 수정했지만 아래와 같이 No changes로 출력된다.

$ terraform apply -auto-approve
	local_file.abc: Refreshing state... [id=63cd1a0c571d59f5b4337364e612786cc63c1bb8]
	
	No changes. Your infrastructure matches the configuration.
	
	Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.
	
	Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

실제로도 해당 파일에 변경사항이 반영되지 않았다

$ cat abc.txt 
lifecycle - step 4

모든 변경 사항을 무시하고 싶다면 ignore_changes = all로 설정할 수 있다!

(4) precondition

Untitled 2

(가시다님 노션 그림 참조)

리소스 생성 이전에 입력된 인수 값을 검증하는 데에 사용한다.

variable "file_name" {
  default = "step0.txt"
}

resource "local_file" "abc" {
  content  = "lifecycle - step 6"
  filename = "${path.module}/${var.file_name}"

  lifecycle {
    precondition {
      condition     = var.file_name == "step6.txt"
      error_message = "file name is not \"step6.txt\""
    }
  }
}
$ terraform apply -auto-approve
	local_file.abc: Refreshing state... [id=63cd1a0c571d59f5b4337364e612786cc63c1bb8]
	╷
	│ Error: Resource precondition failed
	│
	│   on main.tf line 11, in resource "local_file" "abc":
	│   11:       condition     = var.file_name == "step6.txt"
	│     ├────────────────
	│     │ var.file_name is "step0.txt"
	│
	│ file name is not "step6.txt"

사전에 잘못된 프로비저닝을 실행할 수 없도록 구성할 수 있다

apply를 성공 하려면 condition의 파일명을 var.file_name.default와 동일하게 step0.txt로 맞춰야 한다

(5) postcondition

리소스 생성 이후의 조건을 부여한다

resource "local_file" "abc" {
  content  = ""
  filename = "${path.module}/step7.txt"

  lifecycle {
    postcondition {
      condition     = self.content != ""
      error_message = "content cannot empty"
    }
  }
}

output "step7_content" {
  value = local_file.abc.id
}
$ terraform apply -auto-approve
	local_file.abc: Refreshing state... [id=63cd1a0c571d59f5b4337364e612786cc63c1bb8]
	╷
	│ Error: Resource postcondition failed
	│
	│   on main.tf line 7, in resource "local_file" "abc":
	│    7:       condition     = self.content != ""
	│     ├────────────────
	│     │ self.content is ""
	│ 
	│ content cannot empty
	╵

condition에 맞지 않는 경우에 에러가 발생한다.

resource "local_file" "abc" {
  content  = "step7 file ok"
  filename = "${path.module}/step7.txt"

  lifecycle {
    postcondition {
      condition     = self.content != ""
      error_message = "content cannot empty"
    }
  }
}

output "step7_content" {
  value = local_file.abc.id
}

위처럼 content에 null이 아닌 값을 넣어 주면 apply를 할 수 있다

프로비저닝 이후에 생성되는 종속성 갖는 리소스를 구성하는 경우에 잘못된 프로비저닝 작업을 방지할 수 있다!

Categories:

Updated:

Leave a comment