본문 바로가기
DevOps

[T1014-실습] OpenTofu 1.7.0 (2)

by 서어켜엉 2024. 8. 4.
해당 내용은 cloudNet@ 팀의 가시다 님이 진행하는 테라폼 스터디 T101 4기에서 학습한 내용을 정리한 것입니다.

4. State file encryption - AWS KMS

  • AWS KMS 소개
    • AWS 키 관리 서비스 KMS는 공유 하드웨어 보안 모듈HSM 을 사용하면서 암호화키생성하고 관리할 수 있게 도와준다.
    • CloudHSM은 AWS 내에서 암호화키를 관리할 수 있지만 보안 강화를 위해 전용 HSM을 사용할 수 있는 서비스다.
  • [사전준비] AWS KMS 생성 및 실습 : 대칭 키 생성 후 평문 파일을 암호화 및 복호화
# 키 생성(기본값)
# aws kms create-key --description "my text encrypt descript demo"
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=<각자 닉네임>
export ALIAS_SUFFIX=smlim
aws kms create-alias --alias-name alias/$ALIAS_SUFFIX --target-key-id $KEY_ID

# 생성한 별칭 확인 : 키 ID 메모하두기, 아래 테라폼 코드에서 사용
aws kms list-aliases
aws kms list-aliases --query "Aliases[?AliasName=='alias/<각자 닉네임>'].TargetKeyId" --output text
aws kms list-aliases --query "Aliases[?AliasName=='alias/smlim'].TargetKeyId" --output text
c0bfc529-1ede-44f3-a7e5-ae60814c1ca1

# CMK로 평문을 암호화해보기
echo "Hello 123123" > secrect.txt
aws kms encrypt --key-id alias/$ALIAS_SUFFIX --cli-binary-format raw-in-base64-out --plaintext file://secrect.txt --output text --query CiphertextBlob | base64 --decode > secrect.txt.encrypted

# 암호문 확인
cat secrect.txt.encrypted

# 복호화해보기
aws kms decrypt --ciphertext-blob fileb://secrect.txt.encrypted --output text --query Plaintext | base64 --decode
Hello 123123

 

실습

  • 8.3 디렉토리 backend.tf 파일 수정
terraform {
  backend "s3" {
    bucket = "smlim-t1014" # 각자 자신의 S3 버킷명
    key = "terraform.tfstate"
    region = "ap-northeast-2"
    encrypt = true
  }

  encryption {
    key_provider "aws_kms" "kms" {
      kms_key_id = "f8a70e42-e7b6-456b-bf1c-d71b7937a09b" # 각자 자신의 KMS ID
      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
    }
  }
}

 

 

5. Removed block

  • Removed block을 사용하면 상태 파일에서 리소스를 제거하면서도 인프라에 유지할 수 있습니다.

튜토리얼

resource "local_file" "test" {
  content = "Hello world!"
  filename = "test.txt"
}
  • 위 리소스 apply 한 후에 main.tf 파일 수정
removed {
  from = local_file.test
}
  • 다시 apply

test.txt 파일은 존재하지만 state 파일에 local_file.test 리소스는 존재하지 않음.

 

실습

  • 8.3 에서 사용한 main.tf 파일 복사
mkdir 8.4 && cd 8.4
cp ../8.3/main.tf .
touch backend.tf

 

  • main.tf 작성
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-0fb60867d197a2ee8", "i-065168c51db4e2f00"] # 각자 자신의 EC2 ID를 기입 할 것
}

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
}

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
tree .terraform

# 2개 리소스는 import , 2개 리소스는 생성
tofu apply -auto-approve
Plan: 2 to import, 2 to add, 0 to change, 0 to destroy.
aws_instance.this[0]: Importing... [id=i-0e2d4475790337a81]
aws_instance.this[0]: Import complete [id=i-0e2d4475790337a81]
aws_instance.this[1]: Importing... [id=i-00a4daebb71942280]
aws_instance.this[1]: Import complete [id=i-00a4daebb71942280]
aws_ssm_parameter.this[1]: Creating...
aws_ssm_parameter.this[0]: Creating...
aws_ssm_parameter.this[1]: Creation complete after 0s [id=app]
aws_ssm_parameter.this[0]: Creation complete after 0s [id=web]

#
tofu state ls
tofu show
tofu state show 'aws_ssm_parameter.this[0]'
tofu state show 'aws_ssm_parameter.this[1]'

# tfstate 파일 확인 : VSCODE에서 열어보기
cat terraform.tfstate | jq
...
      "type": "aws_ssm_parameter",
      "name": "this",
      "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]",
      "instances": [
        {
          "index_key": 0,
          "schema_version": 0,
          "attributes": {
            "allowed_pattern": "",
            "arn": "arn:aws:ssm:ap-northeast-2:911283464785:parameter/web",
            "data_type": "text",
            "description": "",
            "id": "web",
            "insecure_value": null,
            "key_id": "",
            "name": "web",
            "overwrite": null,
            "tags": null,
            "tags_all": {},
            "tier": "Standard",
            "type": "String",
            "value": "i-0e2d4475790337a81",
...

# parameters 정보 확인
aws ssm describe-parameters | jq
aws ssm get-parameter --name "web"
aws ssm get-parameter --name "web" --query "Parameter.Value" --output text
aws ssm get-parameter --name "app"
aws ssm get-parameter --name "app" --query "Parameter.Value" --output text

 

  • 파라미터 스토어 리소스만 tfstate 에서 제거하고, AWS 상에는 유지하도록 설정
  • main.tf 파일 수정
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-0fb60867d197a2ee8", "i-065168c51db4e2f00"] # 각자 자신의 EC2 ID를 기입 할 것
}

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
}

# 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
}

 

6. Test

  • tofu test 명령을 사용하면 실제 인프라를 생성하고 필요한 조건이 충족되는지 확인하여 OpenTofu 구성을 테스트할 수 있습니다. 테스트가 완료되면 OpenTofu는 생성한 리소스를 삭제합니다.
  • Usage : tofu test [options]
    • 이 command은 현재 디렉토리나 tests라는 디렉토리에 있는 모든 *.tftest.hcl 파일을 실행합니다.

실습

mkdir 8.5 && cd 8.5
touch main.tf
mkdir tests
touch tests/main.tftest.hcl
touch tests/terraform.tfvars
  • 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"
  }
}
  • tests/terraform.tfvars 파일 작성
test = "t101-study-end"
  • 실행 후 확인
# Test 실행
tofu test 

# Apply 확인
tofu apply -auto-approve
tofu state list
cat test.txt