2주차 테라폼 기초 - 데이터 소스/ 입력 변수 variable/ local 지역 값/ 출력/ 반복문

1. 데이터 소스

  • 테라폼으로 정의 되지 않은 외부 리소스 혹은 저장된 정보를 테라폼 내에서 참조해야 될때 사용합니다.

data "local_file" "abc" {    // 데이터 소스 블록은 data 로 시작,
                             // 데이터 소스 유형은 `프로바이더이름_리소스 유형`
  filename = "${path.module}/abc.txt"   // 구성 인수들은 { } 안에 선언
}

A. 데이터 소스 구성

1. 데이터 소스를 생성해서 참조를 해보자!!
data "local_file" "abc" {
  filename = "${path.module}/abc.txt"
}

echo "t101 study - 2week" > abc.txt

terraform console
> // 
data.local_file.abc 
의 해당 결과를 보여준다!

B. 데이터 소스 속성 참조

1. 리소스와 는 달리 앞에 data가 붙는것을 눈여겨 봐야합니다.

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

# 데이터 소스 참조
data.<리소스 유형>.<이름>.<속성>

  1. 데이터 소스를 활용해서 AWS 가용영역 인수를 정의할 수 있습니다.

# 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
}
  1. 다음 링크를 사용하면 다른 리소스들도 쉽게 참조 할 수 있다.

2. 입력 변수 Variable

A. 변수 선언 방식

  1. 인프라를 구성하는 데 필요한 속성 값을 정의해 여러 인프라를 구성하기 위해 입력 변수를 가집니다.

# variable 블록 선언의 예
variable "<이름>" {
 <인수> = <값>
}

variable "image_id" {
 type = string
}
  1. 예외 이름들: source, version, providers, count, for_each, lifecycle, depends_on, locals

  2. 변수 정의 시 사용 가능한 메타인수

    • default : 변수 값을 전달하는 여러 가지 방법을 지정하지 않으면 기본값이 전달됨, 기본값이 없으면 대화식으로 사용자에게 변수에 대한 정보를 물어봄

    • type : 변수에 허용되는 값 유형 정의, string number bool list map set object tuple 와 유형을 지정하지 않으면 any 유형으로 간주

    • description : 입력 변수의 설명

    • validation : 변수 선언의 제약조건을 추가해 유효성 검사 규칙을 정의

    • sensitive : 민감한 변수 값임을 알리고 테라폼의 출력문에서 값 노출을 제한 (암호 등 민감 데이터의 경우)

    • nullable : 변수에 값이 없어도 됨을 지정

B. 변수 유형

  1. 지원되는 변수의 유형

  2. 기본 유형

  • string : 글자 유형

variable "string" {
  type        = string
  default     = "String"
}
  • number : 숫자 유형

variable "number" {
  type    = number
  default = 123
}
  • bool : true 또는 false

variable "boolean" {
  default = true
}
  • any : 명시적으로 모든 유형이 허용됨을 표시

  • 집합 유형

    • list: list를 나타내는 집합

variable "list" {
  default = [
    "google",
    "vmware",
    "amazon",
    "microsoft"
  ]
}
  • map: hashmap 같은 집합

variable "map" { # Sorting
  default = {
    aws   = "amazon",
    azure = "microsoft",
    gcp   = "google"
  }
}

  • set : set 을 나타내는 집합

variable "set" { # Sorting
  type = set(string)
  default = [
    "google",
    "vmware",
    "amazon",
    "microsoft"
  ]
}

  • object : 복잡한 키 밸류 값을 나타내는 집합

variable "object" {
  type = object({ name = string, age = number })
  default = {
    name = "abc"
    age  = 12
  }
}

  • tuple: 간단한 배열을 나타내는 집합


variable "tuple" {
  type    = tuple([string, number, bool])
  default = ["abc", 123, true]
}

C. 유효성 검사

  • 변수에 대한 유효성 검사

variable "AMIID" {
  type        = string
  description = "AMI ID"

  validation {
    condition     = length(var.AMIID) > 5
    error_message = "SHOULD exceed 5"
  }
}

D. 변수 참조

  • 선언되지 않은 변수는 입력 인자를 받게끔 프롬트가 뜨게된다!

variable "my_password" {}

resource "local_file" "abc" {
  content  = var.my_password
  filename = "${path.module}/abc.txt"
}
terraform init -upgrade && terraform apply -auto-approve
var.my_password
  Enter a value: qwe123

E. 민감한 변수 취급

  • 입력 변수에 대한 민감한 정보 포함 여부도 표현할 수 있다.

  • 아래와 같이 RUNNING 시에 해당 값이 가려지게 된다!

variable "my_password" {
  default   = "password"
  sensitive = true
}

resource "local_file" "abc" {
  content  = var.my_password
  filename = "${path.module}/abc.txt"
}
terraform apply -auto-approve  // SENSITIVE는 가려짐
...
 ~ content              = (sensitive value)
...
  
terraform state show local_file.abc  // 여기서도 가려짐
echo "local_file.abc.content" | terraform console
  1. 하지만, terraform.tfstate 파일에서는 해당값이 표현이 된다!

    1. 해당 파일은 안전하게 보관이 되어야하는 방법을 생각해볼 수 있다.

    2. 보통은 s3에 담고 read를 못하게끔 제한 할 수 있을것같다.

F. 변수 입력 방식과 우선순위

  • variable는 입력변수로 적절하게 우선순위를 이용해서 환경에 따른 정의를 해줄 수 있습니다.

항목
우선순위

Env

1 제일 낮음

terraform.tfvars

2

terraform.tfvars.json

3

.auto.tfvars

4

-var 혹은 -var-file

5 제일 높음

1. env (실행 후 입력)
var.my_var
  Enter a value: var1
  
2. default 값
variable "my_var" {
  default = "var2"
}

3. 환경 변수 (TF_VAR 변수 이름)
export TF_VAR_my_var=var3
echo $TF_VAR_my_var

4. terraform.tfvars에 정의된 변수 선언
echo 'my_var="var4"' > terraform.tfvars
cat terraform.tfvars

5.  *.auto.tfvars에 정의된 변수 선언
echo 'my_var="var5_b"' > b.auto.tfvars
ls *.tfvars

6. *.auto.tfvars.json에 정의된 변수 선언
# a.auto.tfvars.json 파일 생성
cat <<EOF > a.auto.tfvars.json
{
  "my_var" : "var6_a"
}
EOF
ls *.tfvars ; ls *.json

7.  CLI 실행 시 -var 인수에 지정 또는 -var-file로 파일 지정
terraform apply -auto-approve -var=my_var=var7
cat abc.txt ; echo

3. local 지역 값

A. local 선언

  • local 변수는 외부에서 입력되지 않고, 코드 내에서만 동작하는 값으로 사용됩니다.

// 1. 선언되는 블록은 locals로 시작 (로컬 변수 이름은 전체 루트 모듈 내에서 유일)
locals {
  content = "${var.prefix} ${local.name}"
}

locals {
  content = "content2" // 중복 선언되어서는 안된다.
}

B. local 참조

  • local 변수local.<이름>으로 참조할 수 있다.

locals {
  name    = "terraform"
}

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

  • 변수 우선 순위 실습

  • 서로 다른 테라폼 파일에서 로컬 값이 참조가 가능하고, 로컬값이 파편화되면 유지보수가 어려워진다.

  • local은 어떻게 사용해야 될까??

4. 출력 output

A. output 선언

  • 프로비저닝 수행 후의 결과 속성 값을 확인하는 용도로 사용한다.

  • output 선언으로 모듈 내에서 생성되는 속성 값들을 정의 할 수 있다.

output "instance_ip_addr" {
  value = "http://${aws_instance.server.private_ip}"
}
  • 속성 값은 프로비저닝이 완료되어야 최종적으로 결과를 확인이 가능하다!

  • 다음과 같은 옵션을 줄 수 있다.

    • description : 출력 값 설명

    • sensitive : 민감한 값으로 출력 제한

    • depends_on : value에 담길 값이 특정 구성에 종속성이 있는 경우 생성되는 순서를 임의로 조정

    • precondition : 출력 전에 조건 검증

B. output 활용 & abspath 함수

  • abspath : 절대 경로로 변환하는 함수

output "file_abspath" {
  value = abspath(local_file.abc.filename)
}
  • 다음과 같은 결과값으로 표기가 된다.

Changes to Outputs:
  + file_abspath = "/Users/simon/Downloads/workspaces/3.8/abc.txt"
  + file_id      = (known after apply)

5. 반복문

  • list 형태의 값 목록을 반복문으로 쉽게 관리할 수 있다.

A. count

resource "local_file" "abc" {
  count    = 5
  content  = "abc"
  filename = "${path.module}/abc.txt"
}
  • count 값이 선언된 만큼 리소스를 생성하게 됩니다.

  • 하지만 위의 코드로 apply하게되면 한개의 파일만 생성되게 됩니다.

resource "local_file" "abc" {
  count    = 5
  content  = "This is filename abc${count.index}.txt"
  filename = "${path.module}/abc${count.index}.txt"
}
  • 다음의 코드를 생성하게 되면 각각 다른 리소스들이 5개 생성됩니다.

리소스를 참조하는 방법

echo "local_file.abc" | terraform console
echo "local_file.abc[0].content" | terraform console // <리소스 타입>.<이름>[<인덱스 번호>]
echo "local_file.abc[2].content" | terraform console

주의사항

  • 중간에 값이 삭제되면 해당 값에 대한 리소스만 삭제되는 것이 아니라 이후의 정의된 리소스들도 삭제되고 재생성된다고 합니다.

6. 실습

A. VPC + 보안그룹 + EC2 배포

  • default VPC 대신 직접 VPC를 만들고, 해당 VPC내에 EC2 1대를 배포

보안 그룹 생성
보안 그룹 배포
ec2 생성
배포 후 ec2 생성 확인

1. VPC 생성을 위해서 vpc.tf 파일을 생성합니다.

provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_vpc" "myvpc" {
  cidr_block       = "10.10.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "t101-study"
  }
}

2. 보안그룹 생성을 위해 sg.tf 파일을 생성합니다.

resource "aws_security_group" "mysg" {
  vpc_id      = aws_vpc.myvpc.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
}

output "aws_security_group_id" {
  value       = aws_security_group.mysg.id
}
vpc 생성확인

  1. ec2 생성을 위해 ec2.tf 를 생성합니다.

data "aws_ami" "my_amazonlinux2" {
  most_recent = true
  filter {
    name   = "owner-alias"
    values = ["amazon"]
  }

  filter {
    name   = "architecture"
    values = ["x86_64"]
  }

  owners = ["amazon"]
}

resource "aws_instance" "myec2" {

  ami                         = data.aws_ami.my_amazonlinux2.id
  instance_type               = "t2.micro"
  vpc_security_group_ids      = ["${aws_security_group.mysg.id}"]
  subnet_id                   = aws_subnet.mysubnet1.id

  user_data = <<-EOF
              #!/bin/bash
              wget https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64
              mv busybox-x86_64 busybox
              chmod +x busybox
              nohup ./busybox httpd -f -p 80 &
              EOF

  user_data_replace_on_change = true

  tags = {
    Name = "t101-myec2"
  }
}

output "myec2_public_ip" {
  value       = aws_instance.myec2.public_ip
  description = "The public IP of the Instance"
}
생성이후 콘솔확인
graphz 결과

B. AWS IAM User 3명 생성/ 반복문 실습

```
provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_iam_user" "myiam" {
  count = 3
  name  = "myuser.${count.index}"
}
```
  • iamuser를 3명 생성합니다.

```
variable "user_names" {
  description = "Create IAM users with these names"
  type        = list(string)
  default     = ["gasida", "akbun", "hyungwook"]
}

provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_iam_user" "myiam" {
  count = length(var.user_names)
  name  = var.user_names[count.index]
}
```
  • 위와 같이 length 와 index를 활용해서 리소스 배열에 대한 IAM 유저를 생성할 수 있습니다.

variable "user_names" {
  description = "Create IAM users with these names"
  type        = list(string)
  default     = ["gasida", "akbun", "hyungwook"]
}

// 위에서 밑으로 'AKBUN'을 제거하고 PLAN 하게 되면 
// 모든 리소스를 삭제한 다음 해당 리소스를 처음부터 다시 만듬.

variable "user_names" {
  description = "Create IAM users with these names"
  type        = list(string)
  default     = ["gasida", "hyungwook"]
}
// User with name hyungwook already exists  -> 오류가 생성되면서 리소스 재생성함

C. 간단한 예제보다는 실제 예제를 통한 반복문 학습(악분님 감사합니다!)

```  main.tf
resource "aws_vpc" "main" {
  cidr_block = var.vpc_cidr

  tags = {
    Name = "terraform VPC"
  }
}

resource "aws_subnet" "main" {
  count = length(var.subnets)  // 2. 서브넷 마다 값들을 저장해두고 꺼내서 사용가능합니다!
  vpc_id     = aws_vpc.main.id
  cidr_block = var.subnets[count.index].cidr
  availability_zone = var.subnets[count.index].az  

  tags = var.subnets[count.index].tags
}

output "myvpc_id" {
  value = aws_vpc.main.id
}
```
```  terraform.tfvars
vpc_cidr            = "192.168.0.0/16"

subnets = [
  {
    cidr = "192.168.1.0/24",
    az = "ap-northeast-2a",
    tags = {
      Name = "public-subnet"
      Environment = "dev"
    }
  },
  {
    cidr = "192.168.2.0/24",
    az = "ap-northeast-2c",
    tags = {
      Name = "private-subnet"
      Environment = "dev"
    }
  }
]
```
``` variables.tf
variable "vpc_cidr" {
  type = string
}

variable "subnets" {   // 3. 다음과 같이 subnet에 대한 object를 생성해주기
  type = list(object({  // subnet 설정 변수를 한 변수로 설정하도록 설정
    cidr = string
    az = string
    tags = map(string)
  }))
}
```

  • 다음과 같이 깔끔하게 변수들로 vpc를 생성할 수 있습니다.

D. 주의사항: COUNT로 생성한 그룹은 임의로 변경하게 되면 모두 삭제되고 재 생성됩니다.

예시

subnets = [
  # 
  # {
  #   cidr = "192.168.1.0/24",
  #   az = "ap-northeast-2a",
  #   tags = {
  #     Name = "public-subnet"
  #     Environment = "dev"
  #   }
  # },

  # {
  #   cidr = "192.168.5.0/24",
  #   az = "ap-northeast-2a",
  #   tags = {
  #     Name = "public-subnet"
  #     Environment = "dev"
  #   }
  # },
  {
    cidr = "192.168.2.0/24",
    az = "ap-northeast-2a",
    tags = {
      Name = "private-subnet"
      Environment = "dev"
    }
  },
  
  // 만약 다음과 같이 두개의 요소들에 대해서 주석처리하고 terraform apply 하게 되면
   밑의 그림과 같이 인덱스값이 한칸씩 땡겨온다.
  
  // 그로인해, RESOURCE 값이 제대로 생성되있지 않은것으로 보고, 모두 재생성하게 됩니다.

  • 예상치 못한 장애 요소를 만날 수 있으니 foreach를 사용하라는 조언과 함께 2주차의 스터디는 마무리가 됐다.

Last updated