728x90
반응형
SMALL
SMALL

 

terraform_remote_state는 어떤것이고 어떻게 활용하는지 알아보자.

terraform_remote_state

이 terraform_remote_state는 Data Source로써 사용이 가능하다. hasicorp에서 공식적으로 지원하는 Terraform 이라는 Provider를 사용하면 이를 구현할 수 있다.

 

The terraform_remote_state Data Source | Terraform | HashiCorp Developer

Retrieves the root module output values from a Terraform state snapshot stored in a remote backend.

developer.hashicorp.com

 

예전에는 Terraform Registry에 Provider에 Terraform이 있었는데 지금은 위 문서로 대체된듯하다. 여튼 위 문서를 참조하면 된다.

 

이 terraform_remote_state의 핵심은 각 워크스페이스 별 상태관리 파일이 있음을 이제 알고 있다. .tfstate 파일로 된. 이 파일을 참조해서 다른 워크스페이스에서 이 상태를 가져다가 사용하겠다는 것이 terraform_remote_state의 핵심이다.

 

근데 이 때 Backend가 무엇이냐에(local, remote) 따라 작성 방식이 살짝 다르다. 이 내용도 위 문서에 있다.

 

The terraform_remote_state Data Source | Terraform | HashiCorp Developer

Retrieves the root module output values from a Terraform state snapshot stored in a remote backend.

developer.hashicorp.com

 

 

The terraform_remote_state Data Source | Terraform | HashiCorp Developer

Retrieves the root module output values from a Terraform state snapshot stored in a remote backend.

developer.hashicorp.com

 

그럼 이제 코드 구조를 한번 살펴보자. 구조는 다음과 같다.

 

설명을 하자면 network 라는 워크스페이스가 만들어내는 리소스가 .tfstate 파일에 저장되어 그 파일을 바라보고 ec2 워크스페이스가 사용할 것이다.

 

network/main.tf

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

variable "vpc_name" {
  description = "생성되는 VPC의 이름"
  type        = string
}

locals {
  common_tags = {
    Project = "Network"
    Owner   = "cwchoiit"
  }
}

output "vpc_name" {
  value = module.vpc.name
}

output "vpc_id" {
  value = module.vpc.id
}

output "vpc_cidr" {
  description = "생성된 VPC의 CIDR 영역"
  value       = module.vpc.cidr_block
}

output "subnet_groups" {
  value = {
    public  = module.subnet_group__public
    private = module.subnet_group__private
  }
}

module "vpc" {
  source  = "tedilabs/network/aws//modules/vpc"
  version = "0.24.0"

  name       = var.vpc_name
  cidr_block = "10.0.0.0/16"

  internet_gateway_enabled = true

  dns_hostnames_enabled = true
  dns_support_enabled   = true

  tags = local.common_tags
}

module "subnet_group__public" {
  source  = "tedilabs/network/aws//modules/subnet-group"
  version = "0.24.0"

  name                    = "${module.vpc.name}-public"
  vpc_id                  = module.vpc.id
  map_public_ip_on_launch = true

  subnets = {
    "${module.vpc.name}-public-001/az1" = {
      cidr_block           = "10.0.0.0/24"
      availability_zone_id = "apne2-az1"
    }
    "${module.vpc.name}-public-002/az2" = {
      cidr_block           = "10.0.1.0/24"
      availability_zone_id = "apne2-az2"
    }
  }

  tags = local.common_tags
}

module "subnet_group__private" {
  source  = "tedilabs/network/aws//modules/subnet-group"
  version = "0.24.0"

  name                    = "${module.vpc.name}-private"
  vpc_id                  = module.vpc.id
  map_public_ip_on_launch = false

  subnets = {
    "${module.vpc.name}-private-001/az1" = {
      cidr_block           = "10.0.10.0/24"
      availability_zone_id = "apne2-az1"
    }
    "${module.vpc.name}-private-002/az2" = {
      cidr_block           = "10.0.11.0/24"
      availability_zone_id = "apne2-az2"
    }
  }

  tags = local.common_tags
}

이 파일은 기존에 계속 사용했던 network 테라폼 소스이다.

 

network/terraform.tfvars

vpc_name = "remote-state"

 

이렇게 두 개의 파일을 가지는 network 워크스페이스를 Apply 해보자. 정상적으로 수행이 잘 됐다.

 

그럼 이제 주의깊게 봐야하는 파일이 이 Apply를 하고 난 후 생성되는 .tfstate 파일이다.

 

이 파일을 바라보게 EC2 워크스페이스가 작업이 될 것이다.

 

ec2/main.tf

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

data "terraform_remote_state" "network" {
  backend = "local"

  config = {
    path = "${path.module}/../network/terraform.tfstate"
  }
}

locals {
  vpc_name      = data.terraform_remote_state.network.outputs.vpc_name
  subnet_groups = data.terraform_remote_state.network.outputs.subnet_groups
}

data "aws_ami" "ubuntu" {
  most_recent = true

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

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

  owners = ["099720109477"] # Canonical
}

resource "aws_instance" "terraform-ec2" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t2.micro"
  subnet_id     = local.subnet_groups["public"].ids[0]

  tags = {
    Name = "${local.vpc_name}-ubuntu"
  }
}

 

여기서 주의깊게 볼 부분은 이 부분이다.

data "terraform_remote_state" "network" {
  backend = "local"

  config = {
    path = "${path.module}/../network/terraform.tfstate"
  }
}

locals {
  vpc_name      = data.terraform_remote_state.network.outputs.vpc_name
  subnet_groups = data.terraform_remote_state.network.outputs.subnet_groups
}

우선, terraform_remote_state를 Data Source로 사용한다. backend는 Local이고 그 경로는 이 파일의 상위 경로에 있는 network 워크스페이스의 .tfstate 파일이 된다.

그리고 로컬 변수로 vpc_name과 subnet_groups를 만들어주는데 이 각각의 변수값을 위에서 선언한 terraform_remote_state의 DataSource를 사용한다. 다만 여기서 한가지 주의할 점은 remote_state를 가져올 땐 .outputs 을 통해서 가져와야한다.

data.terraform_remote_state.network.outputs.vpc_name 이런식으로 말이다.

 

그리고 이 로컬 변수를 EC2 리소스에서도 사용한다. 하단 소스를 보면 subnet_id와 tags의 Name을 로컬변수를 가지고 사용했다.

resource "aws_instance" "terraform-ec2" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t2.micro"
  subnet_id     = local.subnet_groups["public"].ids[0]

  tags = {
    Name = "${local.vpc_name}-ubuntu"
  }
}

 

이 EC2 워크스페이스를 Apply해보자. 다음과 같이 정상적으로 끝난다.

 

이제 AWS Console에서 확인해보자. 우선 VPC, Subnets을 먼저 확인해보자.

 

만든 remote-state가 prefix로 붙은 VPC와 Subnets이 잘 만들어졌고 이 state를 사용한 EC2가 다음과 같이 잘 만들어졌다.

 

 

결론

이렇게 terraform_remote_state를 활용해서 다른 워크스페이스의 state를 가져와 참조하는 기능을 사용해보았다.

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

Packer Part. 1  (0) 2024.03.14
Terraform Provisioner/EC2 Userdata  (2) 2024.03.11
Terraform 나만의 Module 만들어보기  (0) 2024.03.10
Terraform Cloud  (0) 2024.03.10
Terraform Workspace  (0) 2024.03.08
728x90
반응형
SMALL
SMALL

저번 시간에는 Terraform Registry에 있는 모듈을 가져다가 사용하는 것 까지는 해봤다. 이번에는 내가 스스로 모듈을 만들어서 그 모듈을 사용해보자.

 

Directory path

우선, 나의 모듈을 가지고 사용하는 테라폼 소스 코드의 구조는 다음과 같다.

 

account 라는 폴더가 하나의 모듈로서 기능을 할 것이고 그 모듈을 루트 디렉토리에 있는 main.tf 에서 사용할 것이다. 그럼 account 디렉토리에 각각의 파일을 하나씩 알아보자.

 

참고로 파일명은 정해져 있는게 아니라 관습적으로 사용되는 명칭이다. 그니까 꼭 저것을 따르지 않아도 되지만 관습이니 안 따라야할 이유도 없다.

 

versions.tf

이 파일은 테라폼과 프로바이더의 버전을 명시한 파일이다. 소스 코드는 다음과 같다.

terraform {
  required_version = ">= 0.15"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 3.45"
    }
  }
}

 

variables.tf

이 파일은 모듈에서 사용하는 변수를 정의한 파일이다. 소스 코드는 다음과 같다.

variable "name" {
  description = "The name for the AWS account. Used for the account alias."
  type        = string
}

variable "password_policy" {
  description = "Password Policy for the AWS account."
  type = object({
    minimum_password_length        = number
    require_numbers                = bool
    require_symbols                = bool
    require_lowercase_characters   = bool
    require_uppercase_characters   = bool
    allow_users_to_change_password = bool
    hard_expiry                    = bool
    max_password_age               = number
    password_reuse_prevention      = number
  })
  default = {
    minimum_password_length        = 8
    require_numbers                = true
    require_symbols                = true
    require_lowercase_characters   = true
    require_uppercase_characters   = true
    allow_users_to_change_password = true
    hard_expiry                    = false
    max_password_age               = 0
    password_reuse_prevention      = 0
  }
}

 

outputs.tf

이 파일은 모듈에서 만들어내는 outputs을 정의한 파일이다. 특정 테라폼 소스에서 어떤 모듈을 가져다가 사용할 때 모듈이 내뱉는 attribute를 참조해야 하는 경우가 많은데 그 참조할 수 있는 값들은 모듈에서 뱉어내는 output 뿐이다. 그래서 적절한 output이 중요하다.

소스 코드는 다음과 같다.

output "id" {
  description = "The AWS Account ID"
  value       = data.aws_caller_identity.this.account_id
}

output "name" {
  description = "Name of the AWS account. The account alias."
  value       = aws_iam_account_alias.this.account_alias
}

output "signin_url" {
  description = "The URL to signin for the AWS account."
  value       = "https://${var.name}.signin.aws.amazon.com/console"
}

output "password_policy" {
  description = "Password Policy for the AWS Account. `expire_passwords` indicates whether passwords in the account expire. Returns `true` if `max_password_age` contains a value greater than 0."
  value       = aws_iam_account_password_policy.this
}

 

main.tf

모듈의 main.tf 파일이다. 소스 코드는 다음과 같다.

data "aws_caller_identity" "this" {}

resource "aws_iam_account_alias" "this" {
  account_alias = var.name
}

resource "aws_iam_account_password_policy" "this" {
  minimum_password_length        = var.password_policy.minimum_password_length
  require_numbers                = var.password_policy.require_numbers
  require_symbols                = var.password_policy.require_symbols
  require_lowercase_characters   = var.password_policy.require_lowercase_characters
  require_uppercase_characters   = var.password_policy.require_uppercase_characters
  allow_users_to_change_password = var.password_policy.allow_users_to_change_password
  hard_expiry                    = var.password_policy.hard_expiry
  max_password_age               = var.password_policy.max_password_age
  password_reuse_prevention      = var.password_policy.password_reuse_prevention
}

 

README.md

이 파일은 이 모듈이 어떤 형식으로 만들어지고 무엇이 필수값이고 아닌지 또는 버전은 어떻게 되는지 이 모듈의 outputs은 무엇인지를 명시한 파일이다. 그리고 이 파일은 직접 작성한 게 아니고 terraform-docs 라는 툴을 사용했다.

 

terraform-docs

Generate Terraform modules documentation in various formats

terraform-docs.io

이 녀석을 다운받고 시키는 대로 하면 다음과 같이 날 위해 README 파일을 만들어준다.

$ terraform-docs markdown path/to/module

## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.15 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 3.45 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 3.45 |

## Modules

No modules.

## Resources

| Name | Type |
|------|------|
| [aws_iam_account_alias.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_account_alias) | resource |
| [aws_iam_account_password_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_account_password_policy) | resource |
| [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_name"></a> [name](#input\_name) | The name for the AWS account. Used for the account alias. | `string` | n/a | yes |
| <a name="input_password_policy"></a> [password\_policy](#input\_password\_policy) | Password Policy for the AWS account. | <pre>object({<br>    minimum_password_length        = number<br>    require_numbers                = bool<br>    require_symbols                = bool<br>    require_lowercase_characters   = bool<br>    require_uppercase_characters   = bool<br>    allow_users_to_change_password = bool<br>    hard_expiry                    = bool<br>    max_password_age               = number<br>    password_reuse_prevention      = number<br>  })</pre> | <pre>{<br>  "allow_users_to_change_password": true,<br>  "hard_expiry": false,<br>  "max_password_age": 0,<br>  "minimum_password_length": 8,<br>  "password_reuse_prevention": 0,<br>  "require_lowercase_characters": true,<br>  "require_numbers": true,<br>  "require_symbols": true,<br>  "require_uppercase_characters": true<br>}</pre> | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_id"></a> [id](#output\_id) | The AWS Account ID |
| <a name="output_name"></a> [name](#output\_name) | Name of the AWS account. The account alias. |
| <a name="output_password_policy"></a> [password\_policy](#output\_password\_policy) | Password Policy for the AWS Account. `expire_passwords` indicates whether passwords in the account expire. Returns `true` if `max_password_age` contains a value greater than 0. |
| <a name="output_signin_url"></a> [signin\_url](#output\_signin\_url) | The URL to signin for the AWS account. |

 

그리고 이제 모듈을 가져다가 사용하는 루트 디렉토리에 있는 main.tf 파일을 보자.

 

main.tf

하단 소스 코드를 보자.

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

module "account" {
  source = "./account"

  name = "cwchoiit-terraform"
  password_policy = {
    minimum_password_length        = 8
    require_numbers                = true
    require_symbols                = true
    require_lowercase_characters   = true
    require_uppercase_characters   = true
    allow_users_to_change_password = true
    hard_expiry                    = false
    max_password_age               = 0
    password_reuse_prevention      = 0
  }
}

output "id" {
  value = module.account.id
}

output "account_name" {
  value = module.account.name
}

output "signin_url" {
  value = module.account.signin_url
}

output "account_password_policy" {
  value = module.account.password_policy
}

 

저번에는 Registry에 있는 module을 사용할 때 source 형태가 저런식이 아니었다. 그리고 이건 로컬에 있는 모듈을 사용할 때 쓰는 source 형식이다. 이 두 차이점을 잘 알아둬야한다.

 

그리고 account 라는 모듈이 필요한 속성인 name, password_policy 값을 작성한다.

output은 모듈이 주는 output을 그대로 가져다가 사용했다.

 

이 상태에서 Apply를 해보자. 다음과 같이 Apply는 정상적으로 됐다. 

 

그럼 내 계정의 Alias가 저렇게 변경됐는지 확인해보자. 다음은 그 결과이다.

 

 

결론

이렇게 로컬에서 모듈을 직접 만들어보고 사용해보았다. 모듈은 이런식으로 로컬에서도 사용가능하고 Registry에서 가져다가 사용하는 것도 된다. 그리고 한 가지 더 유용한 `terraform-docs`도 사용해봤다.

 

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

Terraform Provisioner/EC2 Userdata  (2) 2024.03.11
terraform_remote_state  (0) 2024.03.10
Terraform Cloud  (0) 2024.03.10
Terraform Workspace  (0) 2024.03.08
Terraform Commands (taint / untaint)  (0) 2024.03.08
728x90
반응형
SMALL
SMALL

저번 포스팅에서 잠시 다루어보았던 Terraform Cloud를 좀 더 이해해보는 포스팅이다. 우선 기존에 만들었던 Organizations의 Workspace로 가보자.

 

Workspace > Settings > General에 가보면 다음과 같은 설정 부분이 있다.

 

저번엔 이 부분을 우선 Local로 하고 추후에 더 자세히 알아보기로 했었는데 이 부분에 대해 얘기할 시간이다. 

 

Execution Mode: Local

Terraform 명령 수행을 Local에서 실행하겠다는 의미이다. 즉, 작업자 PC에서 실행하겠다는 의미가 된다.

Terraform Cloud는 State 저장 역할만 수행하게 된다.

 

Execution Mode: Remote

Remote로 설정하게 되면 Terraform Cloud 인프라에 위치한 테라폼 명령어를 실행시키는 Runner를 이용해서 Terraform Code를 수행하게 된다. 즉, 작업자 PC에서는 어떠한 Terraform 관련 코드도 수행되지 않는다.

 

이건 이 테라폼 수행 코드가 작업자 PC에서만 접근 가능한 경우 Terraform Cloud 인프라에 위치한 Runner는 해당 리소스에 접근할 수 없기 때문에 접근 가능하도록 어떤 설정을 해줘야한다. 그렇기 때문에 저번 게시글에선 Remote로 우선은 작업하지 않은것이다.

 

이 Remote로 작업을 수행하게 되면 몇가지 장점들이 있다. 우선 Plan을 수행했을 때 자동으로 Apply를 실행할 것인지에 대한 선택을 할 수 있다.

 

그리고 Terraform Version 또한 지정할 수 있다.

 

그리고 Working Directory도 지정해줘야 한다.

 

그리고 가장 핵심 기능 중 하나인 워크스페이스에서 Variables 관리가 가능해진다.

 

또 하나 좋은 기능인 Run triggers 라는 기능이 있다. 이는 Organizations > Workspace 내부에서 확인할 수 있는데 다음 화면을 확인하자.

 

이 Run triggers는 어떤 것이냐면 A, B, C, D 라는 Workspace가 있을 때 B라는 워크스페이스는 A, D에 의존하고 있다고 가정해보자. 그럼 A 또는 D가 Apply가 일어나면 B도 자동으로 Apply를 해주는 기능이 Run triggers이다.

 

그럼 한번 Remote Execution Mode로 작업을 해보자.

 

Environment Variables 추가

우선, Provider가 AWS Provider이기 때문에 AWS CLI에 IAM User Credential을 추가한 것 처럼 이 Terraform Cloud한테도 Credential을 알려줘야한다. 그 방법은 위에서 보여준 Variables 화면에서 추가할 수 있다. 

 

위 사진처럼 Environment variable로 AWS Credential을 Sensitive로 추가해주자.

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY

 

그리고 내가 이 Terraform Cloud를 사용해서 관리했던 테라폼 main.tf, terraform.tfvars 파일은 다음과 같다.

main.tf

terraform {
  backend "remote" {
    hostname = "app.terraform.io"
    organization = "cwchoiit-terraform"

    workspaces {
      name = "cwchoiit-terraform-cloud-backend"
    }
  }
}

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

resource "aws_iam_group" "this" {
    for_each = toset(["developer", "employee"])

    name = each.key
  
}

output "groups" {
  value = aws_iam_group.this
}

variable "users" {
    type = list(any)
}

resource "aws_iam_user" "this" {
  for_each = {
    for user in var.users : user.name => user
  }

  name = each.key

  tags = {
    level = each.value.level
    role = each.value.role
  }
}

resource "aws_iam_user_group_membership" "this" {
  for_each = {
    for user in var.users : user.name => user
  }

  user = each.key

  groups = each.value.is_developer ? [
    aws_iam_group.this["developer"].name, aws_iam_group.this["employee"].name
    ] : [
        aws_iam_group.this["employee"].name
    ]

  depends_on = [ aws_iam_user.this ] # 이 리소스가 생성되어야만 가능하게 설정
}

locals {
  developers = [
    for user in var.users : user if user.is_developer
  ]
}

output "developers" {
  value = local.developers
}

output "high_level_users" {
  value = [
    for user in var.users : user if user.level > 5
  ]
}

 

terraform.tfvars

users = [
 {
    name = "john"
    level = 7
    role = "재무"
    is_developer = false
 },
 {
    name = "alice"
    level = 1
    role = "인턴 개발자"
    is_developer = true
 },
 {
    name = "tony"
    level = 4
    role = "데브옵스"
    is_developer = true
 },
 {
    name = "cindy"
    level = 9
    role = "경영"
    is_developer = false
 },
 {
    name = "hoon"
    level = 3
    role = "마케팅"
    is_developer = false
 }   
]

 

여기서 terraform.tfvars 파일에 있는 이 Terraform Variable 역시 Cloud에 추가해줘야 한다.

 

 

이제 모든 준비는 끝났고 위에서 말한 Working Directory 경로만 지정해주면 된다. 나의 경우 디렉토리 구조가 다음과 같이 되어있다.

 

그래서 Working Directory는 Remote Backend를 사용하는 loop로 지정하면 된다.

 

 

이제 Apply를 한번 해보자. 하기 전에 terraform login 명령어를 통해서 로그인을 먼저 해주자. 토큰은 로그인을 한번이라도 했다면 이 경로에 저장되어 있다.

~/.terraform.d/credentials.tfrc.json

 

실행이 되면 다음과 비슷한 모습으로 보여질 것이다. 그래서 우선 Plan 부분이 보여지고 마지막에 'yes' 입력하는 프롬프트에서 멈춘다.

 

'yes'를 입력하고 리소스를 적용하면 다음과 같다. Outputs이 쭉 나오고 AWS Console에서 리소스를 확인할 수 있다.

 

결과:

 

Terraform Cloud에서도 확인해보자. 리소스가 잘 반영되어있다.

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

terraform_remote_state  (0) 2024.03.10
Terraform 나만의 Module 만들어보기  (0) 2024.03.10
Terraform Workspace  (0) 2024.03.08
Terraform Commands (taint / untaint)  (0) 2024.03.08
Terraform State  (0) 2024.03.07
728x90
반응형
SMALL
SMALL

 

Workspace

테라폼에서 Workspace는 프로젝트 단위라고도 생각할 수 있고 상태 관리의 단위라고도 생각할 수 있다. 

예를 들어 환경이 세 가지인(Dev, Staging, Prod) 경우 비슷한 환경에서 살짝 살짝 달라질텐데 이 코드를 복붙해서 세 개의 프로젝트 단위로 관리하기는 비효율적이며 공간 낭비이다. 이럴 때 워크스페이스가 유용하게 사용될 수 있다.

 

이게 가능한 이유는 하나의 테라폼 프로젝트에 여러 Workspace가 만들어질 수 있기 때문이다.

 

현재 프로젝트 트리는 다음과 같다. 아직 Init 조차 하지 않은 상태이다.

다음 main.tf 파일을 보자.

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

variable "vpc_name" {
  description = "생성되는 VPC의 이름"
  type        = string
  default     = "default"
}

locals {
  common_tags = {
    Project = "Network"
    Owner   = "cwchoiit"
  }
}

output "vpc_name" {
  value = module.vpc.name
}

output "vpc_id" {
  value = module.vpc.id
}

output "vpc_cidr" {
  description = "생성된 VPC의 CIDR 영역"
  value = module.vpc.cidr_block
}

output "subnet_groups" {
  value = {
    public  = module.subnet_group__public
    private = module.subnet_group__private
  }
}

module "vpc" {
  source  = "tedilabs/network/aws//modules/vpc"
  version = "0.24.0"

  name                  = var.vpc_name
  cidr_block            = "10.0.0.0/16"

  internet_gateway_enabled = true

  dns_hostnames_enabled = true
  dns_support_enabled   = true

  tags = local.common_tags
}

module "subnet_group__public" {
  source  = "tedilabs/network/aws//modules/subnet-group"
  version = "0.24.0"

  name                    = "${module.vpc.name}-public"
  vpc_id                  = module.vpc.id
  map_public_ip_on_launch = true

  subnets = {
    "${module.vpc.name}-public-001/az1" = {
      cidr_block           = "10.0.0.0/24"
      availability_zone_id = "apne2-az1"
    }
    "${module.vpc.name}-public-002/az2" = {
      cidr_block           = "10.0.1.0/24"
      availability_zone_id = "apne2-az2"
    }
  }

  tags = local.common_tags
}

module "subnet_group__private" {
  source  = "tedilabs/network/aws//modules/subnet-group"
  version = "0.24.0"

  name                    = "${module.vpc.name}-private"
  vpc_id                  = module.vpc.id
  map_public_ip_on_launch = false

  subnets = {
    "${module.vpc.name}-private-001/az1" = {
      cidr_block           = "10.0.10.0/24"
      availability_zone_id = "apne2-az1"
    }
    "${module.vpc.name}-private-002/az2" = {
      cidr_block           = "10.0.11.0/24"
      availability_zone_id = "apne2-az2"
    }
  }

  tags = local.common_tags
}

 

 

Workspace commands

workspace 관련해서 Commands가 존재하는데 다음 help 명령어를 보자.

 

list

list는 현재 디렉토리에서 존재하는 workspace들의 리스트를 보여준다. 현 시점에서 해당 커맨드를 입력해보면,

다른 workspace를 만든적이 없으면 기본으로 'default' 하나를 가지고 있다.

 

show

show는 현재 선택된 workspace가 무엇인지 보여준다. 당연히 현재는 workspace를 만든적도 없으니 'default'가 선택된 상태다.

근데 이거 굳이 안 써도 되는게 list 명령어를 사용하면 별표 표시로 현재 선택된 workspace를 알려준다.

 

new

workspace를 새로 만드는 명령어. 다음과 같이 사용해보자.

 

workspace가 만들어졌고 만듦과 동시에 해당 workspace로 현재 workspace가 변경된다.

그리고 workspace가 둘 이상 생기게 되면 그 동시에 해당 프로젝트에 다음과 같은 폴더가 하나 생긴다.

workspace 별 관리하는 상태가 다 다를테니 당연히 각 workspace 마다 상태 관리 파일이 나뉘어지게 되는것이다.

 

select

workspace를 선택하는 명령어. 다음과 같이 사용하면 된다.

 

delete

workspace를 삭제하는 명령어. 다음과 같이 사용하면 된다.

 

삭제하면 terraform.tfstate.d 폴더 내 해당 workspace 폴더도 삭제된다.

 

 

Workspace Use Case

이제 workspace를 쓸만한 방식으로 한번 구성해보자. 환경이 세 가지가 있다고 가정하자.

그러면 workspace 3개가 필요하다. 3개의 workspace를 일단 만들자.

  • dev
  • prod
  • staging

이 각각의 환경은 구성이 다를 것이다. 비슷한 코드의. 

그렇기에 하나의 main.tf 파일로 관리하고 각 환경마다 변경되는 부분만 다르게 변수로 받아 관리하면 더 효율적일 수 있다.

그래서 나는 다음과 같은 세 개의 파일을 만들었다.

  • dev.tfvars
  • prod.tfvars
  • staging.tfvars

그리고 각각의 파일에 이러한 내용을 추가했다.

 

dev.tfvars

vpc_name = "dev"

 

prod.tfvars

vpc_name = "prod"

 

staging.tfvars

vpc_name = "staging"

 

 

그리고 나의 main.tf 파일은 다음과 같다.

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

variable "vpc_name" {
  description = "생성되는 VPC의 이름"
  type        = string
}

locals {
  common_tags = {
    Project = "Network"
    Owner   = "cwchoiit"
  }
}

output "vpc_name" {
  value = module.vpc.name
}

output "vpc_id" {
  value = module.vpc.id
}

output "vpc_cidr" {
  description = "생성된 VPC의 CIDR 영역"
  value = module.vpc.cidr_block
}

output "subnet_groups" {
  value = {
    public  = module.subnet_group__public
    private = module.subnet_group__private
  }
}

module "vpc" {
  source  = "tedilabs/network/aws//modules/vpc"
  version = "0.24.0"

  name                  = var.vpc_name
  cidr_block            = "10.0.0.0/16"

  internet_gateway_enabled = true

  dns_hostnames_enabled = true
  dns_support_enabled   = true

  tags = local.common_tags
}

module "subnet_group__public" {
  source  = "tedilabs/network/aws//modules/subnet-group"
  version = "0.24.0"

  name                    = "${module.vpc.name}-public"
  vpc_id                  = module.vpc.id
  map_public_ip_on_launch = true

  subnets = {
    "${module.vpc.name}-public-001/az1" = {
      cidr_block           = "10.0.0.0/24"
      availability_zone_id = "apne2-az1"
    }
    "${module.vpc.name}-public-002/az2" = {
      cidr_block           = "10.0.1.0/24"
      availability_zone_id = "apne2-az2"
    }
  }

  tags = local.common_tags
}

module "subnet_group__private" {
  source  = "tedilabs/network/aws//modules/subnet-group"
  version = "0.24.0"

  name                    = "${module.vpc.name}-private"
  vpc_id                  = module.vpc.id
  map_public_ip_on_launch = false

  subnets = {
    "${module.vpc.name}-private-001/az1" = {
      cidr_block           = "10.0.10.0/24"
      availability_zone_id = "apne2-az1"
    }
    "${module.vpc.name}-private-002/az2" = {
      cidr_block           = "10.0.11.0/24"
      availability_zone_id = "apne2-az2"
    }
  }

  tags = local.common_tags
}

 

 

Dev Workspace

이제 workspace 중 dev로 변경하고 apply를 진행해보자.

tf workspace select dev
tf apply -var-file=dev.tfvars

 

성공적으로 수행된 후 AWS Console에 가서 확인해보자.

dev 관련 네트워크가 잘 만들어졌음을 확인할 수 있다.

 

Staging Workspace

마찬가지로 staging workspace로 가서 같은 작업을 수행해보자.

tf workspace select staging
tf apply -var-file=staging.tfvars

 

Prod Workspace

tf workspace select staging
tf apply -var-file=staging.tfvars

 

결론

이렇게 각 Workspace별로 네트워크 구성에 대해 작업해 보았다. 이런식으로 같은 main.tf 파일을 사용하면서 환경에 맞게 변수 파일을 다르게 등록해서 workspace 별 작업이 가능하다. 

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

Terraform 나만의 Module 만들어보기  (0) 2024.03.10
Terraform Cloud  (0) 2024.03.10
Terraform Commands (taint / untaint)  (0) 2024.03.08
Terraform State  (0) 2024.03.07
AWS + Terraform (Loop)  (0) 2024.03.07
728x90
반응형
SMALL
SMALL

Terraform 에서는 taint와 untaint라는 명령어가 있다. 이 명령어가 무엇인지 알아보고 어떻게 사용하는지 알아보자.

 

taint

taint는 Terraform에서 리소스를 관리하는 중 특정 리소스에 문제가 생겼을 때 해당 리소스에 문제가 생겼다는 것을 Terraform에게 알리는 명령어이다. 그럼 Terraform은 이 리소스에 문제가 생겼다는것을 알고 교체하려한다.

 

현재 나의 Workspace에서 관리하는 State는 다음과 같다.

 

이 중에서 Internet Gateway에 어떤 문제가 발생했다고 가정해보자. 그래서 해당 리소스에 taint를 하려고 한다.

tf taint "module.vpc.aws_internet_gateway.this[0]"

 

 

이렇게 한 후 Apply를 해보면 taint 처리한 Internet Gateway 리소스를 교체하려고 시도한다.

 

이게 taint 명령어의 사용 용도이다. 그리고 이 taint는 처리한 리소스를 의존하는 무언가가 있을 때 해당 리소스도 교체한다. 예를 들어, Internet Gateway가 Public Route Table에 연결된 상태일텐데 이 인터넷 게이트웨이를 교체하면 당연히 기존에 매핑되어 있던 Route Table의 Internet Gateway도 새로 만들어지는 녀석으로 교체되려고 할 것이다. 이것을 Terraform은 알아서 해준다.

 

untaint

untaint는 간단하다. taint의 반대다. taint를 처리한 리소스에 대해 취소를 하고 싶으면 untaint를 하면 된다.

tf untaint "module.vpc.aws_internet_gateway.this[0]"

 

그럼 taint 처리된 리소스는 해제된다.

 

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

Terraform Cloud  (0) 2024.03.10
Terraform Workspace  (0) 2024.03.08
Terraform State  (0) 2024.03.07
AWS + Terraform (Loop)  (0) 2024.03.07
AWS + Terraform (Conditions)  (0) 2024.03.07
728x90
반응형
SMALL
SMALL

State

이번엔 상태 관리에 대한 내용이다. Terraform을 이용해서 어떤 작업을 하면 현재 작업한 상태에 대한 파일이 기록되는 것을 terraform.state 파일을 통해 알았다. 이 파일을 보면 지금 이 Terraform이 관리하고 있는 상태를 보여준다. 굉장히 중요한 파일이다. 이 파일을 기점으로 Apply를 했을 때 변경점을 캐치하거나, 관리하고 있는 리소스를 파악하거나 할 수 있고 Destroy 명령어를 했을 때 무엇을 삭제할지 파악할 수 있기 때문이다. 

 

terraform.state

{
  "version": 4,
  "terraform_version": "1.7.4",
  "serial": 3,
  "lineage": "6c566e9f-a596-337a-6760-5e67c5348e36",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "aws_instance",
      "name": "tfec2",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "schema_version": 1,
          "attributes": {
            "ami": "ami-097bf0ec147165215",
            "arn": "arn:aws:ec2:ap-northeast-2:135149110460:instance/i-04d23764108510fc6",
            "associate_public_ip_address": false,
            "availability_zone": "ap-northeast-2a",
            "capacity_reservation_specification": [
              {
                "capacity_reservation_preference": "open",
                "capacity_reservation_target": []
              }
            ],
            "cpu_core_count": 1,
            "cpu_options": [
              {
                "amd_sev_snp": "",
                "core_count": 1,
                "threads_per_core": 1
              }
            ],
            "cpu_threads_per_core": 1,
            "credit_specification": [
              {
                "cpu_credits": "standard"
              }
            ],
            "disable_api_stop": false,
            "disable_api_termination": false,
            "ebs_block_device": [],
            "ebs_optimized": false,
            "enclave_options": [
              {
                "enabled": false
              }
            ],
            "ephemeral_block_device": [],
            "get_password_data": false,
            "hibernation": false,
            "host_id": "",
            "host_resource_group_arn": null,
            "iam_instance_profile": "",
            "id": "i-04d23764108510fc6",
            "instance_initiated_shutdown_behavior": "stop",
            "instance_lifecycle": "",
            "instance_market_options": [],
            "instance_state": "running",
            "instance_type": "t2.micro",
            "ipv6_address_count": 0,
            "ipv6_addresses": [],
            "key_name": "",
            "launch_template": [],
            "maintenance_options": [
              {
                "auto_recovery": "default"
              }
            ],
            "metadata_options": [
              {
                "http_endpoint": "enabled",
                "http_protocol_ipv6": "disabled",
                "http_put_response_hop_limit": 2,
                "http_tokens": "required",
                "instance_metadata_tags": "disabled"
              }
            ],
            "monitoring": false,
            "network_interface": [],
            "outpost_arn": "",
            "password_data": "",
            "placement_group": "",
            "placement_partition_number": 0,
            "primary_network_interface_id": "eni-06fd68c8da4d961c6",
            "private_dns": "ip-10-1-254-36.ap-northeast-2.compute.internal",
            "private_dns_name_options": [
              {
                "enable_resource_name_dns_a_record": false,
                "enable_resource_name_dns_aaaa_record": false,
                "hostname_type": "ip-name"
              }
            ],
            "private_ip": "10.1.254.36",
            "public_dns": "",
            "public_ip": "",
            "root_block_device": [
              {
                "delete_on_termination": true,
                "device_name": "/dev/xvda",
                "encrypted": false,
                "iops": 3000,
                "kms_key_id": "",
                "tags": {},
                "tags_all": {},
                "throughput": 125,
                "volume_id": "vol-0d2e2fa43f0cb3483",
                "volume_size": 8,
                "volume_type": "gp3"
              }
            ],
            "secondary_private_ips": [],
            "security_groups": [],
            "source_dest_check": true,
            "spot_instance_request_id": "",
            "subnet_id": "subnet-0b23fd05b5919269e",
            "tags": {
              "Name": "terraform-ec2"
            },
            "tags_all": {
              "Name": "terraform-ec2"
            },
            "tenancy": "default",
            "timeouts": null,
            "user_data": null,
            "user_data_base64": null,
            "user_data_replace_on_change": false,
            "volume_tags": null,
            "vpc_security_group_ids": [
              "sg-0ac56a28dc1c79f74"
            ]
          },
          "sensitive_attributes": [],
          "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMSJ9"
        }
      ]
    }
  ],
  "check_results": null
}

 

위 코드를 보면 알겠지만 현재 상태에서 관리하고 있는 Resources가 무엇인지 기록해 둔다. 그렇기 때문에 현재 상태에서 뭔가 달라지면 그걸 파악할 수 있는거고 삭제하는것도 어떤것을 삭제하는지 알 수 있는 것이다. 

 

근데 이 State를 관리하는 방법이 크게 Local / Remote 방식이 있다. 지금처럼 작업한 폴더 경로 내에 있는 terraform.state 파일로 관리하는 경우를 Local State 라고 하고 이런 방식을 Local Backend 라고 한다.

 

Backend

Backend는 크게 Local, Remote로 분리가 되고 Local은 바로 위에 설명한 내용 그대로이며 Remote는 말 그대로 원격으로 관리한다는 의미이다. 그리고 이 방법의 대표적인 예가 AWS S3이다. 그냥 말 그대로 S3 Bucket에 terraform.state 파일을 관리하는 것이다. 

그리고 또 다른 대표적인 예는 비교적 최신에 나온 Terraform Cloud이다. 이 두가지 모두 한번 사용해보자.

 

Remote Backend는 중요하게 여겨야 할 게 Lock이 되는지 아닌지가 상당히 중요하다. 혼자서 작업하는 경우 Lock이 상관이 없지만 협업하는 경우 나와 다른 누군가가 동시에 작업을 하는 경우 상태가 꼬여버릴 수 있다. 그래서 작업을 할 땐 Lock을 걸어서 다른 사람은 변경을 하지 못하게 막는것이 중요하다.

 

Remote Backend AWS S3 

S3로 상태 관리를 하려면 당연히 버킷이 일단 있어야 한다. 버킷을 만들자.

 

그리고 이 버킷을 사용하기 위해 다음과 같이 main.tf 파일에 이 내용을 작성한다.

terraform {
  backend "s3" {
    bucket = "cwchoiit-terraform-state-s3"
    key = "s3-backend/terraform.tfstate"
    region = "ap-northeast-2"
  }
}

그냥 봐도 다 이해할 수 있다. S3를 Backend로 사용하겠다는 의미이다.

  • bucket: bucket 이름
  • key: bucket 내 저장될 경로
  • region: bucket의 지역

이렇게 backend를 수정했으면 Init을 다시 해줘야 한다. 그래서 Init을 해보자.

 

성공적으로 backend가 s3에 구성됐다고 나온다. 그럼 이제 Apply를 해보자.

Apply가 진행이 되면 이렇게 버킷에 terraform.state 파일이 지정한 경로에 맞게 만들어진다.

 

 

Remote Backend Terraform Cloud

우선, Terraform Cloud를 사용하기 위해선 계정이 필요하다. 다음 링크에서 계정을 생성하자.

 

https://app.terraform.io/session

 

app.terraform.io

계정 만들면 Organization을 만들면 된다. 그리고 main.tf 파일에 다음과 같은 코드를 추가한다.

terraform {
  backend "remote" {
    hostname = "app.terraform.io"
    organization = "cwchoiit-terraform"

    workspaces {
      name = "cwchoiit-terraform-cloud-backend"
    }
  }
}
  • backend는 "remote"
  • hostname은 Terraform Cloud의 hostname
  • organization은 방금 만든 organization
  • workspaces는 원하는 Name

하고 처음으로 terraform init을 실행하면 다음과 같은 에러를 마주하게 된다. 이는 로그인을 하라는 것.

 

로그인을 하면 Terraform Cloud 창이 띄워지고 토큰을 발급하게 된다. 토큰을 발급하고나서 입력해달라는 프롬프트에 입력을 해준다.

 

잘 입력을 하면 다음과 같이 로그인이 잘 된다.

 

이제 다시 Init을 실행한다. 그럼 정상적으로 진행이 되고 Terraform Cloud로 가면 다음과 같이 내가 작성한 Workspace의 이름으로 Workspace가 만들어진다.

그리고 Workspace 설정에 가서 다음과 같은 작업을 해주자.

이 설정은 추후에 좀 더 깊이 알아보자. 지금은 일단 Local로 설정을 하자.

 

 

위에서 만든 토큰을 로컬에 저장하는 방법이 있는데 다음 경로로 Vim을 사용해 들어가보자.

vi ~/.terraformrc

 

그럼 그 곳에 이 코드를 추가한다. 그리고 본인의 토큰을 저 곳에 넣어주면 된다.

credentials "app.terraform.io" {
	token = "yourtoken"
}

 

그럼 이제 Init을 했으니 Apply를 해보자. 아래처럼 Apply가 잘 됐다.

 

State도 정상적으로 만들어졌다.

 

여기까지 하고 나면 State 관리 파일을 Local, Remote 각각에 저장하는 방법을 배운 것이다. 이제 한발 더 나아가서 CLI 명령어 중 'state'라는 명령어가 있다. 이 녀석에 대해서 좀 더 깊게 알아보자.

 

Command 'state'

다음 명령어를 입력해보자.

tf state

 

그럼 다음과 같이 여러 Subcommands를 확인할 수 있다.

하나씩 알아보자. 그 중에서도 꼭 이해하고 있어야 하는것들은 list, mv, rm 정도이다.

state list

이는 현재 관리하는 상태(리소스나 그 외 정보들)를 나열해주는 명령어이다. 위에서 작업한 main.tf 파일에 대한 state list 명령어를 실행해보자. 다음과 같이 현재 관리중인 상태들을 보여준다.

 

state show

이는 상태 내 특정 리소스에 대한 자세한 내용을 보여주는 명령어이다. 역시 명령어를 실행해보자.

 

state mv

이 명령어는 상태에 어떤 변경을 가할 때 사용된다. 우리가 작성했던 main.tf 파일에 약간의 수정을 가해보자.

아래와 같이 resource 두 개를 각각으로 분리해서 작성했던 것을 바꿔보자

 

Old

resource "aws_iam_group" "developer" {
  name = "developer"
}

resource "aws_iam_group" "employee" {
  name = "employee"
}

output "groups" {
  value = [
    aws_iam_group.developer,
    aws_iam_group.employee
  ]
}

 

New

resource "aws_iam_group" "this" {
    for_each = toset(["developer", "employee"])

    name = each.key
  
}

output "groups" {
  value = aws_iam_group.this
}

 

이렇게 두 개의 리소스를 for-each를 사용해서 하나로 만들었다. 이 상태에서 Apply를 실행해보자.

그럼 작성자 입장에서는 동일한 그룹을 만든다고 생각하겠지만, 테라폼 입장에서는 리소스 이름으로 리소스를 관리하기 때문에 이름이 바뀐 상태에서 Apply를 하면 기존에 리소스를 삭제하고 새로운 리소스를 만들어내려고 한다. 다음이 그 결과다.

 

이런 경우 실제 서비스에 어떤 문제를 야기할지 알 수 없는 미지의 세계가 열리게 된다. 이렇게 하면 안된다. 그래서 이 경우 mv를 사용한다.

이 경우 일단 기존 상태를 확인해보자.

 

기존 상태를 확인 후 다음과 같이 변경하자.

tf state mv 'aws_iam_group.developer' 'aws_iam_group.this["developer"]'
tf state mv 'aws_iam_group.employee' 'aws_iam_group.this["employee"]'

 

이렇게 두 개를 잘 변경해 주면 상태를 건강하게 변경할 수 있다. Apply를 해도 뭔가를 지운다거나 다시 만든다거나 하지 않는다.

Outputs 만 변경될 뿐이다.

 

state rm

이 명령어는 리소스를 유지는하되, 테라폼으로 더이상 관리는 하지 않는 경우에 사용하기 적합하다.

예를 들어 다음과 같은 코드가 있다고 가정하자.

terraform {
  backend "remote" {
    hostname = "app.terraform.io"
    organization = "cwchoiit-terraform"

    workspaces {
      name = "cwchoiit-terraform-cloud-backend"
    }
  }
}

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

resource "aws_iam_group" "this" {
    for_each = toset(["developer", "employee"])

    name = each.key
  
}

output "groups" {
  value = aws_iam_group.this
}

variable "users" {
    type = list(any)
}

resource "aws_iam_user" "this" {
  for_each = {
    for user in var.users : user.name => user
  }

  name = each.key

  tags = {
    level = each.value.level
    role = each.value.role
  }
}

resource "aws_iam_user_group_membership" "this" {
  for_each = {
    for user in var.users : user.name => user
  }

  user = each.key

  groups = each.value.is_developer ? [
    aws_iam_group.this["developer"].name, aws_iam_group.this["employee"].name
    ] : [
        aws_iam_group.this["employee"].name
    ]

  depends_on = [ aws_iam_user.this ] # 이 리소스가 생성되어야만 가능하게 설정
}

locals {
  developers = [
    for user in var.users : user if user.is_developer
  ]
}

resource "aws_iam_user_policy_attachment" "developer" {
  for_each = {
    for user in local.developers : user.name => user
  }

  user = each.key
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"

  depends_on = [ aws_iam_user.this ] # 이 리소스가 생성되어야만 가능하게 설정
}

output "developers" {
  value = local.developers
}

output "high_level_users" {
  value = [
    for user in var.users : user if user.level > 5
  ]
}

 

이 때 테라폼으로 더 이상 Policy 관련 리소스를 다루지 않고 싶어져서 아래 코드를 지웠다고 해보자.

resource "aws_iam_user_policy_attachment" "developer" {
  for_each = {
    for user in local.developers : user.name => user
  }

  user = each.key
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"

  depends_on = [ aws_iam_user.this ] # 이 리소스가 생성되어야만 가능하게 설정
}

 

지운 상태에서 Apply를 하면 다음과 같이 해당 리소스를 제거한다는 내용을 알려준다.

 

그러나, 내가 원하는건 이 정책 자체를 해당 유저에게서 삭제하는게 아니고 그저 테라폼으로 관리하는 것을 그만하려고 하는건데 이렇게 삭제를 해버리면 역시나 어떤 파장을 일으킬지 미지의 세계가 펼쳐질것이다. 이럴때 "state rm"을 사용한다.

 

state rm 명령어를 사용하기 전 먼저 해당 리소스에 대해서 파악을 하기 위해 tf state list 명령어를 실행해보자.

내가 원하는 건 맨 하단 두개를 날리면 된다.

tf state rm 'aws_iam_user_policy_attachment.developer["alice"]'

tf state rm 'aws_iam_user_policy_attachment.developer["tony"]'

 

이렇게 리소스 관리 대상에서 제거를 했다. 이제 Apply를 해도 해당 리소스를 관리하지 않기 때문에 더 이상 고려대상이 아니다. 그래서 지우지도 않는다. 

 

state pull / push

Git을 사용해봤다면 동일한 느낌으로 생각하면 될 것 같다. Pull은 Remote State 저장소에서 Local State 저장소로 State를 땡겨오는 거고 Push는 그 반대다. Push는 지금은 그냥 이런것이다 하고 넘어가자. 꽤나 위험한 행위이기 때문에 굳이 지금 다룰 필요가 없다.

 

하단 명령어를 실행해보자.

tf state pull

 

그럼 Remote State 저장소에서 관리하고 있는 상태들을 표준 출력으로 출력해준다.

 

그래서 이 Remote State 저장소에서 관리하고 있는 상태를 로컬로 내려받아 사용하기 위해 .tfstate 파일에 넣어버리는 것.

tf state pull > xxxx.tfstate

 

그래서 저 파일을 Local State 파일로 사용하면 된다.

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

Terraform Workspace  (0) 2024.03.08
Terraform Commands (taint / untaint)  (0) 2024.03.08
AWS + Terraform (Loop)  (0) 2024.03.07
AWS + Terraform (Conditions)  (0) 2024.03.07
AWS + Terraform (For-Each)  (0) 2024.03.06
728x90
반응형
SMALL
SMALL

이번엔 루프를 이용해보자. 다음 main.tf 파일을 보자.

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

resource "aws_iam_group" "developer" {
  name = "developer"
}

resource "aws_iam_group" "employee" {
  name = "employee"
}

output "groups" {
  value = [
    aws_iam_group.developer,
    aws_iam_group.employee
  ]
}

variable "users" {
    type = list(any)
}

resource "aws_iam_user" "this" {
  for_each = {
    for user in var.users : user.name => user
  }

  name = each.key

  tags = {
    level = each.value.level
    role = each.value.role
  }
}

resource "aws_iam_user_group_membership" "this" {
  for_each = {
    for user in var.users : user.name => user
  }

  user = each.key

  groups = each.value.is_developer ? [
    aws_iam_group.developer.name, aws_iam_group.employee.name
    ] : [
        aws_iam_group.employee.name
    ]
}

locals {
  developers = [
    for user in var.users : user if user.is_developer
  ]
}

resource "aws_iam_user_policy_attachment" "developer" {
  for_each = {
    for user in local.developers : user.name => user
  }

  user = each.key
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"

  depends_on = [ aws_iam_user.this ] # 이 리소스가 생성되어야만 가능하게 설정
}

output "developers" {
  value = local.developers
}

output "high_level_users" {
  value = [
    for user in var.users : user if user.level > 5
  ]
}

 

그리고 변수를 정의한 파일인 terraform.tfvars 파일은 다음과 같다.

users = [
 {
    name = "john"
    level = 7
    role = "재무"
    is_developer = false
 },
 {
    name = "alice"
    level = 1
    role = "인턴 개발자"
    is_developer = true
 },
 {
    name = "tony"
    level = 4
    role = "데브옵스"
    is_developer = true
 },
 {
    name = "cindy"
    level = 9
    role = "경영"
    is_developer = false
 },
 {
    name = "hoon"
    level = 3
    role = "마케팅"
    is_developer = false
 }   
]

 

이런식으로 for 문을 사용할 수 있다.

resource "aws_iam_user" "this" {
  for_each = {
    for user in var.users : user.name => user
  }

  name = each.key

  tags = {
    level = each.value.level
    role = each.value.role
  }
}

 

코드를 보면 이러한 구문이 있다.

for user in var.users : user.name => user

 

선언한 users 라는 변수에 담긴 각각의 유저 하나씩 루프를 돌리는데 이걸 담는 형태가 Map 형식이기 때문에 Key/Value를 다음과 같이 선언하는 것.

user.name => user

 

Map이 아닌 배열로 담는 경우 다음과 같이 사용한다.

locals {
  developers = [
    for user in var.users : user if user.is_developer
  ]
}

 

다만, 이 배열에 담을 땐 조건이 따른다. 그래서 if가 있다. 조건 없이 모두 넣으려고 한다면 이렇게 사용하면 된다.

locals {
  developers = [
    for user in var.users : user
  ]
}

 

그리고 위 main.tf 파일에서 한 가지 새로운 개념인 depends_on 이 있다.

resource "aws_iam_user_policy_attachment" "developer" {
  for_each = {
    for user in local.developers : user.name => user
  }

  user = each.key
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"

  depends_on = [ aws_iam_user.this ] # 이 리소스가 생성되어야만 가능하게 설정
}

 

이 depends_on이 있는 리소스는 depends_on에 걸려있는 리소스가 생성되어야만 만들어지는 리소스임을 의미한다.

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

Terraform Commands (taint / untaint)  (0) 2024.03.08
Terraform State  (0) 2024.03.07
AWS + Terraform (Conditions)  (0) 2024.03.07
AWS + Terraform (For-Each)  (0) 2024.03.06
AWS + Terraform (Module)  (0) 2024.03.06
728x90
반응형
SMALL
SMALL

HCL은 조건문도 사용할 수 있다. Variables와 Condition을 조합해서 한번 조건문을 다뤄보자.

Basic

다음 main.tf 코드를 보자.

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

variable "is_john" {
  type = bool
  default = true
}

locals {
  message = var.is_john ? "Hello John" : "Oh, Your are not John."
}

output "message" {
  value = local.message
}

 

변수로 is_john이라는 녀석을 선언한다. 이 녀석은 boolean 타입의 변수고 기본값이 true이다.

이 때 로컬 변수 message는 is_john의 값에 따라 값이 달라진다. 이 부분에서 조건문이 사용된다. 삼항 연산자로 익숙하다.

 

이 간단한 코드를 실행해보면 다음과 같은 결과를 얻는다.

 

이 때 is_john의 값을 false로 변경하면 당연히 다음과 같은 결과를 얻는다. 이전에 Variables 포스팅에서 알아보았던 실행 시 옵션으로 변수값을 설정하는 방법으로 실행해보자.

 

 

Advanced

이번엔 이 조건문을 좀 더 응용해서 count와 같이 사용해보자. 왜 count와 같이 사용해야 하는 경우가 생기냐면, 리소스를 생성할 땐 그 리소스를 생성할지 말지를 정할 수 있는 방법이 count가 대표적으로 사용되기 때문이다. 다음 main.tf 파일을 보자.

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

variable "internet_gateway_enabled" {
  type = bool
  default = true
}

resource "aws_vpc" "this" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_internet_gateway" "this" {
  count = var.internet_gateway_enabled ? 1 : 0

  vpc_id = aws_vpc.this.id
}

 

여기서 보면 AWS 인터넷 게이트웨이 리소스를 변수값에 따라 생성할지 말지를 count로 구분한다. 이렇게 조건문과 count를 같이 사용해서 리소스를 생성할지 말지를 결정할 수도 있다.

 

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

Terraform State  (0) 2024.03.07
AWS + Terraform (Loop)  (0) 2024.03.07
AWS + Terraform (For-Each)  (0) 2024.03.06
AWS + Terraform (Module)  (0) 2024.03.06
AWS + Terraform  (2) 2024.03.05
728x90
반응형
SMALL
SMALL

이번에는 HCL의 for-each문을 활용해서 여러개의 리소스를 만들어보자.

 

count

우선, for-each를 사용하기 전 count를 먼저 사용해보자. 이 count는 HCL에서 예전부터 있던 기능인데 이 기능에 대한 문제점을 보완하고자 for-each가 나왔다고 생각하면 된다. 우선 코드를 바로 보자.

# ---------
# count

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

resource "aws_iam_user" "count" {
  count = 5
  name  = "count-user-${count.index}"
}

output "count_user_arns" {
  value = aws_iam_user.count.*.arn
}

 

이 코드에서 "count = 5"를 보면 5개를 만들어낼 것을 예측할 수 있다. 그리고 이 count는 자체적으로 가지는 변수 index라는 것이 있다. 그래서 aws_iam_user를 만들어낼 때 그 이름을 count.index로 변형을 주어 5명의 사용자를 만들어낸다.

 

그리고 output을 보면 aws_iam_user.count 리소스가 리스트로 만들어지는데 [count-user-0, count-user-2, ..., count-user-9] 이런식으로 말이다. 그러면 그 각각의 유저들의 arn을 output으로 지정한다는 뜻이다.

 

이 코드를 Init - Apply 해보자. Apply 결과는 다음과 같다. 잘 만들어졌지만 다음 사진이 바로 count의 문제점이다. 

인덱스로 각각의 리소스를 나타내기 때문에 그래서 0번이 누구인지, 1번이 누구인지 알아보기가 상당히 까다롭고, 만약 아래처럼 0, 1, 2, 3, 4 총 5명의 유저가 있을 때 2번 유저를 삭제하면 3, 4이 한칸씩 앞으로 옮겨지게된다. 이러면 또 골머리가 아파진다.

 

AWS Console에서 확인해보면 다음과 같이 잘 만들어졌음을 확인할 수 있다.

 

 

For-Each

For-Each는 Set/Map을 지원한다. Set은 리스트인데 유니크한 값만을 가지는 리스트이고, Map은 {"key": "value", ...} 이러한 형식이다. 바로 코드를 보자. 다음은 Set을 사용한 For-Each문이다.

# ForEach (Set)

resource "aws_iam_user" "for_each_set" {
  for_each = toset([
    "for-each-set-user-1",
    "for-each-set-user-2",
    "for-each-set-user-3",
  ])

  name = each.key
}

output "for_each_set_user_arns" {
  value = values(aws_iam_user.for_each_set).*.arn
}

For-Each의 Set을 사용하면 자체적으로 제공하는 key라는 property가 존재한다. 그래서 사용자 이름을 each.key로 지정했다. 

output을 보면 values()를 사용한다. 이는 aws_iam_user.for_each_set 이라는 리소스는 다음과 같이 생겼다.

for-each-set-user-1: {...}
for-each-set-user-2: {...}
for-each-set-user-3: {...}

그래서 각각의 value가 {...} 이 부분인데 이것 전체를 가져온다는 의미가 values(aws_iam_user.for_each_set)이다. 그래서 그 모양은 [{...}, {...}, ..] 이렇게 될 것이다. 그리고 그 각각의 value가 가지는 모든것을 의미하는 '*'에서 arn을 찍어낸다.

 

Apply를 해보자. 결과는 다음과 같다.

 

AWS Console에서도 확인해보자. 잘 만들어졌다.

 

다음은 Set이 아닌 Map으로 ForEach를 사용해보자. 뭐 크게 달라지는 건 없다. Map이니까 데이터 모양새가 좀 다를것이고 더 많은 정보가 들어갈 수 있을 것 같다.

# ForEach (Map)

resource "aws_iam_user" "for_each_map" {
  for_each = {
    "alice" = {
      level   = "low"
      manager = "chyonee"
    }
    "bob" = {
      level   = "mid"
      manager = "chyonee"
    }
    "john" = {
      level   = "high"
      manager = "chyonee"
    }
  }

  name = each.key
  tags = each.value
}

output "for_each_map_user_arns" {
  value = values(aws_iam_user.for_each_map).*.arn
}

 

Apply 해보자. 잘 만들어졌다.

적용한 태그 역시 잘 들어갔다.

 

 

결론

같은 형태의 리소스를 여러번 만들기위해 반복문(For-Each)을 사용해봤다. 

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

AWS + Terraform (Loop)  (0) 2024.03.07
AWS + Terraform (Conditions)  (0) 2024.03.07
AWS + Terraform (Module)  (0) 2024.03.06
AWS + Terraform  (2) 2024.03.05
Terraform 소개 및 간단하게 다루어보기  (0) 2024.03.05
728x90
반응형
SMALL
SMALL

 

Variables

이번엔 Variable을 사용해서 코드에서 사용할 수 있도록 해보자.

이전 포스팅에서 사용했던 main.tf 파일에서 다음을 추가한다.

variable "vpc_name" {
		
}

 

이 코드는 vpc_name 이라는 변수를 만든 것이다. 여기에 기본값을 설정하지 않으면 Apply를 할 때 변수 이름을 받도록 되어 있다.

그래서 이 변수를 사용해보기 위해 VPC의 name 값을 var.vpc_name 으로 변경해보자.

module "vpc" {
  source  = "tedilabs/network/aws//modules/vpc"
  version = "0.24.0"

  name       = var.vpc_name
  cidr_block = "10.0.0.0/16"

  internet_gateway_enabled = true

  dns_hostnames_enabled = true
  dns_support_enabled   = true

  tags = {}
}

 

최종 소스 코드는 다음과 같다.

#main.tf

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

variable "vpc_name" {
		
}

module "vpc" {
  source  = "tedilabs/network/aws//modules/vpc"
  version = "0.24.0"

  name       = var.vpc_name
  cidr_block = "10.0.0.0/16"

  internet_gateway_enabled = true

  dns_hostnames_enabled = true
  dns_support_enabled   = true

  tags = {}
}

module "subnet_group__public" {
  source  = "tedilabs/network/aws//modules/subnet-group"
  version = "0.24.0"

  name                    = "${module.vpc.name}-public"
  vpc_id                  = module.vpc.id
  map_public_ip_on_launch = true

  subnets = {
    "${module.vpc.name}-public-001/az1" = {
      cidr_block           = "10.0.0.0/24"
      availability_zone_id = "apne2-az1"
    }
    "${module.vpc.name}-public-002/az2" = {
      cidr_block           = "10.0.1.0/24"
      availability_zone_id = "apne2-az2"
    }
  }

  tags = {}
}

module "subnet_group__private" {
  source  = "tedilabs/network/aws//modules/subnet-group"
  version = "0.24.0"

  name                    = "${module.vpc.name}-private"
  vpc_id                  = module.vpc.id
  map_public_ip_on_launch = false

  subnets = {
    "${module.vpc.name}-private-001/az1" = {
      cidr_block           = "10.0.10.0/24"
      availability_zone_id = "apne2-az1"
    }
    "${module.vpc.name}-public-002/az2" = {
      cidr_block           = "10.0.11.0/24"
      availability_zone_id = "apne2-az2"
    }
  }

  tags = {}
}

 

Apply를 실행해보자. 다음과 같이 변수 값을 받는다. 이는 vpc_name의 기본값을 설정해주지 않았기 때문이다.

 

나는 다음과 같이 변수값을 지정했다.

 

이렇게 하고 Enter를 입력하면 이제 Plan 정보들이 나온다.

그 Plan 정보를 잘 보면 내가 넣은 "tftf"값이 보여진다.

 

이제 생성을 완료해서 AWS Console에서 확인해보자. 잘 만들어졌다.

 

Variables 지정 방식들

이러한 변수를 설정하고 지정하는 방식에는 여러 방법이 있는데 문서를 살펴보면 자세히 나와있다.

 

Input Variables - Configuration Language | Terraform | HashiCorp Developer

Input variables allow you to customize modules without altering their source code. Learn how to declare, define, and reference variables in configurations.

developer.hashicorp.com

 

이 문서에 보면 다음과 같은 코드 예시가 있다. 위에서 우리가 작성한 것과 다른 부분은 type을 지정한다거나 default 값을 지정한다.

variable "image_id" {
  type = string
}

variable "availability_zone_names" {
  type    = list(string)
  default = ["us-west-1a"]
}

variable "docker_ports" {
  type = list(object({
    internal = number
    external = number
    protocol = string
  }))
  default = [
    {
      internal = 8300
      external = 8300
      protocol = "tcp"
    }
  ]
}

 

실행 시 옵션으로 변수 지정

그리고 이 변수에 기본값을 지정했더라도 다음처럼 실행 시 변수값을 변경할 수 있다.

terraform apply -var="image_id=ami-abc123"
terraform apply -var='image_id_list=["ami-abc123","ami-def456"]' -var="instance_type=t2.micro"
terraform apply -var='image_id_map={"us-east-1":"ami-abc123","us-east-2":"ami-def456"}'

 

환경변수로 등록해서 변수 지정

또는 다음과 같이 환경변수로도 설정할 수 있다.

export TF_VAR_image_id=ami-abc123

 

변수 파일(.tfvars / .tfvars.json)로 변수 지정

또는 다음과 같이 여러 변수가 있는 경우 변수만을 위한 파일을 만들어서 그 파일을 가리키게 할 수 있다.

파일명은 ".tfvars" 또는 ".tfvars.json"형식이어야 한다.

 

근데, 기본으로 파일명은 "terraform.tfvars"라는 파일만을 바라보게 되어있다. 그래서 이런 파일명이 아닌 경우 Apply시에 인지를 못하는데 그 때 해결하는 방법은 다음처럼 실행 시 옵션을 주는 것이다.

terraform apply -var-file="testing.tfvars"

이 변수 파일은 main.tf 파일이랑 같은 경로에 있으면 된다. 그리고 아래와 같이 작성해주면 된다.

#terraform.tfvars

vpc_name = "varsfile"

 

Local Variables

이 Local Variable은 main.tf 파일 내에서만 공통으로 사용할 변수를 선언하고 그 변수를 사용하기에 적합한 방식이다. 

기존 main.tf 파일에 다음과 같은 코드를 추가해보자.

locals {
  common_tags = {
	Project = "Network"
	Owner = "cwchoi"
  }
}

 

그리고 이 로컬 변수를 각 모듈의 태그에서 사용해보자.

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

variable "vpc_name" {

}

locals {
  common_tags = {
	Project = "Network"
	Owner = "cwchoi"
  }
}

module "vpc" {
  source  = "tedilabs/network/aws//modules/vpc"
  version = "0.24.0"

  name       = var.vpc_name
  cidr_block = "10.0.0.0/16"

  internet_gateway_enabled = true

  dns_hostnames_enabled = true
  dns_support_enabled   = true

  tags = local.common_tags
}

module "subnet_group__public" {
  source  = "tedilabs/network/aws//modules/subnet-group"
  version = "0.24.0"

  name                    = "${module.vpc.name}-public"
  vpc_id                  = module.vpc.id
  map_public_ip_on_launch = true

  subnets = {
    "${module.vpc.name}-public-001/az1" = {
      cidr_block           = "10.0.0.0/24"
      availability_zone_id = "apne2-az1"
    }
    "${module.vpc.name}-public-002/az2" = {
      cidr_block           = "10.0.1.0/24"
      availability_zone_id = "apne2-az2"
    }
  }

  tags = local.common_tags
}

module "subnet_group__private" {
  source  = "tedilabs/network/aws//modules/subnet-group"
  version = "0.24.0"

  name                    = "${module.vpc.name}-private"
  vpc_id                  = module.vpc.id
  map_public_ip_on_launch = false

  subnets = {
    "${module.vpc.name}-private-001/az1" = {
      cidr_block           = "10.0.10.0/24"
      availability_zone_id = "apne2-az1"
    }
    "${module.vpc.name}-public-002/az2" = {
      cidr_block           = "10.0.11.0/24"
      availability_zone_id = "apne2-az2"
    }
  }

  tags = local.common_tags
}

 

이렇게 해당 파일 내에서 반복적으로 사용되는 변수를 로컬 변수라는 형식으로 지정할 수 있다.

 

 

Outputs

Outputs은 Apply를 진행 후 결과를 저장할 때 사용한다. 예를 들어 다음과 같은 코드를 추가했다고 가정해보자.

output "vpc_name" {
  value = module.vpc.name
}

output "vpc_id" {
  value = module.vpc.id
}

output "public_subnet_group" {
  value = module.subnet_group__public
}

output "subnets" {
  value = {
    public  = module.subnet_group__public
    private = module.subnet_group__private
  }
}

 

지금 3개의 output이 있는것을 확인할 수 있다. 이렇게 3개의 아웃풋이 Apply를 진행 후 만들어지는 데이터라고 생각하면 된다.

이 Output을 다른곳에서 가져다가 사용할 수도 있고 Apply 진행 후 결과를 알고 싶을때도 사용할 수 있다. Apply를 해보자.

 

다음과 같은 Outputs 결과를 볼 수 있다.

public_subnet_group = {
  "arns" = [
    "arn:aws:ec2:ap-northeast-2:135149110460:subnet/subnet-0a3d08dd50460e153",
    "arn:aws:ec2:ap-northeast-2:135149110460:subnet/subnet-00c04ce3dead87b6d",
  ]
  "availability_zone_ids" = tolist([
    "apne2-az1",
    "apne2-az2",
  ])
  "availability_zones" = tolist([
    "ap-northeast-2a",
    "ap-northeast-2b",
  ])
  "cache_subnet_group_id" = null
  "cidr_blocks" = [
    "10.0.0.0/24",
    "10.0.1.0/24",
  ]
  "dax_subnet_group_id" = null
  "db_subnet_group_arn" = null
  "db_subnet_group_id" = null
  "dms_replication_subnet_group_id" = null
  "docdb_subnet_group_arn" = null
  "docdb_subnet_group_id" = null
  "ids" = [
    "subnet-0a3d08dd50460e153",
    "subnet-00c04ce3dead87b6d",
  ]
  "ipv6_cidr_blocks" = tolist([])
  "name" = "varsfile-public"
  "neptune_subnet_group_arn" = null
  "neptune_subnet_group_id" = null
  "redshift_subnet_group_arn" = null
  "redshift_subnet_group_id" = null
  "subnets" = [
    {
      "arn" = "arn:aws:ec2:ap-northeast-2:135149110460:subnet/subnet-0a3d08dd50460e153"
      "assign_ipv6_address_on_creation" = false
      "availability_zone" = "ap-northeast-2a"
      "availability_zone_id" = "apne2-az1"
      "cidr_block" = "10.0.0.0/24"
      "customer_owned_ipv4_pool" = ""
      "enable_dns64" = false
      "enable_lni_at_device_index" = 0
      "enable_resource_name_dns_a_record_on_launch" = false
      "enable_resource_name_dns_aaaa_record_on_launch" = false
      "id" = "subnet-0a3d08dd50460e153"
      "ipv6_cidr_block" = ""
      "ipv6_cidr_block_association_id" = ""
      "ipv6_native" = false
      "map_customer_owned_ip_on_launch" = false
      "map_public_ip_on_launch" = true
      "outpost_arn" = ""
      "owner_id" = "135149110460"
      "private_dns_hostname_type_on_launch" = "ip-name"
      "tags" = tomap({
        "Name" = "varsfile-public-001/az1"
        "Owner" = "cwchoi"
        "Project" = "Network"
        "module.terraform.io/full-name" = "terraform-aws-network/subnet-group"
        "module.terraform.io/instance" = "varsfile-public"
        "module.terraform.io/name" = "subnet-group"
        "module.terraform.io/package" = "terraform-aws-network"
        "module.terraform.io/version" = "0.24.0"
      })
      "tags_all" = tomap({
        "Name" = "varsfile-public-001/az1"
        "Owner" = "cwchoi"
        "Project" = "Network"
        "module.terraform.io/full-name" = "terraform-aws-network/subnet-group"
        "module.terraform.io/instance" = "varsfile-public"
        "module.terraform.io/name" = "subnet-group"
        "module.terraform.io/package" = "terraform-aws-network"
        "module.terraform.io/version" = "0.24.0"
      })
      "timeouts" = null /* object */
      "vpc_id" = "vpc-01166744a0ba4ca4a"
    },
    {
      "arn" = "arn:aws:ec2:ap-northeast-2:135149110460:subnet/subnet-00c04ce3dead87b6d"
      "assign_ipv6_address_on_creation" = false
      "availability_zone" = "ap-northeast-2b"
      "availability_zone_id" = "apne2-az2"
      "cidr_block" = "10.0.1.0/24"
      "customer_owned_ipv4_pool" = ""
      "enable_dns64" = false
      "enable_lni_at_device_index" = 0
      "enable_resource_name_dns_a_record_on_launch" = false
      "enable_resource_name_dns_aaaa_record_on_launch" = false
      "id" = "subnet-00c04ce3dead87b6d"
      "ipv6_cidr_block" = ""
      "ipv6_cidr_block_association_id" = ""
      "ipv6_native" = false
      "map_customer_owned_ip_on_launch" = false
      "map_public_ip_on_launch" = true
      "outpost_arn" = ""
      "owner_id" = "135149110460"
      "private_dns_hostname_type_on_launch" = "ip-name"
      "tags" = tomap({
        "Name" = "varsfile-public-002/az2"
        "Owner" = "cwchoi"
        "Project" = "Network"
        "module.terraform.io/full-name" = "terraform-aws-network/subnet-group"
        "module.terraform.io/instance" = "varsfile-public"
        "module.terraform.io/name" = "subnet-group"
        "module.terraform.io/package" = "terraform-aws-network"
        "module.terraform.io/version" = "0.24.0"
      })
      "tags_all" = tomap({
        "Name" = "varsfile-public-002/az2"
        "Owner" = "cwchoi"
        "Project" = "Network"
        "module.terraform.io/full-name" = "terraform-aws-network/subnet-group"
        "module.terraform.io/instance" = "varsfile-public"
        "module.terraform.io/name" = "subnet-group"
        "module.terraform.io/package" = "terraform-aws-network"
        "module.terraform.io/version" = "0.24.0"
      })
      "timeouts" = null /* object */
      "vpc_id" = "vpc-01166744a0ba4ca4a"
    },
  ]
  "vpc_id" = "vpc-01166744a0ba4ca4a"
}
subnets = {
  "private" = {
    "arns" = [
      "arn:aws:ec2:ap-northeast-2:135149110460:subnet/subnet-0396c6b38c61fb88f",
      "arn:aws:ec2:ap-northeast-2:135149110460:subnet/subnet-058b0d0699a85f174",
    ]
    "availability_zone_ids" = tolist([
      "apne2-az1",
      "apne2-az2",
    ])
    "availability_zones" = tolist([
      "ap-northeast-2a",
      "ap-northeast-2b",
    ])
    "cache_subnet_group_id" = null
    "cidr_blocks" = [
      "10.0.10.0/24",
      "10.0.11.0/24",
    ]
    "dax_subnet_group_id" = null
    "db_subnet_group_arn" = null
    "db_subnet_group_id" = null
    "dms_replication_subnet_group_id" = null
    "docdb_subnet_group_arn" = null
    "docdb_subnet_group_id" = null
    "ids" = [
      "subnet-0396c6b38c61fb88f",
      "subnet-058b0d0699a85f174",
    ]
    "ipv6_cidr_blocks" = tolist([])
    "name" = "varsfile-private"
    "neptune_subnet_group_arn" = null
    "neptune_subnet_group_id" = null
    "redshift_subnet_group_arn" = null
    "redshift_subnet_group_id" = null
    "subnets" = [
      {
        "arn" = "arn:aws:ec2:ap-northeast-2:135149110460:subnet/subnet-0396c6b38c61fb88f"
        "assign_ipv6_address_on_creation" = false
        "availability_zone" = "ap-northeast-2a"
        "availability_zone_id" = "apne2-az1"
        "cidr_block" = "10.0.10.0/24"
        "customer_owned_ipv4_pool" = ""
        "enable_dns64" = false
        "enable_lni_at_device_index" = 0
        "enable_resource_name_dns_a_record_on_launch" = false
        "enable_resource_name_dns_aaaa_record_on_launch" = false
        "id" = "subnet-0396c6b38c61fb88f"
        "ipv6_cidr_block" = ""
        "ipv6_cidr_block_association_id" = ""
        "ipv6_native" = false
        "map_customer_owned_ip_on_launch" = false
        "map_public_ip_on_launch" = false
        "outpost_arn" = ""
        "owner_id" = "135149110460"
        "private_dns_hostname_type_on_launch" = "ip-name"
        "tags" = tomap({
          "Name" = "varsfile-private-001/az1"
          "Owner" = "cwchoi"
          "Project" = "Network"
          "module.terraform.io/full-name" = "terraform-aws-network/subnet-group"
          "module.terraform.io/instance" = "varsfile-private"
          "module.terraform.io/name" = "subnet-group"
          "module.terraform.io/package" = "terraform-aws-network"
          "module.terraform.io/version" = "0.24.0"
        })
        "tags_all" = tomap({
          "Name" = "varsfile-private-001/az1"
          "Owner" = "cwchoi"
          "Project" = "Network"
          "module.terraform.io/full-name" = "terraform-aws-network/subnet-group"
          "module.terraform.io/instance" = "varsfile-private"
          "module.terraform.io/name" = "subnet-group"
          "module.terraform.io/package" = "terraform-aws-network"
          "module.terraform.io/version" = "0.24.0"
        })
        "timeouts" = null /* object */
        "vpc_id" = "vpc-01166744a0ba4ca4a"
      },
      {
        "arn" = "arn:aws:ec2:ap-northeast-2:135149110460:subnet/subnet-058b0d0699a85f174"
        "assign_ipv6_address_on_creation" = false
        "availability_zone" = "ap-northeast-2b"
        "availability_zone_id" = "apne2-az2"
        "cidr_block" = "10.0.11.0/24"
        "customer_owned_ipv4_pool" = ""
        "enable_dns64" = false
        "enable_lni_at_device_index" = 0
        "enable_resource_name_dns_a_record_on_launch" = false
        "enable_resource_name_dns_aaaa_record_on_launch" = false
        "id" = "subnet-058b0d0699a85f174"
        "ipv6_cidr_block" = ""
        "ipv6_cidr_block_association_id" = ""
        "ipv6_native" = false
        "map_customer_owned_ip_on_launch" = false
        "map_public_ip_on_launch" = false
        "outpost_arn" = ""
        "owner_id" = "135149110460"
        "private_dns_hostname_type_on_launch" = "ip-name"
        "tags" = tomap({
          "Name" = "varsfile-public-002/az2"
          "Owner" = "cwchoi"
          "Project" = "Network"
          "module.terraform.io/full-name" = "terraform-aws-network/subnet-group"
          "module.terraform.io/instance" = "varsfile-private"
          "module.terraform.io/name" = "subnet-group"
          "module.terraform.io/package" = "terraform-aws-network"
          "module.terraform.io/version" = "0.24.0"
        })
        "tags_all" = tomap({
          "Name" = "varsfile-public-002/az2"
          "Owner" = "cwchoi"
          "Project" = "Network"
          "module.terraform.io/full-name" = "terraform-aws-network/subnet-group"
          "module.terraform.io/instance" = "varsfile-private"
          "module.terraform.io/name" = "subnet-group"
          "module.terraform.io/package" = "terraform-aws-network"
          "module.terraform.io/version" = "0.24.0"
        })
        "timeouts" = null /* object */
        "vpc_id" = "vpc-01166744a0ba4ca4a"
      },
    ]
    "vpc_id" = "vpc-01166744a0ba4ca4a"
  }
  "public" = {
    "arns" = [
      "arn:aws:ec2:ap-northeast-2:135149110460:subnet/subnet-0a3d08dd50460e153",
      "arn:aws:ec2:ap-northeast-2:135149110460:subnet/subnet-00c04ce3dead87b6d",
    ]
    "availability_zone_ids" = tolist([
      "apne2-az1",
      "apne2-az2",
    ])
    "availability_zones" = tolist([
      "ap-northeast-2a",
      "ap-northeast-2b",
    ])
    "cache_subnet_group_id" = null
    "cidr_blocks" = [
      "10.0.0.0/24",
      "10.0.1.0/24",
    ]
    "dax_subnet_group_id" = null
    "db_subnet_group_arn" = null
    "db_subnet_group_id" = null
    "dms_replication_subnet_group_id" = null
    "docdb_subnet_group_arn" = null
    "docdb_subnet_group_id" = null
    "ids" = [
      "subnet-0a3d08dd50460e153",
      "subnet-00c04ce3dead87b6d",
    ]
    "ipv6_cidr_blocks" = tolist([])
    "name" = "varsfile-public"
    "neptune_subnet_group_arn" = null
    "neptune_subnet_group_id" = null
    "redshift_subnet_group_arn" = null
    "redshift_subnet_group_id" = null
    "subnets" = [
      {
        "arn" = "arn:aws:ec2:ap-northeast-2:135149110460:subnet/subnet-0a3d08dd50460e153"
        "assign_ipv6_address_on_creation" = false
        "availability_zone" = "ap-northeast-2a"
        "availability_zone_id" = "apne2-az1"
        "cidr_block" = "10.0.0.0/24"
        "customer_owned_ipv4_pool" = ""
        "enable_dns64" = false
        "enable_lni_at_device_index" = 0
        "enable_resource_name_dns_a_record_on_launch" = false
        "enable_resource_name_dns_aaaa_record_on_launch" = false
        "id" = "subnet-0a3d08dd50460e153"
        "ipv6_cidr_block" = ""
        "ipv6_cidr_block_association_id" = ""
        "ipv6_native" = false
        "map_customer_owned_ip_on_launch" = false
        "map_public_ip_on_launch" = true
        "outpost_arn" = ""
        "owner_id" = "135149110460"
        "private_dns_hostname_type_on_launch" = "ip-name"
        "tags" = tomap({
          "Name" = "varsfile-public-001/az1"
          "Owner" = "cwchoi"
          "Project" = "Network"
          "module.terraform.io/full-name" = "terraform-aws-network/subnet-group"
          "module.terraform.io/instance" = "varsfile-public"
          "module.terraform.io/name" = "subnet-group"
          "module.terraform.io/package" = "terraform-aws-network"
          "module.terraform.io/version" = "0.24.0"
        })
        "tags_all" = tomap({
          "Name" = "varsfile-public-001/az1"
          "Owner" = "cwchoi"
          "Project" = "Network"
          "module.terraform.io/full-name" = "terraform-aws-network/subnet-group"
          "module.terraform.io/instance" = "varsfile-public"
          "module.terraform.io/name" = "subnet-group"
          "module.terraform.io/package" = "terraform-aws-network"
          "module.terraform.io/version" = "0.24.0"
        })
        "timeouts" = null /* object */
        "vpc_id" = "vpc-01166744a0ba4ca4a"
      },
      {
        "arn" = "arn:aws:ec2:ap-northeast-2:135149110460:subnet/subnet-00c04ce3dead87b6d"
        "assign_ipv6_address_on_creation" = false
        "availability_zone" = "ap-northeast-2b"
        "availability_zone_id" = "apne2-az2"
        "cidr_block" = "10.0.1.0/24"
        "customer_owned_ipv4_pool" = ""
        "enable_dns64" = false
        "enable_lni_at_device_index" = 0
        "enable_resource_name_dns_a_record_on_launch" = false
        "enable_resource_name_dns_aaaa_record_on_launch" = false
        "id" = "subnet-00c04ce3dead87b6d"
        "ipv6_cidr_block" = ""
        "ipv6_cidr_block_association_id" = ""
        "ipv6_native" = false
        "map_customer_owned_ip_on_launch" = false
        "map_public_ip_on_launch" = true
        "outpost_arn" = ""
        "owner_id" = "135149110460"
        "private_dns_hostname_type_on_launch" = "ip-name"
        "tags" = tomap({
          "Name" = "varsfile-public-002/az2"
          "Owner" = "cwchoi"
          "Project" = "Network"
          "module.terraform.io/full-name" = "terraform-aws-network/subnet-group"
          "module.terraform.io/instance" = "varsfile-public"
          "module.terraform.io/name" = "subnet-group"
          "module.terraform.io/package" = "terraform-aws-network"
          "module.terraform.io/version" = "0.24.0"
        })
        "tags_all" = tomap({
          "Name" = "varsfile-public-002/az2"
          "Owner" = "cwchoi"
          "Project" = "Network"
          "module.terraform.io/full-name" = "terraform-aws-network/subnet-group"
          "module.terraform.io/instance" = "varsfile-public"
          "module.terraform.io/name" = "subnet-group"
          "module.terraform.io/package" = "terraform-aws-network"
          "module.terraform.io/version" = "0.24.0"
        })
        "timeouts" = null /* object */
        "vpc_id" = "vpc-01166744a0ba4ca4a"
      },
    ]
    "vpc_id" = "vpc-01166744a0ba4ca4a"
  }
}
vpc_id = "vpc-01166744a0ba4ca4a"
vpc_name = "varsfile"

 

 

728x90
반응형
LIST

'AWS' 카테고리의 다른 글

AWS DynamoDB를 Python과 연동해보기  (2) 2024.03.05
AWS Lambda와 DynamoDB를 연동해보기  (0) 2024.03.04
AWS Lambda와 Step functions  (0) 2024.03.04
AWS Lambda와 Layers  (0) 2024.03.04
AWS Lambda와 API Gateway로 Canary Release 하기  (0) 2024.03.03

+ Recent posts