๐ก โํ
๋ผํผ์ผ๋ก ์์ํ๋ IaCโ ์ฑ
์ผ๋ก ์งํํ๋ Terraform ์คํฐ๋[T101] 4์ฃผ์ฐจ ์ ๋ฆฌ๋ด์ฉ์
๋๋ค.
์ด๋ฒ ์ฃผ์ฐจ์์๋ ๊ธฐ๋ณธ ๋ฌธ๋ฒ์ ๋์ด, ์ฝ๋๋ฅผ ๊ตฌ์กฐํํ๊ณ ํ์ ํ๋ ๋ฐฉ๋ฒ์ ๋ํด ๋ฐฐ์ด๋ค. ๊ตฌ์ฒด์ ์ผ๋ก๋ module๊ณผ state์ ๋ํด ํ์ตํ๋ฉฐ, ํ์ ๊ณผ ๊ด๋ จ๋ ๋ด์ฉ์ 5์ฃผ์ฐจ์์ ๋ ์์ธํ๊ฒ ๋ค๋ฃฌ๋ค.
์๋๋ 1์ฃผ์ฐจ ์ ๋ฆฌ๋ด์ฉ์ด๋ค. ํ ๋ผํผ์์๋ State ํ์ผ์ Serial์ ๊ธฐ์ค์ผ๋ก backup ๊ด๋ฆฌํ๋ค.
Terraform์
.tfstate
ํ์ผ ๋ด์serial
๊ฐ์ ์ํ ํ์ผ์ ๋ฒ์ ์ ๋ํ๋ด๋ฉฐ, ๋์์ฑ ์ ์ด์ ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ ํ์ธ์๋ ์ค์ํ ์ญํ ์ ํฉ๋๋ค. ์ด ๊ฐ์ Terraform ๋ช ๋ น์ด ์คํ๋ ๋๋ง๋ค ์๋์ผ๋ก ์ฆ๊ฐํ์ฌ, ์ํ ํ์ผ์ ์ต์ ์ฑ๊ณผ ์ผ๊ด์ฑ์ ์ ์งํฉ๋๋ค. ๊ทธ๋ ๊ธฐ์ backup ํ์ผ์ด ํ์ฌ state ํ์ผ๋ณด๋ค serial ๋ฒํธ๊ฐ ๋ฎ๋ค.
์ด๋ก ์ ์ธ ๋ด์ฉ
์ํ ํ์ผ์ ๋ฐฐํฌํ ๋๋ง๋ค ๋ณ๊ฒฝ๋๋ ํ๋ผ์ด๋น API์ด๋ฉฐ, ์ค์ง ํ ๋ผํผ ๋ด๋ถ์์ ์ฌ์ฉ์ฉ๋์ด๋ ์ง์ ํธ์งํ๊ฑฐ๋ ์์ฑํด์๋ ์๋๋ค. (ํ์ผ ๋ด๋ถ ๋ฐ์ดํฐ๋ฅผ ํตํด, API๋ฅผ ์์ฒญํ๋ ๊ฒ ๊ฐ๋ค.)
๋ง์ฝ, ํ ๋ผํผ์ ํตํด ํ์ ์ ์งํํด์ผ ํ๋ค๋ฉด state ํ์ผ์ ๊ด๋ฆฌํด์ผ ํ๋ค. ์ด๋๋ 1์ฃผ์ฐจ๋ ์งํํ๋ ์๊ฒฉ ๋ฐฑ์๋๋ฅผ ์ฌ์ฉํ๋ค.
ํ ๋จ์ ์ด์์ ํ์ํ ์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
์๋๋ VCS๋ฅผ ์ฌ์ฉํ ๋, ๋ฐ์ํ๋ ๋ฌธ์ ์ ์ด๋ค.
๊ฒฐ๊ตญ, ํ ๋ผํผ์ ์ง์ํ๋ ์๊ฒฉ ๋ฐฑ์๋๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค. S3, Terraform Cloud ๋ฑ์ด ์๋ค.
State ํ์ผ์๋ ๋ฆฌ์์ค์ ๋ํ ๋ชจ๋ ๊ฒ์ด ๋ด๊ฒจ์๋ค. ์๋์ ์ค์ต์ ํตํด, ์ํ ํ์ผ ๊ด๋ฆฌ์ ์ค์์ฑ์ ํ์ธํด๋ณธ๋ค.
resource "random_password" "mypw" {
length = 16
special = true
override_special = "!#$%"
}
์๋์ ๋ช ๋ น์ด๋ฅผ ํตํด ๋ฆฌ์์ค ์์ฑ
terraform init && terraform plan && terraform apply -auto-approve
ํ ๋ผํผ ๋ช ๋ น์ด๋ฅผ ํตํด ๋ฆฌ์์ค๋ฅผ ํ์ธํ๋ฉด, ์ํฌ๋ฆฟ ์ ๋ณด๋ ์๋ ค์ฃผ์ง ์๋๋ค.
โฏ terraform state show random_password.mypw
# random_password.mypw:
resource "random_password" "mypw" {
bcrypt_hash = (sensitive value)
id = "none"
length = 16
lower = true
min_lower = 0
min_numeric = 0
min_special = 0
min_upper = 0
number = true
numeric = true
override_special = "!#$%"
result = (sensitive value)
special = true
upper = true
}
ํ์ง๋ง, state ํ์ผ์ ํ์ธํด๋ณด๋ฉด ๋ฆฌ์์ค์ ๋ํ ์ ๋ณด๊ฐ ์ ๋ถ ์กด์ฌํ๋ค.
{
...
"resources": [
{
"mode": "managed",
"type": "random_password",
...
"instances": [
{
"schema_version": 3,
"attributes": {
"bcrypt_hash": "$2a$10$pLMnmRKSY52ageoVumPlpuP5dyo2GZpomOxo6MsQetO/F28dR2ge2",
"id": "none",
...
"result": "CLTOsYB9zWifY9WT",
"special": true,
"upper": true
},
"sensitive_attributes": []
}
]
}
],
"check_results": null
}
ํ ๋ผํผ ์ฝ์์์๋ sensitive value๋ ๋ณด์ด์ง ์๋๋ค.
echo "random_password.mypw" | terraform console
{
"bcrypt_hash" = (sensitive value)
"id" = "none"
"keepers" = tomap(null) /* of string */
"length" = 16
...
"override_special" = "!#$%"
"result" = (sensitive value)
"special" = true
"upper" = true
}
Terraform์ .tfstate
ํ์ผ ๋ด์ serial
๊ฐ์ ์ํ ํ์ผ์ ๋ฒ์ ์ ๋ํ๋ด๋ฉฐ, ๋์์ฑ ์ ์ด์ ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ ํ์ธ์๋ ์ค์ํ ์ญํ ์ ํฉ๋๋ค. ์ด ๊ฐ์ Terraform ๋ช
๋ น์ด ์คํ๋ ๋๋ง๋ค ์๋์ผ๋ก ์ฆ๊ฐํ์ฌ, ์ํ ํ์ผ์ ์ต์ ์ฑ๊ณผ ์ผ๊ด์ฑ์ ์ ์งํฉ๋๋ค. ๊ทธ๋ ๊ธฐ์ backup ํ์ผ์ด ํ์ฌ state ํ์ผ๋ณด๋ค serial ๋ฒํธ๊ฐ ๋ฎ๋ค.
์ ํ | ๊ตฌ์ฑ ๋ฆฌ์์ค ์ ์ | State ๊ตฌ์ฑ ๋ฐ์ดํฐ | ์ค์ ๋ฆฌ์์ค | ๊ธฐ๋ณธ ์์ ๋์ |
---|---|---|---|---|
1 | ์์ | ๋ฆฌ์์ค ์์ฑ | ||
2 | ์์ | ์์ | ๋ฆฌ์์ค ์์ฑ | |
3 | ์์ | ์์ | ์์ | ๋์ ์์ |
4 | ์์ | ์์ | ๋ฆฌ์์ค ์ญ์ | |
5 | ์์ | ๋์ ์์ | ||
6 | ์์ | ์์ |
-refresh=false
์ต์
์ ์ฌ์ฉํ๋ฉด, ํ์ฌ์ stateํ์ผ๊ณผ ํ
๋ผํผ์ฝ๋๋ฅผ ๋น๊ตํ์ฌ ๊ทธ๋๋ก ์ ์ฉํ๋ค. ์๊ฒฉ ๋ฆฌ์์ค์ ์ค์ ์ํ๋ ํ์ธํ์ง ์์. ๊ทธ๋ ๊ธฐ์ ๋ง์ฝ ์๊ฒฉ๋ฆฌ์์ค๊ฐ ์ ๊ฑฐ๋์์ด๋ ๋ค์ ์์ฑํ์ง ์๋๋ค.์ ํ6๋ฒ ์ค์ต์งํ
locals {
name = "mytest"
}
resource "aws_iam_user" "myiamuser1" {
name = "${local.name}1"
}
resource "aws_iam_user" "myiamuser2" {
name = "${local.name}2"
}
terraform apply -auto-approve
aws iam list-users | jq '.Users[] | .UserName'
"admin"
"mytest1"
"mytest2"
rm -rf terraform.tfstate*
โฏ ls terraform.tfstate*
zsh: no matches found: terraform.tfstate*
$ terraform plan
Terraform used the selected providers to generate the following execution plan. Resource
actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_iam_user.myiamuser1 will be created
+ resource "aws_iam_user" "myiamuser1" {
+ arn = (known after apply)
+ force_destroy = false
+ id = (known after apply)
+ name = "mytest1"
+ path = "/"
+ tags_all = (known after apply)
+ unique_id = (known after apply)
}
$ terraform apply
...
Plan: 2 to add, 0 to change, 0 to destroy.
aws_iam_user.myiamuser2: Creating...
aws_iam_user.myiamuser1: Creating...
โท
โ Error: creating IAM User (mytest1): **EntityAlreadyExists: User with name mytest1 already exists.**
โ status code: 409, request id: e32ae858-e9eb-4c3a-a6ab-d7dba9f8bbd8
โ
โ with aws_iam_user.myiamuser1,
โ on main.tf line 5, in resource "aws_iam_user" "myiamuser1":
โ 5: resource "aws_iam_user" "myiamuser1" {
โ
โต
terraform import [options] ADDRESS ID
terraform import aws_iam_user.myiamuser1 mytest1
aws_iam_user.myiamuser1: Importing from ID "mytest1"...
aws_iam_user.myiamuser1: Import prepared!
Prepared aws_iam_user for import
aws_iam_user.myiamuser1: Refreshing state... [id=mytest1]
Import successful!
The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
{
"version": 4,
"terraform_version": "1.5.6",
"serial": 4,
"lineage": "0e52f56e-fe1e-d6e7-acb3-f521a3c2f365",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "aws_iam_user",
"name": "myiamuser1",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
...
"id": "mytest1",
"name": "mytest1",
},
"sensitive_attributes": [],
...
}
State๋ฅผ ๊ด๋ฆฌํ๋ ๋ ผ๋ฆฌ์ ์ธ ๊ฐ์ ๊ณต๊ฐ์ ์ํฌ์คํ์ด์ค๋ผ๊ณ ํ๋ค.
๊ฐ๋ฐ์ฉ ํ๊ฒฝ, ์คํ
์ด์ง ํ๊ฒฝ, ์ด์ํ๊ฒฝ์ ๋๋ถ๋ถ ๊ฐ์ง๊ณ ์๋ค. ๊ฑฐ์ ์ ์ฌํ ํ๊ฒฝ์ ๊ตฌ์ถํ๋ค๊ณ ํ๋ฉด ์ฌ๋ฌ ๊ฐ์ ํ๋ก์ ํธ๋ฅผ ํตํด ์ด์ํ ์ ์์ง๋ง ๋์ผํ ํ๊ฒฝ์ ๊ตฌ์ฑํ๋ค๋ฉด ์ด๋ ์ผ๊ด์ฑ ์ ์ง์ ์ข์ง ์๋ค. ํ
๋ผํผ์์๋ ์ด๋ฅผ ์ํด workspace๋ฅผ ์ง์ํ๋ค. ํ๋์ ์ฝ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ฌ๋ฌ ๊ฐ์ ํ๊ฒฝ์ ๊ตฌ์ฑํ ์ ์์ผ๋ฉฐ terraform.workspace
๋ณ์๋ฅผ ํตํด ํ๊ฒฝ๋ง๋ค ๋ฆฌ์์ค๋ฅผ ์กฐ์ ํ ์ ์๋ค.
vs ์๋๋ ์ํฌ์คํ์ด์ค๊ฐ ์๋ ์ฌ๋ฌ ๊ฐ์ ํ๋ก์ ํธ๋ก ๊ตฌ์ฑํ ๋ชจ์ต์ด๋ค.
๐ก ์ค๋ช ํด์ฃผ์ ์ํฌ์คํ์ด์ค์ ์ฅ๋จ์ ๋ชจ๋์ ๋๋ถ๋ถ์ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์์ ์ฐ์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํจํค์ง์ ์ญํ ์ด ๋น์ทํ๋ค.
์ค๋ณต๋๊ฑฐ๋, ์์ฃผ์ฐ๋ ์ฝ๋๋ฅผ ๋ชจ๋ํํด์ ํธํ๊ฒ ์ฌ์ฌ์ฉํ ์ ์๋ค.
๋ชจ๋ ๋๋ ํฐ๋ฆฌ ํ์์ terraform-<ํ๋ก๋ฐ์ด๋ ์ด๋ฆ>-<๋ชจ๋ ์ด๋ฆ>
ํ์์ ์ ์ํ๋ค.
์ด ํ์์ Terraform Cloud, Terraform Enterprise์์๋ ์ฌ์ฉ๋๋ ๋ฐฉ์์ผ๋ก
1) ๋๋ ํฐ๋ฆฌ ๋๋ ๋ ์ง์คํธ๋ฆฌ ์ด๋ฆ์ด ํ ๋ผํผ์ ์ํ ๊ฒ์ด๊ณ , 2) ์ด๋ค ํ๋ก๋ฐ์ด๋์ ๋ฆฌ์์ค๋ฅผ ํฌํจํ๊ณ ์์ผ๋ฉฐ, 3) ๋ถ์ฌ๋ ์ด๋ฆ์ด ๋ฌด์์ธ์ง ํ๋ณํ ์ ์๋๋ก ํ๋ค.
์๋์ ๊ฐ์ด ๋ฃจํธ ๋ชจ๋์์ ์์ ๋ชจ๋์ ์ฐธ์กฐํ๋ค. ์์๋ชจ๋์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ญํ ์ ์ํํ๊ณ , ๋ฃจํธ ๋ชจ๋์ด main ํจ์์ด๋ค. ์์ ๋ชจ๋์ ํธ์ถํ ๋, ๋ณ์๋ ๋ง๊ฒ ๋์ ํ๋ค.
์ถ์ฒ: https://jloudon.com/cloud/Azure-Policy-as-Code-with-Terraform-Part-1/
๊ฐ๋จํ ์์๋ฅผ ํ์ธํด๋ณด๋ฉด, ์๋์ ์์ ๋ชจ๋์ ์ฌ์ฉํ๋ค๊ณ ๊ฐ์ ํด๋ณด์. isDB๋ผ๋ ๋ณ์์ ๊ฐ์ ๋์ ํด์ค์ผํ๊ณ , id์ pw๋ฅผ ์ถ๋ ฅํ ์ ์๋ค.
# main.tf
resource "random_pet" "name" {
keepers = {
ami_id = timestamp()
}
}
# DB์ผ ๊ฒฝ์ฐ Password ์์ฑ ๊ท์น์ ๋ค๋ฅด๊ฒ ๋ฐ์
resource "random_password" "password" {
length = var.isDB ? 16 : 10
special = var.isDB ? true : false
override_special = "!#$%*?"
}
# variable.tf
variable "isDB" {
type = bool
default = false
description = "ํจ์ค์๋ ๋์์ DB ์ฌ๋ถ"
}
# output.tf
output "id" {
value = random_pet.name.id
}
output "pw" {
value = nonsensitive(random_password.password.result)
}
๋ฃจํธ ๋ชจ๋
"mypw1"์ ๊ฒฝ์ฐ๋ ๋ณ์๋ฅผ ๋์
ํ์ง ์์์ผ๋, isDB
์ ๊ธฐ๋ณธ๊ฐ์ด ๋ค์ด๊ฐ๋ค. ๋ค์๊ณผ ๊ฐ์ด ๋ชจ๋์ ํตํด ์ฝ๋๋ฅผ ๊ตฌ์กฐํํ๊ณ ์ฌ์ฌ์ฉํ ์ ์๋ค.
module "mypw1" {
source = "../modules/terraform-random-pwgen"
}
module "mypw2" {
source = "../modules/terraform-random-pwgen"
isDB = true
}
output "mypw1" {
value = module.mypw1
}
output "mypw2" {
value = module.mypw2
}
๋ฃจํธ๋ชจ๋์์ ํ๋ก๋ฐ์ด๋๋ฅผ ์ ์ํ๋ ๊ฒ์ด ์ข๋ค. ๋ง์ฝ, ์์๋ชจ๋์์ ํ๋ก๋ฐ์ด๋๋ฅผ ์ ์ํ๋ฉด ๋ฃจํธ ๋ชจ๋์ ๋ฒ์ ์ด ๋ค๋ฅด๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ณ ๋ชจ๋์ ๋ฐ๋ชฉ๋ฌธ์ ์ธ ์ ์๋ค.
์๋์ module โexampleโ๊ณผ ๊ฐ์ด ๋ฃจํธ ๋ชจ๋์ ํ๋ก๋ฐ์ด๋๋ฅผ ์ ์ธํ๋ค.
# The default "aws" configuration is used for AWS resources in the root
# module where no explicit provider instance is selected.
provider "aws" {
region = "us-west-1"
}
# An alternate configuration is also defined for a different
# region, using the alias "usw2".
provider "aws" {
alias = "usw2"
region = "us-west-2"
}
# An example child module is instantiated with the alternate configuration,
# so any AWS resources it defines will use the us-west-2 region.
module "example" {
source = "./example"
providers = {
aws = aws.usw2
}
}
๋ชจ๋์ ์์์ ์งํํ๋ฏ์ด ๋ก์ปฌํ์ผ๋ก ๊ฐ๋ฅํ๋ฉฐ ํ ๋ผํผ ๋ ์ง์คํธ๋ฆฌ, ๊นํ๋ธ ๋ฑ์์ ๊ฐ์ ธ์์ ์ธ ์ ์๋ค.
ex) Terraform registry์์ ๊ฐ์ ธ์ค๊ธฐ
module "consul" {
source = "hashicorp/consul/aws"
version = "0.1.0"
}
S3๋ฅผ ํตํด, ๋ฐฑ์๋๋ฅผ ๊ตฌ์ฑํ๋ ๊ฒ์ 1์ฃผ์ฐจ๋ ์งํํ๋ค. ์ฌ๊ธฐ์๋ Terraform Cloud๋ฅผ ์ด์ฉํ์ฌ TFC ๋ฐฑ์๋๋ฅผ ๊ตฌ์ฑํด๋ณธ๋ค. ๋น์ฐํ Lock ๊ธฐ๋ฅ๊ณผ ๋ฒ์ ๊ด๋ฆฌ๋ ์ง์ํ๋ค.
state ๊ด๋ฆฌ๋ฅผ ์งํํ๋ TFC๋ ๋ฌด์์ด๋ผ๊ณ ํ๋ค.
๐ก TFC์ํฌ์คํ์ด์ค ์์ฑ
terraform login์ ์งํํ๋ค.
$ terraform login
[ํ ํฐ ์
๋ ฅ]
ํ ํฐ ํ์ธ
cat ~/.terraform.d/credentials.tfrc.json | jq
{
"credentials": {
"app.terraform.io": {
"token": "YMgr4VM...EWGuw"
}
}
}
provider.tf์์ ํ ๋ผํผ ํด๋ผ์ฐ๋๋ฅผ ์ ์ํ๋ค.
terraform {
cloud {
organization = "kane-org" # ์์ฑํ ORG ์ด๋ฆ ์ง์
hostname = "app.terraform.io" # default
workspaces {
name = "terraform-stduy" # ์์ผ๋ฉด ์์ฑ๋จ
}
}
}
์ดํ, terraform init ๋ช
๋ น์ด๋ฅผ ์คํํ๋ฉด .terraform
๋๋ ํฐ๋ฆฌ๊ฐ ์์ฑ๋๊ณ ์์ ์ํํ์ผ์ด ์์ฑ๋๋ค. ์๋๋ ์ํํ์ผ ์ธ๋ถ๋ด์ฉ์ด๋ค.
{
"version": 3,
"serial": 1,
...
"backend": {
"type": "cloud",
"config": {
"hostname": "app.terraform.io",
"organization": "kane-org",
"token": null,
"workspaces": {
"name": "terraform-stduy",
"tags": null
}
},
์ด์ init && plan ๋ช ๋ น์ด๋ฅผ ์คํํ๋ฉด, ๋ค์๊ณผ ๊ฐ์ด ํด๋ผ์ฐ๋์์ ๋์ํ๋ค. [terraform cloud local ์ค์ x]
์ค์ ์ ํตํด, terraform ์์ ์ ๋ชจ๋ ๋ก์ปฌ์์ ๋๋ฆฌ๊ณ state ํ์ผ๋ง ์ ๋ก๋ํ ์ ์๋ค.
Plan์ด ๋ชจ๋ ๋์ํ๋ฉด, ์๋์ ๊ฐ์ด UI๋ก ๋ฐฐํฌ๋ ๋ฆฌ์์ค๋ฅผ ์๋ ค์ค๋ค.
ํ์คํ GUI๋ก ๋ณด๋ ๊น๋ํ ๊ฒ ๊ฐ๋ค. ํนํ ๋ฆฌ์์ค๊ฐ ๋ง์์ก์ ๋ ๋ณด๊ธฐํธํ ๊ฒ ๊ฐ๋ค.
๊ฐ์ ์ฌ์ฉํ๊ธฐ ํธ๋ฆฌํ ๋ฆฌ์์ค๋ฅผ ๋ชจ๋ํ ํด๋ณด๊ณ , ํด๋น ๋ชจ๋์ ํ์ฉํด์ ๋ฐ๋ณต ๋ฆฌ์์ค๋ค ๋ฐฐํฌํด๋ณด์ธ์!
VPC, Subnet ๋ฑ EC2์ ํ์ํ ๋ฆฌ์์ค๋ฅผ ๋ชจ๋ํํด๋ดค๋ค. ๋ณด์๊ทธ๋ฃน์ ํฌํธ๋ SSH, HTTP๋ฅผ ์ด์ด๋จ๋ค.
locals {
additional_tags = {
Name = var.namespace
}
}
resource "aws_vpc" "vpc" {
cidr_block = "192.169.0.0/16"
tags = local.additional_tags
}
data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_subnet" "public_subnet" {
vpc_id = aws_vpc.vpc.id
cidr_block = "192.169.1.0/24"
availability_zone = data.aws_availability_zones.available.names[0]
map_public_ip_on_launch = true
tags = local.additional_tags
}
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.vpc.id
tags = local.additional_tags
}
resource "aws_route_table" "public_route_table" {
vpc_id = aws_vpc.vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
tags = local.additional_tags
}
resource "aws_route_table_association" "public_rtb_assoc" {
subnet_id = aws_subnet.public_subnet.id
route_table_id = aws_route_table.public_route_table.id
}
resource "aws_security_group" "web_sg" {
name = var.namespace
vpc_id = aws_vpc.vpc.id
ingress {
from_port = var.ssh_port
to_port = var.ssh_port
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = var.http_port
to_port = var.http_port
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
data "aws_ami" "default" {
most_recent = true
owners = ["amazon"]
filter {
name = "owner-alias"
values = ["amazon"]
}
filter {
name = "name"
values = ["amzn2-ami-hvm*"]
}
}
resource "aws_instance" "app" {
ami = data.aws_ami.default.id
instance_type = var.ec2_instance_type
key_name = var.key_name
vpc_security_group_ids = [aws_security_group.web_sg.id]
subnet_id = aws_subnet.public_subnet.id
tags = local.additional_tags
}
output "instance_public_ip" {
value = aws_instance.app.public_ip
description = "The public IP address of the App instance"
}
variable "ssh_port" {
default = 22
type = number
description = "SSH port"
}
variable "http_port" {
default = 80
type = number
description = "HTTP port"
}
variable "ec2_instance_type" {
type = string
description = "The type of EC2 instance to launch"
}
variable "key_name" {
type = string
description = "The key name to use for an EC2 instance"
}
variable "namespace" {
type = string
description = "env namespace"
}
locals {
env = {
dev = {
instance_type = "t3.micro"
key_name = "m1"
namespace = "dev"
}
prod = {
instance_type = "t3.medium"
key_name = "m1"
namespace = "prod"
}
}
}
provider "aws" {
region = "ap-northeast-2"
}
module "ec2_aws_amazone" {
for_each = local.env
source = "../../module/ec2"
key_name = each.value.key_name
ec2_instance_type = each.value.instance_type
namespace = each.value.namespace
}
# output.tf
output "module_output_instance_public_ip" {
value = [
for k in module.ec2_aws_amazone : k.instance_public_ip
]
}
์ด์ , ํ ๋ผํผ ๋ช ๋ น์ด๋ฅผ ์คํํ์ฌ ๋ฆฌ์์ค๋ฅผ ๋ฐฐํฌํ๋ค.
$ tf apply -auto-approve
module.ec2_aws_amazone["dev"].data.aws_availability_zones.available: Reading...
module.ec2_aws_amazone["prod"].data.aws_availability_zones.available: Reading...
module.ec2_aws_amazone["prod"].data.aws_ami.default: Reading...
module.ec2_aws_amazone["dev"].data.aws_ami.default: Reading...
module.ec2_aws_amazone["dev"].data.aws_availability_zones.available: Read complete after 1s [id=ap-northeast-2]
module.ec2_aws_amazone["prod"].data.aws_availability_zones.available: Read complete after 1s [id=ap-northeast-2]
module.ec2_aws_amazone["dev"].data.aws_ami.default: Read complete after 1s [id=ami-0ec77cfb1037681eb]
module.ec2_aws_amazone["prod"].data.aws_ami.default: Read complete after 1s [id=ami-0ec77cfb1037681eb]
Apply complete! Resources: 14 added, 0 changed, 0 destroyed.
Outputs:
module_output_instance_public_ip = [
"3.35.235.54",
"13.124.205.208",
]
AWS ์ฝ์์์ ๋ฐฐํฌ๋ ๋ชฉ๋ก์ ํ์ธํ๋ค.
ํ๊ฒฝ ๋ณ๋ก, ๋ฐฐํฌ๊ฐ ์ ๋ ๋ชจ์ต์ ํ์ธํ ์ ์๋ค.