목표
- 테라폼 기본 사용법에 대해 알아본다.
- 데이터소스, 변수, 로컬, 출력, 반복문에 대해서 알아 본다.
데이터 소스
데이터 소스는 테라폼으로 정의되지 않은 외부 리소스 또는 저장된 정보를 테라폼 내에서 참조할 때 사용한다
- 식별자는 동일한 유형에서 중복될 수 없다.
- 이름 뒤에는 데이터 소스 유형에 대한 구성 인수들은 { } 안에 선언한다.
인수가 필요하지 않은 유형도 있지만, 그때에도 { } 는 입력한다
- 데이터 소스를 정의할 때 사용 가능한 메타인수는 다음과 같다.
- depends_on : 종속성을 선언하며, 선언된 구성요소와의 생성 시점에 대해 정의
- count : 선언된 개수에 따라 여러 리소스를 생성
- for_each : map 또는 set 타입의 데이터 배열의 값을 기준으로 여러 리소스를 생성
- lifecycle : 리소스의 수명주기 관리
실습 확인을 위해서 abc.txt 파일 생성
$ cat abc.txt
t101 study - 2week
테라폼 코드 적용
$ terraform init && terraform plan && terraform apply -auto-approve
data.local_file.abc: Reading...
데이터소스 생성 확인
$ terraform state list
data.local_file.abc
$ echo "data.local_file.abc" | terraform console
[도전과제1] 위 리전 내에서 사용 가능한 가용영역 목록 가져오기를 사용한 VPC 리소스 생성 실습 진행, 또는 관련된 데이터 소스를 사용한 실습한 내용을 글로 작성.
# Declare the data source
data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_subnet" "primary" {
availability_zone = data.aws_availability_zones.available.names[0]
# e.g. ap-northeast-2a
}
resource "aws_subnet" "secondary" {
availability_zone = data.aws_availability_zones.available.names[1]
# e.g. ap-northeast-2b
}
aws_vpc 리소스 생성
vpc_id 연결 필요 확인
data "aws_availability_zones" "available" {
state = "available"
}
# aws_vpc 생성
resource "aws_vpc" "test01-vpc" {
cidr_block = "10.10.0.0/16"
tags = {
Name = "test-vpc"
}
}
resource "aws_subnet" "primary" {
# vpc_id 추가
vpc_id = aws_vpc.test01-vpc.id
availability_zone = data.aws_availability_zones.available.names[0]
}
resource "aws_subnet" "secondary" {
# vpc_id 추가
vpc_id = aws_vpc.test01-vpc.id
availability_zone = data.aws_availability_zones.available.names[1]
}
terraform plan에서는 문제가 없었지만,
terraform apply시 에러가 발생되면서, 서브넷이 생성되지 않았다.
aws_subnet 리소스에서 추가적인 에러가 발견 되었다.
cidrBlock인데 이것도 명시해줘야 하는것 같았다.
cidr 인수를 추가해줬다.
data "aws_availability_zones" "available" {
state = "available"
}
# aws_vpc 생성
resource "aws_vpc" "test01-vpc" {
cidr_block = "10.10.0.0/16"
tags = {
Name = "test-vpc"
}
}
resource "aws_subnet" "primary" {
# vpc_id 추가
vpc_id = aws_vpc.test01-vpc.id
# cidr_block 추가
cidr_block = "10.10.0.0/24"
availability_zone = data.aws_availability_zones.available.names[0]
}
resource "aws_subnet" "secondary" {
# vpc_id 추가
vpc_id = aws_vpc.test01-vpc.id
# cidr_block 추가
cidr_block = "10.10.1.0/24"
availability_zone = data.aws_availability_zones.available.names[1]
}
의존관계도 확인 했다.
변수
입력 변수는 인프라를 구성하는 데 필요한 속성 값을 정의해 코드의 변경 없이 여러 인프라를 생성하는 데 목적이 있다.
테라폼에서는 이것을 입력 변수 Input Variables 로 정의한다.
- 변수 정의 시 사용 가능한 메타인수
- default : 변수 값을 지정하지 않으면 기본값이 전달됨, 기본값이 없으면 대화식으로 사용자에게 변수에 대한 정보를 물어봄
- type : 변수에 허용되는 값 유형 정의, 유형을 지정하지 않으면 any 유형으로 간주
(string, number, bool, list, map, set, object, tuple) - description : 입력 변수의 설명
- validation : 변수 선언의 제약조건을 추가해 유효성 검사 규칙을 정의 - 링크
- sensitive : 민감한 변수 값임을 알리고 테라폼의 출력문에서 값 노출을 제한 (암호 등 민감 데이터의 경우) - 링크
- sensitive 처리하시더라도 state 파일에 평문으로 남게 됨 - 링크
- 이러한 부분을 처리하기 위해 TFC/TFE를 통해 안전하게 저장하거나 S3 암호화 기능등을 통해서 보안을 강화 필요
- nullable : 변수에 값이 없어도 됨을 지정
- 기본 유형
- string : 글자 유형
- number : 숫자 유형
- bool : true 또는 false
- any : 명시적으로 모든 유형이 허용됨을 표시
- 집합 유형
- list (<유형>): 인덱스 기반 집합
- map (<유형>): 값 = 속성 기반 집합이며 키값 기준 정렬
- set (<유형>): 값 기반 집합이며 정렬 키값 기준 정렬
- object ({<인수 이름>=<유형>, …})
- tuple ([<유형>, …])
- 유효성 검사 : 입력되는 변수 타입 지정 이외, 사용자 지정 유효성 검사가 가능
- 변수 블록 내에 validation 블록에서 조건인 condition에 지정되는 규칙이 true 또는 false를 반환해야 하며, error_message는 condition 값의 결과가 false 인 경우 출력되는 메시지를 정의한다.
- regex 함수는 대상의 문자열에 정규식을 적용하고 일치하는 문자열을 반환하는데, 여기에 can 함수를 함께 사용하면 정규식에 일치하지 않는 경우의 오류를 검출한다.
- validation 블록은 중복으로 선언할 수 있다.
variable "image_id" {
type = string
description = "The id of the machine image (AMI) to use for the server."
validation {
condition = length(var.image_id) > 4
error_message = "The image_id value must exceed 4."
}
validation {
# regex(...) fails if it cannot find a match
condition = can(regex("^ami-", var.image_id))
error_message = "The image_id value must starting with \"ami-\"."
}
}
default 값이 없어 value 프롬프트가 발생했고,
4글자 미만으로 에러 메시지가 발생했다.
- 변수 참조 : variable은 코드 내에서 var.<이름>으로 참조된다.
variable "my_password" {}
resource "local_file" "abc" {
content = var.my_password
filename = "${path.module}/abc.txt"
}
변수를 참조할 수 있다.
- 변수 입력 방식과 우선순위
- variable의 목적은 코드 내용을 수정하지 않고 테라폼의 모듈적 특성을 통해 입력되는 변수로 재사용성을 높이는 데 있다.
- 특히 입력 변수라는 명칭에 맞게 사용자는 프로비저닝 실행 시에 원하는 값으로 변수에 정의할 수 있다.
- 선언되는 방식에 따라 변수의 우선순위가 있으므로, 이를 적절히 사용해 로컬 환경과 빌드 서버 환경에서의 정의를 다르게 하거나, 프로비저닝 파이프라인을 구성하는 경우 외부 값을 변수에 지정할 수 있다.
- 민감한 변수 취급 : 입력 변수의 민감 여부 선언 가능
- 기본값 추가로 입력 항목은 발생하지 않지만, 출력에서 참조되는 변수 값이(sensitive)로 감춰지는 것을 확인 할 수 있다
variable "my_password" {
default = "password"
sensitive = true
}
resource "local_file" "abc" {
content = var.my_password
filename = "${path.module}/abc.txt"
}
sensitive 설정 된 것을 확인 할 수 있다.
- 변수 입력 방식과 우선순위
- variable의 목적은 코드 내용을 수정하지 않고 테라폼의 모듈적 특성을 통해 입력되는 변수로 재사용성을 높이는 데 있다.
- 특히 입력 변수라는 명칭에 맞게 사용자는 프로비저닝 실행 시에 원하는 값으로 변수에 정의할 수 있다.
- 선언되는 방식에 따라 변수의 우선순위가 있으므로, 이를 적절히 사용해 로컬 환경과 빌드 서버 환경에서의 정의를 다르게 하거나, 프로비저닝 파이프라인을 구성하는 경우 외부 값을 변수에 지정할 수 있다.
- https://spacelift.io/blog/terraform-tfvars
도전과제2: 위 3개 코드 파일 내용에 리소스의 이름(myvpc, mysubnet1 등)을 반드시! 꼭! 자신의 닉네임으로 변경해서 배포 실습해보세요!
vpc.tf
data "aws_availability_zones" "available" {
state = "available"
}
# aws_vpc 생성
resource "aws_vpc" "thumbup-vpc" {
cidr_block = "10.10.0.0/16"
tags = {
Name = "thumbup-vpc"
}
}
resource "aws_subnet" "thumbup-subnet1" {
# vpc_id 추가
vpc_id = aws_vpc.thumbup-vpc.id
cidr_block = "10.10.0.0/24"
availability_zone = data.aws_availability_zones.available.names[0]
}
resource "aws_internet_gateway" "myigw" {
vpc_id = aws_vpc.thumbup-vpc.id
}
sg.tf
resource "aws_security_group" "mysg" {
vpc_id = aws_vpc.thumbup-vpc.id
name = "T101 SG"
description = "T101 Study SG"
}
resource "aws_security_group_rule" "mysginbound" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.mysg.id
}
resource "aws_security_group_rule" "mysgoutbound" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.mysg.id
}
ec2.tf
resource "aws_security_group" "mysg" {
vpc_id = aws_vpc.thumbup-vpc.id
name = "T101 SG"
description = "T101 Study SG"
}
resource "aws_security_group_rule" "mysginbound" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.mysg.id
}
resource "aws_security_group_rule" "mysgoutbound" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.mysg.id
}
local 지역 값
코드 내에서 사용자가 지정한 값 또는 속성 값을 가공해 참조 가능한 local (지역 값)은 외부에서 입력되지 않고, 코드 내에서만 가공되어 동작하는 값을 선언한다.
- `local`은 입력 변수와 달리 선언된 모듈 내에서만 접근 가능하고, 변수처럼 실행 시에 입력받을 수 없다.
- `local`은 사용자가 테라폼 코드를 구현할 때 값이나 표현식을 반복적으로 사용할 수 있는 편의를 제공한다.
- 하지만 빈번하게 여러 곳에서 사용되는 경우 실제 값에 대한 추적이 어려워져 유지 관리 측면에서 부담이 발생할 수 있으므로 주의해야 한다.
- lcoals에 선언한 로컬 변수 이름은 전체 루트 모듈 내에서 유일해야 한다.
- 정의되는 속성 값은 지정된 값의 형태에 따라 다양한 유형으로 정의할 수 있다.
출력 output
출력 값은 주로 테라폼 코드의 프로비저닝 수행 후의 결과 속성 값을 확인하는 용도로 사용된다.
또한 프로그래밍 언어에서 코드 내 요소 간에 제한된 노출을 지원하듯, 테라폼 모듈 간, 워크스페이스 간 데이터 접근 요소로도 활용할 수 있다.
- 루트 모듈에서 사용자가 확인하고자 하는 특정 속성 출력
- 자식 모듈의 특정 값을 정의하고 루트 모듈에서 결과를 참조
- 서로 다른 루트 모듈의 결과를 원격으로 읽기 위한 접근 요소
resource "local_file" "abc" {
content = "abc123"
filename = "${path.module}/abc.txt"
}
output "file_id" {
value = local_file.abc.id
}
output "file_abspath" {
value = abspath(local_file.abc.filename)
}
결과값
Outputs:
file_abspath = "/home/shin/workspaces/3.8/abc.txt"
file_id = "6367c48dd193d56ea7b0baad25b19455e529f5ee"
반복문
list 형태의 값 목록이나 Key-Value 형태의 문자열 집합인 데이터가 있는 경우 동일한 내용에 대해 테라폼 구성 정의를 반복적으로 하지 않고 관리할 수 있다.
- count : 반복문, 정수 값만큼 리소스나 모듈을 생성
resource "local_file" "abc" {
count = 5
content = "abc${count.index}"
filename = "${path.module}/abc${count.index}.txt"
}
output "fileid" {
value = local_file.abc.*.id
}
output "filename" {
value = local_file.abc.*.filename
}
output "filecontent" {
value = local_file.abc.*.content
}
- for_each : 반복문, 선언된 key 값 개수만큼 리소스를 생성
- 리소스 또는 모듈 블록에서 for_each에 입력된 데이터 형태가 map 또는 set이면, 선언된 key 값 개수만큼 리소스를 생성하게 된다
- 특징: key 값은 count의 index와는 달리 고유하므로 중간에 값을 삭제한 후 다시 적용해도 삭제한 값에 대해서만 리소스를 삭제한다.
variable "names" {
default = {
a = "content a"
b = "content b"
c = "content c"
}
}
resource "local_file" "abc" {
for_each = var.names
content = each.value
filename = "${path.module}/abc-${each.key}.txt"
}
resource "local_file" "def" {
for_each = local_file.abc
content = each.value.content
filename = "${path.module}/def-${each.key}.txt"
}
- for : 복합 형식 값의 형태를 변환하는 데 사용 ← for_each와 다름
- for 구문을 사용하는 몇 가지 규칙은 다음과 같다
- list 유형의 경우 반환 받는 값이 하나로 되어 있으면 값을, 두 개의 경우 앞의 인수가 인덱스를 반환하고 뒤의 인수가 값을 반환
- 관용적으로 인덱스는 i, 값은 v로 표현
- map 유형의 경우 반환 받는 값이 하나로 되어 있으면 키를, 두 개의 경우 앞의 인수가 키를 반환하고 뒤의 인수가 값을 반환
- 관용적으로 키는 k, 값은 v로 표현
- 결과 값은 for 문을 묶는 기호가 **[ ]**인 경우 tuple로 반환되고 **{ }**인 경우 object 형태로 반환
- object 형태의 경우 키와 값에 대한 쌍은 ⇒ 기호로 구분
- { } 형식을 사용해 object 형태로 결과를 반환하는 경우 키 값은 고유해야 하므로 값 뒤에 그룹화 모드 심볼(…)를 붙여서 키의 중복을 방지(SQL의 group by 문 또는 Java의 MultiValueMap과 같은 개념)
- if 구문을 추가해 조건 부여 가능
- list 유형의 경우 반환 받는 값이 하나로 되어 있으면 값을, 두 개의 경우 앞의 인수가 인덱스를 반환하고 뒤의 인수가 값을 반환
- for 구문을 사용하는 몇 가지 규칙은 다음과 같다
variable "names" {
default = ["a", "b", "c"]
}
resource "local_file" "abc" {
content = jsonencode([for s in var.names : upper(s)]) # 결과 : ["A", "B", "C"]
filename = "${path.module}/abc.txt"
}
- dynamic : 리소스 내부 속성 블록을 동적인 블록으로 생성
- 리소스 내의 블록 속성(Attributes as Blocks)은 리소스 자체의 반복 선언이 아닌 내부 속성 요소 중 블록으로 표현되는 부분에 대해서만 반복 구문을 사용해야 하므로, 이때 dynamic 블록을 사용해 동적인 블록을 생성 할 수 있다.
- dynamic 블록을 작성하려면, 기존 블록의 속성 이름을 dynamic 블록의 이름으로 선언하고 기존 블록 속성에 정의되는 내용을 content 블록에 작성한다.
- 반복 선언에 사용되는 반복문 구문은 for_each를 사용한다. 기존 for_each 적용 시 each 속성에 key, value가 적용되었다면 dynamic에서는 dynamic에 지정한 이름에 대해 속성이 부여된다.
- dynamic 블록 활용 예
느낀점
기본이 있어야 예상치 못했던 이슈를 줄일 수 있는데,
스터디에서 기본을 학습하면서 서비스에서 사용시
조심해야 할 부분들 알 수 있었던 것 같습니다.
'테라폼 > T101[3기]' 카테고리의 다른 글
T101 - 5주차 / 02 (0) | 2023.10.07 |
---|---|
T101 - 5주차 / 01 (0) | 2023.10.06 |
T101 - 4주차 (0) | 2023.09.24 |
T101 - 3주차 (0) | 2023.09.16 |
T101 - 1주차 (0) | 2023.09.02 |