Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,20 @@

# OS 파일
.DS_Store
Thumbs.db
Thumbs.db

# Terraform / OpenTofu
**/.terraform/*
*.tfstate
*.tfstate.*
crash.log
crash.*.log
*.tfvars
*.tfvars.json
override.tf
override.tf.json
*_override.tf
*_override.tf.json
.terraformrc
terraform.rc
.terraform.lock.hcl
80 changes: 80 additions & 0 deletions terraform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Terraform (IaC) — 현재 인프라 스냅샷

운영 중인 AWS 인프라를 [terraformer](https://github.com/GoogleCloudPlatform/terraformer)로 떠낸 **초기 스냅샷**이다.
이 디렉터리는 brownfield IaC 전환의 시작점으로, 아직 모듈화/리팩터링 전의 자동 생성 코드다.

> ⚠️ 이 코드는 **아직 실물과 state로 연결되지 않았다.** `tofu plan`이 "No changes"가 되도록
> 정합성을 맞추는 작업은 후속 단계에서 진행한다. (issue 후속)

## 도구

| 도구 | 버전 | 비고 |
| --- | --- | --- |
| OpenTofu | 1.12.3 | IaC 엔진 (`tofu`) |
| terraformer | 0.8.30 | 기존 인프라 export (upstream archived, deprecated) |
| AWS provider | hashicorp/aws ~> 5.0 | |
| Region | `ap-northeast-2` | 서울 |

## 디렉터리 구조

```
terraform/
├── provider.tf # OpenTofu + AWS provider 선언
└── generated/aws/ # terraformer 자동 생성 (서비스별 분리)
├── vpc/ subnet/ route_table/ igw/ eip/ nacl/ # 네트워크
├── ec2_instance/ eni/ sg/ # 컴퓨트 + 보안그룹
├── route53/ # DNS
└── iam/ # LinClean 관련 IAM만
```

## 스냅샷 범위 (50개 리소스)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

30번째 줄에는 스냅샷 범위가 총 50개 리소스라고 작성되어 있지만, 아래 표의 합계와 실제 Terraform resource 선언은 모두 37개입니다.

리소스를 정리하는 과정에서 문서만 갱신되지 않은 것으로 보입니다. 현재 커밋된 코드를 기준으로 작성한 문서라면 수정 부탁드립니다.

| 서비스 | 개수 | 서비스 | 개수 |
| --- | --- | --- | --- |
| iam | 8 (정리 후) | route_table | 4 |
| route53 | 9 | sg | 3 |
| ec2_instance | 2 (app·db) | eni | 2 |
| subnet | 5 | vpc/igw/eip/nacl | 각 1 |

## 재현 방법

```bash
cd terraform
tofu init # AWS provider 다운로드

# terraformer가 provider를 ~/.terraform.d/plugins 에서 찾으므로 바이너리 연결 필요
mkdir -p ~/.terraform.d/plugins/darwin_arm64
cp .terraform/providers/registry.opentofu.org/hashicorp/aws/*/darwin_arm64/terraform-provider-aws \
~/.terraform.d/plugins/darwin_arm64/terraform-provider-aws_v5.100.0_x5

# EC2 계열은 AWS_REGION 환경변수가 있어야 aws-global 오해석을 피함

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

첫 번째 terraformer import 명령의 --resources에 이미 route53이 포함되어 있지만, 56번째 줄에서 IAM과 Route53을 다시 export하고 있습니다.

README의 설명대로 Route53을 글로벌 서비스로 분리해서 실행하려는 의도라면, 첫 번째 명령에서는 route53을 제거해야 할 것 같습니다.

AWS_REGION=ap-northeast-2 terraformer import aws \
--resources=vpc,subnet,route_table,igw,eip,ec2_instance,eni,sg,nacl,route53 \
--regions=ap-northeast-2 --connect=true

# iam/route53 는 글로벌 서비스라 별도 실행 (AWS_REGION 불필요)
terraformer import aws --resources=iam,route53 --regions=ap-northeast-2
```

## 알려진 한계 / 후속 작업

- [ ] **state 정합성**: 각 서비스별 분리된 tfstate를 통합하고 `tofu plan` "No changes" 검증
- [ ] **모듈화**: `tfer--` prefix의 자동 생성 코드를 의미 있는 모듈(network / compute / iam ...)로 리팩터링
- [ ] **OIDC provider 누락**: terraformer가 `aws_iam_openid_connect_provider`(GitHub Actions OIDC)를
리소스로 export하지 못함. `github-actions-deploy` 역할의 trust policy에 ARN으로만 참조됨 → 수동 import 필요
- [ ] **SSM Parameter Store**: SecureString 값이 평문 노출되므로 스냅샷에서 의도적으로 제외함.
값이 아닌 "껍데기/참조" 형태로 별도 관리 필요
- [ ] **IAM 정리됨**: terraformer는 계정 전체 IAM을 export하므로, LinClean 무관 사용자/access key/
서비스 연결 역할은 제거하고 `github-actions-deploy`·`linclean-ec2-role` 관련만 남김
- [ ] **레거시 provider 주소**: terraformer가 `required_providers`에 `source`를 넣지 않은 구형(TF 0.12)
형식으로 생성 → 현재 상태로는 `tofu init`이 실패함. 모듈화 시 루트 provider로 통합하며 해결
- [ ] **provider region/version 불일치**: 글로벌 서비스(iam/route53) provider.tf는 `us-east-1`,
나머지는 `ap-northeast-2`. 버전 핀도 generated는 `~> 5.100.0`, 루트는 `~> 5.0`. 통합 시 일원화
(글로벌 리소스라 동작엔 영향 없음 — 모듈화 때 정리)

## 주의

- `*.tfstate`, `.terraform/`, `*.tfvars`, `.terraform.lock.hcl` 은 `.gitignore` 처리됨 (민감정보 보호).
- **계정 ID 비노출**: 공개 레포라 IAM 정책/역할 ARN의 계정 ID를 평문 대신
`data.aws_caller_identity.current.account_id`로 동적 참조한다 (`iam/data.tf`).
- 자동 생성 코드의 리소스 이름(`tfer--...`)은 후속 리팩터링에서 정리한다.
11 changes: 11 additions & 0 deletions terraform/generated/aws/ec2_instance/ap-northeast-2/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
provider "aws" {
region = "ap-northeast-2"
}

terraform {
required_providers {
aws = {
version = "~> 5.100.0"
}
}
}
23 changes: 23 additions & 0 deletions terraform/generated/aws/ec2_instance/ap-northeast-2/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
data "terraform_remote_state" "ebs" {
backend = "local"

config = {
path = "../../../../generated/aws/ebs/ap-northeast-2/terraform.tfstate"
}
}

data "terraform_remote_state" "sg" {
backend = "local"

config = {
path = "../../../../generated/aws/sg/ap-northeast-2/terraform.tfstate"
}
}

data "terraform_remote_state" "subnet" {
backend = "local"

config = {
path = "../../../../generated/aws/subnet/ap-northeast-2/terraform.tfstate"
}
}
171 changes: 171 additions & 0 deletions terraform/generated/aws/ec2_instance/instance.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
resource "aws_instance" "tfer--i-0175957e300c3ccd9_linclean-app" {
ami = "ami-0596f7562954deb8e"
associate_public_ip_address = "true"
availability_zone = "ap-northeast-2a"

capacity_reservation_specification {
capacity_reservation_preference = "open"
}

cpu_core_count = "1"

cpu_options {
core_count = "1"
threads_per_core = "2"
}

cpu_threads_per_core = "2"

credit_specification {
cpu_credits = "unlimited"
}

disable_api_stop = "false"
disable_api_termination = "false"
ebs_optimized = "true"

enclave_options {
enabled = "false"
}

get_password_data = "false"
hibernation = "false"
iam_instance_profile = "linclean-ec2-role"
instance_initiated_shutdown_behavior = "stop"
instance_type = "t3.small"
ipv6_address_count = "0"
key_name = "linclean-key"

maintenance_options {
auto_recovery = "default"
}

metadata_options {
http_endpoint = "enabled"
http_protocol_ipv6 = "disabled"
http_put_response_hop_limit = "1"
http_tokens = "optional"
instance_metadata_tags = "disabled"
}

monitoring = "false"
placement_partition_number = "0"

private_dns_name_options {
enable_resource_name_dns_a_record = "false"
enable_resource_name_dns_aaaa_record = "false"
hostname_type = "ip-name"
}

private_ip = "172.31.10.100"

root_block_device {
delete_on_termination = "true"
encrypted = "false"
iops = "3000"
throughput = "125"
volume_size = "30"
volume_type = "gp3"
}

security_groups = ["app-sg"]
source_dest_check = "false"
subnet_id = "${data.terraform_remote_state.subnet.outputs.aws_subnet_tfer--subnet-00b08863bb393665d_id}"

tags = {
Name = "linclean-app"
}

tags_all = {
Name = "linclean-app"
}

tenancy = "default"
vpc_security_group_ids = ["${data.terraform_remote_state.sg.outputs.aws_security_group_tfer--app-sg_sg-01eb4b595c6303b33_id}"]
}

resource "aws_instance" "tfer--i-087e8229818de4182_linclean-db" {
ami = "ami-0596f7562954deb8e"
associate_public_ip_address = "false"
availability_zone = "ap-northeast-2a"

capacity_reservation_specification {
capacity_reservation_preference = "open"
}

cpu_core_count = "1"

cpu_options {
core_count = "1"
threads_per_core = "2"
}

cpu_threads_per_core = "2"

credit_specification {
cpu_credits = "unlimited"
}

disable_api_stop = "false"
disable_api_termination = "false"
ebs_optimized = "true"

enclave_options {
enabled = "false"
}

get_password_data = "false"
hibernation = "false"
iam_instance_profile = "linclean-ec2-role"
instance_initiated_shutdown_behavior = "stop"
instance_type = "t3.micro"
ipv6_address_count = "0"
key_name = "linclean-key"

maintenance_options {
auto_recovery = "default"
}

metadata_options {
http_endpoint = "enabled"
http_protocol_ipv6 = "disabled"
http_put_response_hop_limit = "1"
http_tokens = "optional"
instance_metadata_tags = "disabled"
}

monitoring = "false"
placement_partition_number = "0"

private_dns_name_options {
enable_resource_name_dns_a_record = "false"
enable_resource_name_dns_aaaa_record = "false"
hostname_type = "ip-name"
}

private_ip = "172.31.67.93"

root_block_device {
delete_on_termination = "true"
encrypted = "false"
iops = "3000"
throughput = "125"
volume_size = "30"
volume_type = "gp3"
}

security_groups = ["db-sg"]
source_dest_check = "true"
subnet_id = "${data.terraform_remote_state.subnet.outputs.aws_subnet_tfer--subnet-0632d2f0a805011b3_id}"

tags = {
Name = "linclean-db"
}

tags_all = {
Name = "linclean-db"
}

tenancy = "default"
vpc_security_group_ids = ["${data.terraform_remote_state.sg.outputs.aws_security_group_tfer--db-sg_sg-01d7376ba980bf2e6_id}"]
}
7 changes: 7 additions & 0 deletions terraform/generated/aws/ec2_instance/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
output "aws_instance_tfer--i-0175957e300c3ccd9_linclean-app_id" {
value = "${aws_instance.tfer--i-0175957e300c3ccd9_linclean-app.id}"
}

output "aws_instance_tfer--i-087e8229818de4182_linclean-db_id" {
value = "${aws_instance.tfer--i-087e8229818de4182_linclean-db.id}"
}
11 changes: 11 additions & 0 deletions terraform/generated/aws/ec2_instance/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
provider "aws" {
region = "ap-northeast-2"
}

terraform {
required_providers {
aws = {
version = "~> 5.100.0"
}
}
}
23 changes: 23 additions & 0 deletions terraform/generated/aws/ec2_instance/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
data "terraform_remote_state" "ebs" {
backend = "local"

config = {
path = "../../../generated/aws/ebs/terraform.tfstate"
}
}

data "terraform_remote_state" "sg" {
backend = "local"

config = {
path = "../../../generated/aws/sg/terraform.tfstate"
}
}

data "terraform_remote_state" "subnet" {
backend = "local"

config = {
path = "../../../generated/aws/subnet/terraform.tfstate"
}
}
11 changes: 11 additions & 0 deletions terraform/generated/aws/eip/ap-northeast-2/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
provider "aws" {
region = "ap-northeast-2"
}

terraform {
required_providers {
aws = {
version = "~> 5.100.0"
}
}
}
17 changes: 17 additions & 0 deletions terraform/generated/aws/eip/eip.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
resource "aws_eip" "tfer--eipalloc-02a4533b598a1859a" {
domain = "vpc"
instance = "i-0175957e300c3ccd9"
network_border_group = "ap-northeast-2"
network_interface = "eni-01e3526fd0a19ee2d"
public_ipv4_pool = "amazon"

tags = {
Name = "linclean-app-eip"
}

tags_all = {
Name = "linclean-app-eip"
}

vpc = "true"
}
3 changes: 3 additions & 0 deletions terraform/generated/aws/eip/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output "aws_eip_tfer--eipalloc-02a4533b598a1859a_id" {
value = "${aws_eip.tfer--eipalloc-02a4533b598a1859a.id}"
}
Loading