가시다 테라폼 스터디 2주차 정리

강재민·2023년 9월 7일
0
post-thumbnail

테라폼으로 시작하는 IaC 책을 기준으로 정리하였습니다.


데이터 소스

데이터 소스는 테라폼으로 정의되지 않은 외부 리소스 또는 저장된 정보를 테라폼 내에서 참조할 때 사용한다.

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

# 데이터 소스 참조
data.<리소스 유형>.<이름>.<속성>
  • 데이터 소스를 정의할 때 사용 가능한 메타인수는 다음과 같다.
    • depends_on : 종속성을 선언하며, 선언된 구성요소와의 생성 시점에 대해 정의
    • count : 선언된 개수에 따라 여러 리소스를 생성
    • for_each : map 또는 set 타입의 데이터 배열의 값을 기준으로 여러 리소스를 생성
    • lifecycle : 리소스의 수명주기 관리

도전과제1 리전 내에서 사용 가능한 가용영역 목록 가져오기를 사용한 VPC 리소스 생성 실습 진행 - 링크 혹은 아무거나 데이터 소스를 사용한 실습 진행

# Create a VPC for the region associated with the AZ
resource "aws_vpc" "myvpc" {
  cidr_block           = "10.10.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "repush"
  }
}

# Declare the data source
data "aws_availability_zones" "available" {
  state = "available"
}

resource "aws_subnet" "primary" {
  vpc_id            = aws_vpc.myvpc.id
  cidr_block        = "10.10.1.0/24"
  availability_zone = data.aws_availability_zones.available.names[0]
  # e.g. ap-northeast-2a
}
resource "aws_subnet" "secondary" {
  vpc_id            = aws_vpc.myvpc.id
  cidr_block        = "10.10.2.0/24"
  availability_zone = data.aws_availability_zones.available.names[2]
  # e.g. ap-northeast-2c
}


입력 변수 Variable

입력 변수는 인프라를 구성하는 데 필요한 속성 값을 정의해 코드의 변경 없이 여러 인프라를 생성하는 데 목적이 있다.
테라폼에서는 이것을 입력변수 Input Variables로 정의한다.

유효성 검사: 입력되는 변수 타입 지정 이외, 사용자 지정 유효성 검사가 가능

  • 변수 블록 내에 validation 블록에서 조건인 condition에 지정되는 규칙이 true 또는 false를 반환해야 하며, error_message는 condition 값의 결과가 false 인 경우 출력되는 메시지를 정의한다.
  • regex 함수는 대상의 문자열에 정규식을 적용하고 일치하는 문자열을 반환하는데, 여기에 can 함수를 함께 사용하면 정규식에 일치하지 않는 경우의 오류를 검출한다.
  • validation 블록은 중복으로 선언할 수 있다.
  • variable 유효성 검사의 예 - main.tf 코드 파일 내용 수정
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-\"."
  }
}

결과

#
terraform apply -auto-approve
var.image_id
  The id of the machine image (AMI) to use for the server.

  Enter a value: ami-12345678
...

변수 참조: variable은 코드 내에서 var.<이름>으로 참조된다.

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
...

# 확인
terraform state list
terraform state show local_file.abc
cat abc.txt ; echo

민감한 변수 취급: 입력 변수의 민감 여부 선언 가능

확인 : 민감한 변수로 지정해도 terraform.tfstate 파일에는 결과물이 평문으로 기록되므로 State 파일의 보안에 유의해야 한다. [참고 Docs - sensitive-variables , State 암호화]

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

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

결과

# 출력부분에 내용 안보임!
terraform apply -auto-approve
terraform state show local_file.abc

# 결과물 파일 확인
cat abc.txt ; echo

# terraform.tfstate 파일 확인
cat terraform.tfstate | grep '"content":'
            "content": "password",

변수 입력 방식과 우선순위

아래 순위와 같이 변수는 우선순위에 따라 결정되게 된다.


도전과제2 : 위 3개 코드 파일 내용에 리소스의 이름(myvpc, mysubnet1 등)을 반드시! 꼭! 자신의 닉네임으로 변경해서 배포 실습해보세요!

  • 리소스의 유형리소스의 이름이 차이를 알고, 리소스의 속성(예. ID)를 참조하는 방법에 대해서 익숙해지자

리소스의 유형은 테라폼에서 미리 정의되어 있는 리소스 템플릿을 활용한다는 의미이고 리소스의 이름은 고유하게 커스텀한 리소스에 대하여 이름을 정의하고 참조하거나 활용할 수 있게 하는 것이다.

vpc.tf

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

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

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

resource "aws_subnet" "repush_subnet_1" {
  vpc_id     = aws_vpc.repush_vpc.id
  cidr_block = "10.10.1.0/24"

  availability_zone = "ap-northeast-2a"

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

resource "aws_subnet" "repush_subnet_2" {
  vpc_id     = aws_vpc.repush_vpc.id
  cidr_block = "10.10.2.0/24"

  availability_zone = "ap-northeast-2c"

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


resource "aws_internet_gateway" "repush_igw" {
  vpc_id = aws_vpc.repush_vpc.id

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

resource "aws_route_table" "repush_rt" {
  vpc_id = aws_vpc.repush_vpc.id

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

resource "aws_route_table_association" "repush_rtassociation1" {
  subnet_id      = aws_subnet.repush_subnet_1.id
  route_table_id = aws_route_table.repush_rt.id
}

resource "aws_route_table_association" "repush_rtassociation2" {
  subnet_id      = aws_subnet.repush_subnet_2.id
  route_table_id = aws_route_table.repush_rt.id
}

resource "aws_route" "repush_default_route" {
  route_table_id         = aws_route_table.repush_rt.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.repush_igw.id
}

output "aws_vpc_id" {
  value = aws_vpc.repush_vpc.id
}

sg.tf

resource "aws_security_group" "repush_sg" {
  vpc_id      = aws_vpc.repush_vpc.id
  name        = "T101 SG"
  description = "T101 Study SG"
}

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

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

ec2.tf

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

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

  owners = ["amazon"]
}

resource "aws_instance" "repush_ec2" {

  depends_on = [
    aws_internet_gateway.repush_igw
  ]

  ami                         = data.aws_ami.repush_amazonelinux2.id
  associate_public_ip_address = true
  instance_type               = "t2.micro"
  vpc_security_group_ids      = ["${aws_security_group.repush_sg.id}"]
  subnet_id                   = aws_subnet.repush_subnet_1.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 = "t101-repush_ec2"
  }
}

output "repush_ec2_public_ip" {
  value       = aws_instance.repush_ec2.public_ip
  description = "The public IP of the Instance"
}

결과


local 지역 값

코드 내에서 사용자가 지정한 값 또는 속성 값을 가공해 참조 가능한 local (지역 값)은 외부에서 입력되지 않고, 코드 내에서만 가공되어 동작하는 값을 선언한다.

local은 입력 변수와 달리 선언된 모듈 내에서만 접근 가능하고, 변수처럼 실행 시에 입력받을 수 없다.

로컬은 사용자가 테라폼 코드를 구현할 때 이나 표현식을 반복적으로 사용할 수 있는 편의를 제공한다.
하지만 빈번하게 여러 곳에서 사용되는 경우 실제 값에 대한 추적이 어려워져 유지 관리 측면에서 부담이 발생할 수 있으므로 주의해야 한다.

도전과제4 : local를 활용해서 리소스(어떤 리소스든지 상관없음)를 배포해보고, 해당 코드를 정리해주세요!

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

locals {
  name = "repushtest"
  team = {
    group = "dev"
  }
}										# local을 활용해서 name 및 team에 대한 값을 등록한다.

resource "aws_iam_user" "repushiamuser1" {
  name = "${local.name}1"
  tags = local.team
}										# local에서 정의한 값을 사용한다.

resource "aws_iam_user" "repushiamuser2" {
  name = "${local.name}2"
  tags = local.team
}


출력 output

출력 값은 주로 테라폼 코드의 프로비저닝 수행 후의 결과 속성 값을 확인하는 용도로 사용된다.

또한 프로그래밍 언어에서 코드 내 요소 간에 제한된 노출을 지원하듯, 테라폼 모듈 간, 워크스페이스 간 데이터 접근 요소로도 활용할 수 있다.

예를 들면 자바의 getter와 비슷한 역할이다. 출력 값의 용도는 다음과 같이 정의할 수 있다.

  • 루트 모듈에서 사용자가 확인하고자 하는 특정 속성 출력
  • 자식 모듈의 특정 값을 정의하고 루트 모듈에서 결과를 참조
  • 서로 다른 루트 모듈의 결과를 원격으로 읽기 위한 접근 요소

반복문

list 형태의 값 목록이나 Key-Value 형태의 문자열 집합인 데이터가 있는 경우 동일한 내용에 대해 테라폼 구성 정의를 반복적으로 하지 않고 관리할 수 있다.

  • count : 반복문, 정수 값만큼 리소스나 모듈을 생성
  • for_each : 반복문, 선언된 key 값 개수만큼 리소스를 생성
  • for : 복합 형식 값의 형태를 변환하는 데 사용 ← for_each와 다름
  • dynamic : 리소스 내부 속성 블록을 동적인 블록으로 생성

도전과제5 : count, for_each 반복문, for문, dynamic문 을 활용해서 리소스(어떤 리소스든지 상관없음)를 배포해보고, 해당 코드를 정리해주세요!

variables.tf

variable "user_names" {
  description = "Create IAM users with these names"
  type        = list(string)
  default     = ["repush", "minchan", "bigmnt"]
}

iam.tf

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

resource "aws_iam_user" "repushiam" {
  count = length(var.user_names)
  name  = var.user_names[count.index]
}

output "first_arn" {
  value       = aws_iam_user.repushiam[0].arn
  description = "The ARN for the first user"
}

output "all_arns" {
  value       = aws_iam_user.repushiam[*].arn
  description = "The ARNs for all users"
}

결과


0개의 댓글