본문 바로가기
DevOps/IaC (Infrastructure as Code)

[T1012] Week 2. 테라폼 기본 사용법 정리 (2/3)

by Hwan,. 2023. 7. 15.
728x90
반응형

데이터 소스

 Terraform의 데이터 소스는 테라폼 설정 내에서 읽기 전용의 값들을 제공한다. 이 값을 활용하면 이미 존재하는 리소스의 세부사항이나 외부 데이터를 참조할 수 있다.

데이터 소스 블록은 아래처럼 data로 시작하고 ‘데이터 소스 유형’과 '이름'을 정의한다.

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

# 참조 : data.<리소스 유형>.<이름>.<속성>
# 사용가능한 메타인수:
# depends_on : 종속성 선언
# count : 선언된 개수만큼 리소스를 생성
# for_each : map또는 set 타입 변수로 리소스 반복 생성
# lifecycle : 리소스 수명주기

 

예를 들면 아래 코드는 aws의 az 정보를 얻어와 각 az에 subnet을 생성할 수 있다.

data "aws_availability_zones" "available" {
  state = "available"
}

resource "aws_subnet" "primary_AZ" {
  availability_zone = data.aws_availability_zones.available.names[0]
}

resource "aws_subnet" "secondary_AZ" {
  availability_zone = data.aws_availability_zones.available.names[1]
}

 

연습 코드 : 

  • data 모듈로 az 이름 받아와서 vpc, subnet 생성하기
data "aws_availability_zones" "available" {
  state = "available"
}


resource "aws_vpc" "hwan001-vpc" {
    cidr_block = "10.10.0.0/16"

    tags = {
        Name = "hwan001-vpc"
    }
}

resource "aws_subnet" "primary_az" {
    vpc_id = aws_vpc.hwan001-vpc.id
    cidr_block =  "10.10.1.0/24"
    availability_zone = data.aws_availability_zones.available.names[0]
    tags = {
        Name = "hwan001-subnet-1"
    }
}

resource "aws_subnet" "secondary_az" {
    vpc_id = aws_vpc.hwan001-vpc.id
    cidr_block =  "10.10.2.0/24"
    availability_zone = data.aws_availability_zones.available.names[1]
    tags = {
        Name = "hwan001-subnet-2"
    }
}

 


변수(Variables) 종류 및 우선순위

변수 종류 : 

 변수 종류는 크게 기본 유형과 집합 유형으로 나뉜다.

기본 유형에는 string, number, bool, any가 있고, 집합 유형에는 list, map, set, object, tuple이 있다.

변수 정의할 땐 아래 메타인수가 사용 가능하다.

  • default : 값이 전달되지 않은 경우의 기본 값, default가 없으면 apply 시 변수에 대한 정보를 물어봄
  • type : 변수 유형 정의, string number bool list map set object tuple 와 유형을 지정하지 않으면 any 유형으로 간주
  • description : 입력 변수의 설명
  • validation : 변수 선언의 제약조건을 추가해 유효성 검사 규칙을 정의
  • sensitive : 민감한 변수 값으로 테라폼의 출력문에서 값 노출을 제한함 (암호 등 민감 데이터의 경우)
  • nullable : 변수에 값이 없어도 됨 (null 허용 여부)

사용 예시는 아래와 같다.

variable "string" {
  type        = string
  description = "var String"
  default     = "myString"
}

variable "number" {
  type    = number
  default = 123
}

variable "boolean" {
  default = true
}

variable "list" {
  default = [
    "google",
    "vmware",
    "amazon",
    "microsoft"
  ]
}

output "list_index_0" {
  value = var.list.0 # google, 1이면 vmware
}

output "list_all" {
  value = [
    for name in var.list : upper(name)
  ]
}

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

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

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

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

variable "ingress_rules" { # optional ( >= terraform 1.3.0)
  type = list(object({
    port        = number,
    description = optional(string),
    protocol    = optional(string, "tcp"),
  }))
  default = [
    { port = 80, description = "web" },
  { port = 53, protocol = "udp" }]
}

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

 

우선 순위 : 

 변수가 선언되는 방식에 따라 우선순위가 존재한다.

실행 후 입력 < variable 블록의 default 값 < 환경 변수 (TF_VAR 변수 이름) < terraform.tfvars < *.auto.tfvars < *.auto.tfvars.json < CLI 실행 시 -var 인수에 지정 또는 -var-file로 파일 지정

오른쪽으로 갈수록 우선순위가 높다. 예를들어 변수의 값을 출력해주는 코드가 있을 때, terraform.tfvars 내부 변수에 1이라는 값이 정의되어 있고, 실행후에 3이라는 값을 입력 한다면 결과는 1이 나오게 된다.

# main.tf 
variable "my_var" {
  default = "var2"
}

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

#terraform.tf
my_var="var4"


기본 문법 

local : 로컬에서만 사용하는 변수

output : 프로비저닝 수행 결과의 속성 값을 출력

반복문 : count, for each, for expression 등 여러개의 데이터를 한개의 리소스로 반복적으로 생성

# count
resource "local_file" "abc" {
  count    = 5
  content  = "abc"
  filename = "${path.module}/abc.txt"
}


# for each
resource "local_file" "abc" {
  for_each = {
    a = "content a"
    b = "content b"
  }
  content  = each.value
  filename = "${path.module}/${each.key}.txt"
}


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

# for의 다양한 활용
variable "names" {
  type    = list(string)
  default = ["a", "b"]
}

output "A_upper_value" {
  value = [for v in var.names : upper(v)]
}

output "B_index_and_value" {
  value = [for i, v in var.names : "${i} is ${v}"]
}

output "C_make_object" {
  value = { for v in var.names : v => upper(v) }
}

output "D_with_filter" {
  value = [for v in var.names : upper(v) if v != "a"]
}

 

Dynamic : 리소스 내부 속성 블록 동적인 블록으로 생성한다. dynamic을 사용할 경우 특정 리소스 블록 내부에서 하나의 속성 블록에서 여러 개의 속성을 부여할 수 있게 된다.

# 일반적인 속성 부여
resource "provider_resource" "name" {
  name = "some_resource"

  some_setting {
    key = a_value
  }

  some_setting {
    key = b_value
  }

  some_setting {
    key = c_value
  }

  some_setting {
    key = d_value
  }
}

# dynamic을 활용한 부여
resource "provider_resource" "name" {
  name = "some_resource"

  dynamic "some_setting" {
    for_each = {
      a_key = a_value
      b_key = b_value
      c_key = c_value
      d_key = d_value
    }

    content {
      key = some_setting.value
    }
  }
}

 

연습 코드 :

  • 입력받은 수 만큼 count로 sqs에 큐 생성하기
provider "aws" {
  region = "ap-northeast-2" 
}

variable "number_of_queue" {
    type = number
}

resource "aws_sqs_queue" "main_queue" {
    count                     = var.number_of_queue
    name                      = "queue-${count.index+1}"
    delay_seconds             = 0
    max_message_size          = 1024
    message_retention_seconds = 345600
    visibility_timeout_seconds = 60
}

 

  • dynamic을 활용한  security group 생성하기
variable "ingress_rules" {
    type = list(object(
        {
            from_port   = number
            to_port     = number
            protocol    = string
            cidr_blocks = list(string)
        }
    ))

    default = [
        {
            from_port   = 22
            to_port     = 22
            protocol    = "tcp"
            cidr_blocks = ["0.0.0.0/0"]
        },
        {
            from_port   = 80
            to_port     = 80
            protocol    = "tcp"
            cidr_blocks = ["0.0.0.0/0"]
        },
        {
            from_port   = 443
            to_port     = 443
            protocol    = "tcp"
            cidr_blocks = ["0.0.0.0/0"]
        },
        {
            from_port   = 8080
            to_port     = 8080
            protocol    = "tcp"
            cidr_blocks = ["0.0.0.0/0"]
        }
    ]
}

resource "aws_security_group" "hwan001-sg" {
    description = "hwan001 Security Group"
    vpc_id      = aws_vpc.hwan001-vpc.id
    
    dynamic "ingress" {
        for_each = var.ingress_rules
        content {
            from_port   = ingress.value.from_port
            to_port     = ingress.value.to_port
            protocol    = ingress.value.protocol
            cidr_blocks = ingress.value.cidr_blocks
        }
    }

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

    tags = {
        Name = "hwan001-sg"
    }
}

도전 과제

  • 리전 내에서 사용 가능한 가용영역 목록 가져오기를 사용한 VPC 리소스 생성 실습 진행
  • 위 3개 코드 파일 내용에 리소스의 이름(myvpc, mysubnet1 등)을 반드시! 꼭! 자신의 닉네임으로 변경해서 배포 실습해보세요!
  • 입력변수를 활용해서 리소스(어떤 리소스든지 상관없음)를 배포해보고, 해당 코드를 정리해주세요!
  • local를 활용해서 리소스(어떤 리소스든지 상관없음)를 배포해보고, 해당 코드를 정리해주세요!
  • count, for_each, for, dynamic문 을 활용해서 리소스(어떤 리소스든지 상관없음)를 배포해보고, 해당 코드를 정리해주세요!

 

network.tf :

  • data를 사용해 az 이름 가져오기
  • vpc, subnet 생성하기
data "aws_availability_zones" "available" {
  state = "available"
}


resource "aws_vpc" "hwan001-vpc" {
    cidr_block = "10.10.0.0/16"

    tags = {
        Name = "hwan001-vpc"
    }
}

resource "aws_subnet" "primary_az" {
    vpc_id = aws_vpc.hwan001-vpc.id
    cidr_block =  "10.10.1.0/24"
    availability_zone = data.aws_availability_zones.available.names[0]
    tags = {
        Name = "hwan001-subnet-1"
    }
}

resource "aws_subnet" "secondary_az" {
    vpc_id = aws_vpc.hwan001-vpc.id
    cidr_block =  "10.10.2.0/24"
    availability_zone = data.aws_availability_zones.available.names[1]
    tags = {
        Name = "hwan001-subnet-2"
    }
}

 

sg.tf : 

  • 다이나믹을 사용해서 여러 개의  ingress rule 정의하기
  • 보안 그룹 생성
variable "ingress_rules" {
    type = list(object(
        {
            from_port   = number
            to_port     = number
            protocol    = string
            cidr_blocks = list(string)
        }
    ))

    default = [
        {
            from_port   = 22
            to_port     = 22
            protocol    = "tcp"
            cidr_blocks = ["0.0.0.0/0"]
        },
        {
            from_port   = 80
            to_port     = 80
            protocol    = "tcp"
            cidr_blocks = ["0.0.0.0/0"]
        },
        {
            from_port   = 443
            to_port     = 443
            protocol    = "tcp"
            cidr_blocks = ["0.0.0.0/0"]
        },
        {
            from_port   = 8080
            to_port     = 8080
            protocol    = "tcp"
            cidr_blocks = ["0.0.0.0/0"]
        }
    ]
}

resource "aws_security_group" "hwan001-sg" {
    description = "hwan001 Security Group"
    vpc_id      = aws_vpc.hwan001-vpc.id
    
    dynamic "ingress" {
        for_each = var.ingress_rules
        content {
            from_port   = ingress.value.from_port
            to_port     = ingress.value.to_port
            protocol    = ingress.value.protocol
            cidr_blocks = ingress.value.cidr_blocks
        }
    }

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

    tags = {
        Name = "hwan001-sg"
    }
}

 

main.tf : 

  • ubuntu 20.04 공식 ami 정보를 data로 가져오기
  • 해당 ami를 사용해서 ec2 인스턴스 띄우기 (subnet, sg는 위에서 생성한 리소스로 사용)
provider "aws" {
  region = "ap-northeast-2" 
}

data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["099720109477"]  # Canonical
}

# EC2 Instance 생성
resource "aws_instance" "hwan001-ec2" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t2.micro"
  subnet_id                   = aws_subnet.primary_az.id
  vpc_security_group_ids      = [aws_security_group.hwan001-sg.id]
  associate_public_ip_address = true

  tags = {
    Name = "hwan001-ec2"
  }
}

 

결과 : 

생성된 서브넷
생성된 ec2
생성된 보안 그룹

728x90
반응형

댓글