티스토리 뷰
[IaC] IaC/테라폼 이해, 기본 명령 사용법( command, HCL, block, resource)
Hayley Shim 2023. 10. 28. 17:58안녕하세요. 최근 도서 “테라폼으로 시작하는 IaC” 의 내용을 기준으로 스터디한 내용을 정리했습니다. 해당 글에서는 IaC와 테라폼에 대해 간단히 이해하고 기본 명령 사용법에 대해 알아보겠습니다.
1. IaC/테라폼 이해
IaC와 테라폼
- IaC : 코드형 인프라(Infrastructure as Code, IaC)는 코드를 통해 인프라를 관리하고 프로비저닝하는 것을 말합니다.
- 테라폼 : 하시코프사에서 공개한 IaC 도구로 “워크플로우에 집중, 코드형 인프라, 실용주의”라는 하시코프의 철학을 담은 도구입니다.
테라폼 제공 유형별 기능
- 테라폼 Core
- 테라폼 Cloud : Terraform Cloud는 팀이 Terraform을 사용하여 인프라를 프로비저닝하는 데 도움이 되는 애플리케이션
- 테라폼 Enterprise : 고급 보안 및 규정 준수 요구 사항이 있는 조직은 Terraform Cloud의 자체 호스팅 배포판인 Terraform Enterprise를 구입
테라폼 클라우드 가격정책 — [참고: Pricing, Feature]
- Free : 리소스 500개 까지 무료 → 커뮤니티 버전
- Standard : Free + 워크플로우 기능 추가 + 동시실행(Concurrency 개수 3개)
- Plus : 정책, 보안, 신뢰성, 확장성 등 기업형 고객에게 적합(대규모 사용자를 위한 비용모델)
- Enterprise : Plus와 대부분 유사하며 설치형 모델
테라폼 환경 구성
- 여러 실행 환경 구성 방법 중 OS 패키지 관리자 활용하여 테라폼을 설치합니다.
- OS에 맞게 테라폼 설치
# macOS 환경
# tfenv 설치
$ brew install tfenv
# 설치 가능 버전 리스트 확인
$ tfenv list-remote
# 테라폼 1.5.6 버전 설치
$ tfenv install 1.5.6
# tfenv로 설치한 버전 확인(변경 전)
$ tfenv list
  1.5.6
* 1.5.1 (set by /usr/local/Cellar/tfenv/3.0.0/version)
# 테라폼 1.5.6 버전 사용 설정 
$ tfenv use 1.5.6
# tfenv로 설치한 버전 확인(변경 후)
$ tfenv list
* 1.5.6 (set by /usr/local/Cellar/tfenv/3.0.0/version)
  1.5.1
# 테라폼 버전 정보 확인
$ terraform version
# 자동완성
$ terraform -install-autocomplete
## 참고 .zshrc 에 아래 추가됨
$ cat ~/.zshrc
autoload -U +X bashcompinit && bashcompinit
complete -o nospace -C /usr/local/bin/terraform terraformwindowOS에서 WSL2 리눅스 환경 설치 후 사용하는 것이 가능합니다. 하지만 WSL의 경우 종종 이슈가 발생하여 개인적으로 macOS 환경을 권장합니다.
2. OS 환경에 맞게 VS Code 설치 후 Externtions 확장 설치
- VS Code — [Extentions]에서 HashiCorp HCL 을 검색 후 설치합니다.

- 설정(Command+,) → 일반적으로 사용되는 설정 ⇒ Files: Auto Save 값을 afterDelay 로 선택하여 자동저장 설정을 합니다.

3. AWS CLI 설치 및 자격증명 설정
# 방안1. macOS 설치 방법
$ brew install awscli
# 방안2. Linux 설치 방법
$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
$ unzip awscliv2.zip
$ sudo ./aws/install
# aws cli 버전 확인
$ aws --version
aws-cli/2.13.13 Python/3.11.5 Darwin/22.6.0 source/x86_64 prompt/off
# aws cli 사용 시도(실패)
$ aws s3 ls
#(결과예시)
1.InvalidToken: An error occurred (InvalidToken) when calling the ListBuckets operation: The provided token is malformed or otherwise invalid.
2.configure 미설정 : Unable to locate credentials. You can configure credentials by running "aws configure".
---
# 방안1. aws configure 로 자격증명 설정
$ aws configure
... >> 입력
$ aws configure list
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile                <not set>             None    None
access_key     ****************67BY shared-credentials-file
secret_key     ****************67BY shared-credentials-file
    region           ap-northeast-2      config-file    ~/.aws/config
# 방안2. 환경 변수로 자격증명 설정
# Linux or macOS
$ export AWS_ACCESS_KEY_ID=AKIXXXXXXXXXX
$ export AWS_SECRET_ACCESS_KEY=QkHsXXXXXXXXXXXXXXXXXXXX
$ export AWS_DEFAULT_REGION=ap-northeast-2
# 페이저 사용 비활성화[참고: AWS CLI 페이지 매김]
$ export AWS_PAGER=""
# aws cli 사용 시도(성공)
$ aws s3 ls4. 실습에 편리한 Tool 설치
# macOS
$ brew install tree jq watch
# Linux
$ sudo apt install -y tree jq5. AWS 서울 리전(ap-northeast-2)에 default VPC 존재 확인 : 없을 경우 아래 (옵션) default VPC 생성 해두기 참고 — 링크
$ aws ec2 describe-vpcs --filter 'Name=isDefault,Values=true' | jq
{
...
$ aws ec2 describe-vpcs --filter 'Name=isDefault,Values=true' | jq '.Vpcs[0].VpcId'
"vpc-0dc6bc54bbf096999"
#aws ec2 describe-subnets --filter 'Name=vpc-id,Values=vpc-3912a952' --output table
$ aws ec2 describe-subnets --filter 'Name=vpc-id,Values=vpc-<자신의VPC ID>' --output table
--------------------------------------------------------------------------------------------------------------
|                                               DescribeSubnets                                              |
+------------------------------------------------------------------------------------------------------------+
||                                                  Subnets                                                 ||
|+------------------------------+---------------------------------------------------------------------------+|
||  AssignIpv6AddressOnCreation |  False                                                                    ||
||  AvailabilityZone            |  ap-northeast-2a                                                          ||
||  AvailabilityZoneId          |  apne2-az1                                                                ||
||  AvailableIpAddressCount     |  4091                                                                     ||
||  CidrBlock                   |  172.31.0.0/20                                                            ||
||  DefaultForAz                |  True                                                                     ||
||  EnableDns64                 |  False                    
# default vpc 없을 경우 생성
# default VPC를 생성
$ aws ec2 create-default-vpc
# default Subnet 생성
$ aws ec2 create-default-subnet --availability-zone ap-northeast-2a
$ aws ec2 create-default-subnet --availability-zone ap-northeast-2b
$ aws ec2 create-default-subnet --availability-zone ap-northeast-2c
$ aws ec2 create-default-subnet --availability-zone ap-northeast-2d
6. EC2 1대 배포
배포전 준비
# 각자 편한 디렉터리를 생성해준다
$ mkdir t101-1week-ec2
$ cd t101-1week-ec2
# Amazon Linux 2 최신 ami id 찾기 : ami-09bae2fe0380843e0 
#aws ec2 describe-images --owners self amazon
$ aws ec2 describe-images --owners self amazon --query 'Images[*].[ImageId]' --output text
$ aws ssm get-parameters-by-path --path /aws/service/ami-amazon-linux-latest
$ aws ssm get-parameters-by-path --path /aws/service/ami-amazon-linux-latest --query "Parameters[].Name"
$ aws ssm get-parameters-by-path --path /aws/service/ami-amazon-linux-latest --query "Parameters[].Value"
# EC2 생성 모니터링 - VS Code 터미널1에서 실행
$ export AWS_PAGER=""
$ while true; do aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text ; echo "------------------------------" ; sleep 1; doneEC2 1대 배포 실행 — 터미널1 : HCL Hashicorp Configuration Language 코드 파일 생성 — VS Code 터미널2에서 실행
# VS Code 터미널2
$ cat <<EOT > main.tf
provider "aws" {
  region = "ap-northeast-2"
}
resource "aws_instance" "example" {
  ami           = "ami-09bae2fe0380843e0"
  instance_type = "t2.micro"
}
EOT배포 실행 — VS Code 터미널2에서 실행
# 초기화
$ terraform init
$ ls -al
total 16
drwxr-xr-x  5 yhshim  staff  160 Sep  2 21:03 .
drwxr-xr-x  3 yhshim  staff   96 Sep  2 20:52 ..
drwxr-xr-x  3 yhshim  staff   96 Sep  2 21:03 .terraform
-rw-r--r--  1 yhshim  staff  252 Sep  2 21:03 .terraform.lock.hcl
-rw-r--r--  1 yhshim  staff  157 Sep  2 21:03 main.tf
$ tree .terraform
.terraform
└── providers
    └── registry.terraform.io
        └── hashicorp
            └── aws
                └── 4.29.0
                    └── darwin_arm64
                        └── terraform-provider-aws_v4.29.0_x5
# plan 실행 시 아래와 같은 정보가 출력
$ 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:
  # aws_instance.example will be created
  + resource "aws_instance" "example" {
      + ami                                  = "ami-09bae2fe0380843e0"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + host_resource_group_arn              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_partition_number           = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tags_all                             = (known after apply)
      + tenancy                              = (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_change          = false
      + vpc_security_group_ids               = (known after apply)
    }
Plan: 1 to add, 0 to change, 0 to destroy.
# apply 실행
$ terraform apply
 Enter a value: yes 입력
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_instance.example will be created
  + resource "aws_instance" "example" {
      + ami                                  = "ami-09bae2fe0380843e0"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + host_resource_group_arn              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_partition_number           = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tags_all                             = (known after apply)
      + tenancy                              = (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_change          = false
      + vpc_security_group_ids               = (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: yes
aws_instance.example: Creating...
aws_instance.example: Still creating... [10s elapsed]
aws_instance.example: Still creating... [20s elapsed]
aws_instance.example: Still creating... [30s elapsed]
aws_instance.example: Creation complete after 31s [id=i-0330d4be110f21edc]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
# 모니터링 : [터미널1]에 Name 확인EC2 태그 정보 수정 — 터미널2
cat <<EOT > main.tf
provider "aws" {
  region = "ap-northeast-2"
}
resource "aws_instance" "example" {
  ami           = "ami-09bae2fe0380843e0"
  instance_type = "t2.micro"
  tags = {
    Name = "t101-study"
  }
}
EOT배포 실행
# plan 실행 시 아래와 같은 정보가 출력
$ terraform plan
aws_instance.example: Refreshing state... [id=i-0330d4be110f21edc]
Terraform used the selected providers to generate the
following execution plan. Resource actions are indicated
with the following symbols:
  ~ update in-place
Terraform will perform the following actions:
  # aws_instance.example will be updated in-place
  ~ resource "aws_instance" "example" {
        id                                   = "i-0330d4be110f21edc"
      ~ tags                                 = {
          + "Name" = "t101-study"
        }
      ~ tags_all                             = {
          + "Name" = "t101-study"
        }
        # (29 unchanged attributes hidden)
        # (7 unchanged blocks hidden)
    }
Plan: 0 to add, 1 to change, 0 to destroy.
# apply 실행
$ terraform apply
 Enter a value: yes 입력
aws_instance.example: Refreshing state... [id=i-0330d4be110f21edc]
Terraform used the selected providers to generate the
following execution plan. Resource actions are indicated
with the following symbols:
  ~ update in-place
Terraform will perform the following actions:
  # aws_instance.example will be updated in-place
  ~ resource "aws_instance" "example" {
        id                                   = "i-0330d4be110f21edc"
      ~ tags                                 = {
          + "Name" = "t101-study"
        }
      ~ tags_all                             = {
          + "Name" = "t101-study"
        }
        # (29 unchanged attributes hidden)
        # (7 unchanged blocks hidden)
    }
Plan: 0 to add, 1 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: yes
aws_instance.example: Modifying... [id=i-0330d4be110f21edc]
aws_instance.example: Modifications complete after 0s [id=i-0330d4be110f21edc]
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
# 모니터링 : [터미널1]에 Name 확인

EC2 삭제 — 터미널2
# 리소스 삭제
$ terraform destroy
 Enter a value: yes 입력
혹은
$ terraform destroy -auto-approve
2. 기본 사용법
- 주요 커맨드
실습 디렉터리 생성 (workspaces)
# 실습 디렉터리 생성 후 이동
$ mkdir workspaces
$ cd workspaces
# 테라폼 실행
$ terraform
Usage: terraform [-version] [-help] <command> [args]
...VS Code 폴더 생성 (03.start) → 새파일 생성 (main.tf)
resource "local_file" "abc" {
content  = "abc!"
filename = "${path.module}/abc.txt"
}help & init 초기화
# 서브커맨드 help 지원
$ terraform console -help
$ terraform init -help
# init 초기화
# 테라폼 실행을 위해 코드 파일이 있는 디렉터리로 이동
# (참고) 테라폼이 실행되는 디렉터리 = 모듈(테라폼 코드 파일과 변수 파일), 기본 작업디렉터리는 '루트 모듈', 호출 모듈은 '자식 모듈'
$ cd 03.start/
# plan 실행 시 에러 출력
$ terraform plan
│ 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/local: 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
# 초기화 : 코드 사용 구문 기반으로 필요한 프로바이더 플러그인을 찾고 설치, 추가로 '프로바이더/모듈/백엔드' 구성 설정/변경 시 수행 필요
$ terraform init
$ ls -al
$ tree .terraform  # VS Code에서 탐색기 확인
.terraform
└── providers
    └── registry.terraform.io
        └── hashicorp
            └── local
                └── 2.4.0
                    └── darwin_arm64
                        └── terraform-provider-local_v2.4.0_x5
7 directories, 1 file- Error: Inconsistent dependency lock file : 참고
plan 계획 & apply 실행
# plan 실행 : 구성 내용을 바탕으로 어떤 리소스가 생성되는지 상세 내역 출력, 기본값 자동 입력 적용
$ 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:
  # local_file.abc will be created
  + resource "local_file" "abc" {
      + content              = "abc!"
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./abc.txt"
      + id                   = (known after apply)
    }
Plan: 1 to add, 0 to change, 0 to destroy. # 하나의 리소스가 추가되고, 변경되거나 삭제되는 것은 없을 예정
# plan 결과를 시스템 코드로 출력
$ terraform plan -detailed-exitcode
...
# 코드 확인 : 0(변경 사항이 없는 성공), 1(오류가 있음), 2(변경 사항이 있는 성공)
$ echo $?
2
# (참고) apply 결과에 적용 시도
$ terraform apply -auto-approve -detailed-exitcode
# apply 실행 : no 입력
$ terraform apply
...
Enter a value: no
...
# plan 결과를 지정된 파일(바이너리 형태) 이름으로 생성
$ terraform plan -out=tfplan
$ cat tfplan
+�"W    tfplanUT�G�dd��J�@����O�tᢈHwv)4ͥi�K��])N&�fj�    g��n}B�w+B �
                                                                    y��
�.����)����;�3�C"��     τ�425AORb                                      �
ٌ@E�Kʚ���O���Q�4*�����T�!໓e�(l����V8^�`�{�S0���$�
ƞW�"�
     �&k�Z���4�\K�R"�2[�N�'b���3L!��    7���a�؛�T       -��/��o�Ǽ�9��?{jB��{��\�;���q:�_.$��
�9�[]��:��w��P�ŕ�,�+�"W tfstateUT�G�dD�=                                                    8�����b���mzf���r,�mX�
B1
��P���rm�+�"W"�R�c+I���ݥqp�~��-�b��nr'�tk�Z��i9�w%)�1�ޑK�t�9�z����a��d��ru������Y�9��
                tfstate-prevUT�G�dD�=
B1
��P���rm�+�"W"�Rtfconfig/m-/main.tfUT�G�d�A��i9�w%)�1�ޑK�t�9�z����a��d��ru������Y�9��
1
 �����xIcD!m�� ��}h����罉�̅��������5��[/�q��E�^Ǘ㝊?��q���c���Pg�fV\+�"W  tfconfig/modules.jsonUT�G�d��RP��RPPPP�N�T�RPRҁp]2�@\=%.�Z�X@��P��k))+�"W     .terraform.lock.hclUT�G�dT�Mo�E
                                                       ���������{�cW��p��'q�����M6�ݤI�$���������t��Kn�(۹��v��>����x����px)��/q:iOwe��.��ݛ��?ꡄo���KYQ��l�%/��(���8��v����q*�S���/����ɛ���VϷ�OoG�þ��+�)N��x_ޗ=����ߕ�z����+���m{���o~�xw�������K�����;���ϧ���|x��-:�.����S��ʳ�6�\9�@V�Qk�Ԯ��F
                              �Jj
jyEͪ��<��q{��-��tn�f���8u̺2�Ve�9�)���]�G�B#v`X�B�uT05h�Bh�Vg\xE1(WL���xM��C��!Ʋ���%�$3%�q&�-W���ڣW'��X�ј���
���iZ�*45V�K
��Eu�6xd�Q�a7Y.ցyu��<����f�tE��N�F�-#+����ci�@ʜ�;�R^}��Qk�l��:����yA��l�Jck,�82�����U��6V�+*j��s�
                                                                                                 1�Ui!�3qWt���u��U-�*��̐��!   |z�-b��2�Zu!�tlC��Ô>����0UpL��tZ���6a�lR[W'��}-�e]��՞=M������?v��        ��P�2   ���+�"W�ŕ�,�    tfplanUT�G�d+�"W���rm�  itfstateUT�G�d+�"W���rm�
                        tfstate-prevUT�G�d+�"Wg�fV\     �tfconfig/m-/main.tfUT�G�d+�"W��k))     dtfconfig/modules.jsonUT�G�+�"W�2     ���     �.terraform.lock.hclUT�G�dPK��%      
$ file tfplan
tfplan: Zip archive data, at least v2.0 to extract, compression method=deflate
# apply 실행 : 실행계획이 있으므로 즉시 적용됨
$ terraform apply tfplan
$ ls -al abc.txt
-rwxr-xr-x  1 yhshim  staff  4 Sep  2 23:34 abc.txt
# apply 실행을 다시 하게 되면
# 테라폼은 선언적 구성 관리를 제공하는 언어로 멱등성 idempotence을 갖고, 
# 상태를 관리하기 때문에 동일한 구성에 대해서는 다시 실행하거나 변경하는 작업을 
# 수행하지 않음
$ terraform apply
local_file.abc: Refreshing state... [id=5678fb68a642f3c6c8004c1bdc21e7142087287b]
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.
$ terraform state list
local_file.abc
코드 파일 수정
# 코드 파일 수정
resource "local_file" "abc" {
content = "abc!"
filename = "${path.module}/abc.txt"
}
resource "local_file" "dev" {
  content  = "def!"
  filename = "${path.module}/def.txt"
}# 실행 > 어떻게 되나요?
$ terraform apply
...
Enter a value: yes
...
# 확인
$ terraform state list
$ tree
.
├── abc.txt
├── def.txt
├── main.tf
├── terraform.tfstate
├── terraform.tfstate.backup
└── tfplan
1 directory, 6 files
$ ls *.txt
abc.txt def.txt
# 현재 배포된 리소스 확인
$ terraform state list
local_file.abc
local_file.dev
# 변경 이전의 실행 계획 적용 시도하면 아래와 같은 에러 ㅏㄹ생
$ terraform apply tfplan
Error: Saved plan is stale
│ 
│ The given plan file can no longer be applied because the state was changed by another operation after the plan was created.다시 코드 내용 삭제
resource "local_file" "abc" {
content = "abc!"
filename = "${path.module}/abc.txt"
}# 실행 > 어떻게 되나요?
$ terraform apply
...
Enter a value: yes
...
Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.
  Enter a value: yes
local_file.dev: Destroying... [id=15f946cb27f0730866cefea4f0923248d9366cb0]
local_file.dev: Destruction complete after 0s]
# 확인
$ terraform state list
local_file.abc
$ tree
.
├── abc.txt
├── main.tf
├── terraform.tfstate
├── terraform.tfstate.backup
└── tfplan
1 directory, 5 files
$ ls *.txt
abc.txtdestroy 제거 & fmt
# 
$ terraform destroy
...
Enter a value: yes
...
# 확인
$ terraform state list
$ ls *.txt
zsh: no matches found: *.txt
# 적용 후 코드 파일 내용 확인 -> 들여쓰기 확인
$ terraform fmt
# fmt : format 또는 reformat 줄임 표시로 terraform tmt 명령어로 수행, 테라폼 구성 파일을 표준 형식과 표준 스타일로 적용. 코드 가독성 높임추가실습 : EC2 1대 배포 & 웹 서버 설정
- 테라폼 방식 : immutable, 이뮤터블(인프라스트럭처의 상태를 변경할 때, 기존의 인프라스트럭처를 수정하거나 업데이트하는 것이 아니라 새로운 infrastructure를 생성하여 이전 상태의 인프라스트럭처를 교체하는 방식)
실습 목표 : EC2 1대를 배포하면서 userdata에 웹 서버 설정하여 간단한 애플리케이션 설정을 자동화합니다.
배포 : Ubuntu 22.04 LTS 사용 (ami-0c9c942bd7bf113a2)
# 각자 편한 디렉터리를 생성
$ cd ..
$ mkdir t101-1week-web
$ cd t101-1week-web코드 파일 작성 : user_data 에 실행 명령어 작성
cat <<EOT > main.tf
provider "aws" {
  region = "ap-northeast-2"
}
resource "aws_instance" "example" {
  ami                    = "ami-0c9c942bd7bf113a2"
  instance_type          = "t2.micro"
  user_data = <<-EOF
              #!/bin/bash
              echo "Hello, T101 Study" > index.html
              nohup busybox httpd -f -p 8080 &
              EOF
  tags = {
    Name = "terraform-Study-101"
  }
}
EOT배포 실행
# init
$ terraform init
# plan
$ terraform plan
+ user_data                            = "d91ca31904077f0b641b5dd5a783401396ffbf3f"
# apply 실행
$ terraform apply -auto-approve웹 서버 접속 시도 : 터미널3에서 실행
# [터미널3] 변수 지정
$ PIP=<각자 자신의 EC2 Public IP>
$ PIP=3.35.10.132
while true; do curl --connect-timeout 1  http://$PIP:8080/ ; echo "------------------------------"; date; sleep 1; done문제 해결 : 웹 서버 접속
- 코드 파일 수정 : 보안 그룹 생성 후 연동
$ cat <<EOT > main.tf
provider "aws" {
  region = "ap-northeast-2"
}
resource "aws_instance" "example" {
  ami                    = "ami-0c9c942bd7bf113a2"
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.instance.id]
  user_data = <<-EOF
              #!/bin/bash
              echo "Hello, T101 Study" > index.html
              nohup busybox httpd -f -p 8080 &
              EOF
  tags = {
    Name = "Single-WebSrv"
  }
}
resource "aws_security_group" "instance" {
  name = var.security_group_name
  ingress {
    from_port   = 8080
    to_port     = 8080
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
variable "security_group_name" {
  description = "The name of the security group"
  type        = string
  default     = "terraform-example-instance"
}
output "public_ip" {
  value       = aws_instance.example.public_ip
  description = "The public IP of the Instance"
}
EOTError: with the retirement of EC2-Classic no new Security Groups can be created without referencing a VPC
│
│ with aws_security_group.instance,
│ on main.tf line 21, in resource “aws_security_group” “instance”:
│ 21: resource “aws_security_group” “instance” {
│
위와 같은 에러가 발생할 경우 아래와 같이 수정합니다 : 참고
provider "aws" {
  region = "ap-northeast-2"
}
resource "aws_default_vpc" "default" {
  tags = {
    Name = "Default VPC"
  }
}
resource "aws_instance" "example" {
  ami                    = "ami-0c9c942bd7bf113a2"
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.instance.id]
  user_data = <<-EOF
              #!/bin/bash
              echo "Hello, T101 Study" > index.html
              nohup busybox httpd -f -p 8080 &
              EOF
  tags = {
    Name = "Single-WebSrv"
  }
}
resource "aws_security_group" "instance" {
  name = var.security_group_name
  ingress {
    from_port   = 8080
    to_port     = 8080
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  vpc_id      = aws_default_vpc.default.id
  
}
variable "security_group_name" {
  description = "The name of the security group"
  type        = string
  default     = "terraform-example-instance"
}
output "public_ip" {
  value       = aws_instance.example.public_ip
  description = "The public IP of the Instance"
}배포 실행
# plan/apply
$ terraform plan
$ terraform apply -auto-approve
# 모니터링 : EC2 정보와 curl 접속 확인
# (옵션) 리소스 생성 그래프 확인
$ terraform graph
digraph {
        compound = "true"
        newrank = "true"
        subgraph "root" {
                "[root] aws_default_vpc.default (expand)" [label = "aws_default_vpc.default", shape = "box"]
                "[root] aws_instance.example (expand)" [label = "aws_instance.example", shape = "box"]
                "[root] aws_security_group.instance (expand)" [label = "aws_security_group.instance", shape = "box"]
                "[root] provider[\"registry.terraform.io/hashicorp/aws\"]" [label = "provider[\"registry.terraform.io/hashicorp/aws\"]", shape = "diamond"]
                "[root] var.security_group_name" [label = "var.security_group_name", shape = "note"]
                "[root] aws_default_vpc.default (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/aws\"]"
                "[root] aws_instance.example (expand)" -> "[root] aws_security_group.instance (expand)"
                "[root] aws_security_group.instance (expand)" -> "[root] aws_default_vpc.default (expand)"
                "[root] aws_security_group.instance (expand)" -> "[root] var.security_group_name"
                "[root] output.public_ip (expand)" -> "[root] aws_instance.example (expand)"
                "[root] provider[\"registry.terraform.io/hashicorp/aws\"] (close)" -> "[root] aws_instance.example (expand)"
                "[root] root" -> "[root] output.public_ip (expand)"
                "[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/aws\"] (close)"
        }
}
# graph 확인 > 파일 선택 후 오른쪽 상단 DOT 클릭
$ terraform graph > graph.dot리소스 삭제
# 리소스 삭제
$ terraform destroy -auto-approve2. HCL
- HCL HashiCorp Configuration Language은 하시코프사에서 IaC와 구성 정보를 명시하기 위해 개발된 오픈 소스 도구
HCL 표현식
// 한줄 주석 방법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
}- 테라폼으로 인프라를 구성하기 위한 선언 블록도 다수 존재 : terraform, resource, data, variable, local, output
3. 테라폼 블록
테라폼 블록 : 테라폼 구성을 명시하는 데 사용
- 테라폼 버전이나 프로바이더 버전과 같은 값들은 자동으로 설정되지만, 함께 작업할 때는 버전을 명시적으로 선언하고 필요한 조건을 입력하여 실행 오류를 최소화 할 것을 권장 [Docs] [Docs 예제]
- 버전 체계는 시맨틱 버전 관리 Semantic Versioning(SemVer) 방식을 따른다 [참고], [Kubernetes Release Versioning]
terraform {
  required_version = "~> 1.3.0" # 테라폼 버전
  required_providers { # 프로바이더 버전을 나열
    random = {
      version = ">= 3.0.0, < 3.1.0"
    }
    aws = {
      version = "4.2.0"
    }
  }
  cloud { # Cloud/Enterprise 같은 원격 실행을 위한 정보 [참고: Docs]
    organization = "<MY_ORG_NAME>"
    workspaces {
      name = "my-first-workspace"
    }
  }
  backend "local" { # state를 보관하는 위치를 지정 [참고: Docs, local, remote, s3]
    path = "relative/path/to/terraform.tfstate"
  }
}테라폼 버전 : required_version
프로바이더 버전
- 테라폼 0.13 버전 이전에는 provider 블록에 함께 버전을 명시했지만 해당 버전 이후 프로바이더 버전은 terraform 블록에서 required_providers에 정의
- 코드 파일 수정 : 프로바이더 버전을 과하게 높게 설정 — 링크
terraform {
  required_version = ">= 1.0.0"
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = ">=10000.0.0"
    }
  }
}
resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}cloud 블록
- Terraform Cloud, Terraform Enterprise는 CLI, VCS, API 기반의 실행 방식을 지원하고 cloud 블록으로 선언
백엔드 블록
- 백엔드 블록의 구성은 테라폼 실행 시 저장되는 State(상태 파일)의 저장 위치를 선언 (기본: local)
- 주의할 점은 하나의 백엔드만 허용한다는 점
- State에는 외부로 노출되면 안 되는 패스워드 또는 인증서 정보 같은 민감한 데이터들이 포함될 수 있으므로 State의 접근 제어 및 안전한 관리방안 대책수립이 필요
4. 리소스
리소스 구성 : 리소스 블록은 선언된 항목을 생성하는 동작을 수행
종속성
- 테라폼 종속성은 resource, module 선언으로 프로비저닝되는 각 요소의 생성 순서를 구분함
- 기본적으로 다른 리소스에서 값을 참조해 불러올 경우 생성 선후 관계에 따라 작업자가 의도하지는 않았지만 자동으로 연관 관계가 정의되는 암시적 종속성을 갖게 되고, 강제로 리소스 간 명시적 종속성을 부여할 경우에는 메타인수인 depends_on을 활용함 — [참고: Docs]
- 리소스 참조값을 설정해 두 개의 리소스 간 암시적 종속성 부여
- 리소스의 속성을 주입하지 않아도 두 리소스 간에 종속성이 필요한 경우에, depends_on 선언으로 적용 가능(명시적 종속성 부여)
리소스 속성 참조
- 리소스 구성에서 참조 가능한 값은 인수와 속성
- 인수 : 리소스 생성 시 사용자가 선언하는 값
- 속성 : 사용자가 설정하는 것은 불가능하지만 리소스 생성 이후 획득 가능한 리소스 고유 값
수명주기
- lifecycle은 리소스의 기본 수명주기를 작업자가 의도적으로 변경하는 메타인수다. 메타인수 내에는 아래 선언이 가능.
- create_before_destroy (bool): 리소스 수정 시 신규 리소스를 우선 생성하고 기존 리소스를 삭제
- prevent_destroy (bool): 해당 리소스를 삭제 Destroy 하려 할 때 명시적으로 거부
- ignore_changes (list): 리소스 요소에 선언된 인수의 변경 사항을 테라폼 실행 시 무시
- precondition: 리소스 요소에 선언해 인수의 조건을 검증
- postcondition: Plan과 Apply 이후의 결과를 속성 값으로 검증
위 내용 외에 도서 ‘Terraform: Up & Running(By Yevgeniy Brikman)’ 를 기준으로 작성된 지난 블로그 글을 참고하여 테라폼 학습에 도움되시길 바랍니다.
[IaC] Terraform tips & tricks — Loops & Conditions
[IaC] Managing Secrets with Terraform
[IaC] Production-grade Terraform code
written in 2023.9.3
https://medium.com/techblog-hayleyshim/iac-iac-%ED%85%8C%EB%9D%BC%ED%8F%BC-%EC%9D%B4%ED%95%B4-%EA%B8%B0%EB%B3%B8-%EB%AA%85%EB%A0%B9-%EC%82%AC%EC%9A%A9%EB%B2%95-90bd4bd8bae9
'Programming > IaC' 카테고리의 다른 글
| [IaC] 기본 사용법( condition, function, provisioner, null_resource와 terraform_data, moved block, cli를 위한 시스템 환경 변수) & 프로바이더 (0) | 2023.10.28 | 
|---|---|
| [IaC] 기본 사용법( data source, input variables, local, output, loop) (0) | 2023.10.28 | 
| [IaC] Working with Multiple Providers (0) | 2023.10.28 | 
| [IaC] GKE configuration deep dive (0) | 2023.10.28 | 
| [IaC] Production-grade Terraform code (0) | 2023.10.28 | 
- Total
- Today
- Yesterday
- GKE
- IaC
- 혼공챌린지
- autoscaling
- GCP
- handson
- OS
- NFT
- operator
- 혼공단
- AI
- security
- S3
- CICD
- SDWAN
- 파이썬
- VPN
- terraform
- k8s cni
- k8s calico
- AWS
- EKS
- k8s
- 도서
- cni
- 혼공파
- cloud
- NW
- PYTHON
- gcp serverless
| 일 | 월 | 화 | 수 | 목 | 금 | 토 | 
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 | 
| 12 | 13 | 14 | 15 | 16 | 17 | 18 | 
| 19 | 20 | 21 | 22 | 23 | 24 | 25 | 
| 26 | 27 | 28 | 29 | 30 | 31 |