본문 바로가기
Terraform/Study

Terraform-101-Study Week2 [data, Variable, local, 반복문]

by 식사법 2023. 7. 15.

Week2

Github를 통해 코드 관리

1. Data 소스

Data 소스의 경우 외부 리소스 또는 저장된 정보를 테라폼 내에서 참조할 때 사용함

기존에 Console 상에서 만들었던 리소스들을 Terraform code 내에서 활용하고 싶을때 data 를 통해 가져올 있음

  • 예시) ec2 중 lee 라는 이름의 instance 에대한 정보를 가져오기
    • 이제 data.aws_instance.example.[원하는 값] 을 통해 해당 인스턴스에 대한 정보를 사용할 수 있음
  • data "aws_instance" "example" { filter { name = "tag:Name" values = ["lee"] } }

2. Variable

1) 변수 읽어들이는 순서

Env → terraform.tfvars → terraform.tfvars.json → *.auto.tfvars / *.auto.tfvars.json

→ CLI comment -var -var-file

2) 변수 종류

  • type 미 지정 시 default 값을 보고 어느정도 맞춰줌

(1) string

variable "변수 이름" { 
    type = string
    description = "var String"   // 해당 변수가 어떤 의미인지 설명하는 것
    default = "myString"  // 변수의 기본 값
}

(2) number

variable "변수 이름" {
    type = number
    default = 123
}

(3) boolean

variable "변수 이름" {
    default = true
}

(4) list

variable "변수 이름" {
    default = [
        "google",
        "vmware",
        "amazone",
        "microsoft"
}

사용 예시

0 번 인덱스 출력
output "list_index_0" {
    value = var.list.0
}

리스트 내부 모두 출력
output "list_all" {
    value = [
        for name in var.list :
                name
    ]
}

(5) map

  • 모두 출력 시 키 값을 기준으로 정렬되어 출력
variable = "변수 이름" {
    default = {
        azure = "microsoft",
        gcp = "google",
        aws = "amazon"
    }
}

사용 예시

value 값 출력
output "map_key" {
    value = var.map.aws
}

모든 값 출력
output = "map_all" {
    value = var.map
}

(6) set

  • list와 달리 정렬하여 출력
  • map 과 달리 값이 하나
variable = "변수 이름" {
    type = set(string)
    default = [
        "google",
        "vmware",
        "amazone",
        "microsoft"
    ]
}

(7) object

  • 서로 다른 type을 묶어 사용할 수 있게 해
variable "obejct" {
    type = object({ name = string, age= number })
    default = {
        name = "abc"
        age = 12
    }
}

(8) tuple

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

(9) 변수의 유효성 체크 가능

하단에 있는 코드로 활성화 가능

terraform {
    experiments = [variable_validation]
}

예시)

  • myVar의 길이가 4보다 커야함
variable "myVar" {
    type = string
    default = "myVar"

    validation {
        condition = length(var.myVar) > 4
        error_message = "에러 메세지 등록"
    }
}
  • yourVar 변수는 앞에 your로 시작해야함
variable "yourVar" {
    type = string
    default = "yourVar"

    validation {
        condition = can(regex("^your", var.yourVar))
        error_message = "에러 메세지 출력"
    }
}

3. Local

코드내에서 가공하여 사용하는 변수라고 이해하면 쉬움. 이미 선언한 여러변수를 조합하여 새로운 변수를 만들어 사용할 때 적절함

  • 예시) 두 변수를 조합하여 활용
variable "homepage" {
  type = string
  default = "leechanghwan"
}

variable "index" {
  type = list(string)
  default = [ "_0", "_1", "_2"]
}

locals {
  name = ["${var.homepage}${var.index[0]}", "${var.homepage}${var.index[1]}", "${var.homepage}${var.index[2]}"]
}
  • locals.name 이라는 변수에 var.hompage, var.index 를 활용하여 조합하여 사용

4. 반복문

1) Count

  • 리소스 블록내 주어진 정수값 만큼 같은 리소스를 생성함
  • 예시) count를 사용하여 ec2를 3개 생성
resource "aws_instance" "Challange4" {
  ami = "ami-0676d41f079015f32"
  instance_type = "t2.micro"
  vpc_security_group_ids = [aws_security_group.Challange4-sg.id]


  count = 3 

  user_data = <<-EOF
  #!/bin/bash
  sudo yum update -y 
  sudo yum install httpd -y
  sudo systemctl start httpd
  sudo systemctl enable httpd
  sleep 10
  sudo echo "${count.index}" > /var/www/html/index.html
  sudo systemctl restart httpd
  EOF
  tags = { Name = local.name[count.index] }

}

2)for_each

  • 리소스 블록내 선언된 key 개수만큼 리소스를 생성
  • count 와 차이점 : for_each는 key와 vaule 값이 존재하여 해당 값으로 리소스 블록내 요소 값들에 key와 value 값을 전달 가능
    • count 도 해당 기능이 구현가능하나 list, map 등의 variable을 활용하여 count.index를 통해 접근 해야함
  • 예시)
resource "aws_instance" "Challange3" {
  ami = "ami-0676d41f079015f32"
  instance_type = "t2.micro"
  vpc_security_group_ids = [aws_security_group.Challange3-sg.id]
  for_each = {
    index1 = var.homepage[0]
    index2 = var.homepage[1]
    index3 = var.homepage[2]
  }
  user_data = <<-EOF
  #!/bin/bash
  sudo yum update -y 
  sudo yum install httpd -y
  sudo systemctl start httpd
  sudo systemctl enable httpd
  sleep 10

    #each.value를 활용하여 value 값을 전달하고 있다.
  sudo echo "${each.value}" > /var/www/html/index.html

  sudo systemctl restart httpd
  EOF
  tags = { Name = each.value}

}

3) for

  • 리소스 , 블록 단위가 아닌 한 줄 정도의 반복문을 사용할 때 사용한다
  • 예시) ingress 블록내 cidr 블록내 반복문을 사용하여 값 지정
variable "allow" {
variable "allow" {
  type = list(string)
  default = [ "172.31.50.0/24", "172.31.70.0/24", "172.31.90.0/24"]
}

resource "aws_security_group" "example-sg" {
  name   = "example-sg"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "TCP"
    cidr_blocks = [for x in var.allow : x]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]

  }
}

4) dynamic

  • 리소스 내부에 블록에 대해서 반복문을 사용할 시 사용
  • 보통 sg 를 선언 시 정책을 설정할때 dynamic 구문을 사용한다.
  • 예시) 443, 80 포트에 대해서 ingress 를 dynamic 구문을 통해 선언
variable "ingresses" {
  default = [
    {
      from_port : 443
      to_port : 443
    },

    {
      from_port : 80
      to_port : 80
    }
  ]
}

resource "aws_security_group" "example-sg" {
  name   = "example-sg"

  dynamic "ingress" {
    for_each = var.ingresses
    content {
      from_port = ingress.value["from_port"]
      to_port = ingress.value["to_port"]
      protocol = "tcp"
      cidr_blocks = [0.0.0.0/0]
    }
  }


  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]

  }
}

5. 도전과제

1) 리전 내에서 사용 가능한 가용영역 목록을 가져와 VPC 리소스 생성하기

(1) data 소스를 통해 리전내에 존재하는 az를 가져옴

#data.tf
#data 소스를 활용하여 사용가능한 az 가져오기
data "aws_availability_zones" "available" {
  state = "available"
}

(2) terraform state show 명령어를 활용하여 data 소스 확인

terraform state show data.aws_availability_zones.available
# data.aws_availability_zones.available:
data "aws_availability_zones" "available" {
    group_names = [
        "ap-northeast-2",
    ]
    id          = "ap-northeast-2"
    names       = [
        "ap-northeast-2a",
        "ap-northeast-2b",
        "ap-northeast-2c",
        "ap-northeast-2d",
    ]
    state       = "available"
    zone_ids    = [
        "apne2-az1",
        "apne2-az2",
        "apne2-az3",
        "apne2-az4",
    ]
}

(3) 해당 data 소스를 availability_zone에 기입

#vpc.tf

#vpc 선언
resource "aws_vpc" "Challange1_vpc" {
  cidr_block = "192.168.0.0/16"

  tags = {
    Name = "Challange1"
  }
}

#ap-northeast-2a 에 subnet 선언
resource "aws_subnet" "Challange1_subnet_a" {
  vpc_id            = aws_vpc.Challange1_vpc.id
  cidr_block        = "192.168.0.0/24"

    #가져온 데이터 소스 활용
  availability_zone = data.aws_availability_zones.available.names[0]
  tags = {
    Name = "Challange1_subnet_a"
  }
}

#ap-northeast-2b 에 subnet 선언
resource "aws_subnet" "Challange1_subnet_b" {
  vpc_id            = aws_vpc.Challange1_vpc.id
  cidr_block        = "192.168.1.0/24"

    #가져온 데이터 소스 활용
  availability_zone = data.aws_availability_zones.available.names[1]
  tags = {
    Name = "Challange1_subnet_b"
  }
}

#ap-northeast-2c 에 subnet 선언
resource "aws_subnet" "Challange1_subnet_c" {
  vpc_id            = aws_vpc.Challange1_vpc.id
  cidr_block        = "192.168.2.0/24"

    #가져온 데이터 소스 활용
  availability_zone = data.aws_availability_zones.available.names[2]
  tags = {
    Name = "Challange1_subnet_c"
  }
}

(4) console 확인

aws_console : vpc

2) 노션 내의 Terraform 코드의 리소스를 자신의 닉네임으로 변경하여 배포 실습하기

  • (Option) 선행조건
    • 리소스에 대한 이해
        // 리소스 종류가 EC2일때
        resource "aws_instance" "my-ec2" {
            ami = "ami-코드 입력"
            instance_type = "t2.micro"
            associate_public_ip_address = true
        }
    • resource "리소스 종류" "리소스 이름" { //리소스 종류에 따른 내부 구성요소 입력 }

(1) data 소스를 통해 리전내에 존재하는 az를 가져옴

#data.tf
#data 소스를 활용하여 사용가능한 az 가져오기
data "aws_availability_zones" "available" {
  state = "available"
}

(2) vpc 선언

resource "aws_vpc" "Challange2_vpc" {
  cidr_block = "192.168.0.0/16"

  tags = {
    Name = "Challange2"
  }
}

(3) subnet 선언

#availability_zones_a 에 subnet 영역 생성
resource "aws_subnet" "Challange2_subnet_a" {
  vpc_id            = aws_vpc.Challange2_vpc.id
  cidr_block        = "192.168.0.0/24"
  availability_zone = data.aws_availability_zones.available.names[0]
  tags = {
    Name = "Challange2_subnet_a"
  }
}

#availability_zones_b 에 subnet 영역 생성
resource "aws_subnet" "Challange2_subnet_b" {
  vpc_id            = aws_vpc.Challange2_vpc.id
  cidr_block        = "192.168.1.0/24"
  availability_zone = data.aws_availability_zones.available.names[1]
  tags = {
    Name = "Challange2_subnet_b"
  }
}

#availability_zones_c 에 subnet 영역 생성
resource "aws_subnet" "Challange2_subnet_c" {
  vpc_id            = aws_vpc.Challange2_vpc.id
  cidr_block        = "192.168.2.0/24"
  availability_zone = data.aws_availability_zones.available.names[2]
  tags = {
    Name = "Challange2_subnet_c"
  }
}

(4) internet gateway 생성

resource "aws_internet_gateway" "Challange2_igw" {
  vpc_id = aws_vpc.Challange2_vpc.id

  tags = {
    Name = "Challange2_igw"
  }
}

(5) 라우팅 테이블 생성

#라우팅 테이블 생성
resource "aws_route_table" "Challange2_rtb" {
  vpc_id = aws_vpc.Challange2_vpc.id

  tags = {
    Name = "Challange2_rtb_Public_a"
  }
}

(6) 라우팅 테이블과 서브넷 연결

#라우팅 테이블 과 subnet_a 와 연결
resource "aws_route_table_association" "asso_with_subnet_a" {
  subnet_id      = aws_subnet.Challange2_subnet_a.id
  route_table_id = aws_route_table.Challange2_rtb.id
}

#라우팅 테이블 과 sbunet_b 와 연결
resource "aws_route_table_association" "asso_with_subnet_b" {
  subnet_id      = aws_subnet.Challange2_subnet_b.id
  route_table_id = aws_route_table.Challange2_rtb.id
}

#라우팅 테이블 과 sbunet_c 와 연결
resource "aws_route_table_association" "asso_with_sub_a" {
  subnet_id      = aws_subnet.Challange2_subnet_c.id
  route_table_id = aws_route_table.Challange2_rtb.id
}

(7) 보안 그룹 생성과 보안그룹 룰 생성

resource "aws_security_group" "Challange2_sg" {
  vpc_id      = aws_vpc.Challange2_vpc.id
  name        = "Challange2_sg"
}

resource "aws_security_group_rule" "Challange2_inbound" {
  type              = "ingress"
  from_port         = 80
  to_port           = 80
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.Challange2_sg.id
}

resource "aws_security_group_rule" "Challange2_outbound" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.Challange2_sg.id
}

(8) data 소스를 활용하여 ami 이미지 가져오기

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

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-ebs"]
  }

  owners = ["amazon"]
}

(9) (8)에서 생성한 data 소스를 활용하여 ec2 생성

resource "aws_instance" "Challange2_ec2" {

  depends_on = [
    aws_internet_gateway.Challange2_igw
  ]

  ami                         = data.aws_ami.Challange2_amazonlinux2.id
  associate_public_ip_address = true
  instance_type               = "t2.micro"
  vpc_security_group_ids      = ["${aws_security_group.Challange2_sg.id}"]
  subnet_id                   = aws_subnet.Challange2_subnet_a.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
              RZAZ=$(curl http://169.254.169.254/latest/meta-data/placement/availability-zone-id)
              IID=$(curl 169.254.169.254/latest/meta-data/instance-id)
              LIP=$(curl 169.254.169.254/latest/meta-data/local-ipv4)
              echo "<h1>RegionAz($RZAZ) : Instance ID($IID) : Private IP($LIP) : Web Server</h1>" > index.html
              nohup ./busybox httpd -f -p 80 &
              EOF

  user_data_replace_on_change = true

  tags = {
    Name = "Challange2_ec2"
  }
}

(10) Console 확인

a. terraform state list

terraform state list

b. aws_console : vpc

aws_console : vpc

 

3)입력변수를 활용해서 리소스를 배포

(1) 변수 생성

#variable.tf

variable "homepage" {
  type = list(string)
  default = [ "lee", "chang", "hwan" ]
}

(2) 보안그룹과 ec2 생성

#main.tf

resource "aws_instance" "Challange3" {
  ami = "ami-0676d41f079015f32"
  instance_type = "t2.micro"
  vpc_security_group_ids = [aws_security_group.Challange3-sg.id]

    #(a)for_each 에서 사용할 key, vaule 선언 value는 변수에서 가져옴
  for_each = {
    index1 = var.homepage[0]
    index2 = var.homepage[1]
    index3 = var.homepage[2]
  }

    #(b) 홈페이지 문구와 ec2이름을 for_each문에서의 value로 사용함
  user_data = <<-EOF
  #!/bin/bash
  sudo yum update -y 
  sudo yum install httpd -y
  sudo systemctl start httpd
  sudo systemctl enable httpd
  sleep 10
  sudo echo "${each.value}" > /var/www/html/index.html
  sudo systemctl restart httpd
  EOF

  tags = { Name = each.value}

}

resource "aws_security_group" "Challange3-sg" {
  name   = "Challange3-sg"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "TCP"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "TCP"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]

  }
}
  • (a) for_each 에서 사용할 key, vaule 선언 value는 변수에서 가져옴
  • (b) 홈페이지 문구와 ec2이름을 for_each문에서의 value로 사용함

(3) console 확인 및 홈페이지 확인

a. aws_console : ec2

aws_console : ec2

b. curl을 통한 홈페이지 확인

홈페이지 &nbsp;확인

4) local을 활용해서 리소스를 배포

(1) 변수 생성 - local 활용 확인

variable "homepage" {
  type = string
  default = "leechanghwan"
}

variable "index" {
  type = list(string)
  default = [ "_0", "_1", "_2"]
}

# (a) [local.name](http://local.name) 내에서 homepage 변수와, index 변수를 가공하여 활용함
locals {
  name = ["${var.homepage}${var.index[0]}", "${var.homepage}${var.index[1]}", "${var.homepage}${var.index[2]}"]
}
  • (a) local.name 내에서 homepage 변수와, index 변수를 가공하여 활용함

(2) 보안그룹과 ec2 생성 - local 활용 확인

resource "aws_instance" "Challange4" {
  ami = "ami-0676d41f079015f32"
  instance_type = "t2.micro"
  vpc_security_group_ids = [aws_security_group.Challange4-sg.id]

    # (a) count 를 활용하여 반복문 실행
  count = 3

    # (b) count.index가 홈페이지의 문구로 등록됨
  user_data = <<-EOF
  #!/bin/bash
  sudo yum update -y 
  sudo yum install httpd -y
  sudo systemctl start httpd
  sudo systemctl enable httpd
  sleep 10
  sudo echo "${count.index}" > /var/www/html/index.html
  sudo systemctl restart httpd
  EOF

    # (c) local.name 에 있는 요소들을 ec2 이름으로 등록
  tags = { Name = local.name[count.index] }

}

resource "aws_security_group" "Challange4-sg" {
  name   = "Challange4-sg"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "TCP"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "TCP"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]

  }
}
  • (a) count 를 활용하여 반복문 실행
  • (b) count.index가 홈페이지의 문구로 등록됨
  • c) local.name 에 있는 요소들을 ec2 이름으로 등록

(3) console 확인 및 홈페이지 확인

a. aws_console : ec2

aws_console &nbsp;: ec2

b. curl 을 통한 홈페이지 확인

홈페이지 &nbsp;확인

 

5) count, for_each, for문, dynamic 문 활용

  • for_each 는 3번 도전과제, count는 4번 도전과제에서 사용했으므로 for문, dynamic문 활용을 정리

(1) 변수 선언

#variable.tf

# (a) ingressess - ingress로 등록할 포트들을 저장한 변수
variable "ingresses" {
  default = [
    {
      from_port : 443
      to_port : 443
    },

    {
      from_port : 80
      to_port : 80
    }
  ]
}

# (b) allow - 허용할 cidr 목록을 저장한 변수
variable "allow" {
  type = list(string)
  default = [ "172.31.50.0/24", "172.31.70.0/24", "172.31.90.0/24"]
}
  • (a) ingressess - ingress로 등록할 포트들을 저장한 변수
  • (b) allow - 허용할 cidr 목록을 저장한 변수

(2) sg 생성 - for문, dynamic 문 활용 확인

resource "aws_security_group" "Challange5-sg" {
  name   = "Challange5-sg"

    # (a) dynamic 블록을 활용하여 ingress 다수 생성
  dynamic "ingress" {
    for_each = var.ingresses
    content {
      from_port = ingress.value["from_port"]
      to_port = ingress.value["to_port"]
      protocol = "tcp"

            # (b) cidr_blockd을 allow 변수에 있는 목록을 가져와 등록
      cidr_blocks = [for x in var.allow : x]
    }
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]

  }
}
  • (a) dynamic 블록을 활용하여 ingress 다수 생성
  • (b) cidr_blockd을 allow 변수에 있는 목록을 가져와 등록

(3) console 확인

aws_console &nbsp;: sg