T101-7주차(Last study) [Terraform 101 Study]

cloudNet@ 팀의 가시다 님이 진행하는 테라폼 102 스터디 7주차 정리입니다. (테라폼으로 시작하는 IaC” (한빛미디어) 도서로 진행!)

1. 워크플로

1.1 규모에 따른 워크플로

테라폼 워크플로 : Write → Plan → Apply, 워크스페이스 별로 접근 권한을 관리하고 중앙에서 관리되는 실행 환경을 설계하여 규모에 맞는 워크플로 설계가 필요함

개인 워크플로 : 개인이 테라폼으로 일하는 방식의 예

https://www.freecodecamp.org/news/terraform-workflow-working-individually-and-in-a-team/

  • Write : 프로비저닝하려는 목적에 따라 테라폼 코드를 작성
    • 개인 작업이더라도 반복적인 사용성을 고려하자.
    • 인수에 할당되는 값을 입력 변수화하고 반복적인 구조가 발생하는 경우 리소스 단위별로 반복문을 사용할지 다수의 리소스를 모듈화할지 결정한다.
  • Plan : 적용하기 위한 실행 계획을 통해 리뷰
    • 테라폼의 Plan뿐 아니라, terraform fmt를 통해 코드 형태를 포멧팅하고 변경되는 리소스를 리뷰한다.
    • 또한 테라폼과 함께 동작하는 tfsec이나 terrascan 같은 보안 취약성 점검 툴 등을 활용하는 것도 좋은 방안이다.
  • Apply : 코드로 실제 인프라를 프로비저닝
    • 실행 계획상으로는 정상이지만 실제 프로비저닝하는 단계에서 인수 값, 생성 순서, 종속성에 따라 오류가 발생할 수 있다.
    • 성공적인 완료를 위해 Write > Plan > Apply 단계를 반복하고 성공하는 경우 코드 관리를 위해 VCS에 코드를 병합한다.

다중 작업자 워크플로

애플리케이션 개발 워크플로와 유사하다.

https://www.freecodecamp.org/news/terraform-workflow-working-individually-and-in-a-team/

  • Write
    • 여러 작업자의 테라폼 코드가 충돌하지 않도록 VCS와 같은 형상관리 도구에 익숙해져야 한다.
    • 작업자는 작업 전에 미리 원격 저장소코드를 받고 깃에서는 브랜치를 활용해 개별적으로 작업한다.
    • 개인의 워크플로에서 고려한 변수화와 더불어 패스워드와 인증서 같은 민감 데이터가 포함되지 않도록 코드를 설계한다.
    • 또한 개인 작업 환경에서만 사용되는 변수는 공유하지 않는다.
    • 깃을 사용한다면 작업자 개인의 변수terraform.tfvars 에 선언하고 .gitignore에 추가해 개별적으로 테스트할 수 있는 환경을 구성할 수 있다
    • 이 단계에서 개별 작업자는 **작은 단위의 개별 워크플로(**Write > Plan > Apply)를 반복해야 한다.
    • 개별 작업 환경과 별개로 병합되는 코드가 실제 운영 중인 인프라에 즉시 반영되면 실행 후 발생할 오류 예측이 어려워 부담이 될 수 있다.
    • 이를 보완하기 위해 프로비저닝 대상의 환경을 검증운영, 또는 그 이상의 환경으로 구성 가능하도록 구조화한다.
    • 이때 사용하는 방식은 디렉터리 기반 격리깃 기반의 브랜치 격리다.
  • Plan
    • 둘 이상의 작업자는 프로비저닝 이전에 팀원 간 리뷰를 거쳐 변경된 내역을 확인하고 공통 저장소에 병합해야 한다.
    • 리뷰 단계에서는 추가, 삭제, 수정된 내역을 관련 작업자가 검증, 질의, 배움의 단계를 거쳐 복기함으로써 코드 상태를 개선 유지하고 작업자 간에 의도를 공유한다.
    • 코드 자체 외에도 테라폼의 Plan 결과를 풀 리퀘스트 단계에 같이 제공하면 영향을 받는 리소스와 서비스 중단에 대한 예측이 더 쉬워진다.
    • CI 툴과 연계하거나 Terraform Cloud/Enterprise의 VCS 통합 기능으로 자동화할 수 있다.
  • Apply
    • 코드가 최종 병합되면 인프라 변경이 수행됨을 알리고 변경되는 대상 환경의 중요도에 따라 승인이 필요할 수 있다.
    • 또한 변경하는 코드가 특정 기능, 버그 픽스, 최종 릴리즈를 위한 병합인가에 따라 이 단계에 추가로 코드 병합이 발생할 수 있다.
    • 관리하는 단위를 나누는 기준은 조직 R&R, 서비스, 인프라 종류 등으로 구분된다.

다수 팀의 워크플로 : R&R이 분리된 다수 팀 또는 조직의 경우

  • R&R이 분리된 다수 팀 또는 조직의 경우 테라폼의 프로비저닝 대상은 하나이지만 관리하는 리소스가 분리된다.
  • 단일 팀의 워크플로가 유지되고 그 결과에 대해 공유해야 하는 핵심 워크플로가 필요하다.

  • Write
    • 대상 리소스가 하나의 모듈에서 관리되지 않고 R&R에 의해 워크스페이스가 분리된다.
    • 서로 다른 워크스페이스에서 구성된 리소스 데이터를 권한이 다른 팀에게 공유하기 위해, 저장된 State 접근 권한을 제공하고 output을 통해 공유 대상 데이터를 노출한다.
    • 테라폼 코드 작성 시 다른 워크스페이스에서의 변경 사항을 데이터 소스로 받아 오는 terraform_remote_state 또는 별도 KV-store를 활용하는 코드 구성이 요구된다.
    • 또한 관리 주체가 다른 곳에서 생긴 변경 사항의 영향을 최소화하도록 리모트 데이터 소스의 기본값을 정의하거나 코드적인 보상 로직을 구현하는 작업이 필요하다.
  • Plan
    • 코드 기반으로 진행되는 리뷰는 반영되는 다른 팀의 인프라를 VCS상의 코드 리뷰만으로도 공유받고 영향도를 검토할 수 있다.
    • 병합을 승인하는 단계에 영향을 받는 다른 팀의 작업자도 참여해야 한다.
  • Apply
    • 프로비저닝 실행과 결과에 대한 안내가 관련 팀에 알려져야 하므로 파이프라인 구조에서 자동화하는 것을 추천한다.
    • 실행 후의 영향도가 여러 팀이 관리하는 리소스에 전파될 수 있으므로 코드 롤백 훈련이 필요하다.
    • 생성된 결과에 다른 워크스페이스에서 참조되는 output 값의 업데이트된 내용을 다른 팀이 확인하는 권한 관리가 필요하다

1.2 격리 구조

  • 테라폼 수준의 격리 목표 : State를 분리
  • 테라폼은 파일이나 하위 모듈로 구분하더라고 동작 기준은 실행하는 루트 모듈에서 코드를 통합하고 하나의 State로 관리한다.
  • 애플리케이션 구조가 모놀리식(+아키텍처)에서 MSA로 변화하는 과정은 테라폼의 IaC 특성과도 결부된다.
  • 테라폼 또한 사용하는 리소스가 적고 구조가 단순하면 모놀리식 방식으로 구성하는 것이 인프라 프로비저닝 구축 속도는 빠를 수 있다.
  • 하지만 유지 보수, 인수인계, 운영의 관점에서는 프로비저닝 단위별로 분류하는, 마치 MSA와도 같은 분산된 설계가 매몰 비용과 기술 부채를 줄이는 데 효과적이다.
  • 규모가 큰 워크플로를 만들기 위해서는 간단하고 조합 가능한 부분들이 모여 집합을 이루어야 한다.
  • 이러한 집합에서 발생하는 정보는 다른 집합과 교환할 수 있지만, 각 집합은 독립적으로 실행되며 다른 집합에 영향을 받지 않는 격리된 구조가 필요하다.
  • 초기 테라폼 적용 단계에서 단일 또는 소수의 작업자는 단일 대상에 대해 IaC를 적용하고 하나의 루트 모듈에 많은 기능을 포함시킬 가능성이 높다.

1.2.1 루트 모듈 격리(파일/디렉터리)

  • 단일 작업자가 테라폼으로 프로비저닝을 하는 많은 경우에 관리 편의성 및 배포 단순화를 위해 하나의 루트 디렉터리에 파일로 리소스들을 구분하거나, 디렉터리를 생성하고 하위에 구성 파일 묶음을 위치시켜 루트 모듈에서 하위 디렉터리를 모듈로 읽는 구조를 사용한다.
  • 작업자가 관리하는 영역 또는 프로비저닝되는 리소스 묶음의 독립적인 실행을 위해 단일 루트 모듈 내의 리소스를 다수의 루트 모듈로 분리하고 각 모듈의 State를 참조하도록 격리한다.
  • 관리적인 측면으로는 작업자들의 관리 영역을 분리시키고 깃 기준의 리모트 저장소도 접근 권한을 관리할 수 있다.
  • 협업과 관련해 작업자별로 특정 루트 모듈을 선정해 구성 작업을 진행해 코드 충돌을 최소화하는 환경을 구성하고 인수인계 과정에서 리뷰하는 영역을 최소화할 수 있다.

1.2.2 환경 격리 – 깃 브랜치

  • 서비스의 테스트, 검증, 운영 배포를 위해 테라폼으로 관리하는 리소스가 환경별로 격리되어야 한다면 디렉터리 구조로 분리하는 방안을 고려할 수 있다.
  • 디렉터리별로 각 환경을 나누는 것은 개인의 관리 편의성은 높지만, 환경의 아키텍처를 고정시키고 코드 수준의 승인 체계를 만들기 위해서는 최종 형상에 대한 환경별 브랜치를 구성하기를 권장한다.
  • 디렉터리 구조만으로는 환경에 따라 사용자를 격리할 수 없다. 이때 깃의 브랜치 기능을 활용하면 환경별로 구별된 작업과 협업이 가능하다.

https://born4joy.files.wordpress.com/2019/05/git-flow.png

  • main : 운영 코드가 관리되며 이곳에는 직접적으로 구성 변경을 수행하지 않음
  • QA : 검증 대상 인프라를 구성하는 코드로, 메인 브랜치와 같이 직접적인 구성 변경을 수행하지 않음
  • DEV : QA 전 단계로 메인 코드 구성과 기능 브랜치의 병합을 담당
  • feature : 새로운 리소스를 추가하고 구현하며 여러 개가 될 수 있음
  • 관리의 편의성을 고려해 Hot-fix와 Release 브랜치를 추가할 수도 있지만 인프라의 특성상 개발, 검증, 운영으로 나눈다.
  • 환경 간에 프로비저닝이 되는 리소스를 갖추고 있다면 운영을 위한 프로비저닝 환경을 안정적으로 유지할 수 있다는 장점이 있다.
  • 디렉터리 구조로 관리하는 환경별 디렉터리 구성 방식에서는 개발할 때 작성한 구성을 다시 복사해 검증 또는 운영에 반영하므로 환경별로 구성이 다른 상황이 발생할 여지가 높고, 모든 디렉터리에 접근 가능할 경우 검증과 운영을 위한 구성을 직접 수정하는 일이 발생할 가능성이 높다.
  • 따라서 작업자가 다수의 환경을 동시에 관리한다면 디렉터리로 구분하더라도 각 디렉터리마다 동일한 깃 저장소의 브랜치별 리모트 구성을 하는 것이 바람직하다.

이 방식은 동일한 브랜치를 변경해가면 작업해 발생하는 실수를 줄일 수 있고, 각 브랜치가 연결되어 있으므로 단일 작업자가 다수의 환경을 관리하는 이점과 각 환경별로 리소스 구성이 동일하게 유지되는 장점이 있다.

1.3 프로비저닝 파이프라인 설계 – 깃허브

  • 프로비저닝 파이프라인 + Github Action 준비
    • 실제 서비스가 실행되는 대상을 프로비저닝하면 테라폼의 Plan과 Apply 과정상에 추가로 코드 검증, 실행 계획 검증, 실행 후 결과 확인과 같은 추가 동작을 자동화해 연계할 필요성이 생긴다.
    • 도구 : 젠킨스, Github Action, TFC/TFE
    • Github Action : 깃허브 환경에서 제공하는 CI/CD 자동화 도구 – 워크플로를 설계하고 다양한 라이브러리들을 이용해 다양한 작업 구성이 가능
    • 저장소 포크 : https://github.com/terraform101/terraform-aws-github-action
  • Github Action은 별도의 State 저장소(=공유 저장소)를 제공하지 않기 때문에 테라폼 실행으로 생성되는 State가 항상 초기화되어 프로비저닝 결과를 유지할 수 없다. (해결 방은로는 Amazon S3, Google Cloud Storage, Terraform Cloud 등 있습니다.)
  • 따라서 아래의 과정으로 백엔드를 활성화

1.리모트 저장소를 로컬 환경에 복제

#
MyGit=<각자 자신의 깃허브 계정>
MyGit=gasida
git clone https://github.com/$MyGit/terraform-aws-github-action

# 확인
tree terraform-aws-github-action
cd terraform-aws-github-action
#이 명령어를 사용하면 로컬 저장소에서 현재 연결된 원격 저장소의 URL을 확인할 수 있으며, 이를 통해 해당 원격 저장소가 올바르게 설정되어 있는지 확인가능 합니다.
git remote get-url origin

2.main.tf의 terraform 블록에서 사용자의 TFC 설정 organization으로 변경

# terraform login 설정 토큰 확인
cat ~/.terraform.d/credentials.tfrc.json | jq

3.main.tf 내용 수정

terraform {
  cloud {
    organization = "<MY_ORG_NAME>"         # 생성한 ORG 이름 지정
    hostname     = "app.terraform.io"      # default

    workspaces {
      name = "terraform-aws-github-action"
    }
  }
...

.github/workflow/action.yml 내용 수정

env:
  MY_PREFIX: DEV
  TF_VERSION: 1.2.6 # 1.2.5에서 변경

push

# 파일 내용이 변경되었음으로 gid add해줍니다. 
git add main.tf
git add .github/workflows/action.yml
git commit -m "init"
git push

– .github/workflow/action.yml 파일 push 시 에러 발생 시 → 해당 토큰에 workflow 권한 추가 후 다시 push 할 것

=> 조치 방법: settings → Developer Settings → Tokens 클릭 후 workflow 권한 추가 합니다.

1. 지정된 Terraform Cloud 백엔드 활성화를 위해 terraform init을 수행

#
terraform init
tree .terraform
==
.terraform
├── environment
├── providers
│   └── registry.terraform.io
│       └── hashicorp
│           ├── aws
│           │   └── 4.67.0
│           │       └── linux_amd64
│           │           └── terraform-provider-aws_v4.67.0_x5
│           ├── null
│           │   └── 3.2.1
│           │       └── linux_amd64
│           │           └── terraform-provider-null_v3.2.1_x5
│           └── tls
│               └── 4.0.4
│                   └── linux_amd64
│                       └── terraform-provider-tls_v4.0.4_x5
└── terraform.tfstate
==

2. 생성된 TFC 워크스페이스의 실행 모드 Execution mode를 Local로 수정

  • terraform-aws-github-action 워크스페이스(State 백엔드 역할만 수행) 확인 → 선택 후 좌측에 Settings 클릭 → General
  • 실행 모드 변경 : Execution Mode 에서 Local 선택 → 하단의 Save settings 클릭하여 변경 적용
  • 실습에서 사용되는 Github Action에 정의된 동작
  • Job ‘Scan’ : 테라폼 코드 검증
    • Check out code : 검증을 위해 저장소의 코드를 체크아웃
    • Run terrascan : 테라폼 코드 검증 도구인 Terrascan을 실행
    • Upload SARIF file : 검증의 결과(정적 분석 결과 표준 포맷)을 업로드
    • Job ‘Terraform’ : 테라폼 실행 ← Job ‘Scan’ 이후 실행
    • Check out code : 검증을 위해 저장소의 코드를 체크아웃
    • Configure AWS credentials : AWS 환경을 프로비저닝하기 위한 Credential 설정
    • Terraform Fmt : 표준 스타일 수정 대상 확인
    • Terraform init : 테라폼 실행을 위한 init 수행
    • Terraform validate : 코드 문법 오류 검사
    • Terraform plan : 실행 계획 확인
      • env.TF_LOG: info : 로그 수준을 info로 출력해 실행 디버깅
    • Plan output : 풀 리퀘스트인 경우 실행 계획을 정리해 출력
    • Terraform apply : 메인 브랜치 변경 시에만 Apply 수행

– 코드 내용 설명

-> action.yml : Github Action의 구성은 .github/workflows의 yml 파일 형태로 작성

-> main.tf : 프로비저닝의 실행은 작업자나 Github Action에서 발생하더라도 동일한 State유지를 위해 백엔드 구성 추가

terraform {
  cloud {
    organization = "<MY_ORG_NAME>"         # 생성한 ORG 이름 지정
    hostname     = "app.terraform.io" # default

    workspaces {
      name = "terraform-aws-github-action"
    }
  }
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}

provider "aws" {
  region = var.region
  default_tags {
    tags = {
      Project = "Coffee-Mug-Cake"
      Owner   = "jerry & tom"
    }
  }
}

resource "aws_vpc" "hashicat" {
  cidr_block           = var.address_space
  enable_dns_hostnames = true

  tags = {
    name        = "${var.prefix}-vpc-${var.region}"
    environment = "Production"
  }
}

resource "aws_subnet" "hashicat" {
  vpc_id     = aws_vpc.hashicat.id
  cidr_block = var.subnet_prefix

  tags = {
    name = "${var.prefix}-subnet"
  }
}

resource "aws_security_group" "hashicat" {
  name = "${var.prefix}-security-group"

  vpc_id = aws_vpc.hashicat.id

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

  ingress {
    from_port   = 443
    to_port     = 443
    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"]
    prefix_list_ids = []
  }

  tags = {
    Name = "${var.prefix}-security-group"
  }
}

resource "aws_internet_gateway" "hashicat" {
  vpc_id = aws_vpc.hashicat.id

  tags = {
    Name = "${var.prefix}-internet-gateway"
  }
}

resource "aws_route_table" "hashicat" {
  vpc_id = aws_vpc.hashicat.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.hashicat.id
  }
}

resource "aws_route_table_association" "hashicat" {
  subnet_id      = aws_subnet.hashicat.id
  route_table_id = aws_route_table.hashicat.id
}

data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"] # Canonical

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

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

resource "aws_eip" "hashicat" {
  instance = aws_instance.hashicat.id
  vpc      = true
}

resource "aws_eip_association" "hashicat" {
  instance_id   = aws_instance.hashicat.id
  allocation_id = aws_eip.hashicat.id
}

resource "aws_instance" "hashicat" {
  ami                         = data.aws_ami.ubuntu.id
  instance_type               = var.instance_type
  key_name                    = aws_key_pair.hashicat.key_name
  associate_public_ip_address = true
  subnet_id                   = aws_subnet.hashicat.id
  vpc_security_group_ids      = [aws_security_group.hashicat.id]

  tags = {
    Name = "${var.prefix}-hashicat-instance"
  }
}

# We're using a little trick here so we can run the provisioner without
# destroying the VM. Do not do this in production.

# If you need ongoing management (Day N) of your virtual machines a tool such
# as Chef or Puppet is a better choice. These tools track the state of
# individual files and can keep them in the correct configuration.

# Here we do the following steps:
# Sync everything in files/ to the remote VM.
# Set up some environment variables for our script.
# Add execute permissions to our scripts.
# Run the deploy_app.sh script.
resource "null_resource" "configure-cat-app" {
  depends_on = [aws_eip_association.hashicat]

  // triggers = {
  //   build_number = timestamp()
  // }

  provisioner "file" {
    source      = "files/"
    destination = "/home/ubuntu/"

    connection {
      type        = "ssh"
      user        = "ubuntu"
      private_key = tls_private_key.hashicat.private_key_pem
      host        = aws_eip.hashicat.public_ip
    }
  }

  provisioner "remote-exec" {
    inline = [
      "sudo apt -y update",
      "sleep 15",
      "sudo apt -y update",
      "sudo apt -y install apache2",
      "sudo systemctl start apache2",
      "sudo chown -R ubuntu:ubuntu /var/www/html",
      "chmod +x *.sh",
      "PLACEHOLDER=${var.placeholder} WIDTH=${var.width} HEIGHT=${var.height} PREFIX=${var.prefix} ./deploy_app.sh",
      "sudo apt -y install cowsay",
      "cowsay Mooooooooooo!",
    ]

    connection {
      type        = "ssh"
      user        = "ubuntu"
      private_key = tls_private_key.hashicat.private_key_pem
      host        = aws_eip.hashicat.public_ip
    }
  }
}

resource "tls_private_key" "hashicat" {
  algorithm = "RSA"
}

locals {
  private_key_filename = "${var.prefix}-ssh-key.pem"
}

resource "aws_key_pair" "hashicat" {
  key_name   = local.private_key_filename
  public_key = tls_private_key.hashicat.public_key_openssh
}

Github Action 과정에서 필요로 하는 State 공유를 위한 Terraform Cloud의 토큰, AWS 프로비저닝을 위한 AWS Credential과 같은 민감 데이터를 저장소에서 민감 변수로 처리할 수 있다.

#(옵션) AWS IAM 계정 생성 및 자격증명 획득
aws iam create-user --user-name test
aws iam create-access-key --user-name test
aws iam attach-user-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess --user-name test

해당 저장소의 [Setting] → [Secrets and variables – Actions] 선택 ⇒ 새로운 민감 변수 등록 [New repository secret]

  • Name : TF_API_TOKEN
  • Secret : <각자 자신의 TFC Token>

설정된 민감 변수는 Github Acction 정의 파일에서 ${{ secrets. 변수이름 }} 으로 호출된다

grep secrets .github/workflows/action.yml
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
github-token: ${{ secrets.GITHUB_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
  • TF_API_TOKEN : TFC의 기존 사용 토큰(credentials.tfrc.json 에 저장됨) 또는 신규 토큰을 생성해 입력
  • AWS_ACCESS_KEY_ID : AWS Access Key 입력
  • AWS_SECRET_ACCESS_KEY : AWS Secret Access Key 입력
  • 들어가는 방법으로는

1.해당 레파지 토리의 세팅 누릅니다.

2.액션 누릅니다.

3.뉴 레파지토리 시크릿 들어옵니다.

4.아래와 같은 형식으로 하여 작성 합니다

-포크되어 비활성화되어 있는 저장소의 [Actions] 탭으로 이동해 [I understand my workflows, go ahead and enable them] 버튼을 클릭, 활성화한다D

ㄴ활성화 상태 입니다.

연습 문제 [성공은 하지 못하였습니다.. 성공하면 내용 추가하겠습니다..]

실습전 사전 사항

* Automate Terraform with GitHub Actions 가이드 참고해서 토큰 생성

https://developer.hashicorp.com/terraform/tutorials/automation/github-actions#prerequisites

  • 조건
    • 테라폼 입력 변수 추가
      • 이름 : environment
      • 설명 : Define infrastructure’s environment
      • 타입 : string
      • 기본값 : dev
      • 변수 확인
      • 조건 : dev, qa, prod 인 경우 허용
        • 에러 메시지 : The environment value must be dev, qa, or prod.
      • aws_vpctags.environment를 입력 변수 environment 값으로 선언
  • 새로운 브랜치의 변경 내용을 커밋, 푸시하고 깃허브 웹페이지에서 풀 리퀘스트를 생성한다 ← 주의: 자신의 저장소로 요청하는지 꼭 확인
  • Github Action의 동작 조건은 메인 브랜치에 푸시가 발생하거나 풀 리퀘스트가 발생하는 경우로 정의되어 있다

main.tf 내용 수정

resource "aws_vpc" "hashicat" {
  cidr_block           = var.address_space
  enable_dns_hostnames = true

  tags = {
    name        = "${var.prefix}-vpc-${var.region}"
    environment = var.environment  # 원래 값 Production
  }
}

variable.tf 내용 추가

# Infrastructure 환경을 정의하는 변수를 선언합니다.
variable "environment" {
  # 변수의 타입을 문자열(string)로 설정합니다.
  type = string
  
  # 변수의 설명을 추가합니다. 여기서는 인프라의 환경을 정의한다는 것을 설명하고 있습니다.
  description = "Define infrastructure’s environment"
  
  # 변수의 기본값을 'dev'로 설정합니다.
  default = "dev"
  
  # 변수의 유효성을 검사합니다.
  validation {
    # var.environment의 값이 "dev", "qa" 또는 "prod" 중 하나인지 확인합니다.
    condition = contains(["dev", "qa", "prod"], var.environment)

    # 유효성 검사를 통과하지 못했을 경우 출력할 오류 메시지를 설정합니다.
    error_message = "The environment value must be dev, qa, or prod."
  }
}

push

terraform fmt
git add .
git commit -m "add env variable"
git push origin HEAD

ㄴ여기서 주의 할점은 나의 레파지토리로 해야한다.

Github에 Action 확인

# error 메시지
Terraform Unhandled error: HttpError: Resource not accessible by integration
Terraform The following actions uses node12 which is deprecated and will be forced to run on node16: actions/github-script@v3. For more info: https://github.blog/changelog/2023-06-13-github-actions-all-actions-will-run-on-node16-instead-of-node12-by-default/

1.4 Terraform Cloud (TFC)

TFC : 워크플로 구성 환경 제공, Github Action 보다 자유도는 낮지만 VCS연동, 변수 구성, RBAC(접근제어), 원격 실행 환경 등의 기능 활용

*TFC의 워크플로를 수행하기 위해 Standard 활성화 필요

MyGit=<>
MyGit=gasida
git clone https://github.com/$MyGit/terraform-aws-tfc-workflow
cd terraform-aws-tfc-workflow

MyTfcOrg=<각자 자신의 TFC 조직명>
MyTfcOrg=gasida-org
sed -i -e "s/<MY-ORG>/$MyTfcOrg/g" main.tf

# 파일 "main.tf"를 Git 스테이지에 추가합니다. 추후 커밋에 의해 추적될 파일로 지정되는 과정입니다.
git add main.tf

# 스테이지에 추가된 변경 사항들을 새로운 커밋으로 저장합니다. 이 때 커밋 메시지로 "init"가 사용됩니다.
git commit -m "init"

# 현재까지의 변경 사항을 원격 저장소로 푸시합니다. 이를 통해 원격 저장소가 최신 상태로 업데이트됩니다.
git push

terraform init

plan : 실행 후 출력은 스트림으로 처리 → [CTRL+C] 입력 시 원격 환경에서 실행 안내 받음, 실행 확인 url 출력

terraform plan
==
Running plan in Terraform Cloud. Output will stream here. Pressing Ctrl-C
will stop streaming the logs, but will not stop the plan running remotely.

Preparing the remote plan...

To view this run in a browser, visit:

# 아래와 같은 url 에 들어가면 아래 그림과 같이 결과를 알수가 있다.
https://app.terraform.io/app/sjmtom-org/terraform-aws-tfc-workflow/runs/run-osscf...
==

ㄴ변수가 설정안되어 있어 발생하는 오류이다.

  • TFC 리모트 실행 환경에서의 테라폼 입력 변수와 시스템 환경 변수 관리 : 워크스페이스 → Variables
    • 입력되는 변수 형태는 테라폼 코드에서 입력되는 입력 변수 Terraform variable와 테라폼이 실행되는 환경에서 환경 변수로 읽어 오는 시스템 환경 변수 Environment variable를 입력할 수 있다
      • 입력 변수 : TFC에서는 실행 후 인라인으로 넣을 수 없기 때문이며, 기존 terraform.tfvars 를 대체하는 역할을 수행함.
        • 따라서 기존 terraform.tfvars 파일은 동작하지 않는다
      • 환경 변수 : 테라폼 실행 환경은 더 이상 로컬 작업 환경이 아닌 원격지의 TFC 환경이다. 프로비저닝하는 클라우드의 API 키 또는 테라폼 실행 설정 등이 구성되어야 하는 경우와 같은 상황에서 사용된다
    • 입력되는 값의 형태 옵션에는 HCL과 Sensitive가 있다
      • HCL : 값 입력 시 선택 항목인 HCL은 입력 변수가 문자열이 아닌 HCL로 정의되는 리스트, 오브젝트 형태처럼 코드 형태를 띠는 경우 선택
      • Sensitive : 민감한 변수의 경우 선택, 선언된 민감 변수는 복호화할 수 없다. 클라우드 API Key, 패스워드, SSH Private Key, SSL 인증서 등
    • TFC 환경과 같이 중앙 실행 관리 환경을 사용하면 민감한 정보를 공유하거나 로컬에 저장하지 않고 안전하게 사용할 수 있다

+Add variable 로 입력

  • Prefix : Type(Terraform variable), Key(prefix), value(사용자 닉네임)
  • Access Key ID : Type(Environment variable), Key(AWS_ACCESS_KEY_ID), value(자신의 값)
  • Secret Access Key : Type(Environment variable), Key(AWS_SECRET_ACCESS_KEY), value(자신의 값), Sensitive 선택

그럼 총 아래와 같이 3개의 값이 들어갑니다.

terraform plan 실행 시 확인 가능 합니다.

apply 실행 : 원격에서 실행하지만 승인 여부 물어봄

terraform apply
>> 아직 yes 입력 하지 말자

TFC 워크스페이스에서 Overview 확인 → [See details] 클릭

스트림으로 출력된 실행 계획 확인 : 승인 및 실행, 취소, 추가 코멘트를 남길 수 있음 → [Confirm & Apply] 클릭

CLI에서도 승인 처리가 되었음을 확인

TFC Overview : 결과 확인

  • Metrics : 해당 워크스페이스에서 발생한 프로비저닝 작업의 측정된 수치 표기
  • Resource : 생성된 리소스의 타입, 생성일, 모듈 이름 등의 정보
  • Outputs : 출력 값


EC2 확인

다음의 권한 관리를 위해 리소스 삭제

terraform destroy -auto-approve

TFC + VCS 연계

  • VCS와 직접 통합되어 별도 워크플로 작성 없이도 풀 리퀘스트에 대한 Plan 결과를 확인할 수 있으며 지정된 브랜치에 병합이 발생하면 테라폼을 실행하는 자동화된 구성이 가능하다. 지원되는 VCS는 아래와 같다
    • Github.com / Github.com (OAuth) / Github Enterprise
    • Gitlab.com / Gitlab EE와 CE
    • Bitbucket Cloud / Bitbucket Server
    • Azure DevOps Server / Azure DevOps Services
  • 여기서는 Github.com 와 연동해보자
    • TFC Org → Setting → Provider ⇒ [Add a VCS provider] 클릭 : Github.com (Custom) 선택 후 → [register a new OAuth Application] 클릭
      • 아래 내용 입력 → 하단 Register application 클릭 ⇒ Generagate a new client secret 클릭 후 생성 메모 ⇒ 맨 하단 Update application 아래의 각 항목들을 복사하여

Register a new OAuth application 넣어줍니다. (enable 체크 안함)

  • TFC 입력 → 맨 하단 Connect and continue ⇒ 변경 화면에서 Grant 처리하고 최종적으로 [Authorize] 클릭
    • Name : My Github.com
    • Client ID : ab948…..
    • Client Secret : <>
  • SSH Keypair 은 필수는 아니므로 [Skip and finish] 로 연동 마무리

대상 VCS와의 인증 및 권한 처리가 완료되었으므로 다음 과정을 통해 워크스페이스에 VCS를 연계, 워크스페이스는 Admin 권한으로 수행한다

앞 부분 사용 워크스페이스 선택 → Setting → Version Control 선택 ⇒ [Connect to version control] 클릭

1.Version control workflow 선택

2.연동된 VCS목록에서 [My Github.com] 선택

3.포크된 저장소 선택

4.Confirm changes :Auto Apply 선택, Always trigger runs 선택, VCS branch(main 입력), Pull Request(Automatic… 체크) , 아래와 같으면 됩니다.

→ 하단 Update VCS Setting 클릭

워크스페이스와 VCS간 최초 연동되면 마지막 커밋 내용을 기반으로 Run이 실행된다. (자동 승인설정에 의하여 자동으로 만들어짐)

ㄴ트리거되는 레파지 정보도 확인 가능하다.

[Run] 메뉴로 이동해 깃허브에 의해 트리거링된 정보를 확인하고 Apply를 실행한다

  • 리소스 생성 완료 확인
    • VCS를 통한 풀 리퀘스트 동작 확인
      • main.tf 파일 수정 : terraform.cloud 블록 주석 처리 ← VCS 연동으로 더 이상 필요하지 않음
terraform {
  # cloud {
  #   organization = "gasida-org"
  #   hostname     = "app.terraform.io" # default

  #   workspaces {
  #     name = "terraform-aws-tfc-workflow"
  #   }
  # }
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}

provider "aws" {
  region = var.region
  default_tags {
    tags = {
      Project = "terraform cloud workflow123"
      Owner   = "jerry & tom111"
    }
  }
}

tfc-workflow 브랜치 생성, 커밋, 푸시

git branch -M tfc-workflow
git add .
git commit -m "tfc workflow"
git push origin HEAD

자신의 github 에서 rp 생성 → Merge pull request : Confirm

TFC 워크스페이스에서 확인

AWS 확인

  • TFC에서 생성된 리소스 삭제 : TFC 워크스페이스 Setting → Destruction and Delete ⇒ Queue destroy plan 로 생성된 리소스 삭제
  • 실습 완료 후 최종 삭제
  1. TFC Workspace 삭제
  2. TFC Provider 삭제
  3. Github Repo 삭제
  4. 로컬에 Repo 디렉터리 삭제
  5. (옵션) 임시로 AWS IAM 생성 했을 경우, AWS IAM 계정 삭제

위로 스크롤