✍ Posted by Immersive Builder Seong
1. OpenTofu 소개
OpenTofu 란?
- HashiCorp에서 Terraform 라이선스를 MPL(Mozilla Public License)에서 BSL(Business Source License)로 변경하는 결정에 대응하여 OpenTF 이니셔티브에서 Terraform의 포크 버전인 OpenTF를 발표함('23.08)
- OpenTF 저장소가 공개되고, 포크 버전의 명칭을 OpenTF에서 OpenTofu로 변경함('23.09)
- IBM사에서 HashiCorp사를 64억 달러에 인수 계약 체결함('24.04)
- Oracle E-Business Suite Cloud Manager 제품의 최신 버전에서 사용중인 Terraform을 OpenTofu로 대체함('24.05)
- 엔터프라이즈 수준에서 사용 가능한 첫 번째 버전인 OpenTofu v1.7.0이 출시됨('24.05)
- OpenTofu는 오픈소스로 커뮤니티에서 주도적으로 개발하고 리눅스 재단(Linux Foundation)에서 관리함
- 다양한 유형의 리소스와 서비스를 관리하기 위해 수천 개의 프로바이더를 작성함
- Early Evaluation, Provider Mocking, Coder-Friendly Future 등 기능이 추가된 OpenTofu v1.8.0이 출시됨('24.07)
OpenTofu vs Terraform
- OpenTofu와 Terraform은 동작 방식과 사용 방법이 거의 동일함
- 사용하는 명령어 정도가 terraform → tofu로 다름
- 기술적인 측면에서 OpenTofu v1.6과 Terraform v1.6이 기능적으로 매우 유사하며, 해당 버전을 기점으로 추가되는 기능이 달라질 예정임
- OpenTofu의 목표는 단일 회사가 로드맵을 지시할 수 없는 협력적인 방식으로 추진되는 것임
- OpenTofu는 Terraform v1.5에서 생성한 상태파일까지만 지원함
- OpenTofu는 Terraform과 달리 자체 프로바이더가 없으며 Terraform 프로바이더를 차용함
- OpenTofu는 별도의 레지스트리(registry.opentofu.org)를 사용함
2. OpenTofu 설치
Tenv 설치
Tenv는 Terraform, OpenTofu, Terragrunt, Atmos 버전을 한 번에 관리할 수 있는 도구입니다.
# 기존 Terraform 삭제
$ sudo apt remove terraform
# Tenv 설치
$ LATEST_VERSION=$(curl --silent https://api.github.com/repos/tofuutils/tenv/releases/latest | jq -r .tag_name)
$ curl -O -L "https://github.com/tofuutils/tenv/releases/latest/download/tenv_${LATEST_VERSION}_amd64.deb"
$ sudo dpkg -i "tenv_${LATEST_VERSION}_amd64.deb"
# Tenv 설치 확인
$ tenv --version
tenv version v2.7.9
$ tenv tofu -h
# Tenv 자동 완성
$ tenv completion bash > ~/.tenv.completion.bash
$ echo "source '~/.tenv.completion.bash'" >> ~/.bashrc
OpenTofu 설치
# 설치 지원 버전 확인
$ tenv tofu list-remote
# OpenTofu 설치
$ tenv tofu install 1.7.3
$ tenv tofu list
$ tenv tofu use 1.7.3
# OpenTofu 설치 확인
$ tofu -h
$ tofu version
3. OpenTofu v1.7.0
OpenTofu v1.7.0에서 제공하는 몇 가지 특징적인 기능에 대해서 소개합니다.
Provider-defined functions
프로바이더에 직접 함수를 정의하여 사용하는 방법입니다.
달리 말해, 내장 함수가 아닌 사용자 정의함수와 유사한 개념으로 이해하면 쉬울 것입니다.
- corefunc 프로바이더 예시 링크
terraform {
required_providers {
corefunc = {
source = "northwood-labs/corefunc"
version = "1.4.0"
}
}
}
provider "corefunc" {
}
output "test_1" {
value = provider::corefunc::str_snake("Hello world!")
}
output "test_2" {
value = provider::corefunc::str_camel("Hello world!")
}
required_providers 블록 안에 함수를 정의한 프로바이더(corefunc)의 소스와 버전을 명시하며, 함수를 호출할 때에는 provider::corefunc::str_ooooo("Hello world!")와 같은 형식을 사용합니다.
corefunc::str_snake 함수는 문자열을 소문자로 일괄 변경하고, 공백을 '_'로 변경 및 특수문자를 제거하는 함수입니다.
corefunc::str_camel 함수는 문자열을 소문자로 변경하되 공백으로 구분되는 따라오는 문자열의 첫 문자는 대문자로 변경하고, 공백과 특수문자를 제거하는 함수입니다.
- 실행 결과
$ tofu init && tofu plan && tofu apply -auto-approve
$ tofu output
test_1 = "hello_world"
test_2 = "helloWorld"
Loopable import blocks
기존 인프라 형상을 상태 파일로 가져오기 위해 구성 파일에 import 블록을 사용할 수 있습니다.
특히, 유사한 구성의 리소스를 반복적으로 가져와야 할 때 import 블록에 for_each 구문을 활용하면 효과적입니다.
- import 블록 사용 예시
이미 web, app 2개의 인스턴스는 실행중이고 상태 파일은 삭제된 것으로 상황을 가정합니다.
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}
variable "instance_ids" {
type = list(string)
default = ["i-05682fce159c49186", "i-07beb1f8c2d00ce36"]
}
variable "instance_tags" {
type = list(string)
default = ["web", "app"]
}
resource "aws_instance" "this" {
count = length(var.instance_tags)
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = {
Name = var.instance_tags[count.index]
}
}
import {
for_each = { for idx, item in var.instance_ids : idx => item }
to = aws_instance.this[tonumber(each.key)]
id = each.value
}
인스턴스 ID를 리스트 변수에 담고, import 블록 내에서 for_each와 함께 사용하여 인스턴스를 반복하여 가져옵니다.
- 실행 결과
$ tofu init -json && tofu apply -auto-approve
Apply complete! Resources: 2 imported, 0 added, 2 changed, 0 destroyed.
$ tofu state ls
data.aws_ami.ubuntu
aws_instance.this[0]
aws_instance.this[1]
State file encryption
OpenTofu는 실행 계획과 상태 파일을 암호화하는 기능을 지원합니다.
pbkdf2 키 프로바이더는 로컬 백엔드에서 암호화 기능을 제공합니다.
AES-GCM과 같은 암호화 방식으로 키를 생성하기 위해 길고 복잡한 문자열(passphrase)을 사용합니다.
- State file encrytion 예시
terraform {
encryption {
key_provider "pbkdf2" "my_passphrase" {
## Enter a passphrase here:
passphrase = "ChangeIt_123abcd"
}
method "aes_gcm" "my_method" {
keys = key_provider.pbkdf2.my_passphrase
}
## Remove this after the migration:
method "unencrypted" "migration" {
}
state {
method = method.aes_gcm.my_method
## Remove the fallback block after migration:
fallback{
method = method.unencrypted.migration
}
## Enable this after migration:
enforced = true
}
}
}
pbkdf2 키 프로바이더는 "ChangeIt_123abcd" 문자열을 사용하여 키를 생성합니다.
method는 AES-GCM 알고리즘으로 암호화하며, fallback은 암호화 과정에서 문제가 발생할 시 이전 암호화 설정으로 변경 적용하는 구문입니다.
method.unencrypted.migration은 암호화된 상태 파일을 평문으로 마이그레이션함을 의미합니다.
그리고 암호화되지 않은 환경 변수가 저장되는 것을 차단하기 위해서 enforced=true 옵션으로 조금 더 강화된 보안 정책을 적용할 수 있습니다.
- 실행 결과
- Rolling back encryption 예시
terraform {
encryption {
key_provider "pbkdf2" "my_passphrase" {
## Enter a passphrase here:
passphrase = "ChangeIt_123abcd"
}
method "aes_gcm" "my_method" {
keys = key_provider.pbkdf2.my_passphrase
}
## Remove this after the migration:
method "unencrypted" "migration" {
}
state {
method = method.unencrypted.migration
## Remove the fallback block after migration:
fallback{
method = method.aes_gcm.my_method
}
# Enable this after migration:
# enforced = false
}
}
}
method 방식이 method.unencrypted.migration으로 선언되어 암호화된 상태 파일이 평문으로 복호화됩니다.
- 실행 결과
State file encryption - AWS KMS
aws_kms 키 프로바이더는 원격 백엔드에서 암호화 기능을 제공합니다.
암/복호화는 대칭키(Symmetric)를 사용합니다.
- S3 백엔드 State 암호화 예시
사전에 S3 버킷과 KMS 키를 생성합니다.
# S3 버킷 생성
$ aws s3 mb s3://okms1017 --region ap-northeast-2
$ aws s3 ls | grep okms1017
# KMS 키 생성
$ CREATE_KEY_JSON=$(aws kms create-key --description "my text encrypt descript demo")
$ echo $CREATE_KEY_JSON | jq
# 키 ID확인
$ KEY_ID=$(echo $CREATE_KEY_JSON | jq -r ".KeyMetadata.KeyId")
$ echo $KEY_ID
# 키 alias 생성
$ export ALIAS_SUFFIX=okms1017
$ aws kms create-alias --alias-name alias/$ALIAS_SUFFIX --target-key-id $KEY_ID
$ aws kms list-aliases
$ aws kms list-aliases --query "Aliases[?AliasName=='alias/okms1017'].TargetKeyId" --output text
# CMK로 암호화 테스트
$ echo "Hello 123123" > secret.txt
$ aws kms encrypt --key-id alias/$ALIAS_SUFFIX --cli-binary-format raw-in-base64-out --plaintext file://secret.txt --output text --query CiphertextBlob | base64 --decode > secret.txt.encrypted
# 암호문 확인
$ cat secret.txt.encrypted
# 복호화 테스트
$ aws kms decrypt --ciphertext-blob fileb://secret.txt.encrypted --output text --query Plaintext | base64 --decode
terraform {
backend "s3" {
bucket = "okms1017"
key = "terraform.tfstate"
region = "ap-northeast-2"
encrypt = true
}
encryption {
key_provider "aws_kms" "kms" {
kms_key_id = "63483d75-ade8-4a0a-8e**********"
region = "ap-northeast-2"
key_spec = "AES_256"
}
method "aes_gcm" "my_method" {
keys = key_provider.aws_kms.kms
}
## Remove this after the migration:
method "unencrypted" "migration" {
}
state {
method = method.aes_gcm.my_method
## Remove the fallback block after migration:
fallback{
method = method.unencrypted.migration
}
# Enable this after migration:
# enforced = false
}
}
}
aws_kms 키 프로바이더는 AES-256 알고리즘을 적용하여 S3 버킷에 저장되는 상태 파일을 암호화합니다.
- 실행 결과
S3 버킷의 상태 파일을 로컬로 내려받아 암호화 여부를 확인합니다.
$ aws s3 ls s3://okms1017 --recursive
$ aws s3 cp s3://okms1017/terraform.tfstate .
download: s3://okms1017/terraform.tfstate to ./terraform.tfstate
Removed block
OpenTofu의 관리 대상에서 인프라의 특정 리소스를 제외하고자 할 때 유용한 방법입니다.
- Removed block 사용 예시
우선 Systems Manager Parameter를 생성하고 상태 파일에서 존재 여부를 확인합니다.
resource "aws_ssm_parameter" "this" {
count = length(var.instance_tags)
name = var.instance_tags[count.index]
type = "String"
value = aws_instance.this[count.index].id
}
$ tofu init && tofu apply -auto-approve
$ tofu state ls
data.aws_ami.ubuntu
aws_instance.this[0]
aws_instance.this[1]
aws_ssm_parameter.this[0]
aws_ssm_parameter.this[1]
Systems Manager Parameter Store 리소스를 그대로 유지하되, 상태 파일에서만 제거하여 관리 대상에서 제외합니다.
# resource "aws_ssm_parameter" "this" {
# count = length(var.instance_tags)
# name = var.instance_tags[count.index]
# type = "String"
# value = aws_instance.this[count.index].id
# }
removed {
from = aws_ssm_parameter.this
}
기존 리소스를 주석처리하고 removed 블록을 선언하여 상태 파일에서 제거합니다.
- 실행 결과
$ tofu apply -auto-approve
# aws_ssm_parameter.this[0] will be removed from the OpenTofu state but will not be destroyed
# aws_ssm_parameter.this[1] will be removed from the OpenTofu state but will not be destroyed
$ tofu state ls
data.aws_ami.ubuntu
aws_instance.this[0]
aws_instance.this[1]
Tofu Test
tofu test 명령어를 사용하여 실제 인프라를 생성하고 필수 조건을 충족하는지 구성 파일을 테스트할 수 있습니다.
테스트가 완료되면 OpenTofu는 생성한 리소스를 삭제합니다.
tofu test 명령어는 *.tftest.hcl 파일 또는 tests 디렉토리 내 파일들을 실행합니다.
- main.tf
variable "test" {
type = string
}
resource "local_file" "this" {
filename = "${path.module}/test.txt"
content = var.test
}
- tests/main.tftest.hcl
run "test" {
assert {
condition = file(local_file.this.filename) == var.test
error_message = "Incorrect content in file"
}
}
- test/terraform.tfvars
test = "t101-study-end"
- 결과 확인
$ tofu init && tofu test
tests/main.tftest.hcl... pass
run "test"... pass
Success! 1 passed, 0 failed.
Built-in functions changes
- 새로 추가된 내장 함수 : templatestring, base64gunzip, cidrcontains, urldecode, issensitive
- 기능이 변경·추가된 내장 함수 : nonsensitive, templatefile
CLI changes
- tofu init : -json 옵션 지원
- tofu plan : -concise 옵션 지원(refreshing log 생략 및 플랜 간소화)
- tofu console : Solaris/AIX 운영체제 지원
- XDG 디렉토리 사양 지원
- Alias 명령어
state list | state ls |
state move | state mv |
state remove | state rm |
Migrating to OpenTofu 1.7.x from Terraform
Terraform 여러 버전에서 OpenTofu 1.7 버전으로 마이그레이션하는 방법입니다. 링크
- Terraform v1.8.5 ▶ OpenTofu v1.7
# Terraform v1.8.5 설치
$ tenv tf list-remote
$ tenv tf install 1.8.5
$ tenv tf list
$ tenv tf use 1.8.5
$ terraform -version
Terraform v1.8.5
on linux_amd64
테라폼으로 실행한 현재 프로바이더의 정보와 상태 파일 정보는 다음과 같습니다.
$ tree .terraform
.terraform
└── providers
└── registry.terraform.io
└── hashicorp
└── aws
└── 5.61.0
└── linux_amd64
├── LICENSE.txt
└── terraform-provider-aws_v5.61.0_x5
$ cat terraform.tfstate
```
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
```
마이그레이션
1. 최신 상태 여부 확인
$ terraform plan
2. 상태 파일 및 구성 파일 백업
$ cp terraform.tfstate ../../backup/terraform.tfstate
$ cp *.tf ../../backup/
3. 코드 변경
A. S3 backend : skip_s3_checksum 옵션 사용 중이면 삭제
endpoints → sso 옵션 또는 AWS_ENDPOINT_URL 환경 변수 사용 중이면 삭제
B. Removed block : lifecycle 블록 삭제
만약 lifecycle 블록에서 destroy=true 설정이 되어 있을 시, 전체 removed 블록을 삭제할 것
C. Testing changes : mock_provider를 사용 중이면 이 기능없이 동작하도록 재구성 필요
override_resource, override_data, override_module을 사용 중이면 이 기능없이 동작하도록 재구성 필요
4. OpenTofu 초기화 및 플랜
$ tofu init
$ tree .terraform
.terraform
└── providers
├── registry.opentofu.org
│ └── hashicorp
│ └── aws
│ └── 5.61.0
│ └── linux_amd64
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ └── terraform-provider-aws
└── registry.terraform.io
└── hashicorp
└── aws
└── 5.61.0
└── linux_amd64
├── LICENSE.txt
└── terraform-provider-aws_v5.61.0_x5
$ tofu plan
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 1s [id=ami-0443a434a57db296a]
aws_instance.this[0]: Refreshing state... [id=i-02d67039b2e178dac]
aws_instance.this[1]: Refreshing state... [id=i-0b7bfa7ddc9094e60]
No changes. Your infrastructure matches the configuration.
OpenTofu has compared your real infrastructure against your configuration and
found no differences, so no changes are needed.
5. Apply
$ tofu apply -auto-approve
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 0s [id=ami-0443a434a57db296a]
aws_instance.this[1]: Refreshing state... [id=i-0b7bfa7ddc9094e60]
aws_instance.this[0]: Refreshing state... [id=i-02d67039b2e178dac]
No changes. Your infrastructure matches the configuration.
OpenTofu 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.
$ diff terraform.tfstate terraform.tfstate.backup
4c4
< "serial": 4,
---
> "serial": 3,
12c12
< "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]",
---
> "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
111c111
< "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]",
---
> "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
6. Tag 추가하여 테스트
$ tofu apply -auto-approve
[출처]
1) CloudNet@, T1014 실습 스터디
'IaC > Terraform' 카테고리의 다른 글
T1014 스터디 완주 후기 🎉 (0) | 2024.08.08 |
---|---|
[Terraform] 7-1. Karpenter controller를 EKS Fargate로 실행하기 (0) | 2024.07.28 |
[Terraform] 6-1. Terraform Enterprise + VCS-driven workflow (0) | 2024.07.21 |
[Terraform] 5-1. GitHub와 Atlantis를 연동하여 Terraform 자동화 환경 구축하기 (1) | 2024.07.14 |
[Terraform] 4-2. 다중 리전에서 동작하는 S3 버킷 구성하기 (0) | 2024.07.07 |