From 4ea10e1eaa8028a099a059cf97023857ca65c274 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Fri, 20 Jun 2025 12:55:28 +0900 Subject: [PATCH 01/66] Init Deployment --- .github/workflows/deploy.yml | 0 .github/workflows/terraform-apply.yml | 0 .gitignore | 7 ++ alb.tf | 111 +++++++++++++++++++++ codedeploy.tf | 51 ++++++++++ ecs.tf | 135 ++++++++++++++++++++++++++ iam.tf | 61 ++++++++++++ main.tf | 14 +++ network.tf | 121 +++++++++++++++++++++++ output.tf | 20 ++++ security_group.tf | 44 +++++++++ variables.tf | 24 +++++ 12 files changed, 588 insertions(+) create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/terraform-apply.yml create mode 100644 .gitignore create mode 100644 alb.tf create mode 100644 codedeploy.tf create mode 100644 ecs.tf create mode 100644 iam.tf create mode 100644 main.tf create mode 100644 network.tf create mode 100644 output.tf create mode 100644 security_group.tf create mode 100644 variables.tf diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..e69de29 diff --git a/.github/workflows/terraform-apply.yml b/.github/workflows/terraform-apply.yml new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c653a96 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.DS_Store + +.terraform/ +.terraform.lock.hcl +*.tfstate +*.tfstate.backup +*.tfvars \ No newline at end of file diff --git a/alb.tf b/alb.tf new file mode 100644 index 0000000..31a1ece --- /dev/null +++ b/alb.tf @@ -0,0 +1,111 @@ +# WAF +resource "aws_wafv2_web_acl" "alb_waf" { + name = "${var.project_name}-alb-waf" + description = "WAF for ALB" + scope = "REGIONAL" + + default_action { + allow {} + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "waf-alb-metric" + sampled_requests_enabled = true + } + + rule { + name = "AWS-AWSManagedRulesCommonRuleSet" + priority = 1 + override_action { + none {} + } + statement { + managed_rule_group_statement { + vendor_name = "AWS" + name = "AWSManagedRulesCommonRuleSet" + } + } + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "AWSManagedRulesCommonRuleSet" + sampled_requests_enabled = true + } + } + + tags = { + Name = "${var.project_name}-alb-waf" + } +} + +# ALB +resource "aws_lb" "alb" { + name = "${var.project_name}-alb" + internal = false + load_balancer_type = "application" + security_groups = [aws_security_group.alb_sg.id] + subnets = [aws_subnet.public1.id, aws_subnet.public2.id] + + enable_deletion_protection = true + + tags = { + Name = "${var.project_name}-alb" + } +} + +# Target Group +resource "aws_lb_target_group" "blue" { + name = "${var.project_name}-blue-tg" + port = 80 + protocol = "HTTP" + vpc_id = aws_vpc.vpc.id + target_type = "instance" + health_check { + path = "/" + protocol = "HTTP" + interval = 30 + timeout = 5 + healthy_threshold = 2 + unhealthy_threshold = 2 + } + tags = { + Name = "${var.project_name}-blue-tg" + } +} + +resource "aws_lb_target_group" "green" { + name = "${var.project_name}-green-tg" + port = 80 + protocol = "HTTP" + vpc_id = aws_vpc.vpc.id + target_type = "instance" + health_check { + path = "/" + protocol = "HTTP" + interval = 30 + timeout = 5 + healthy_threshold = 2 + unhealthy_threshold = 2 + } + tags = { + Name = "${var.project_name}-green-tg" + } +} + +# ALB 리스너 +resource "aws_lb_listener" "http" { + load_balancer_arn = aws_lb.alb.arn + port = 80 + protocol = "HTTP" + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.blue.arn + } +} + +# WAF와 ALB 연결 +resource "aws_wafv2_web_acl_association" "alb_association" { + resource_arn = aws_lb.alb.arn + web_acl_arn = aws_wafv2_web_acl.alb_waf.arn + depends_on = [aws_lb.alb] +} \ No newline at end of file diff --git a/codedeploy.tf b/codedeploy.tf new file mode 100644 index 0000000..9c6301b --- /dev/null +++ b/codedeploy.tf @@ -0,0 +1,51 @@ +# CodeDeploy +resource "aws_codedeploy_app" "ecs_app" { + name = "${var.project_name}-ecs-app" + compute_platform = "ECS" +} + +resource "aws_codedeploy_deployment_group" "ecs_deployment_group" { + app_name = aws_codedeploy_app.ecs_app.name + deployment_group_name = "${var.project_name}-ecs-deployment-group" + service_role_arn = aws_iam_role.codedeploy_role.arn + + deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" + + ecs_service { + cluster_name = aws_ecs_cluster.ecs_cluster.name + service_name = aws_ecs_service.ecs_service.name + } + + deployment_style { + deployment_type = "BLUE_GREEN" + deployment_option = "WITH_TRAFFIC_CONTROL" + } + + blue_green_deployment_config { + deployment_ready_option { + action_on_timeout = "CONTINUE_DEPLOYMENT" + } + terminate_blue_instances_on_deployment_success { + action = "TERMINATE" + termination_wait_time_in_minutes = 5 + } + } + load_balancer_info { + target_group_pair_info { + target_group { + name = aws_lb_target_group.blue.name + } + target_group { + name = aws_lb_target_group.green.name + } + prod_traffic_route { + listener_arns = [aws_lb_listener.http.arn] + } + } + } + auto_rollback_configuration { + enabled = true + events = ["DEPLOYMENT_FAILURE"] + } + +} \ No newline at end of file diff --git a/ecs.tf b/ecs.tf new file mode 100644 index 0000000..22bc2b7 --- /dev/null +++ b/ecs.tf @@ -0,0 +1,135 @@ +# ECS 클러스터 생성 +resource "aws_ecs_cluster" "ecs_cluster" { + name = "${var.project_name}-ecs-cluster" +} + +# ECS Launch Template +resource "aws_launch_template" "ecs_launch_template" { + name_prefix = "${var.project_name}-ecs-launch-template-" + image_id = "ami-0bc365768d185847c" + instance_type = "t3.micro" + + iam_instance_profile { + name = aws_iam_instance_profile.ecs_instance_profile.name + } + network_interfaces { + associate_public_ip_address = false + security_groups = [aws_security_group.ecs_sg.id] + } + + user_data = base64encode(<<-EOF + #!/bin/bash + echo ECS_CLUSTER=${aws_ecs_cluster.ecs_cluster.name} >> /etc/ecs/ecs.config + EOF + ) + + tags = { + Name = "${var.project_name}-ecs-launch-template" + } +} + +# ECS Auto Scaling Group +resource "aws_autoscaling_group" "ecs_auto_scaling_group" { + launch_template { + id = aws_launch_template.ecs_launch_template.id + version = "$Latest" + } + + min_size = 1 + max_size = 4 + desired_capacity = 2 + vpc_zone_identifier = [aws_subnet.private1.id, aws_subnet.private2.id] + health_check_type = "EC2" + force_delete = true + protect_from_scale_in = true + + tag { + key = "ECS_Manage" + value = "${var.project_name}-ecs-auto-scaling-group" + propagate_at_launch = true + } + +} + +# ECS capacity provider +resource "aws_ecs_capacity_provider" "ecs_capacity_provider" { + name = "${var.project_name}-ecs-capacity-provider" + auto_scaling_group_provider { + auto_scaling_group_arn = aws_autoscaling_group.ecs_auto_scaling_group.arn + managed_termination_protection = "ENABLED" + managed_scaling { + status = "ENABLED" + target_capacity = 100 + } + } +} + +# Capacity provider association +resource "aws_ecs_cluster_capacity_providers" "ecs_cluster_capacity_providers" { + cluster_name = aws_ecs_cluster.ecs_cluster.name + capacity_providers = [aws_ecs_capacity_provider.ecs_capacity_provider.name] + default_capacity_provider_strategy { + capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name + weight = 100 + base = 1 + } +} + +# ECS Task Definition +resource "aws_ecs_task_definition" "ecs_task_definition" { + family = "${var.project_name}-ecs-task" + network_mode = "bridge" + requires_compatibilities = ["EC2"] + + container_definitions = jsonencode([ + { + name = "${var.project_name}-container" + image = "sh1220/mnrader" + cpu = 256 + memory = 512 + essential = true + portMappings = [ + { + containerPort = 80 + hostPort = 80 + protocol = "tcp" + } + ] + } + ]) +} + +# ECS Service +resource "aws_ecs_service" "ecs_service" { + name = "${var.project_name}-ecs-service" + cluster = aws_ecs_cluster.ecs_cluster.id + task_definition = aws_ecs_task_definition.ecs_task_definition.arn + desired_count = 2 + + + capacity_provider_strategy { + capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name + weight = 100 + } + + load_balancer { + target_group_arn = aws_lb_target_group.blue.arn + container_name = "${var.project_name}-container" + container_port = 80 + } + + deployment_controller { + type = "CODE_DEPLOY" + } + + lifecycle { + ignore_changes = [task_definition, desired_count] + } + + health_check_grace_period_seconds = 60 + + tags = { + Name = "${var.project_name}-ecs-service" + } +} + diff --git a/iam.tf b/iam.tf new file mode 100644 index 0000000..4599c65 --- /dev/null +++ b/iam.tf @@ -0,0 +1,61 @@ +# ECS 인스턴스가 사용할 IAM 역할 생성 +resource "aws_iam_role" "ecs_instance_role" { + name = "${var.project_name}-ecs-instance-role" + + # 이 역할을 EC2 인스턴스가 사용할 수 있도록 신뢰 정책 설정 + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "ec2.amazonaws.com" + } + } + ] + }) + + tags = { + Name = "${var.project_name}-ecs-instance-role" + } +} + +# AWS에서 관리하는 정책을 위에서 만든 역할에 연결 +resource "aws_iam_role_policy_attachment" "ecs_instance_role_attachment" { + role = aws_iam_role.ecs_instance_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" +} + +# EC2 인스턴스에 역할을 부여하기 위한 인스턴스 프로파일 생성 +resource "aws_iam_instance_profile" "ecs_instance_profile" { + name = "${var.project_name}-ecs-instance-profile" + role = aws_iam_role.ecs_instance_role.name +} + +# ECS 작업 실행 역할 +resource "aws_iam_role" "ecs_task_execution_role" { + name = "${var.project_name}-ecs-task-execution-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [{ Action = "sts:AssumeRole", Effect = "Allow", Principal = { Service = "ecs-tasks.amazonaws.com" } }] + }) +} +resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_attachment" { + role = aws_iam_role.ecs_task_execution_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" +} + +# CodeDeploy를 위한 IAM 역할 +resource "aws_iam_role" "codedeploy_role" { + name = "${var.project_name}-codedeploy-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [{ Action = "sts:AssumeRole", Effect = "Allow", Principal = { Service = "codedeploy.amazonaws.com" } }] + }) +} +resource "aws_iam_role_policy_attachment" "codedeploy_role_attachment" { + role = aws_iam_role.codedeploy_role.name + policy_arn = "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS" +} + diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..7c9372f --- /dev/null +++ b/main.tf @@ -0,0 +1,14 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} + diff --git a/network.tf b/network.tf new file mode 100644 index 0000000..0f7fbc9 --- /dev/null +++ b/network.tf @@ -0,0 +1,121 @@ +# VPC +resource "aws_vpc" "vpc" { + cidr_block = var.vpc_cidr + enable_dns_support = true + enable_dns_hostnames = true + tags = { + Name = "${var.project_name}-vpc" + } +} + +# subnet(public) +resource "aws_subnet" "public1" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.1.0/24" + availability_zone = "ap-northeast-2a" + map_public_ip_on_launch = true + tags = { + Name = "public_subnet1" + } +} + +resource "aws_subnet" "public2" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.2.0/24" + availability_zone = "ap-northeast-2b" + map_public_ip_on_launch = true + tags = { + Name = "public_subnet2" + } +} + +# subnet(private) + +resource "aws_subnet" "private1" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.101.0/24" + availability_zone = "ap-northeast-2a" + tags = { + Name = "private_subnet1" + } +} + +resource "aws_subnet" "private2" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.102.0/24" + availability_zone = "ap-northeast-2b" + tags = { + Name = "private_subnet2" + } +} + +# Internet Gateway +resource "aws_internet_gateway" "igw" { + vpc_id = aws_vpc.vpc.id + tags = { + Name = "${var.project_name}-igw" + } +} + +# Public Route Table +resource "aws_route_table" "public" { + vpc_id = aws_vpc.vpc.id + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.igw.id + } + tags = { + Name = "${var.project_name}-public-route-table" + } +} + +# Associate Public Subnets with Route Table +resource "aws_route_table_association" "public1" { + subnet_id = aws_subnet.public1.id + route_table_id = aws_route_table.public.id +} + +resource "aws_route_table_association" "public2" { + subnet_id = aws_subnet.public2.id + route_table_id = aws_route_table.public.id +} + +# NAT Gateway +resource "aws_eip" "nat" { + domain = "vpc" + tags = { + Name = "${var.project_name}-nat-eip" + } +} + +resource "aws_nat_gateway" "nat" { + allocation_id = aws_eip.nat.id + subnet_id = aws_subnet.public1.id + tags = { + Name = "${var.project_name}-nat-gateway" + } + depends_on = [aws_internet_gateway.igw] +} + +# Private Route Table +resource "aws_route_table" "private" { + vpc_id = aws_vpc.vpc.id + route { + cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.nat.id + } + tags = { + Name = "${var.project_name}-private-route-table" + } +} + +# Associate Private Subnets with Route Table +resource "aws_route_table_association" "private1" { + subnet_id = aws_subnet.private1.id + route_table_id = aws_route_table.private.id +} + +resource "aws_route_table_association" "private2" { + subnet_id = aws_subnet.private2.id + route_table_id = aws_route_table.private.id +} \ No newline at end of file diff --git a/output.tf b/output.tf new file mode 100644 index 0000000..79e62d3 --- /dev/null +++ b/output.tf @@ -0,0 +1,20 @@ +# outputs.tf +output "vpc_id" { + description = "생성된 VPC의 ID" + value = aws_vpc.vpc.id +} + +output "public_subnet_ids" { + description = "생성된 Public Subnet들의 ID 목록" + value = [aws_subnet.public1.id, aws_subnet.public2.id] +} + +output "private_subnet_ids" { + description = "생성된 Private Subnet들의 ID 목록" + value = [aws_subnet.private1.id, aws_subnet.private2.id] +} + +output "alb_dns_name" { + description = "ALB의 DNS 이름" + value = aws_lb.alb.dns_name +} \ No newline at end of file diff --git a/security_group.tf b/security_group.tf new file mode 100644 index 0000000..ff9059b --- /dev/null +++ b/security_group.tf @@ -0,0 +1,44 @@ +# ALB +resource "aws_security_group" "alb_sg" { + name = "${var.project_name}-alb-sg" + description = "Security group for ALB" + vpc_id = aws_vpc.vpc.id + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + description = "Allow HTTP" + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + +} + + # ECS + resource "aws_security_group" "ecs_sg" { + name = "${var.project_name}-ecs-sg" + description = "Security group for ECS tasks" + vpc_id = aws_vpc.vpc.id + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + security_groups = [aws_security_group.alb_sg.id] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + +} diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..b96a003 --- /dev/null +++ b/variables.tf @@ -0,0 +1,24 @@ +variable "project_name" { + description = "프로젝트를 식별하는 이름" + type = string + default = "WHS-CloudFence" +} + +variable "vpc_cidr" { + description = "VPC에 할당할 IP 주소 범위" + type = string + default = "10.0.0.0/16" +} + +variable "custom_ami_id" { + description = "ECS 인스턴스에 사용할 사용자 정의 AMI ID" + type = string + # 중요: 반드시 본인의 리전에 맞는 최신 ECS 최적화 AMI ID로 변경하세요. + default = "ami-0c55b159cbfafe1f0" +} + +variable "instance_type" { + description = "ECS 인스턴스의 타입" + type = string + default = "t3.micro" +} \ No newline at end of file From e3d4ab869c158008118363a9e09ca32856baa191 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 23 Jun 2025 21:49:19 +0900 Subject: [PATCH 02/66] ECR demo --- .github/workflows/deploy.yml | 71 ++++++++++++++++++++++++ .github/workflows/terraform-apply.yml | 79 +++++++++++++++++++++++++++ ecr.tf | 14 +++++ ecs.tf | 2 +- iam.tf | 24 +++++++- variables.tf | 2 +- 6 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 ecr.tf diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e69de29..d92de3d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -0,0 +1,71 @@ +name: Deploy Application to ECS + +on: + push: + branches: [ "main" ] + +env: + AWS_REGION: ap-northeast-2 + ECR_REPOSITORY: ${{ secrets.ECR_REPOSITORY_NAME }} + ECS_CLUSTER_NAME: "ecs-cluster" + ECS_SERVICE_NAME: "ecs-service" + CODEDEPLOY_APP_NAME: "ecs-app" + CODEDEPLOY_DEPLOYMENT_GROUP: "ecs-deployment-group" + CONTAINER_NAME: "ecs-container" + +permissions: + id-token: write + contents: read + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }} + aws-region: ap-northeast-2 + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Build, tag, and push image to Amazon ECR + id: build-image + run: | + IMAGE_TAG=$(echo "${{ github.sha }}" | cut -c1-7) + docker build -t ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:$IMAGE_TAG . + docker push ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:$IMAGE_TAG + echo "image_uri=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:$IMAGE_TAG" >> $GITHUB_OUTPUT + + - name: Render Amazon ECS task definition + id: render-task-def + run: | + TASK_DEF_TEMPLATE=$(cat task-definition.template.json) + NEW_TASK_DEF_CONTENT=$(echo "$TASK_DEF_TEMPLATE" | sed "s||${{ steps.build-image.outputs.image_uri }}|") + echo "$NEW_TASK_DEF_CONTENT" > new-task-definition.json + echo "task_definition_path=$(pwd)/new-task-definition.json" >> $GITHUB_OUTPUT + + - name: Register new task definition + id: register-task-def + run: | + TASK_DEF_ARN=$(aws ecs register-task-definition --cli-input-json file://${{ steps.render-task-def.outputs.task_definition_path }} --query 'taskDefinition.taskDefinitionArn' --output text) + echo "task_definition_arn=$TASK_DEF_ARN" >> $GITHUB_OUTPUT + + - name: Render AppSpec + id: render-appspec + run: | + APPSPEC_TEMPLATE=$(cat appspec.template.yml) + NEW_APPSPEC_CONTENT=$(echo "$APPSPEC_TEMPLATE" | sed "s||${{ steps.register-task-def.outputs.task_definition_arn }}|") + echo "$NEW_APPSPEC_CONTENT" > appspec.yml + + - name: Start CodeDeploy Deployment + run: | + aws deploy create-deployment \ + --application-name ${{ env.CODEDEPLOY_APP_NAME }} \ + --deployment-group-name ${{ env.CODEDEPLOY_DEPLOYMENT_GROUP }} \ + --revision "revisionType=AppSpecContent,appSpecContent={content=`cat appspec.yml`}" \ No newline at end of file diff --git a/.github/workflows/terraform-apply.yml b/.github/workflows/terraform-apply.yml index e69de29..b77621d 100644 --- a/.github/workflows/terraform-apply.yml +++ b/.github/workflows/terraform-apply.yml @@ -0,0 +1,79 @@ +name: Deploy Infrastructure with Terraform + +on: + push: + branches: [ "main" ] + paths: + - '**.tf' + +permissions: + id-token: write + contents: read + pull-requests: write + +jobs: + terraform-plan: + runs-on: ubuntu-latest + outputs: + tfplan_path: steps.plan.outputs.plan_path + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }} + aws-region: ap-northeast-2 + + - name: Terraform Init + id: init + run: terraform init + + - name: Terraform Plan + id: plan + run: terraform plan -out=tfplan.binary + + - name: Upload Plan Artifact + uses: actions/upload-artifact@v4 + with: + name: tfplan + path: tfplan.binary + + terraform-apply: + runs-on: ubuntu-latest + needs: terraform-plan # plan 잡이 성공해야 실행됨 + + environment: production + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }} + aws-region: ap-northeast-2 + + - name: Download Plan Artifact + uses: actions/download-artifact@v4 + with: + name: tfplan + + - name: Terraform Init + id: init + run: terraform init + + - name: Terraform Apply + id: apply + run: terraform apply -input=false tfplan.binary + + diff --git a/ecr.tf b/ecr.tf new file mode 100644 index 0000000..d3a5272 --- /dev/null +++ b/ecr.tf @@ -0,0 +1,14 @@ +resource "aws_ecr_repository" "app" { + name = var.project_name + + image_tag_mutability = "IMMUTABLE" + + image_scanning_configuration { + scan_on_push = true + } + + tags = { + Name = "${var.project_name}-repo" + Project = var.project_name + } +} \ No newline at end of file diff --git a/ecs.tf b/ecs.tf index 22bc2b7..9c459d5 100644 --- a/ecs.tf +++ b/ecs.tf @@ -84,7 +84,7 @@ resource "aws_ecs_task_definition" "ecs_task_definition" { container_definitions = jsonencode([ { name = "${var.project_name}-container" - image = "sh1220/mnrader" + image = "${aws_ecr_repository.app.repository_url}:latest" cpu = 256 memory = 512 essential = true diff --git a/iam.tf b/iam.tf index 4599c65..a693a4f 100644 --- a/iam.tf +++ b/iam.tf @@ -33,12 +33,22 @@ resource "aws_iam_instance_profile" "ecs_instance_profile" { role = aws_iam_role.ecs_instance_role.name } + + # ECS 작업 실행 역할 resource "aws_iam_role" "ecs_task_execution_role" { name = "${var.project_name}-ecs-task-execution-role" assume_role_policy = jsonencode({ Version = "2012-10-17", - Statement = [{ Action = "sts:AssumeRole", Effect = "Allow", Principal = { Service = "ecs-tasks.amazonaws.com" } }] + Statement = [ + { + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "ecs-tasks.amazonaws.com" + } + } + ] }) } resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_attachment" { @@ -46,12 +56,22 @@ resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_attachment" { policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" } + + # CodeDeploy를 위한 IAM 역할 resource "aws_iam_role" "codedeploy_role" { name = "${var.project_name}-codedeploy-role" assume_role_policy = jsonencode({ Version = "2012-10-17", - Statement = [{ Action = "sts:AssumeRole", Effect = "Allow", Principal = { Service = "codedeploy.amazonaws.com" } }] + Statement = [ + { + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "codedeploy.amazonaws.com" + } + } + ] }) } resource "aws_iam_role_policy_attachment" "codedeploy_role_attachment" { diff --git a/variables.tf b/variables.tf index b96a003..6fb2432 100644 --- a/variables.tf +++ b/variables.tf @@ -1,7 +1,7 @@ variable "project_name" { description = "프로젝트를 식별하는 이름" type = string - default = "WHS-CloudFence" + default = "cloudfence" } variable "vpc_cidr" { From bf11ac5da4d30d1b083f624da58eee3e8068aed0 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Sat, 5 Jul 2025 18:54:11 +0900 Subject: [PATCH 03/66] =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 + .terraform.lock.hcl | 25 ++++++ operation-team-account/ecr.tf | 14 +++ prod-team-account/alb.tf | 111 +++++++++++++++++++++++ prod-team-account/codedeploy.tf | 51 +++++++++++ prod-team-account/ecs.tf | 135 ++++++++++++++++++++++++++++ prod-team-account/iam.tf | 80 +++++++++++++++++ prod-team-account/main.tf | 13 +++ prod-team-account/network.tf | 121 +++++++++++++++++++++++++ prod-team-account/output.tf | 121 +++++++++++++++++++++++++ prod-team-account/security_group.tf | 44 +++++++++ prod-team-account/variables.tf | 24 +++++ 12 files changed, 743 insertions(+) create mode 100644 .gitignore create mode 100644 .terraform.lock.hcl create mode 100644 operation-team-account/ecr.tf create mode 100644 prod-team-account/alb.tf create mode 100644 prod-team-account/codedeploy.tf create mode 100644 prod-team-account/ecs.tf create mode 100644 prod-team-account/iam.tf create mode 100644 prod-team-account/main.tf create mode 100644 prod-team-account/network.tf create mode 100644 prod-team-account/output.tf create mode 100644 prod-team-account/security_group.tf create mode 100644 prod-team-account/variables.tf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f82c980 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +.idea +*.tfstate +*.tfstate.* \ No newline at end of file diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl new file mode 100644 index 0000000..cdc1668 --- /dev/null +++ b/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.100.0" + constraints = "~> 5.0" + hashes = [ + "h1:Ijt7pOlB7Tr7maGQIqtsLFbl7pSMIj06TVdkoSBcYOw=", + "zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644", + "zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2", + "zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274", + "zh:6330766f1d85f01ae6ea90d1b214b8b74cc8c1badc4696b165b36ddd4cc15f7b", + "zh:7c8c2e30d8e55291b86fcb64bdf6c25489d538688545eb48fd74ad622e5d3862", + "zh:99b1003bd9bd32ee323544da897148f46a527f622dc3971af63ea3e251596342", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9f8b909d3ec50ade83c8062290378b1ec553edef6a447c56dadc01a99f4eaa93", + "zh:aaef921ff9aabaf8b1869a86d692ebd24fbd4e12c21205034bb679b9caf883a2", + "zh:ac882313207aba00dd5a76dbd572a0ddc818bb9cbf5c9d61b28fe30efaec951e", + "zh:bb64e8aff37becab373a1a0cc1080990785304141af42ed6aa3dd4913b000421", + "zh:dfe495f6621df5540d9c92ad40b8067376350b005c637ea6efac5dc15028add4", + "zh:f0ddf0eaf052766cfe09dea8200a946519f653c384ab4336e2a4a64fdd6310e9", + "zh:f1b7e684f4c7ae1eed272b6de7d2049bb87a0275cb04dbb7cda6636f600699c9", + "zh:ff461571e3f233699bf690db319dfe46aec75e58726636a0d97dd9ac6e32fb70", + ] +} diff --git a/operation-team-account/ecr.tf b/operation-team-account/ecr.tf new file mode 100644 index 0000000..d3a5272 --- /dev/null +++ b/operation-team-account/ecr.tf @@ -0,0 +1,14 @@ +resource "aws_ecr_repository" "app" { + name = var.project_name + + image_tag_mutability = "IMMUTABLE" + + image_scanning_configuration { + scan_on_push = true + } + + tags = { + Name = "${var.project_name}-repo" + Project = var.project_name + } +} \ No newline at end of file diff --git a/prod-team-account/alb.tf b/prod-team-account/alb.tf new file mode 100644 index 0000000..31a1ece --- /dev/null +++ b/prod-team-account/alb.tf @@ -0,0 +1,111 @@ +# WAF +resource "aws_wafv2_web_acl" "alb_waf" { + name = "${var.project_name}-alb-waf" + description = "WAF for ALB" + scope = "REGIONAL" + + default_action { + allow {} + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "waf-alb-metric" + sampled_requests_enabled = true + } + + rule { + name = "AWS-AWSManagedRulesCommonRuleSet" + priority = 1 + override_action { + none {} + } + statement { + managed_rule_group_statement { + vendor_name = "AWS" + name = "AWSManagedRulesCommonRuleSet" + } + } + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "AWSManagedRulesCommonRuleSet" + sampled_requests_enabled = true + } + } + + tags = { + Name = "${var.project_name}-alb-waf" + } +} + +# ALB +resource "aws_lb" "alb" { + name = "${var.project_name}-alb" + internal = false + load_balancer_type = "application" + security_groups = [aws_security_group.alb_sg.id] + subnets = [aws_subnet.public1.id, aws_subnet.public2.id] + + enable_deletion_protection = true + + tags = { + Name = "${var.project_name}-alb" + } +} + +# Target Group +resource "aws_lb_target_group" "blue" { + name = "${var.project_name}-blue-tg" + port = 80 + protocol = "HTTP" + vpc_id = aws_vpc.vpc.id + target_type = "instance" + health_check { + path = "/" + protocol = "HTTP" + interval = 30 + timeout = 5 + healthy_threshold = 2 + unhealthy_threshold = 2 + } + tags = { + Name = "${var.project_name}-blue-tg" + } +} + +resource "aws_lb_target_group" "green" { + name = "${var.project_name}-green-tg" + port = 80 + protocol = "HTTP" + vpc_id = aws_vpc.vpc.id + target_type = "instance" + health_check { + path = "/" + protocol = "HTTP" + interval = 30 + timeout = 5 + healthy_threshold = 2 + unhealthy_threshold = 2 + } + tags = { + Name = "${var.project_name}-green-tg" + } +} + +# ALB 리스너 +resource "aws_lb_listener" "http" { + load_balancer_arn = aws_lb.alb.arn + port = 80 + protocol = "HTTP" + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.blue.arn + } +} + +# WAF와 ALB 연결 +resource "aws_wafv2_web_acl_association" "alb_association" { + resource_arn = aws_lb.alb.arn + web_acl_arn = aws_wafv2_web_acl.alb_waf.arn + depends_on = [aws_lb.alb] +} \ No newline at end of file diff --git a/prod-team-account/codedeploy.tf b/prod-team-account/codedeploy.tf new file mode 100644 index 0000000..9c6301b --- /dev/null +++ b/prod-team-account/codedeploy.tf @@ -0,0 +1,51 @@ +# CodeDeploy +resource "aws_codedeploy_app" "ecs_app" { + name = "${var.project_name}-ecs-app" + compute_platform = "ECS" +} + +resource "aws_codedeploy_deployment_group" "ecs_deployment_group" { + app_name = aws_codedeploy_app.ecs_app.name + deployment_group_name = "${var.project_name}-ecs-deployment-group" + service_role_arn = aws_iam_role.codedeploy_role.arn + + deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" + + ecs_service { + cluster_name = aws_ecs_cluster.ecs_cluster.name + service_name = aws_ecs_service.ecs_service.name + } + + deployment_style { + deployment_type = "BLUE_GREEN" + deployment_option = "WITH_TRAFFIC_CONTROL" + } + + blue_green_deployment_config { + deployment_ready_option { + action_on_timeout = "CONTINUE_DEPLOYMENT" + } + terminate_blue_instances_on_deployment_success { + action = "TERMINATE" + termination_wait_time_in_minutes = 5 + } + } + load_balancer_info { + target_group_pair_info { + target_group { + name = aws_lb_target_group.blue.name + } + target_group { + name = aws_lb_target_group.green.name + } + prod_traffic_route { + listener_arns = [aws_lb_listener.http.arn] + } + } + } + auto_rollback_configuration { + enabled = true + events = ["DEPLOYMENT_FAILURE"] + } + +} \ No newline at end of file diff --git a/prod-team-account/ecs.tf b/prod-team-account/ecs.tf new file mode 100644 index 0000000..68bf3b4 --- /dev/null +++ b/prod-team-account/ecs.tf @@ -0,0 +1,135 @@ +# ECS 클러스터 생성 +resource "aws_ecs_cluster" "ecs_cluster" { + name = "${var.project_name}-ecs-cluster" +} + +# ECS Launch Template +resource "aws_launch_template" "ecs_launch_template" { + name_prefix = "${var.project_name}-ecs-launch-template-" + image_id = "ami-0bc365768d185847c" + instance_type = "t2.micro" + + iam_instance_profile { + name = aws_iam_instance_profile.ecs_instance_profile.name + } + network_interfaces { + associate_public_ip_address = false + security_groups = [aws_security_group.ecs_sg.id] + } + + user_data = base64encode(<<-EOF + #!/bin/bash + echo ECS_CLUSTER=${aws_ecs_cluster.ecs_cluster.name} >> /etc/ecs/ecs.config + EOF + ) + + tags = { + Name = "${var.project_name}-ecs-launch-template" + } +} + +# ECS Auto Scaling Group +resource "aws_autoscaling_group" "ecs_auto_scaling_group" { + launch_template { + id = aws_launch_template.ecs_launch_template.id + version = "$Latest" + } + + min_size = 1 + max_size = 4 + desired_capacity = 2 + vpc_zone_identifier = [aws_subnet.private1.id, aws_subnet.private2.id] + health_check_type = "EC2" + force_delete = true + protect_from_scale_in = true + + tag { + key = "ECS_Manage" + value = "${var.project_name}-ecs-auto-scaling-group" + propagate_at_launch = true + } + +} + +# ECS capacity provider +resource "aws_ecs_capacity_provider" "ecs_capacity_provider" { + name = "${var.project_name}-ecs-capacity-provider" + auto_scaling_group_provider { + auto_scaling_group_arn = aws_autoscaling_group.ecs_auto_scaling_group.arn + managed_termination_protection = "ENABLED" + managed_scaling { + status = "ENABLED" + target_capacity = 100 + } + } +} + +# Capacity provider association +resource "aws_ecs_cluster_capacity_providers" "ecs_cluster_capacity_providers" { + cluster_name = aws_ecs_cluster.ecs_cluster.name + capacity_providers = [aws_ecs_capacity_provider.ecs_capacity_provider.name] + default_capacity_provider_strategy { + capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name + weight = 100 + base = 1 + } +} + +# ECS Task Definition +resource "aws_ecs_task_definition" "ecs_task_definition" { + family = "${var.project_name}-ecs-task" + network_mode = "bridge" + requires_compatibilities = ["EC2"] + + container_definitions = jsonencode([ + { + name = "${var.project_name}-container" + image = "${aws_ecr_repository.app.repository_url}:latest" + cpu = 256 + memory = 512 + essential = true + portMappings = [ + { + containerPort = 80 + hostPort = 80 + protocol = "tcp" + } + ] + } + ]) +} + +# ECS Service +resource "aws_ecs_service" "ecs_service" { + name = "${var.project_name}-ecs-service" + cluster = aws_ecs_cluster.ecs_cluster.id + task_definition = aws_ecs_task_definition.ecs_task_definition.arn + desired_count = 2 + + + capacity_provider_strategy { + capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name + weight = 100 + } + + load_balancer { + target_group_arn = aws_lb_target_group.blue.arn + container_name = "${var.project_name}-container" + container_port = 80 + } + + deployment_controller { + type = "CODE_DEPLOY" + } + + lifecycle { + ignore_changes = [task_definition, desired_count] + } + + health_check_grace_period_seconds = 60 + + tags = { + Name = "${var.project_name}-ecs-service" + } +} + \ No newline at end of file diff --git a/prod-team-account/iam.tf b/prod-team-account/iam.tf new file mode 100644 index 0000000..0b281a0 --- /dev/null +++ b/prod-team-account/iam.tf @@ -0,0 +1,80 @@ +# ECS 인스턴스가 사용할 IAM 역할 생성 +resource "aws_iam_role" "ecs_instance_role" { + name = "${var.project_name}-ecs-instance-role" + + # 이 역할을 EC2 인스턴스가 사용할 수 있도록 신뢰 정책 설정 + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "ec2.amazonaws.com" + } + } + ] + }) + + tags = { + Name = "${var.project_name}-ecs-instance-role" + } +} + +# AWS에서 관리하는 정책을 위에서 만든 역할에 연결 +resource "aws_iam_role_policy_attachment" "ecs_instance_role_attachment" { + role = aws_iam_role.ecs_instance_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" +} + +# EC2 인스턴스에 역할을 부여하기 위한 인스턴스 프로파일 생성 +resource "aws_iam_instance_profile" "ecs_instance_profile" { + name = "${var.project_name}-ecs-instance-profile" + role = aws_iam_role.ecs_instance_role.name +} + + + +# ECS 작업 실행 역할 +resource "aws_iam_role" "ecs_task_execution_role" { + name = "${var.project_name}-ecs-task-execution-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "ecs-tasks.amazonaws.com" + } + } + ] + }) +} +resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_attachment" { + role = aws_iam_role.ecs_task_execution_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" +} + + + +# CodeDeploy를 위한 IAM 역할 +resource "aws_iam_role" "codedeploy_role" { + name = "${var.project_name}-codedeploy-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "codedeploy.amazonaws.com" + } + } + ] + }) +} +resource "aws_iam_role_policy_attachment" "codedeploy_role_attachment" { + role = aws_iam_role.codedeploy_role.name + policy_arn = "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS" +} \ No newline at end of file diff --git a/prod-team-account/main.tf b/prod-team-account/main.tf new file mode 100644 index 0000000..1b0717f --- /dev/null +++ b/prod-team-account/main.tf @@ -0,0 +1,13 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} \ No newline at end of file diff --git a/prod-team-account/network.tf b/prod-team-account/network.tf new file mode 100644 index 0000000..0f7fbc9 --- /dev/null +++ b/prod-team-account/network.tf @@ -0,0 +1,121 @@ +# VPC +resource "aws_vpc" "vpc" { + cidr_block = var.vpc_cidr + enable_dns_support = true + enable_dns_hostnames = true + tags = { + Name = "${var.project_name}-vpc" + } +} + +# subnet(public) +resource "aws_subnet" "public1" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.1.0/24" + availability_zone = "ap-northeast-2a" + map_public_ip_on_launch = true + tags = { + Name = "public_subnet1" + } +} + +resource "aws_subnet" "public2" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.2.0/24" + availability_zone = "ap-northeast-2b" + map_public_ip_on_launch = true + tags = { + Name = "public_subnet2" + } +} + +# subnet(private) + +resource "aws_subnet" "private1" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.101.0/24" + availability_zone = "ap-northeast-2a" + tags = { + Name = "private_subnet1" + } +} + +resource "aws_subnet" "private2" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.102.0/24" + availability_zone = "ap-northeast-2b" + tags = { + Name = "private_subnet2" + } +} + +# Internet Gateway +resource "aws_internet_gateway" "igw" { + vpc_id = aws_vpc.vpc.id + tags = { + Name = "${var.project_name}-igw" + } +} + +# Public Route Table +resource "aws_route_table" "public" { + vpc_id = aws_vpc.vpc.id + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.igw.id + } + tags = { + Name = "${var.project_name}-public-route-table" + } +} + +# Associate Public Subnets with Route Table +resource "aws_route_table_association" "public1" { + subnet_id = aws_subnet.public1.id + route_table_id = aws_route_table.public.id +} + +resource "aws_route_table_association" "public2" { + subnet_id = aws_subnet.public2.id + route_table_id = aws_route_table.public.id +} + +# NAT Gateway +resource "aws_eip" "nat" { + domain = "vpc" + tags = { + Name = "${var.project_name}-nat-eip" + } +} + +resource "aws_nat_gateway" "nat" { + allocation_id = aws_eip.nat.id + subnet_id = aws_subnet.public1.id + tags = { + Name = "${var.project_name}-nat-gateway" + } + depends_on = [aws_internet_gateway.igw] +} + +# Private Route Table +resource "aws_route_table" "private" { + vpc_id = aws_vpc.vpc.id + route { + cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.nat.id + } + tags = { + Name = "${var.project_name}-private-route-table" + } +} + +# Associate Private Subnets with Route Table +resource "aws_route_table_association" "private1" { + subnet_id = aws_subnet.private1.id + route_table_id = aws_route_table.private.id +} + +resource "aws_route_table_association" "private2" { + subnet_id = aws_subnet.private2.id + route_table_id = aws_route_table.private.id +} \ No newline at end of file diff --git a/prod-team-account/output.tf b/prod-team-account/output.tf new file mode 100644 index 0000000..0f7fbc9 --- /dev/null +++ b/prod-team-account/output.tf @@ -0,0 +1,121 @@ +# VPC +resource "aws_vpc" "vpc" { + cidr_block = var.vpc_cidr + enable_dns_support = true + enable_dns_hostnames = true + tags = { + Name = "${var.project_name}-vpc" + } +} + +# subnet(public) +resource "aws_subnet" "public1" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.1.0/24" + availability_zone = "ap-northeast-2a" + map_public_ip_on_launch = true + tags = { + Name = "public_subnet1" + } +} + +resource "aws_subnet" "public2" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.2.0/24" + availability_zone = "ap-northeast-2b" + map_public_ip_on_launch = true + tags = { + Name = "public_subnet2" + } +} + +# subnet(private) + +resource "aws_subnet" "private1" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.101.0/24" + availability_zone = "ap-northeast-2a" + tags = { + Name = "private_subnet1" + } +} + +resource "aws_subnet" "private2" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.102.0/24" + availability_zone = "ap-northeast-2b" + tags = { + Name = "private_subnet2" + } +} + +# Internet Gateway +resource "aws_internet_gateway" "igw" { + vpc_id = aws_vpc.vpc.id + tags = { + Name = "${var.project_name}-igw" + } +} + +# Public Route Table +resource "aws_route_table" "public" { + vpc_id = aws_vpc.vpc.id + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.igw.id + } + tags = { + Name = "${var.project_name}-public-route-table" + } +} + +# Associate Public Subnets with Route Table +resource "aws_route_table_association" "public1" { + subnet_id = aws_subnet.public1.id + route_table_id = aws_route_table.public.id +} + +resource "aws_route_table_association" "public2" { + subnet_id = aws_subnet.public2.id + route_table_id = aws_route_table.public.id +} + +# NAT Gateway +resource "aws_eip" "nat" { + domain = "vpc" + tags = { + Name = "${var.project_name}-nat-eip" + } +} + +resource "aws_nat_gateway" "nat" { + allocation_id = aws_eip.nat.id + subnet_id = aws_subnet.public1.id + tags = { + Name = "${var.project_name}-nat-gateway" + } + depends_on = [aws_internet_gateway.igw] +} + +# Private Route Table +resource "aws_route_table" "private" { + vpc_id = aws_vpc.vpc.id + route { + cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.nat.id + } + tags = { + Name = "${var.project_name}-private-route-table" + } +} + +# Associate Private Subnets with Route Table +resource "aws_route_table_association" "private1" { + subnet_id = aws_subnet.private1.id + route_table_id = aws_route_table.private.id +} + +resource "aws_route_table_association" "private2" { + subnet_id = aws_subnet.private2.id + route_table_id = aws_route_table.private.id +} \ No newline at end of file diff --git a/prod-team-account/security_group.tf b/prod-team-account/security_group.tf new file mode 100644 index 0000000..fc6e37c --- /dev/null +++ b/prod-team-account/security_group.tf @@ -0,0 +1,44 @@ +# ALB +resource "aws_security_group" "alb_sg" { + name = "${var.project_name}-alb-sg" + description = "Security group for ALB" + vpc_id = aws_vpc.vpc.id + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + description = "Allow HTTP" + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + +} + + # ECS + resource "aws_security_group" "ecs_sg" { + name = "${var.project_name}-ecs-sg" + description = "Security group for ECS tasks" + vpc_id = aws_vpc.vpc.id + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + security_groups = [aws_security_group.alb_sg.id] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + +} \ No newline at end of file diff --git a/prod-team-account/variables.tf b/prod-team-account/variables.tf new file mode 100644 index 0000000..6fb2432 --- /dev/null +++ b/prod-team-account/variables.tf @@ -0,0 +1,24 @@ +variable "project_name" { + description = "프로젝트를 식별하는 이름" + type = string + default = "cloudfence" +} + +variable "vpc_cidr" { + description = "VPC에 할당할 IP 주소 범위" + type = string + default = "10.0.0.0/16" +} + +variable "custom_ami_id" { + description = "ECS 인스턴스에 사용할 사용자 정의 AMI ID" + type = string + # 중요: 반드시 본인의 리전에 맞는 최신 ECS 최적화 AMI ID로 변경하세요. + default = "ami-0c55b159cbfafe1f0" +} + +variable "instance_type" { + description = "ECS 인스턴스의 타입" + type = string + default = "t3.micro" +} \ No newline at end of file From 79fe8b4a962cf481d972c14fa00f37a138bdebc7 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Sat, 5 Jul 2025 19:40:58 +0900 Subject: [PATCH 04/66] =?UTF-8?q?ECR=20=EA=B3=84=EC=A0=95=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20=EB=B0=8F=20ECS=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- operation-team-account/iam.tf | 32 +++++++++++++++++++++++++++++ operation-team-account/main.tf | 28 +++++++++++++++++++++++++ operation-team-account/variables.tf | 5 +++++ prod-team-account/ecs.tf | 7 +++++++ prod-team-account/iam.tf | 6 ++++++ prod-team-account/main.tf | 15 ++++++++++++++ 6 files changed, 93 insertions(+) create mode 100644 operation-team-account/iam.tf create mode 100644 operation-team-account/main.tf create mode 100644 operation-team-account/variables.tf diff --git a/operation-team-account/iam.tf b/operation-team-account/iam.tf new file mode 100644 index 0000000..aa315a4 --- /dev/null +++ b/operation-team-account/iam.tf @@ -0,0 +1,32 @@ +# prod-team-account의 역할 ARN을 변수로 받기 +variable "prod_github_actions_role_arn" { + type = string + description = "The ARN of the IAM role for GitHub Actions in prod-team-account" +} + +# ECR 리포지토리 정책을 생성하기 위한 IAM 정책 문서 데이터 소스 +data "aws_iam_policy_document" "ecr_repo_policy_document" { + statement { + sid = "AllowCrossAccountPush" + effect = "Allow" + principals { + type = "AWS" + identifiers = [var.prod_github_actions_role_arn] + } + actions = [ + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + "ecr:BatchCheckLayerAvailability", + "ecr:PutImage", + "ecr:InitiateLayerUpload", + "ecr:UploadLayerPart", + "ecr:CompleteLayerUpload" + ] + } +} + +# 생성된 정책 문서(JSON)를 output으로 출력 +output "ecr_policy_json" { + description = "The JSON policy document for the ECR repository" + value = data.aws_iam_policy_document.ecr_repo_policy_document.json +} \ No newline at end of file diff --git a/operation-team-account/main.tf b/operation-team-account/main.tf new file mode 100644 index 0000000..6f4d88f --- /dev/null +++ b/operation-team-account/main.tf @@ -0,0 +1,28 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} + +module "iam" { + source = "./modules/iam" + prod_github_actions_role_arn = var.prod_github_actions_role_arn +} + +module "ecr" { + source = "./modules/ecr" + ecr_policy_json = module.iam.ecr_policy_json +} + +output "aws_ecr_repository_url" { + description = "The URL of the ECR repository" + value = module.ecr.ecr_repository_url +} \ No newline at end of file diff --git a/operation-team-account/variables.tf b/operation-team-account/variables.tf new file mode 100644 index 0000000..0a9280f --- /dev/null +++ b/operation-team-account/variables.tf @@ -0,0 +1,5 @@ +variable "project_name" { + description = "프로젝트를 식별하는 이름" + type = string + default = "cloudfence" +} \ No newline at end of file diff --git a/prod-team-account/ecs.tf b/prod-team-account/ecs.tf index 68bf3b4..671a07b 100644 --- a/prod-team-account/ecs.tf +++ b/prod-team-account/ecs.tf @@ -1,3 +1,9 @@ +# iam에서 생성한 ARN 변수 전달 +variable "ecs_task_execution_role_arn" { + type = string + description = "The ARN of the ECS Task Execution Role" +} + # ECS 클러스터 생성 resource "aws_ecs_cluster" "ecs_cluster" { name = "${var.project_name}-ecs-cluster" @@ -80,6 +86,7 @@ resource "aws_ecs_task_definition" "ecs_task_definition" { family = "${var.project_name}-ecs-task" network_mode = "bridge" requires_compatibilities = ["EC2"] + execution_role_arn = var.ecs_task_execution_role_arn container_definitions = jsonencode([ { diff --git a/prod-team-account/iam.tf b/prod-team-account/iam.tf index 0b281a0..7cf0a4c 100644 --- a/prod-team-account/iam.tf +++ b/prod-team-account/iam.tf @@ -56,6 +56,12 @@ resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_attachment" { policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" } +# ECS 작업 실행 역할의 ARN 출력 +# 다른 리소스에서 이 ARN을 참조할 수 있도록 출력 변수 설정 +output "ecs_task_execution_role_arn" { + description = "ARN of the ECS Task Execution Role" + value = aws_iam_role.ecs_task_execution_role.arn +} # CodeDeploy를 위한 IAM 역할 diff --git a/prod-team-account/main.tf b/prod-team-account/main.tf index 1b0717f..ea8de80 100644 --- a/prod-team-account/main.tf +++ b/prod-team-account/main.tf @@ -10,4 +10,19 @@ terraform { provider "aws" { region = "ap-northeast-2" +} + +module "iam" { + source = "./modules/iam" + +} + +module "ecs" { + source = "./modules/ecs" + ecs_task_execution_role_arn = module.iam.ecs_task_execution_role_arn +} + +output "github_actions_role_arn" { + description = "The ARN of the IAM role for GitHub Actions in prod-team-account" + value = module.iam.github_actions_role_arn } \ No newline at end of file From 68e3d4c55696fe681c822b90d95bcb4005023f53 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Sat, 5 Jul 2025 23:09:28 +0900 Subject: [PATCH 05/66] =?UTF-8?q?=EB=A6=AC=EC=86=8C=EC=8A=A4=20=EB=B3=84?= =?UTF-8?q?=20=EB=AA=A8=EB=93=88=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- operation-team-account/{ => modules/ecr}/ecr.tf | 0 operation-team-account/{ => modules/iam}/iam.tf | 0 prod-team-account/{ => modules/alb}/alb.tf | 0 prod-team-account/{ => modules/codedeploy}/codedeploy.tf | 0 prod-team-account/{ => modules/ecs}/ecs.tf | 0 prod-team-account/{ => modules/iam}/iam.tf | 0 prod-team-account/{ => modules/vpc}/network.tf | 0 prod-team-account/{ => modules/vpc}/security_group.tf | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename operation-team-account/{ => modules/ecr}/ecr.tf (100%) rename operation-team-account/{ => modules/iam}/iam.tf (100%) rename prod-team-account/{ => modules/alb}/alb.tf (100%) rename prod-team-account/{ => modules/codedeploy}/codedeploy.tf (100%) rename prod-team-account/{ => modules/ecs}/ecs.tf (100%) rename prod-team-account/{ => modules/iam}/iam.tf (100%) rename prod-team-account/{ => modules/vpc}/network.tf (100%) rename prod-team-account/{ => modules/vpc}/security_group.tf (100%) diff --git a/operation-team-account/ecr.tf b/operation-team-account/modules/ecr/ecr.tf similarity index 100% rename from operation-team-account/ecr.tf rename to operation-team-account/modules/ecr/ecr.tf diff --git a/operation-team-account/iam.tf b/operation-team-account/modules/iam/iam.tf similarity index 100% rename from operation-team-account/iam.tf rename to operation-team-account/modules/iam/iam.tf diff --git a/prod-team-account/alb.tf b/prod-team-account/modules/alb/alb.tf similarity index 100% rename from prod-team-account/alb.tf rename to prod-team-account/modules/alb/alb.tf diff --git a/prod-team-account/codedeploy.tf b/prod-team-account/modules/codedeploy/codedeploy.tf similarity index 100% rename from prod-team-account/codedeploy.tf rename to prod-team-account/modules/codedeploy/codedeploy.tf diff --git a/prod-team-account/ecs.tf b/prod-team-account/modules/ecs/ecs.tf similarity index 100% rename from prod-team-account/ecs.tf rename to prod-team-account/modules/ecs/ecs.tf diff --git a/prod-team-account/iam.tf b/prod-team-account/modules/iam/iam.tf similarity index 100% rename from prod-team-account/iam.tf rename to prod-team-account/modules/iam/iam.tf diff --git a/prod-team-account/network.tf b/prod-team-account/modules/vpc/network.tf similarity index 100% rename from prod-team-account/network.tf rename to prod-team-account/modules/vpc/network.tf diff --git a/prod-team-account/security_group.tf b/prod-team-account/modules/vpc/security_group.tf similarity index 100% rename from prod-team-account/security_group.tf rename to prod-team-account/modules/vpc/security_group.tf From 389103c975a4dd1c4aba74dee9990774c6cc9c45 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Sat, 5 Jul 2025 23:14:42 +0900 Subject: [PATCH 06/66] =?UTF-8?q?output.tf=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prod-team-account/output.tf | 126 ++++-------------------------------- 1 file changed, 12 insertions(+), 114 deletions(-) diff --git a/prod-team-account/output.tf b/prod-team-account/output.tf index 0f7fbc9..ad696ed 100644 --- a/prod-team-account/output.tf +++ b/prod-team-account/output.tf @@ -1,121 +1,19 @@ -# VPC -resource "aws_vpc" "vpc" { - cidr_block = var.vpc_cidr - enable_dns_support = true - enable_dns_hostnames = true - tags = { - Name = "${var.project_name}-vpc" - } +output "vpc_id" { + description = "생성된 VPC의 ID" + value = aws_vpc.vpc.id } -# subnet(public) -resource "aws_subnet" "public1" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.1.0/24" - availability_zone = "ap-northeast-2a" - map_public_ip_on_launch = true - tags = { - Name = "public_subnet1" - } +output "public_subnet_ids" { + description = "생성된 Public Subnet들의 ID 목록" + value = [aws_subnet.public1.id, aws_subnet.public2.id] } -resource "aws_subnet" "public2" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.2.0/24" - availability_zone = "ap-northeast-2b" - map_public_ip_on_launch = true - tags = { - Name = "public_subnet2" - } +output "private_subnet_ids" { + description = "생성된 Private Subnet들의 ID 목록" + value = [aws_subnet.private1.id, aws_subnet.private2.id] } -# subnet(private) - -resource "aws_subnet" "private1" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.101.0/24" - availability_zone = "ap-northeast-2a" - tags = { - Name = "private_subnet1" - } -} - -resource "aws_subnet" "private2" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.102.0/24" - availability_zone = "ap-northeast-2b" - tags = { - Name = "private_subnet2" - } -} - -# Internet Gateway -resource "aws_internet_gateway" "igw" { - vpc_id = aws_vpc.vpc.id - tags = { - Name = "${var.project_name}-igw" - } -} - -# Public Route Table -resource "aws_route_table" "public" { - vpc_id = aws_vpc.vpc.id - route { - cidr_block = "0.0.0.0/0" - gateway_id = aws_internet_gateway.igw.id - } - tags = { - Name = "${var.project_name}-public-route-table" - } -} - -# Associate Public Subnets with Route Table -resource "aws_route_table_association" "public1" { - subnet_id = aws_subnet.public1.id - route_table_id = aws_route_table.public.id -} - -resource "aws_route_table_association" "public2" { - subnet_id = aws_subnet.public2.id - route_table_id = aws_route_table.public.id -} - -# NAT Gateway -resource "aws_eip" "nat" { - domain = "vpc" - tags = { - Name = "${var.project_name}-nat-eip" - } -} - -resource "aws_nat_gateway" "nat" { - allocation_id = aws_eip.nat.id - subnet_id = aws_subnet.public1.id - tags = { - Name = "${var.project_name}-nat-gateway" - } - depends_on = [aws_internet_gateway.igw] -} - -# Private Route Table -resource "aws_route_table" "private" { - vpc_id = aws_vpc.vpc.id - route { - cidr_block = "0.0.0.0/0" - nat_gateway_id = aws_nat_gateway.nat.id - } - tags = { - Name = "${var.project_name}-private-route-table" - } -} - -# Associate Private Subnets with Route Table -resource "aws_route_table_association" "private1" { - subnet_id = aws_subnet.private1.id - route_table_id = aws_route_table.private.id -} - -resource "aws_route_table_association" "private2" { - subnet_id = aws_subnet.private2.id - route_table_id = aws_route_table.private.id +output "alb_dns_name" { + description = "ALB의 DNS 이름" + value = aws_lb.alb.dns_name } \ No newline at end of file From ccd1a9a2beb7150446ead9cbd6d6cd20d455a800 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Sat, 5 Jul 2025 23:57:44 +0900 Subject: [PATCH 07/66] =?UTF-8?q?terraform=20state=EB=A5=BC=20=ED=86=B5?= =?UTF-8?q?=ED=95=B4=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=A1=9C=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 16 +++++++++++++++- operation-team-account/modules/ecr/ecr.tf | 19 +++++++++++++------ operation-team-account/modules/iam/iam.tf | 1 + prod-team-account/data.tf | 8 ++++++++ prod-team-account/modules/ecs/ecs.tf | 2 +- 5 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 prod-team-account/data.tf diff --git a/.gitignore b/.gitignore index f82c980..7b70452 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,18 @@ .DS_Store .idea + +# tfstate files *.tfstate -*.tfstate.* \ No newline at end of file +*.tfstate.* +.terraform +.terraform/ +*.tfvars +*.tfvars.json +*.tfplan +*.tfplan.json +*.tfignore +*.tfbackup +*.tfstate.backup +*.tfstate.backup.* +*.tfstate.backup.json +*.tfstate.backup.json.* diff --git a/operation-team-account/modules/ecr/ecr.tf b/operation-team-account/modules/ecr/ecr.tf index d3a5272..8c84270 100644 --- a/operation-team-account/modules/ecr/ecr.tf +++ b/operation-team-account/modules/ecr/ecr.tf @@ -1,14 +1,21 @@ -resource "aws_ecr_repository" "app" { - name = var.project_name +# iam.tf에서 만든 정책 JSON을 받을 변수 선언 +variable "ecr_policy_json" { + type = string + description = "The JSON policy to attach to the ECR repository" +} +# ECR 리포지토리 생성 +resource "aws_ecr_repository" "app_ecr_repo" { + name = var.project_name image_tag_mutability = "IMMUTABLE" image_scanning_configuration { scan_on_push = true } +} - tags = { - Name = "${var.project_name}-repo" - Project = var.project_name - } +# 정책을 리포지토리에 연결 +resource "aws_ecr_repository_policy" "app_ecr_repo_policy" { + repository = aws_ecr_repository.app_ecr_repo.name + policy = var.ecr_policy_json # 변수로 받은 정책을 사용 } \ No newline at end of file diff --git a/operation-team-account/modules/iam/iam.tf b/operation-team-account/modules/iam/iam.tf index aa315a4..9f50db1 100644 --- a/operation-team-account/modules/iam/iam.tf +++ b/operation-team-account/modules/iam/iam.tf @@ -21,6 +21,7 @@ data "aws_iam_policy_document" "ecr_repo_policy_document" { "ecr:InitiateLayerUpload", "ecr:UploadLayerPart", "ecr:CompleteLayerUpload" + "ecr:GetAuthorizationToken" ] } } diff --git a/prod-team-account/data.tf b/prod-team-account/data.tf new file mode 100644 index 0000000..72a643e --- /dev/null +++ b/prod-team-account/data.tf @@ -0,0 +1,8 @@ +data "terraform_remote_state" "operation_account" { + backend = "s3" # operation-team-account의 state가 저장된 백엔드 + config = { + bucket = "cloudfence-tfstate-app" + key = "operation-team/terraform.tfstate" + region = "ap-northeast-2" + } +} diff --git a/prod-team-account/modules/ecs/ecs.tf b/prod-team-account/modules/ecs/ecs.tf index 671a07b..61d3d33 100644 --- a/prod-team-account/modules/ecs/ecs.tf +++ b/prod-team-account/modules/ecs/ecs.tf @@ -91,7 +91,7 @@ resource "aws_ecs_task_definition" "ecs_task_definition" { container_definitions = jsonencode([ { name = "${var.project_name}-container" - image = "${aws_ecr_repository.app.repository_url}:latest" + image = "${data.terraform_remote_state.operation_account.outputs.ecr_repository_url}:latest" cpu = 256 memory = 512 essential = true From 7bc4f0d4e86f3d515e30864eb49a118abfd0f656 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Sun, 6 Jul 2025 00:25:29 +0900 Subject: [PATCH 08/66] =?UTF-8?q?account=20=EB=B3=84=20S3=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- operation-team-account/backend.tf | 9 +++++++++ backend.tf => prod-team-account/backend.tf | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 operation-team-account/backend.tf rename backend.tf => prod-team-account/backend.tf (74%) diff --git a/operation-team-account/backend.tf b/operation-team-account/backend.tf new file mode 100644 index 0000000..f55e9e3 --- /dev/null +++ b/operation-team-account/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-tfstate-app" + key = "prod-team/terraform.tfstate" # prod 전용 경로 + region = "ap-northeast-2" + dynamodb_table = "tfstate-lock-app" + encrypt = true + } +} \ No newline at end of file diff --git a/backend.tf b/prod-team-account/backend.tf similarity index 74% rename from backend.tf rename to prod-team-account/backend.tf index 36798f0..d26e06f 100644 --- a/backend.tf +++ b/prod-team-account/backend.tf @@ -1,7 +1,7 @@ terraform { backend "s3" { bucket = "cloudfence-tfstate-app" - key = "application-deployment/terraform.tfstate" + key = "prod-team/terraform.tfstate" region = "ap-northeast-2" dynamodb_table = "tfstate-lock-app" encrypt = true From 233425590a3f0c314d00ee0c35ff35cfe652c190 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Sun, 6 Jul 2025 03:51:59 +0900 Subject: [PATCH 09/66] =?UTF-8?q?=EC=B5=9C=EC=8B=A0=20AMI=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prod-team-account/data.tf | 9 +++++++++ prod-team-account/modules/ecs/ecs.tf | 2 +- prod-team-account/variables.tf | 13 ++++++------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/prod-team-account/data.tf b/prod-team-account/data.tf index 72a643e..cc645c1 100644 --- a/prod-team-account/data.tf +++ b/prod-team-account/data.tf @@ -6,3 +6,12 @@ data "terraform_remote_state" "operation_account" { region = "ap-northeast-2" } } + +data "golen_ami" "latest_shared_ami" { + most_recent = true + owners = [var.ami_owner_id] # operation-team-account의 AMI + filter { + name = "name" + values = ["WHS-CloudFence-*"] + } +} \ No newline at end of file diff --git a/prod-team-account/modules/ecs/ecs.tf b/prod-team-account/modules/ecs/ecs.tf index 61d3d33..1d221cf 100644 --- a/prod-team-account/modules/ecs/ecs.tf +++ b/prod-team-account/modules/ecs/ecs.tf @@ -12,7 +12,7 @@ resource "aws_ecs_cluster" "ecs_cluster" { # ECS Launch Template resource "aws_launch_template" "ecs_launch_template" { name_prefix = "${var.project_name}-ecs-launch-template-" - image_id = "ami-0bc365768d185847c" + image_id = data.golen_ami.latest_shared_ami.id instance_type = "t2.micro" iam_instance_profile { diff --git a/prod-team-account/variables.tf b/prod-team-account/variables.tf index 6fb2432..4875d35 100644 --- a/prod-team-account/variables.tf +++ b/prod-team-account/variables.tf @@ -10,15 +10,14 @@ variable "vpc_cidr" { default = "10.0.0.0/16" } -variable "custom_ami_id" { - description = "ECS 인스턴스에 사용할 사용자 정의 AMI ID" - type = string - # 중요: 반드시 본인의 리전에 맞는 최신 ECS 최적화 AMI ID로 변경하세요. - default = "ami-0c55b159cbfafe1f0" -} - variable "instance_type" { description = "ECS 인스턴스의 타입" type = string default = "t3.micro" +} + +variable "ami_owner_id" { + description = "ECS 인스턴스에 사용할 AMI의 소유자 ID" + type = string + default = "502676416967" # operation-team-account } \ No newline at end of file From 33351af6a773ac7ca049389c034d301c2470f7d5 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Sun, 6 Jul 2025 08:41:02 +0900 Subject: [PATCH 10/66] =?UTF-8?q?=EB=A6=AC=EC=86=8C=EC=8A=A4=20=EB=B3=84?= =?UTF-8?q?=20=EB=AA=A8=EB=93=88=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- operation-team-account/main.tf | 1 + operation-team-account/modules/ecr/outputs.tf | 4 ++ .../modules/ecr/variables.tf | 10 +++++ operation-team-account/modules/iam/iam.tf | 5 --- operation-team-account/modules/iam/outputs.tf | 5 +++ prod-team-account/main.tf | 44 +++++++++++++++++-- prod-team-account/modules/alb/alb.tf | 8 ++-- prod-team-account/modules/alb/outputs.tf | 19 ++++++++ prod-team-account/modules/alb/variables.tf | 19 ++++++++ .../modules/codedeploy/codedeploy.tf | 12 ++--- .../modules/codedeploy/outputs.tf | 9 ++++ .../modules/codedeploy/variables.tf | 34 ++++++++++++++ prod-team-account/modules/ecs/ecs.tf | 8 ++-- prod-team-account/modules/ecs/outputs.tf | 9 ++++ prod-team-account/modules/ecs/variables.tf | 43 ++++++++++++++++++ prod-team-account/modules/iam/outputs.tf | 14 ++++++ prod-team-account/modules/vpc/outputs.tf | 32 ++++++++++++++ prod-team-account/modules/vpc/variables.tf | 9 ++++ prod-team-account/{output.tf => outputs.tf} | 0 19 files changed, 263 insertions(+), 22 deletions(-) create mode 100644 operation-team-account/modules/ecr/outputs.tf create mode 100644 operation-team-account/modules/ecr/variables.tf create mode 100644 operation-team-account/modules/iam/outputs.tf create mode 100644 prod-team-account/modules/alb/outputs.tf create mode 100644 prod-team-account/modules/alb/variables.tf create mode 100644 prod-team-account/modules/codedeploy/outputs.tf create mode 100644 prod-team-account/modules/codedeploy/variables.tf create mode 100644 prod-team-account/modules/ecs/outputs.tf create mode 100644 prod-team-account/modules/ecs/variables.tf create mode 100644 prod-team-account/modules/iam/outputs.tf create mode 100644 prod-team-account/modules/vpc/outputs.tf create mode 100644 prod-team-account/modules/vpc/variables.tf rename prod-team-account/{output.tf => outputs.tf} (100%) diff --git a/operation-team-account/main.tf b/operation-team-account/main.tf index 6f4d88f..c90d2b9 100644 --- a/operation-team-account/main.tf +++ b/operation-team-account/main.tf @@ -19,6 +19,7 @@ module "iam" { module "ecr" { source = "./modules/ecr" + project_name = var.project_name ecr_policy_json = module.iam.ecr_policy_json } diff --git a/operation-team-account/modules/ecr/outputs.tf b/operation-team-account/modules/ecr/outputs.tf new file mode 100644 index 0000000..c2e7fe6 --- /dev/null +++ b/operation-team-account/modules/ecr/outputs.tf @@ -0,0 +1,4 @@ +output "repository_url" { + description = "The URL of the ECR repository" + value = aws_ecr_repository.app_ecr_repo.repository_url +} \ No newline at end of file diff --git a/operation-team-account/modules/ecr/variables.tf b/operation-team-account/modules/ecr/variables.tf new file mode 100644 index 0000000..091424e --- /dev/null +++ b/operation-team-account/modules/ecr/variables.tf @@ -0,0 +1,10 @@ +variable "project_name" { + description = "The name of the project to use for the ECR repository name" + type = string +} + +variable "ecr_policy_json" { + description = "The JSON policy document from the IAM module to attach to the ECR repository" + type = string + sensitive = true +} \ No newline at end of file diff --git a/operation-team-account/modules/iam/iam.tf b/operation-team-account/modules/iam/iam.tf index 9f50db1..21a671d 100644 --- a/operation-team-account/modules/iam/iam.tf +++ b/operation-team-account/modules/iam/iam.tf @@ -26,8 +26,3 @@ data "aws_iam_policy_document" "ecr_repo_policy_document" { } } -# 생성된 정책 문서(JSON)를 output으로 출력 -output "ecr_policy_json" { - description = "The JSON policy document for the ECR repository" - value = data.aws_iam_policy_document.ecr_repo_policy_document.json -} \ No newline at end of file diff --git a/operation-team-account/modules/iam/outputs.tf b/operation-team-account/modules/iam/outputs.tf new file mode 100644 index 0000000..1c12136 --- /dev/null +++ b/operation-team-account/modules/iam/outputs.tf @@ -0,0 +1,5 @@ +# 생성된 정책 문서(JSON)를 output으로 출력 +output "ecr_policy_json" { + description = "The JSON policy document for the ECR repository" + value = data.aws_iam_policy_document.ecr_repo_policy_document.json +} \ No newline at end of file diff --git a/prod-team-account/main.tf b/prod-team-account/main.tf index ea8de80..5c51786 100644 --- a/prod-team-account/main.tf +++ b/prod-team-account/main.tf @@ -12,16 +12,54 @@ provider "aws" { region = "ap-northeast-2" } -module "iam" { - source = "./modules/iam" - + +module "vpc" { + source = "./modules/vpc" + vpc_cide = var.vpc_cidr + project_name = var.project_name +} + + +module "alb" { + source = "./modules/alb" + project_name = var.project_name + vpc_id = module.vpc.vpc_id + public_subnet_ids = module.vpc.public_subnet_ids + alb_security_group_id = module.vpc.alb_security_group_id +} + +module "codedeploy" { + source = "./modules/codedeploy" + project_name = var.project_name + ecs_cluster_name = module.ecs.cluster_name + ecs_service_name = module.ecs.service_name + alb_listener_arn = module.alb.listener_arn + blue_target_group_name = module.alb.blue_target_group_name + green_target_group_name = module.alb.green_target_group_name + ecs_task_execution_role_arn = module.iam.ecs_task_execution_role_arn } module "ecs" { source = "./modules/ecs" + project_name = var.project_name ecs_task_execution_role_arn = module.iam.ecs_task_execution_role_arn + ecs_instance_profile_name = module.iam.ecs_instance_profile_name + private_subnet_ids = module.vpc.private_subnet_ids + ecs_security_group_id = module.vpc.ecs_security_group_id + blue_target_group_arn = module.alb.blue_target_group_arn + green_target_group_arn = module.alb.green_target_group_arn + golden_ami = data.aws_ami.latest_shared_ami.id + ecr_image_url = data.terraform_remote_state.operation_account.outputs.ecr_repository_url + +} + +module "iam" { + source = "./modules/iam" + project_name = var.project_name } + + output "github_actions_role_arn" { description = "The ARN of the IAM role for GitHub Actions in prod-team-account" value = module.iam.github_actions_role_arn diff --git a/prod-team-account/modules/alb/alb.tf b/prod-team-account/modules/alb/alb.tf index 31a1ece..1ddbc83 100644 --- a/prod-team-account/modules/alb/alb.tf +++ b/prod-team-account/modules/alb/alb.tf @@ -43,8 +43,8 @@ resource "aws_lb" "alb" { name = "${var.project_name}-alb" internal = false load_balancer_type = "application" - security_groups = [aws_security_group.alb_sg.id] - subnets = [aws_subnet.public1.id, aws_subnet.public2.id] + security_groups = [var.alb_security_group_id] + subnets = var.public_subnet_ids enable_deletion_protection = true @@ -58,7 +58,7 @@ resource "aws_lb_target_group" "blue" { name = "${var.project_name}-blue-tg" port = 80 protocol = "HTTP" - vpc_id = aws_vpc.vpc.id + vpc_id = var.vpc_id target_type = "instance" health_check { path = "/" @@ -77,7 +77,7 @@ resource "aws_lb_target_group" "green" { name = "${var.project_name}-green-tg" port = 80 protocol = "HTTP" - vpc_id = aws_vpc.vpc.id + vpc_id = var.vpc_id target_type = "instance" health_check { path = "/" diff --git a/prod-team-account/modules/alb/outputs.tf b/prod-team-account/modules/alb/outputs.tf new file mode 100644 index 0000000..35c0df4 --- /dev/null +++ b/prod-team-account/modules/alb/outputs.tf @@ -0,0 +1,19 @@ +output "dns_name" { + description = "The DNS name of the ALB" + value = aws_lb.alb.dns_name +} + +output "listener_arn" { + description = "The ARN of the ALB listener" + value = aws_lb_listener.http.arn +} + +output "blue_target_group_name" { + description = "The name of the blue target group" + value = aws_lb_target_group.blue.name +} + +output "green_target_group_name" { + description = "The name of the green target group" + value = aws_lb_target_group.green.name +} \ No newline at end of file diff --git a/prod-team-account/modules/alb/variables.tf b/prod-team-account/modules/alb/variables.tf new file mode 100644 index 0000000..5cb7b64 --- /dev/null +++ b/prod-team-account/modules/alb/variables.tf @@ -0,0 +1,19 @@ +variable "project_name" { + description = "The name of the project" + type = string +} + +variable "vpc_id" { + description = "The ID of the VPC" + type = string +} + +variable "public_subnet_ids" { + description = "A list of public subnet IDs for the ALB" + type = list(string) +} + +variable "alb_security_group_id" { + description = "The ID of the security group for the ALB" + type = string +} \ No newline at end of file diff --git a/prod-team-account/modules/codedeploy/codedeploy.tf b/prod-team-account/modules/codedeploy/codedeploy.tf index 9c6301b..a85c806 100644 --- a/prod-team-account/modules/codedeploy/codedeploy.tf +++ b/prod-team-account/modules/codedeploy/codedeploy.tf @@ -7,13 +7,13 @@ resource "aws_codedeploy_app" "ecs_app" { resource "aws_codedeploy_deployment_group" "ecs_deployment_group" { app_name = aws_codedeploy_app.ecs_app.name deployment_group_name = "${var.project_name}-ecs-deployment-group" - service_role_arn = aws_iam_role.codedeploy_role.arn + service_role_arn = var.codedeploy_service_role_arn deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" ecs_service { - cluster_name = aws_ecs_cluster.ecs_cluster.name - service_name = aws_ecs_service.ecs_service.name + cluster_name = var.ecs_cluster_name + service_name = var.ecs_service_name } deployment_style { @@ -33,13 +33,13 @@ resource "aws_codedeploy_deployment_group" "ecs_deployment_group" { load_balancer_info { target_group_pair_info { target_group { - name = aws_lb_target_group.blue.name + name = var.blue_target_group_name } target_group { - name = aws_lb_target_group.green.name + name = var.green_target_group_name } prod_traffic_route { - listener_arns = [aws_lb_listener.http.arn] + listener_arns = [var.alb_listener_arn] } } } diff --git a/prod-team-account/modules/codedeploy/outputs.tf b/prod-team-account/modules/codedeploy/outputs.tf new file mode 100644 index 0000000..ac1099b --- /dev/null +++ b/prod-team-account/modules/codedeploy/outputs.tf @@ -0,0 +1,9 @@ +output "application_name" { + description = "The name of the CodeDeploy application" + value = aws_codedeploy_app.ecs_app.name +} + +output "deployment_group_name" { + description = "The name of the CodeDeploy deployment group" + value = aws_codedeploy_deployment_group.ecs_deployment_group.deployment_group_name +} \ No newline at end of file diff --git a/prod-team-account/modules/codedeploy/variables.tf b/prod-team-account/modules/codedeploy/variables.tf new file mode 100644 index 0000000..7c30610 --- /dev/null +++ b/prod-team-account/modules/codedeploy/variables.tf @@ -0,0 +1,34 @@ +variable "project_name" { + description = "The name of the project" + type = string +} + +variable "ecs_cluster_name" { + description = "The name of the ECS cluster" + type = string +} + +variable "ecs_service_name" { + description = "The name of the ECS service" + type = string +} + +variable "codedeploy_service_role_arn" { + description = "The ARN of the IAM role for CodeDeploy to use" + type = string +} + +variable "alb_listener_arn" { + description = "The ARN of the ALB listener for traffic shifting" + type = string +} + +variable "blue_target_group_name" { + description = "The name of the blue target group" + type = string +} + +variable "green_target_group_name" { + description = "The name of the green target group" + type = string +} \ No newline at end of file diff --git a/prod-team-account/modules/ecs/ecs.tf b/prod-team-account/modules/ecs/ecs.tf index 1d221cf..6bb7945 100644 --- a/prod-team-account/modules/ecs/ecs.tf +++ b/prod-team-account/modules/ecs/ecs.tf @@ -12,7 +12,7 @@ resource "aws_ecs_cluster" "ecs_cluster" { # ECS Launch Template resource "aws_launch_template" "ecs_launch_template" { name_prefix = "${var.project_name}-ecs-launch-template-" - image_id = data.golen_ami.latest_shared_ami.id + image_id = "${var.golden_ami}" instance_type = "t2.micro" iam_instance_profile { @@ -20,7 +20,7 @@ resource "aws_launch_template" "ecs_launch_template" { } network_interfaces { associate_public_ip_address = false - security_groups = [aws_security_group.ecs_sg.id] + security_groups = [var.ecs_security_group_id] } user_data = base64encode(<<-EOF @@ -91,7 +91,7 @@ resource "aws_ecs_task_definition" "ecs_task_definition" { container_definitions = jsonencode([ { name = "${var.project_name}-container" - image = "${data.terraform_remote_state.operation_account.outputs.ecr_repository_url}:latest" + image = "${var.ecr_image_url}:latest" cpu = 256 memory = 512 essential = true @@ -120,7 +120,7 @@ resource "aws_ecs_service" "ecs_service" { } load_balancer { - target_group_arn = aws_lb_target_group.blue.arn + target_group_arn = var.blue_target_group_arn container_name = "${var.project_name}-container" container_port = 80 } diff --git a/prod-team-account/modules/ecs/outputs.tf b/prod-team-account/modules/ecs/outputs.tf new file mode 100644 index 0000000..c23b71b --- /dev/null +++ b/prod-team-account/modules/ecs/outputs.tf @@ -0,0 +1,9 @@ +output "cluster_name" { + description = "The name of the ECS cluster" + value = aws_ecs_cluster.ecs_cluster.name +} + +output "service_name" { + description = "The name of the ECS service" + value = aws_ecs_service.ecs_service.name +} \ No newline at end of file diff --git a/prod-team-account/modules/ecs/variables.tf b/prod-team-account/modules/ecs/variables.tf new file mode 100644 index 0000000..631536a --- /dev/null +++ b/prod-team-account/modules/ecs/variables.tf @@ -0,0 +1,43 @@ +variable "project_name" { + description = "The name of the project for resource naming" + type = string +} + +# --- IAM 모듈로부터 받을 정보 --- +variable "ecs_task_execution_role_arn" { + description = "The ARN of the ECS Task Execution Role for tasks to pull images, etc." + type = string +} + +variable "ecs_instance_profile_name" { + description = "The name of the IAM instance profile for ECS container instances" + type = string +} + +# --- VPC 모듈로부터 받을 정보 --- +variable "private_subnet_ids" { + description = "A list of private subnet IDs for the ECS instances" + type = list(string) +} + +variable "ecs_security_group_id" { + description = "The ID of the security group for the ECS instances" + type = string +} + +# --- 외부 데이터 소스로부터 받을 정보 --- +variable "golden_ami" { + description = "The ID of the AMI to use for the ECS instances" + type = string +} + +variable "ecr_image_url" { + description = "The full URL of the Docker image in ECR" + type = string +} + +# --- ALB 모듈로부터 받을 정보 --- +variable "blue_target_group_arn" { + description = "The ARN of the blue target group from the ALB" + type = string +} \ No newline at end of file diff --git a/prod-team-account/modules/iam/outputs.tf b/prod-team-account/modules/iam/outputs.tf new file mode 100644 index 0000000..497971b --- /dev/null +++ b/prod-team-account/modules/iam/outputs.tf @@ -0,0 +1,14 @@ +output "ecs_task_execution_role_arn" { + description = "ARN of the ECS Task Execution Role" + value = aws_iam_role.ecs_task_execution_role.arn +} + +output "codedeploy_service_role_arn" { + description = "The ARN of the IAM role for CodeDeploy" + value = aws_iam_role.codedeploy_role.arn +} + +output "ecs_instance_profile_name" { + description = "The name of the IAM instance profile for ECS container instances" + value = aws_iam_instance_profile.ecs_instance_profile.name +} \ No newline at end of file diff --git a/prod-team-account/modules/vpc/outputs.tf b/prod-team-account/modules/vpc/outputs.tf new file mode 100644 index 0000000..257c1a3 --- /dev/null +++ b/prod-team-account/modules/vpc/outputs.tf @@ -0,0 +1,32 @@ +# VPC의 ID + +output "vpc_id" { + description = "The ID of the VPC" + value = aws_vpc.vpc.id # network.tf 파일에 정의된 aws_vpc 리소스의 id 값 +} + +# Public Subnet들의 ID 목록을 출력 +output "public_subnet_ids" { + description = "A list of public subnet IDs" + # network.tf 파일에 정의된 public 서브넷 리소스들의 id를 리스트로 묶어서 출력 + value = [aws_subnet.public1.id, aws_subnet.public2.id] +} + +# Private Subnet들의 ID 목록을 출력 +output "private_subnet_ids" { + description = "A list of private subnet IDs" + value = [aws_subnet.private1.id, aws_subnet.private2.id] +} + +# ALB용 security group의 이름을 출력 +output "alb_security_group_id" { + description = "The ID of the security group for the ALB" + value = aws_security_group.alb_sg.id # security_group.tf 파일에 정의된 보안 그룹의 id +} + +# ECS용 보안 그룹의 ID를 출력 + +output "ecs_security_group_id" { + description = "The ID of the security group for the ECS tasks" + value = aws_security_group.ecs_sg.id # security_group.tf 파일에 정의된 보안 그룹의 id +} \ No newline at end of file diff --git a/prod-team-account/modules/vpc/variables.tf b/prod-team-account/modules/vpc/variables.tf new file mode 100644 index 0000000..ad775e0 --- /dev/null +++ b/prod-team-account/modules/vpc/variables.tf @@ -0,0 +1,9 @@ +variable "project_name" { + description = "The name of the project" + type = string +} + +variable "vpc_id" { + description = "The ID of the VPC where the ECS cluster will be deployed" + type = string +} \ No newline at end of file diff --git a/prod-team-account/output.tf b/prod-team-account/outputs.tf similarity index 100% rename from prod-team-account/output.tf rename to prod-team-account/outputs.tf From d58e78e61788d299595f4e7afb4fdd403060fbe4 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Sun, 6 Jul 2025 16:10:23 +0900 Subject: [PATCH 11/66] =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alb.tf | 111 -------------------------------------- codedeploy.tf | 51 ------------------ ecr.tf | 14 ----- ecs.tf | 135 ---------------------------------------------- iam.tf | 81 ---------------------------- main.tf | 89 ------------------------------ network.tf | 121 ----------------------------------------- output.tf | 20 ------- security_group.tf | 44 --------------- variables.tf | 24 --------- 10 files changed, 690 deletions(-) delete mode 100644 alb.tf delete mode 100644 codedeploy.tf delete mode 100644 ecr.tf delete mode 100644 ecs.tf delete mode 100644 iam.tf delete mode 100644 main.tf delete mode 100644 network.tf delete mode 100644 output.tf delete mode 100644 security_group.tf delete mode 100644 variables.tf diff --git a/alb.tf b/alb.tf deleted file mode 100644 index 31a1ece..0000000 --- a/alb.tf +++ /dev/null @@ -1,111 +0,0 @@ -# WAF -resource "aws_wafv2_web_acl" "alb_waf" { - name = "${var.project_name}-alb-waf" - description = "WAF for ALB" - scope = "REGIONAL" - - default_action { - allow {} - } - - visibility_config { - cloudwatch_metrics_enabled = true - metric_name = "waf-alb-metric" - sampled_requests_enabled = true - } - - rule { - name = "AWS-AWSManagedRulesCommonRuleSet" - priority = 1 - override_action { - none {} - } - statement { - managed_rule_group_statement { - vendor_name = "AWS" - name = "AWSManagedRulesCommonRuleSet" - } - } - visibility_config { - cloudwatch_metrics_enabled = true - metric_name = "AWSManagedRulesCommonRuleSet" - sampled_requests_enabled = true - } - } - - tags = { - Name = "${var.project_name}-alb-waf" - } -} - -# ALB -resource "aws_lb" "alb" { - name = "${var.project_name}-alb" - internal = false - load_balancer_type = "application" - security_groups = [aws_security_group.alb_sg.id] - subnets = [aws_subnet.public1.id, aws_subnet.public2.id] - - enable_deletion_protection = true - - tags = { - Name = "${var.project_name}-alb" - } -} - -# Target Group -resource "aws_lb_target_group" "blue" { - name = "${var.project_name}-blue-tg" - port = 80 - protocol = "HTTP" - vpc_id = aws_vpc.vpc.id - target_type = "instance" - health_check { - path = "/" - protocol = "HTTP" - interval = 30 - timeout = 5 - healthy_threshold = 2 - unhealthy_threshold = 2 - } - tags = { - Name = "${var.project_name}-blue-tg" - } -} - -resource "aws_lb_target_group" "green" { - name = "${var.project_name}-green-tg" - port = 80 - protocol = "HTTP" - vpc_id = aws_vpc.vpc.id - target_type = "instance" - health_check { - path = "/" - protocol = "HTTP" - interval = 30 - timeout = 5 - healthy_threshold = 2 - unhealthy_threshold = 2 - } - tags = { - Name = "${var.project_name}-green-tg" - } -} - -# ALB 리스너 -resource "aws_lb_listener" "http" { - load_balancer_arn = aws_lb.alb.arn - port = 80 - protocol = "HTTP" - default_action { - type = "forward" - target_group_arn = aws_lb_target_group.blue.arn - } -} - -# WAF와 ALB 연결 -resource "aws_wafv2_web_acl_association" "alb_association" { - resource_arn = aws_lb.alb.arn - web_acl_arn = aws_wafv2_web_acl.alb_waf.arn - depends_on = [aws_lb.alb] -} \ No newline at end of file diff --git a/codedeploy.tf b/codedeploy.tf deleted file mode 100644 index 9c6301b..0000000 --- a/codedeploy.tf +++ /dev/null @@ -1,51 +0,0 @@ -# CodeDeploy -resource "aws_codedeploy_app" "ecs_app" { - name = "${var.project_name}-ecs-app" - compute_platform = "ECS" -} - -resource "aws_codedeploy_deployment_group" "ecs_deployment_group" { - app_name = aws_codedeploy_app.ecs_app.name - deployment_group_name = "${var.project_name}-ecs-deployment-group" - service_role_arn = aws_iam_role.codedeploy_role.arn - - deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" - - ecs_service { - cluster_name = aws_ecs_cluster.ecs_cluster.name - service_name = aws_ecs_service.ecs_service.name - } - - deployment_style { - deployment_type = "BLUE_GREEN" - deployment_option = "WITH_TRAFFIC_CONTROL" - } - - blue_green_deployment_config { - deployment_ready_option { - action_on_timeout = "CONTINUE_DEPLOYMENT" - } - terminate_blue_instances_on_deployment_success { - action = "TERMINATE" - termination_wait_time_in_minutes = 5 - } - } - load_balancer_info { - target_group_pair_info { - target_group { - name = aws_lb_target_group.blue.name - } - target_group { - name = aws_lb_target_group.green.name - } - prod_traffic_route { - listener_arns = [aws_lb_listener.http.arn] - } - } - } - auto_rollback_configuration { - enabled = true - events = ["DEPLOYMENT_FAILURE"] - } - -} \ No newline at end of file diff --git a/ecr.tf b/ecr.tf deleted file mode 100644 index d3a5272..0000000 --- a/ecr.tf +++ /dev/null @@ -1,14 +0,0 @@ -resource "aws_ecr_repository" "app" { - name = var.project_name - - image_tag_mutability = "IMMUTABLE" - - image_scanning_configuration { - scan_on_push = true - } - - tags = { - Name = "${var.project_name}-repo" - Project = var.project_name - } -} \ No newline at end of file diff --git a/ecs.tf b/ecs.tf deleted file mode 100644 index 9c459d5..0000000 --- a/ecs.tf +++ /dev/null @@ -1,135 +0,0 @@ -# ECS 클러스터 생성 -resource "aws_ecs_cluster" "ecs_cluster" { - name = "${var.project_name}-ecs-cluster" -} - -# ECS Launch Template -resource "aws_launch_template" "ecs_launch_template" { - name_prefix = "${var.project_name}-ecs-launch-template-" - image_id = "ami-0bc365768d185847c" - instance_type = "t3.micro" - - iam_instance_profile { - name = aws_iam_instance_profile.ecs_instance_profile.name - } - network_interfaces { - associate_public_ip_address = false - security_groups = [aws_security_group.ecs_sg.id] - } - - user_data = base64encode(<<-EOF - #!/bin/bash - echo ECS_CLUSTER=${aws_ecs_cluster.ecs_cluster.name} >> /etc/ecs/ecs.config - EOF - ) - - tags = { - Name = "${var.project_name}-ecs-launch-template" - } -} - -# ECS Auto Scaling Group -resource "aws_autoscaling_group" "ecs_auto_scaling_group" { - launch_template { - id = aws_launch_template.ecs_launch_template.id - version = "$Latest" - } - - min_size = 1 - max_size = 4 - desired_capacity = 2 - vpc_zone_identifier = [aws_subnet.private1.id, aws_subnet.private2.id] - health_check_type = "EC2" - force_delete = true - protect_from_scale_in = true - - tag { - key = "ECS_Manage" - value = "${var.project_name}-ecs-auto-scaling-group" - propagate_at_launch = true - } - -} - -# ECS capacity provider -resource "aws_ecs_capacity_provider" "ecs_capacity_provider" { - name = "${var.project_name}-ecs-capacity-provider" - auto_scaling_group_provider { - auto_scaling_group_arn = aws_autoscaling_group.ecs_auto_scaling_group.arn - managed_termination_protection = "ENABLED" - managed_scaling { - status = "ENABLED" - target_capacity = 100 - } - } -} - -# Capacity provider association -resource "aws_ecs_cluster_capacity_providers" "ecs_cluster_capacity_providers" { - cluster_name = aws_ecs_cluster.ecs_cluster.name - capacity_providers = [aws_ecs_capacity_provider.ecs_capacity_provider.name] - default_capacity_provider_strategy { - capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name - weight = 100 - base = 1 - } -} - -# ECS Task Definition -resource "aws_ecs_task_definition" "ecs_task_definition" { - family = "${var.project_name}-ecs-task" - network_mode = "bridge" - requires_compatibilities = ["EC2"] - - container_definitions = jsonencode([ - { - name = "${var.project_name}-container" - image = "${aws_ecr_repository.app.repository_url}:latest" - cpu = 256 - memory = 512 - essential = true - portMappings = [ - { - containerPort = 80 - hostPort = 80 - protocol = "tcp" - } - ] - } - ]) -} - -# ECS Service -resource "aws_ecs_service" "ecs_service" { - name = "${var.project_name}-ecs-service" - cluster = aws_ecs_cluster.ecs_cluster.id - task_definition = aws_ecs_task_definition.ecs_task_definition.arn - desired_count = 2 - - - capacity_provider_strategy { - capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name - weight = 100 - } - - load_balancer { - target_group_arn = aws_lb_target_group.blue.arn - container_name = "${var.project_name}-container" - container_port = 80 - } - - deployment_controller { - type = "CODE_DEPLOY" - } - - lifecycle { - ignore_changes = [task_definition, desired_count] - } - - health_check_grace_period_seconds = 60 - - tags = { - Name = "${var.project_name}-ecs-service" - } -} - diff --git a/iam.tf b/iam.tf deleted file mode 100644 index a693a4f..0000000 --- a/iam.tf +++ /dev/null @@ -1,81 +0,0 @@ -# ECS 인스턴스가 사용할 IAM 역할 생성 -resource "aws_iam_role" "ecs_instance_role" { - name = "${var.project_name}-ecs-instance-role" - - # 이 역할을 EC2 인스턴스가 사용할 수 있도록 신뢰 정책 설정 - assume_role_policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - { - Action = "sts:AssumeRole", - Effect = "Allow", - Principal = { - Service = "ec2.amazonaws.com" - } - } - ] - }) - - tags = { - Name = "${var.project_name}-ecs-instance-role" - } -} - -# AWS에서 관리하는 정책을 위에서 만든 역할에 연결 -resource "aws_iam_role_policy_attachment" "ecs_instance_role_attachment" { - role = aws_iam_role.ecs_instance_role.name - policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" -} - -# EC2 인스턴스에 역할을 부여하기 위한 인스턴스 프로파일 생성 -resource "aws_iam_instance_profile" "ecs_instance_profile" { - name = "${var.project_name}-ecs-instance-profile" - role = aws_iam_role.ecs_instance_role.name -} - - - -# ECS 작업 실행 역할 -resource "aws_iam_role" "ecs_task_execution_role" { - name = "${var.project_name}-ecs-task-execution-role" - assume_role_policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - { - Action = "sts:AssumeRole", - Effect = "Allow", - Principal = { - Service = "ecs-tasks.amazonaws.com" - } - } - ] - }) -} -resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_attachment" { - role = aws_iam_role.ecs_task_execution_role.name - policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" -} - - - -# CodeDeploy를 위한 IAM 역할 -resource "aws_iam_role" "codedeploy_role" { - name = "${var.project_name}-codedeploy-role" - assume_role_policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - { - Action = "sts:AssumeRole", - Effect = "Allow", - Principal = { - Service = "codedeploy.amazonaws.com" - } - } - ] - }) -} -resource "aws_iam_role_policy_attachment" "codedeploy_role_attachment" { - role = aws_iam_role.codedeploy_role.name - policy_arn = "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS" -} - diff --git a/main.tf b/main.tf deleted file mode 100644 index be2277c..0000000 --- a/main.tf +++ /dev/null @@ -1,89 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - } - } -} - -provider "aws" { - region = "ap-northeast-2" -} - - -# S3 버킷 생성 -resource "aws_s3_bucket" "tfstate_app" { - bucket = "cloudfence-tfstate-app" - - lifecycle { - prevent_destroy = true - } - - tags = { - Name = "Terraform State Bucket" - Environment = "application-deployment" - } -} - -# 버전 관리 활성화 -resource "aws_s3_bucket_versioning" "tfstate_app_versioning" { - bucket = aws_s3_bucket.tfstate_app.id - - versioning_configuration { - status = "Enabled" - } -} - -# 객체 소유권 충돌 방지 -resource "aws_s3_bucket_ownership_controls" "ownership" { - bucket = aws_s3_bucket.tfstate_app.id - - rule { - object_ownership = "ObjectWriter" - } -} - -# 퍼블릭 접근 차단 -resource "aws_s3_bucket_public_access_block" "tfstate_app_block" { - bucket = aws_s3_bucket.tfstate_app.id - block_public_acls = true - block_public_policy = true - ignore_public_acls = true - restrict_public_buckets = true -} - -# 기본 암호화 설정 -resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { - bucket = aws_s3_bucket.tfstate_app.id - - rule { - apply_server_side_encryption_by_default { - sse_algorithm = "AES256" - } - } -} - -# DynamoDB 테이블 생성 -resource "aws_dynamodb_table" "lock_app" { - name = "tfstate-lock-app" - billing_mode = "PAY_PER_REQUEST" - hash_key = "LockID" - - attribute { - name = "LockID" - type = "S" - } - - server_side_encryption { - enabled = true - } - - tags = { - Name = "Terraform Lock Table" - Environment = "application-deployment" - } -} - -} - - diff --git a/network.tf b/network.tf deleted file mode 100644 index 0f7fbc9..0000000 --- a/network.tf +++ /dev/null @@ -1,121 +0,0 @@ -# VPC -resource "aws_vpc" "vpc" { - cidr_block = var.vpc_cidr - enable_dns_support = true - enable_dns_hostnames = true - tags = { - Name = "${var.project_name}-vpc" - } -} - -# subnet(public) -resource "aws_subnet" "public1" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.1.0/24" - availability_zone = "ap-northeast-2a" - map_public_ip_on_launch = true - tags = { - Name = "public_subnet1" - } -} - -resource "aws_subnet" "public2" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.2.0/24" - availability_zone = "ap-northeast-2b" - map_public_ip_on_launch = true - tags = { - Name = "public_subnet2" - } -} - -# subnet(private) - -resource "aws_subnet" "private1" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.101.0/24" - availability_zone = "ap-northeast-2a" - tags = { - Name = "private_subnet1" - } -} - -resource "aws_subnet" "private2" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.102.0/24" - availability_zone = "ap-northeast-2b" - tags = { - Name = "private_subnet2" - } -} - -# Internet Gateway -resource "aws_internet_gateway" "igw" { - vpc_id = aws_vpc.vpc.id - tags = { - Name = "${var.project_name}-igw" - } -} - -# Public Route Table -resource "aws_route_table" "public" { - vpc_id = aws_vpc.vpc.id - route { - cidr_block = "0.0.0.0/0" - gateway_id = aws_internet_gateway.igw.id - } - tags = { - Name = "${var.project_name}-public-route-table" - } -} - -# Associate Public Subnets with Route Table -resource "aws_route_table_association" "public1" { - subnet_id = aws_subnet.public1.id - route_table_id = aws_route_table.public.id -} - -resource "aws_route_table_association" "public2" { - subnet_id = aws_subnet.public2.id - route_table_id = aws_route_table.public.id -} - -# NAT Gateway -resource "aws_eip" "nat" { - domain = "vpc" - tags = { - Name = "${var.project_name}-nat-eip" - } -} - -resource "aws_nat_gateway" "nat" { - allocation_id = aws_eip.nat.id - subnet_id = aws_subnet.public1.id - tags = { - Name = "${var.project_name}-nat-gateway" - } - depends_on = [aws_internet_gateway.igw] -} - -# Private Route Table -resource "aws_route_table" "private" { - vpc_id = aws_vpc.vpc.id - route { - cidr_block = "0.0.0.0/0" - nat_gateway_id = aws_nat_gateway.nat.id - } - tags = { - Name = "${var.project_name}-private-route-table" - } -} - -# Associate Private Subnets with Route Table -resource "aws_route_table_association" "private1" { - subnet_id = aws_subnet.private1.id - route_table_id = aws_route_table.private.id -} - -resource "aws_route_table_association" "private2" { - subnet_id = aws_subnet.private2.id - route_table_id = aws_route_table.private.id -} \ No newline at end of file diff --git a/output.tf b/output.tf deleted file mode 100644 index 79e62d3..0000000 --- a/output.tf +++ /dev/null @@ -1,20 +0,0 @@ -# outputs.tf -output "vpc_id" { - description = "생성된 VPC의 ID" - value = aws_vpc.vpc.id -} - -output "public_subnet_ids" { - description = "생성된 Public Subnet들의 ID 목록" - value = [aws_subnet.public1.id, aws_subnet.public2.id] -} - -output "private_subnet_ids" { - description = "생성된 Private Subnet들의 ID 목록" - value = [aws_subnet.private1.id, aws_subnet.private2.id] -} - -output "alb_dns_name" { - description = "ALB의 DNS 이름" - value = aws_lb.alb.dns_name -} \ No newline at end of file diff --git a/security_group.tf b/security_group.tf deleted file mode 100644 index ff9059b..0000000 --- a/security_group.tf +++ /dev/null @@ -1,44 +0,0 @@ -# ALB -resource "aws_security_group" "alb_sg" { - name = "${var.project_name}-alb-sg" - description = "Security group for ALB" - vpc_id = aws_vpc.vpc.id - - ingress { - from_port = 80 - to_port = 80 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - description = "Allow HTTP" - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - -} - - # ECS - resource "aws_security_group" "ecs_sg" { - name = "${var.project_name}-ecs-sg" - description = "Security group for ECS tasks" - vpc_id = aws_vpc.vpc.id - - ingress { - from_port = 80 - to_port = 80 - protocol = "tcp" - security_groups = [aws_security_group.alb_sg.id] - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - -} diff --git a/variables.tf b/variables.tf deleted file mode 100644 index 6fb2432..0000000 --- a/variables.tf +++ /dev/null @@ -1,24 +0,0 @@ -variable "project_name" { - description = "프로젝트를 식별하는 이름" - type = string - default = "cloudfence" -} - -variable "vpc_cidr" { - description = "VPC에 할당할 IP 주소 범위" - type = string - default = "10.0.0.0/16" -} - -variable "custom_ami_id" { - description = "ECS 인스턴스에 사용할 사용자 정의 AMI ID" - type = string - # 중요: 반드시 본인의 리전에 맞는 최신 ECS 최적화 AMI ID로 변경하세요. - default = "ami-0c55b159cbfafe1f0" -} - -variable "instance_type" { - description = "ECS 인스턴스의 타입" - type = string - default = "t3.micro" -} \ No newline at end of file From e260dad5318da0bdbeb1a2984f58458fa827d5c6 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Sun, 6 Jul 2025 21:35:23 +0900 Subject: [PATCH 12/66] =?UTF-8?q?CI/CD=20.tf=EC=97=86=EB=8A=94=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=20=EC=8B=A4=ED=96=89=20X?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd.yml | 14 ++++++++++++-- .github/workflows/ci.yml | 22 ++++++++++++++++++---- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 0ea6e46..60ed96c 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -66,10 +66,20 @@ jobs: if [ "$VALUE" = "true" ]; then BASE_DIR="${KEY}-team-account" + + # 루트 디렉터리 검사 + TF_COUNT_ROOT=$(find "$BASE_DIR" -maxdepth 1 -name '*.tf' | wc -l) + if [ "$TF_COUNT_ROOT" -gt 0 ]; then + MATRIX_ITEMS+=("{\"dir\":\"$BASE_DIR\",\"role_key\":\"${ROLE_MAP[$KEY]}\"}") + fi + # 하위 디렉터리 검사 for DIR in $(find $BASE_DIR -type d -mindepth 1); do - if [[ "$DIR" != *".terraform"* ]]; then - MATRIX_ITEMS+=("{\"dir\":\"$DIR\",\"role_key\":\"${ROLE_MAP[$KEY]}\"}") + if [[ "$DIR" != *".terraform"* && "$DIR" != "$BASE_DIR/modules" ]]; then + TF_COUNT=$(find "$DIR" -maxdepth 1 -name '*.tf' | wc -l) + if [ "$TF_COUNT" -gt 0 ]; then + MATRIX_ITEMS+=("{\"dir\":\"$DIR\",\"role_key\":\"${ROLE_MAP[$KEY]}\"}") + fi fi done fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4d5c4a..ddd41c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Application CI +name: Application-DeploymentCI on: pull_request: @@ -7,7 +7,10 @@ on: - "operation-team-account/**" - "identity-team-account/**" - "prod-team-account/**" - + - "dev-team-account/**" + - "security-team-account/**" + - "stage-team-account/**" + - "management-team-account/**" permissions: contents: read @@ -48,13 +51,24 @@ jobs: TMP_FILE=$(mktemp) for FILE in $FILES; do - # ex) identity-team-account/S3/main.tf DIR=$(dirname "$FILE") TOP_DIR=$(echo $DIR | cut -d/ -f1) ROLE_KEY="${ROLE_MAP[$TOP_DIR]}" if [ -n "$ROLE_KEY" ]; then - echo "$DIR|$ROLE_KEY" >> $TMP_FILE + # 루트 디렉터리 + if [ "$DIR" == "$TOP_DIR" ]; then + TF_COUNT=$(find "$DIR" -maxdepth 1 -name '*.tf' | wc -l) + if [ "$TF_COUNT" -gt 0 ]; then + echo "$DIR|$ROLE_KEY" >> $TMP_FILE + fi + else + # 하위 디렉터리 + TF_COUNT=$(find "$DIR" -maxdepth 1 -name '*.tf' | wc -l) + if [ "$TF_COUNT" -gt 0 ]; then + echo "$DIR|$ROLE_KEY" >> $TMP_FILE + fi + fi fi done From 588676ab80f8756b259fbec914aaa9c46d859a4c Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Sun, 6 Jul 2025 21:43:16 +0900 Subject: [PATCH 13/66] CI/CD Test CI/CD Test CI/CD Test --- .github/workflows/deploy.yml | 71 ----------------- .github/workflows/terraform-apply.yml | 79 ------------------- operation-team-account/main.tf | 1 + operation-team-account/modules/ecr/ecr.tf | 1 + operation-team-account/modules/iam/iam.tf | 1 + prod-team-account/data.tf | 1 + prod-team-account/main.tf | 1 + prod-team-account/modules/alb/alb.tf | 1 + .../modules/codedeploy/codedeploy.tf | 1 + prod-team-account/modules/ecs/ecs.tf | 1 + prod-team-account/modules/iam/iam.tf | 1 + prod-team-account/modules/vpc/network.tf | 1 + .../modules/vpc/security_group.tf | 1 + 13 files changed, 11 insertions(+), 150 deletions(-) delete mode 100644 .github/workflows/deploy.yml delete mode 100644 .github/workflows/terraform-apply.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index d92de3d..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Deploy Application to ECS - -on: - push: - branches: [ "main" ] - -env: - AWS_REGION: ap-northeast-2 - ECR_REPOSITORY: ${{ secrets.ECR_REPOSITORY_NAME }} - ECS_CLUSTER_NAME: "ecs-cluster" - ECS_SERVICE_NAME: "ecs-service" - CODEDEPLOY_APP_NAME: "ecs-app" - CODEDEPLOY_DEPLOYMENT_GROUP: "ecs-deployment-group" - CONTAINER_NAME: "ecs-container" - -permissions: - id-token: write - contents: read - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }} - aws-region: ap-northeast-2 - - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2 - - - name: Build, tag, and push image to Amazon ECR - id: build-image - run: | - IMAGE_TAG=$(echo "${{ github.sha }}" | cut -c1-7) - docker build -t ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:$IMAGE_TAG . - docker push ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:$IMAGE_TAG - echo "image_uri=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:$IMAGE_TAG" >> $GITHUB_OUTPUT - - - name: Render Amazon ECS task definition - id: render-task-def - run: | - TASK_DEF_TEMPLATE=$(cat task-definition.template.json) - NEW_TASK_DEF_CONTENT=$(echo "$TASK_DEF_TEMPLATE" | sed "s||${{ steps.build-image.outputs.image_uri }}|") - echo "$NEW_TASK_DEF_CONTENT" > new-task-definition.json - echo "task_definition_path=$(pwd)/new-task-definition.json" >> $GITHUB_OUTPUT - - - name: Register new task definition - id: register-task-def - run: | - TASK_DEF_ARN=$(aws ecs register-task-definition --cli-input-json file://${{ steps.render-task-def.outputs.task_definition_path }} --query 'taskDefinition.taskDefinitionArn' --output text) - echo "task_definition_arn=$TASK_DEF_ARN" >> $GITHUB_OUTPUT - - - name: Render AppSpec - id: render-appspec - run: | - APPSPEC_TEMPLATE=$(cat appspec.template.yml) - NEW_APPSPEC_CONTENT=$(echo "$APPSPEC_TEMPLATE" | sed "s||${{ steps.register-task-def.outputs.task_definition_arn }}|") - echo "$NEW_APPSPEC_CONTENT" > appspec.yml - - - name: Start CodeDeploy Deployment - run: | - aws deploy create-deployment \ - --application-name ${{ env.CODEDEPLOY_APP_NAME }} \ - --deployment-group-name ${{ env.CODEDEPLOY_DEPLOYMENT_GROUP }} \ - --revision "revisionType=AppSpecContent,appSpecContent={content=`cat appspec.yml`}" \ No newline at end of file diff --git a/.github/workflows/terraform-apply.yml b/.github/workflows/terraform-apply.yml deleted file mode 100644 index b77621d..0000000 --- a/.github/workflows/terraform-apply.yml +++ /dev/null @@ -1,79 +0,0 @@ -name: Deploy Infrastructure with Terraform - -on: - push: - branches: [ "main" ] - paths: - - '**.tf' - -permissions: - id-token: write - contents: read - pull-requests: write - -jobs: - terraform-plan: - runs-on: ubuntu-latest - outputs: - tfplan_path: steps.plan.outputs.plan_path - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Terraform - uses: hashicorp/setup-terraform@v3 - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }} - aws-region: ap-northeast-2 - - - name: Terraform Init - id: init - run: terraform init - - - name: Terraform Plan - id: plan - run: terraform plan -out=tfplan.binary - - - name: Upload Plan Artifact - uses: actions/upload-artifact@v4 - with: - name: tfplan - path: tfplan.binary - - terraform-apply: - runs-on: ubuntu-latest - needs: terraform-plan # plan 잡이 성공해야 실행됨 - - environment: production - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Terraform - uses: hashicorp/setup-terraform@v3 - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }} - aws-region: ap-northeast-2 - - - name: Download Plan Artifact - uses: actions/download-artifact@v4 - with: - name: tfplan - - - name: Terraform Init - id: init - run: terraform init - - - name: Terraform Apply - id: apply - run: terraform apply -input=false tfplan.binary - - diff --git a/operation-team-account/main.tf b/operation-team-account/main.tf index c90d2b9..13cd3f7 100644 --- a/operation-team-account/main.tf +++ b/operation-team-account/main.tf @@ -1,3 +1,4 @@ +# CI/CD Test terraform { required_providers { aws = { diff --git a/operation-team-account/modules/ecr/ecr.tf b/operation-team-account/modules/ecr/ecr.tf index 8c84270..b376f40 100644 --- a/operation-team-account/modules/ecr/ecr.tf +++ b/operation-team-account/modules/ecr/ecr.tf @@ -1,3 +1,4 @@ +# CI/CD Test # iam.tf에서 만든 정책 JSON을 받을 변수 선언 variable "ecr_policy_json" { type = string diff --git a/operation-team-account/modules/iam/iam.tf b/operation-team-account/modules/iam/iam.tf index 21a671d..48a22dc 100644 --- a/operation-team-account/modules/iam/iam.tf +++ b/operation-team-account/modules/iam/iam.tf @@ -1,3 +1,4 @@ +# CI/CD Test # prod-team-account의 역할 ARN을 변수로 받기 variable "prod_github_actions_role_arn" { type = string diff --git a/prod-team-account/data.tf b/prod-team-account/data.tf index cc645c1..86a55ea 100644 --- a/prod-team-account/data.tf +++ b/prod-team-account/data.tf @@ -1,3 +1,4 @@ +# CI/CD Test data "terraform_remote_state" "operation_account" { backend = "s3" # operation-team-account의 state가 저장된 백엔드 config = { diff --git a/prod-team-account/main.tf b/prod-team-account/main.tf index 5c51786..5047eba 100644 --- a/prod-team-account/main.tf +++ b/prod-team-account/main.tf @@ -1,3 +1,4 @@ +# CI/CD Test terraform { required_providers { aws = { diff --git a/prod-team-account/modules/alb/alb.tf b/prod-team-account/modules/alb/alb.tf index 1ddbc83..0bfbb01 100644 --- a/prod-team-account/modules/alb/alb.tf +++ b/prod-team-account/modules/alb/alb.tf @@ -1,3 +1,4 @@ +# CI/CD Test # WAF resource "aws_wafv2_web_acl" "alb_waf" { name = "${var.project_name}-alb-waf" diff --git a/prod-team-account/modules/codedeploy/codedeploy.tf b/prod-team-account/modules/codedeploy/codedeploy.tf index a85c806..76c7ce1 100644 --- a/prod-team-account/modules/codedeploy/codedeploy.tf +++ b/prod-team-account/modules/codedeploy/codedeploy.tf @@ -1,3 +1,4 @@ +# CI/CD Test # CodeDeploy resource "aws_codedeploy_app" "ecs_app" { name = "${var.project_name}-ecs-app" diff --git a/prod-team-account/modules/ecs/ecs.tf b/prod-team-account/modules/ecs/ecs.tf index 6bb7945..7967213 100644 --- a/prod-team-account/modules/ecs/ecs.tf +++ b/prod-team-account/modules/ecs/ecs.tf @@ -1,3 +1,4 @@ +# CI/CD Test # iam에서 생성한 ARN 변수 전달 variable "ecs_task_execution_role_arn" { type = string diff --git a/prod-team-account/modules/iam/iam.tf b/prod-team-account/modules/iam/iam.tf index 7cf0a4c..8bcda19 100644 --- a/prod-team-account/modules/iam/iam.tf +++ b/prod-team-account/modules/iam/iam.tf @@ -1,3 +1,4 @@ +# CI/CD Test # ECS 인스턴스가 사용할 IAM 역할 생성 resource "aws_iam_role" "ecs_instance_role" { name = "${var.project_name}-ecs-instance-role" diff --git a/prod-team-account/modules/vpc/network.tf b/prod-team-account/modules/vpc/network.tf index 0f7fbc9..c3b50cf 100644 --- a/prod-team-account/modules/vpc/network.tf +++ b/prod-team-account/modules/vpc/network.tf @@ -1,3 +1,4 @@ +# CI/CD Test # VPC resource "aws_vpc" "vpc" { cidr_block = var.vpc_cidr diff --git a/prod-team-account/modules/vpc/security_group.tf b/prod-team-account/modules/vpc/security_group.tf index fc6e37c..cf0e89a 100644 --- a/prod-team-account/modules/vpc/security_group.tf +++ b/prod-team-account/modules/vpc/security_group.tf @@ -1,3 +1,4 @@ +# CI/CD Test # ALB resource "aws_security_group" "alb_sg" { name = "${var.project_name}-alb-sg" From 53197daf3c8f08d822a1379a7ffd3f73325266b7 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 7 Jul 2025 03:30:56 +0900 Subject: [PATCH 14/66] =?UTF-8?q?=ED=8F=B4=EB=8D=94=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=20=EA=B0=9C=ED=8E=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 폴더 구조 개편 S3 버킷 수정 output 중복 제거 --- operation-team-account/backend.tf | 9 -- operation-team-account/ecr/backend.tf | 9 ++ operation-team-account/ecr/main.tf | 54 +++++++++++ .../{modules => }/ecr/outputs.tf | 0 operation-team-account/ecr/variables.tf | 10 ++ operation-team-account/iam/backend.tf | 9 ++ .../{modules/iam/iam.tf => iam/main.tf} | 0 .../{modules => }/iam/outputs.tf | 0 operation-team-account/main.tf | 30 ------ operation-team-account/modules/ecr/ecr.tf | 22 ----- .../modules/ecr/variables.tf | 10 -- operation-team-account/variables.tf | 5 - prod-team-account/alb/backend.tf | 9 ++ .../{modules/alb/alb.tf => alb/main.tf} | 33 ++++++- .../{modules => }/alb/outputs.tf | 5 + prod-team-account/alb/variables.tf | 4 + prod-team-account/backend.tf | 9 -- prod-team-account/codedeploy/backend.tf | 9 ++ prod-team-account/codedeploy/main.tf | 93 +++++++++++++++++++ .../{modules => }/codedeploy/outputs.tf | 0 .../{modules => }/codedeploy/variables.tf | 0 prod-team-account/data.tf | 18 ---- prod-team-account/ecs/backend.tf | 9 ++ .../{modules/ecs/ecs.tf => ecs/main.tf} | 82 ++++++++++++++-- .../{modules => }/ecs/outputs.tf | 0 prod-team-account/ecs/variables.tf | 10 ++ prod-team-account/iam/backend.tf | 9 ++ .../{modules/iam/iam.tf => iam/main.tf} | 22 +++-- .../{modules => }/iam/outputs.tf | 0 prod-team-account/iam/variables.tf | 4 + prod-team-account/main.tf | 67 ------------- prod-team-account/modules/alb/variables.tf | 19 ---- .../modules/codedeploy/codedeploy.tf | 52 ----------- prod-team-account/modules/ecs/variables.tf | 43 --------- .../modules/vpc/security_group.tf | 45 --------- prod-team-account/outputs.tf | 19 ---- prod-team-account/variables.tf | 23 ----- prod-team-account/vpc/backend.tf | 9 ++ .../{modules/vpc/network.tf => vpc/main.tf} | 59 ++++++++++++ .../{modules => }/vpc/outputs.tf | 0 .../{modules => }/vpc/variables.tf | 0 41 files changed, 417 insertions(+), 393 deletions(-) delete mode 100644 operation-team-account/backend.tf create mode 100644 operation-team-account/ecr/backend.tf create mode 100644 operation-team-account/ecr/main.tf rename operation-team-account/{modules => }/ecr/outputs.tf (100%) create mode 100644 operation-team-account/ecr/variables.tf create mode 100644 operation-team-account/iam/backend.tf rename operation-team-account/{modules/iam/iam.tf => iam/main.tf} (100%) rename operation-team-account/{modules => }/iam/outputs.tf (100%) delete mode 100644 operation-team-account/main.tf delete mode 100644 operation-team-account/modules/ecr/ecr.tf delete mode 100644 operation-team-account/modules/ecr/variables.tf delete mode 100644 operation-team-account/variables.tf create mode 100644 prod-team-account/alb/backend.tf rename prod-team-account/{modules/alb/alb.tf => alb/main.tf} (76%) rename prod-team-account/{modules => }/alb/outputs.tf (78%) create mode 100644 prod-team-account/alb/variables.tf delete mode 100644 prod-team-account/backend.tf create mode 100644 prod-team-account/codedeploy/backend.tf create mode 100644 prod-team-account/codedeploy/main.tf rename prod-team-account/{modules => }/codedeploy/outputs.tf (100%) rename prod-team-account/{modules => }/codedeploy/variables.tf (100%) delete mode 100644 prod-team-account/data.tf create mode 100644 prod-team-account/ecs/backend.tf rename prod-team-account/{modules/ecs/ecs.tf => ecs/main.tf} (64%) rename prod-team-account/{modules => }/ecs/outputs.tf (100%) create mode 100644 prod-team-account/ecs/variables.tf create mode 100644 prod-team-account/iam/backend.tf rename prod-team-account/{modules/iam/iam.tf => iam/main.tf} (89%) rename prod-team-account/{modules => }/iam/outputs.tf (100%) create mode 100644 prod-team-account/iam/variables.tf delete mode 100644 prod-team-account/main.tf delete mode 100644 prod-team-account/modules/alb/variables.tf delete mode 100644 prod-team-account/modules/codedeploy/codedeploy.tf delete mode 100644 prod-team-account/modules/ecs/variables.tf delete mode 100644 prod-team-account/modules/vpc/security_group.tf delete mode 100644 prod-team-account/outputs.tf delete mode 100644 prod-team-account/variables.tf create mode 100644 prod-team-account/vpc/backend.tf rename prod-team-account/{modules/vpc/network.tf => vpc/main.tf} (72%) rename prod-team-account/{modules => }/vpc/outputs.tf (100%) rename prod-team-account/{modules => }/vpc/variables.tf (100%) diff --git a/operation-team-account/backend.tf b/operation-team-account/backend.tf deleted file mode 100644 index f55e9e3..0000000 --- a/operation-team-account/backend.tf +++ /dev/null @@ -1,9 +0,0 @@ -terraform { - backend "s3" { - bucket = "cloudfence-tfstate-app" - key = "prod-team/terraform.tfstate" # prod 전용 경로 - region = "ap-northeast-2" - dynamodb_table = "tfstate-lock-app" - encrypt = true - } -} \ No newline at end of file diff --git a/operation-team-account/ecr/backend.tf b/operation-team-account/ecr/backend.tf new file mode 100644 index 0000000..11bb7aa --- /dev/null +++ b/operation-team-account/ecr/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-operation-state" + key = "operation-team-account/ecr/terraform.tfstate" + region = "ap-northeast-2" + dynamodb_table = "s3-operation-lock" + encrypt = true + } +} \ No newline at end of file diff --git a/operation-team-account/ecr/main.tf b/operation-team-account/ecr/main.tf new file mode 100644 index 0000000..0722bc2 --- /dev/null +++ b/operation-team-account/ecr/main.tf @@ -0,0 +1,54 @@ +# CI/CD Test +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} + +# operation-team-account의 ECR 리포지토리 생성 및 정책 설정 +data "aws_iam_policy_document" "ecr_repo_policy_document" { + statement { + sid = "AllowCrossAccountPush" + effect = "Allow" + principals { + type = "AWS" + # prod 계정의 역할 ARN은 변수로 전달 + identifiers = [var.prod_github_actions_role_arn] + } + actions = [ + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + "ecr:BatchCheckLayerAvailability", + "ecr:PutImage", + "ecr:InitiateLayerUpload", + "ecr:UploadLayerPart", + "ecr:CompleteLayerUpload", + "ecr:GetAuthorizationToken" + ] + } +} + + +# ECR 리포지토리 생성 +resource "aws_ecr_repository" "app_ecr_repo" { + name = var.project_name + image_tag_mutability = "IMMUTABLE" + + image_scanning_configuration { + scan_on_push = true + } +} + +# 정책을 리포지토리에 연결 +resource "aws_ecr_repository_policy" "app_ecr_repo_policy" { + repository = aws_ecr_repository.app_ecr_repo.name + policy = data.aws_iam_policy_document.ecr_repo_policy_document.json +} \ No newline at end of file diff --git a/operation-team-account/modules/ecr/outputs.tf b/operation-team-account/ecr/outputs.tf similarity index 100% rename from operation-team-account/modules/ecr/outputs.tf rename to operation-team-account/ecr/outputs.tf diff --git a/operation-team-account/ecr/variables.tf b/operation-team-account/ecr/variables.tf new file mode 100644 index 0000000..0666894 --- /dev/null +++ b/operation-team-account/ecr/variables.tf @@ -0,0 +1,10 @@ +variable "project_name" { + description = "The name of the project" + type = string +} + +variable "prod_github_actions_role_arn" { + description = "The ARN of the IAM role for GitHub Actions in prod-team-account to grant push access" + type = string + sensitive = true +} \ No newline at end of file diff --git a/operation-team-account/iam/backend.tf b/operation-team-account/iam/backend.tf new file mode 100644 index 0000000..7d140de --- /dev/null +++ b/operation-team-account/iam/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-operation-state" + key = "operation-team-account/iam/terraform.tfstate" + region = "ap-northeast-2" + dynamodb_table = "s3-operation-lock" + encrypt = true + } +} \ No newline at end of file diff --git a/operation-team-account/modules/iam/iam.tf b/operation-team-account/iam/main.tf similarity index 100% rename from operation-team-account/modules/iam/iam.tf rename to operation-team-account/iam/main.tf diff --git a/operation-team-account/modules/iam/outputs.tf b/operation-team-account/iam/outputs.tf similarity index 100% rename from operation-team-account/modules/iam/outputs.tf rename to operation-team-account/iam/outputs.tf diff --git a/operation-team-account/main.tf b/operation-team-account/main.tf deleted file mode 100644 index 13cd3f7..0000000 --- a/operation-team-account/main.tf +++ /dev/null @@ -1,30 +0,0 @@ -# CI/CD Test -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.0" - } - } - -} - -provider "aws" { - region = "ap-northeast-2" -} - -module "iam" { - source = "./modules/iam" - prod_github_actions_role_arn = var.prod_github_actions_role_arn -} - -module "ecr" { - source = "./modules/ecr" - project_name = var.project_name - ecr_policy_json = module.iam.ecr_policy_json -} - -output "aws_ecr_repository_url" { - description = "The URL of the ECR repository" - value = module.ecr.ecr_repository_url -} \ No newline at end of file diff --git a/operation-team-account/modules/ecr/ecr.tf b/operation-team-account/modules/ecr/ecr.tf deleted file mode 100644 index b376f40..0000000 --- a/operation-team-account/modules/ecr/ecr.tf +++ /dev/null @@ -1,22 +0,0 @@ -# CI/CD Test -# iam.tf에서 만든 정책 JSON을 받을 변수 선언 -variable "ecr_policy_json" { - type = string - description = "The JSON policy to attach to the ECR repository" -} - -# ECR 리포지토리 생성 -resource "aws_ecr_repository" "app_ecr_repo" { - name = var.project_name - image_tag_mutability = "IMMUTABLE" - - image_scanning_configuration { - scan_on_push = true - } -} - -# 정책을 리포지토리에 연결 -resource "aws_ecr_repository_policy" "app_ecr_repo_policy" { - repository = aws_ecr_repository.app_ecr_repo.name - policy = var.ecr_policy_json # 변수로 받은 정책을 사용 -} \ No newline at end of file diff --git a/operation-team-account/modules/ecr/variables.tf b/operation-team-account/modules/ecr/variables.tf deleted file mode 100644 index 091424e..0000000 --- a/operation-team-account/modules/ecr/variables.tf +++ /dev/null @@ -1,10 +0,0 @@ -variable "project_name" { - description = "The name of the project to use for the ECR repository name" - type = string -} - -variable "ecr_policy_json" { - description = "The JSON policy document from the IAM module to attach to the ECR repository" - type = string - sensitive = true -} \ No newline at end of file diff --git a/operation-team-account/variables.tf b/operation-team-account/variables.tf deleted file mode 100644 index 0a9280f..0000000 --- a/operation-team-account/variables.tf +++ /dev/null @@ -1,5 +0,0 @@ -variable "project_name" { - description = "프로젝트를 식별하는 이름" - type = string - default = "cloudfence" -} \ No newline at end of file diff --git a/prod-team-account/alb/backend.tf b/prod-team-account/alb/backend.tf new file mode 100644 index 0000000..db6c068 --- /dev/null +++ b/prod-team-account/alb/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-prod-state" + key = "prod-team-account/alb/terraform.tfstate" + region = "ap-northeast-2" + dynamodb_table = "s3-prod-lock" + encrypt = true + } +} \ No newline at end of file diff --git a/prod-team-account/modules/alb/alb.tf b/prod-team-account/alb/main.tf similarity index 76% rename from prod-team-account/modules/alb/alb.tf rename to prod-team-account/alb/main.tf index 0bfbb01..418275e 100644 --- a/prod-team-account/modules/alb/alb.tf +++ b/prod-team-account/alb/main.tf @@ -1,4 +1,29 @@ # CI/CD Test +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} + +data "terraform_remote_state" "vpc" { + backend = "s3" + config = { + bucket = "cloudfence-tfstate-app" + key = "prod-team-account/vpc/terraform.tfstate" + region = "ap-northeast-2" + dynamodb_table = "tfstate-lock-app" + encrypt = true + } +} + # WAF resource "aws_wafv2_web_acl" "alb_waf" { name = "${var.project_name}-alb-waf" @@ -44,8 +69,8 @@ resource "aws_lb" "alb" { name = "${var.project_name}-alb" internal = false load_balancer_type = "application" - security_groups = [var.alb_security_group_id] - subnets = var.public_subnet_ids + security_groups = [data.terraform_remote_state.vpc.outputs.alb_security_group_id] + subnets = data.terraform_remote_state.vpc.outputs.public_subnet_ids enable_deletion_protection = true @@ -59,7 +84,7 @@ resource "aws_lb_target_group" "blue" { name = "${var.project_name}-blue-tg" port = 80 protocol = "HTTP" - vpc_id = var.vpc_id + vpc_id = data.terraform_remote_state.vpc.outputs.vpc_id target_type = "instance" health_check { path = "/" @@ -78,7 +103,7 @@ resource "aws_lb_target_group" "green" { name = "${var.project_name}-green-tg" port = 80 protocol = "HTTP" - vpc_id = var.vpc_id + vpc_id = data.terraform_remote_state.vpc.outputs.vpc_id target_type = "instance" health_check { path = "/" diff --git a/prod-team-account/modules/alb/outputs.tf b/prod-team-account/alb/outputs.tf similarity index 78% rename from prod-team-account/modules/alb/outputs.tf rename to prod-team-account/alb/outputs.tf index 35c0df4..6f12284 100644 --- a/prod-team-account/modules/alb/outputs.tf +++ b/prod-team-account/alb/outputs.tf @@ -16,4 +16,9 @@ output "blue_target_group_name" { output "green_target_group_name" { description = "The name of the green target group" value = aws_lb_target_group.green.name +} + +output "blue_target_group_arn" { + description = "The ARN of the blue target group" + value = aws_lb_target_group.blue.arn } \ No newline at end of file diff --git a/prod-team-account/alb/variables.tf b/prod-team-account/alb/variables.tf new file mode 100644 index 0000000..d6d5c96 --- /dev/null +++ b/prod-team-account/alb/variables.tf @@ -0,0 +1,4 @@ +variable "project_name" { + description = "The name of the project" + type = string +} \ No newline at end of file diff --git a/prod-team-account/backend.tf b/prod-team-account/backend.tf deleted file mode 100644 index d26e06f..0000000 --- a/prod-team-account/backend.tf +++ /dev/null @@ -1,9 +0,0 @@ -terraform { - backend "s3" { - bucket = "cloudfence-tfstate-app" - key = "prod-team/terraform.tfstate" - region = "ap-northeast-2" - dynamodb_table = "tfstate-lock-app" - encrypt = true - } -} \ No newline at end of file diff --git a/prod-team-account/codedeploy/backend.tf b/prod-team-account/codedeploy/backend.tf new file mode 100644 index 0000000..da9681d --- /dev/null +++ b/prod-team-account/codedeploy/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-prod-state" + key = "prod-team-account/codedeploy/terraform.tfstate" + region = "ap-northeast-2" + dynamodb_table = "s3-prod-lock" + encrypt = true + } +} \ No newline at end of file diff --git a/prod-team-account/codedeploy/main.tf b/prod-team-account/codedeploy/main.tf new file mode 100644 index 0000000..e503be0 --- /dev/null +++ b/prod-team-account/codedeploy/main.tf @@ -0,0 +1,93 @@ +# CI/CD Test +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} + +data "terraform_remote_state" "alb" { + backend = "s3" + config = { + bucket = "cloudfence-tfstate-app" + key = "prod-team-account/alb/terraform.tfstate" + region = "ap-northeast-2" + } +} + +data "terraform_remote_state" "iam" { + backend = "s3" + config = { + bucket = "cloudfence-tfstate-app" + key = "prod-team-account/iam/terraform.tfstate" + region = "ap-northeast-2" + } +} + +data "terraform_remote_state" "ecs" { + backend = "s3" + config = { + bucket = "cloudfence-tfstate-app" + key = "prod-team-account/ecs/terraform.tfstate" + region = "ap-northeast-2" + } +} + +# CodeDeploy +resource "aws_codedeploy_app" "ecs_app" { + name = "${var.project_name}-ecs-app" + compute_platform = "ECS" +} + +resource "aws_codedeploy_deployment_group" "ecs_deployment_group" { + app_name = aws_codedeploy_app.ecs_app.name + deployment_group_name = "${var.project_name}-ecs-deployment-group" + service_role_arn = data.terraform_remote_state.iam.outputs.codedeploy_service_role_arn + + deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" + + ecs_service { + cluster_name = data.terraform_remote_state.ecs.outputs.ecs_cluster_name + service_name = data.terraform_remote_state.ecs.outputs.ecs_service_name + } + + deployment_style { + deployment_type = "BLUE_GREEN" + deployment_option = "WITH_TRAFFIC_CONTROL" + } + + blue_green_deployment_config { + deployment_ready_option { + action_on_timeout = "CONTINUE_DEPLOYMENT" + } + terminate_blue_instances_on_deployment_success { + action = "TERMINATE" + termination_wait_time_in_minutes = 5 + } + } + load_balancer_info { + target_group_pair_info { + target_group { + name = data.terraform_remote_state.alb.outputs.blue_target_group_name + } + target_group { + name = data.terraform_remote_state.alb.outputs.green_target_group_name + } + prod_traffic_route { + listener_arns = [data.terraform_remote_state.alb.outputs.listener_arn] + } + } + } + auto_rollback_configuration { + enabled = true + events = ["DEPLOYMENT_FAILURE"] + } + +} \ No newline at end of file diff --git a/prod-team-account/modules/codedeploy/outputs.tf b/prod-team-account/codedeploy/outputs.tf similarity index 100% rename from prod-team-account/modules/codedeploy/outputs.tf rename to prod-team-account/codedeploy/outputs.tf diff --git a/prod-team-account/modules/codedeploy/variables.tf b/prod-team-account/codedeploy/variables.tf similarity index 100% rename from prod-team-account/modules/codedeploy/variables.tf rename to prod-team-account/codedeploy/variables.tf diff --git a/prod-team-account/data.tf b/prod-team-account/data.tf deleted file mode 100644 index 86a55ea..0000000 --- a/prod-team-account/data.tf +++ /dev/null @@ -1,18 +0,0 @@ -# CI/CD Test -data "terraform_remote_state" "operation_account" { - backend = "s3" # operation-team-account의 state가 저장된 백엔드 - config = { - bucket = "cloudfence-tfstate-app" - key = "operation-team/terraform.tfstate" - region = "ap-northeast-2" - } -} - -data "golen_ami" "latest_shared_ami" { - most_recent = true - owners = [var.ami_owner_id] # operation-team-account의 AMI - filter { - name = "name" - values = ["WHS-CloudFence-*"] - } -} \ No newline at end of file diff --git a/prod-team-account/ecs/backend.tf b/prod-team-account/ecs/backend.tf new file mode 100644 index 0000000..a149188 --- /dev/null +++ b/prod-team-account/ecs/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-prod-state" + key = "prod-team-account/ecs/terraform.tfstate" + region = "ap-northeast-2" + dynamodb_table = "s3-prod-lock" + encrypt = true + } +} \ No newline at end of file diff --git a/prod-team-account/modules/ecs/ecs.tf b/prod-team-account/ecs/main.tf similarity index 64% rename from prod-team-account/modules/ecs/ecs.tf rename to prod-team-account/ecs/main.tf index 7967213..1b37b2a 100644 --- a/prod-team-account/modules/ecs/ecs.tf +++ b/prod-team-account/ecs/main.tf @@ -1,8 +1,70 @@ # CI/CD Test -# iam에서 생성한 ARN 변수 전달 -variable "ecs_task_execution_role_arn" { - type = string - description = "The ARN of the ECS Task Execution Role" +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} + +data "terraform_remote_state" "vpc" { + backend = "s3" + config = { + bucket = "cloudfence-tfstate-app" + key = "prod-team-account/vpc/terraform.tfstate" + region = "ap-northeast-2" + } +} + +data "terraform_remote_state" "alb" { + backend = "s3" + config = { + bucket = "cloudfence-tfstate-app" + key = "prod-team-account/alb/terraform.tfstate" + region = "ap-northeast-2" + } +} + +data "terraform_remote_state" "iam" { + backend = "s3" + config = { + bucket = "cloudfence-tfstate-app" + key = "prod-team-account/iam/terraform.tfstate" + region = "ap-northeast-2" + } +} + +data "terraform_remote_state" "ecs" { + backend = "s3" + config = { + bucket = "cloudfence-tfstate-app" + key = "prod-team-account/ecs/terraform.tfstate" + region = "ap-northeast-2" + } +} + +data "aws_ami" "latest_shared_ami" { + most_recent = true + owners = [var.ami_owner_id] # operation-team-account의 AMI + filter { + name = "name" + values = ["WHS-CloudFence-*"] + } +} + +data "terraform_remote_state" "ecr" { + backend = "s3" + config = { + bucket = "cloudfence-tfstate-app" + key = "operation-team-account/ecr/terraform.tfstate" + region = "ap-northeast-2" + } } # ECS 클러스터 생성 @@ -17,11 +79,11 @@ resource "aws_launch_template" "ecs_launch_template" { instance_type = "t2.micro" iam_instance_profile { - name = aws_iam_instance_profile.ecs_instance_profile.name + name = data.terraform_remote_state.iam.outputs.ecs_instance_profile_name } network_interfaces { associate_public_ip_address = false - security_groups = [var.ecs_security_group_id] + security_groups = [data.terraform_remote_state.vpc.outputs.ecs_security_group_id] } user_data = base64encode(<<-EOF @@ -45,7 +107,7 @@ resource "aws_autoscaling_group" "ecs_auto_scaling_group" { min_size = 1 max_size = 4 desired_capacity = 2 - vpc_zone_identifier = [aws_subnet.private1.id, aws_subnet.private2.id] + vpc_zone_identifier = [for subnet in data.terraform_remote_state.vpc.outputs.private_subnet_ids : subnet] health_check_type = "EC2" force_delete = true protect_from_scale_in = true @@ -87,12 +149,12 @@ resource "aws_ecs_task_definition" "ecs_task_definition" { family = "${var.project_name}-ecs-task" network_mode = "bridge" requires_compatibilities = ["EC2"] - execution_role_arn = var.ecs_task_execution_role_arn + execution_role_arn = data.terraform_remote_state.iam.outputs.ecs_task_execution_role_arn container_definitions = jsonencode([ { name = "${var.project_name}-container" - image = "${var.ecr_image_url}:latest" + image = "${data.terraform_remote_state.ecr.outputs.image_url}:latest" cpu = 256 memory = 512 essential = true @@ -121,7 +183,7 @@ resource "aws_ecs_service" "ecs_service" { } load_balancer { - target_group_arn = var.blue_target_group_arn + target_group_arn = data.terraform_remote_state.alb.outputs.blue_target_group_arn container_name = "${var.project_name}-container" container_port = 80 } diff --git a/prod-team-account/modules/ecs/outputs.tf b/prod-team-account/ecs/outputs.tf similarity index 100% rename from prod-team-account/modules/ecs/outputs.tf rename to prod-team-account/ecs/outputs.tf diff --git a/prod-team-account/ecs/variables.tf b/prod-team-account/ecs/variables.tf new file mode 100644 index 0000000..dd3c33a --- /dev/null +++ b/prod-team-account/ecs/variables.tf @@ -0,0 +1,10 @@ +variable "project_name" { + description = "The name of the project for resource naming" + type = string +} + +variable "ami_owner_account_id" { + description = "The AWS Account ID of the account that owns the shared AMI" + type = string + default = "502676416967" # operation-team-account +} \ No newline at end of file diff --git a/prod-team-account/iam/backend.tf b/prod-team-account/iam/backend.tf new file mode 100644 index 0000000..6c16478 --- /dev/null +++ b/prod-team-account/iam/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-prod-state" + key = "prod-team-account/iam/terraform.tfstate" + region = "ap-northeast-2" + dynamodb_table = "s3-prod-lock" + encrypt = true + } +} \ No newline at end of file diff --git a/prod-team-account/modules/iam/iam.tf b/prod-team-account/iam/main.tf similarity index 89% rename from prod-team-account/modules/iam/iam.tf rename to prod-team-account/iam/main.tf index 8bcda19..3d42d4f 100644 --- a/prod-team-account/modules/iam/iam.tf +++ b/prod-team-account/iam/main.tf @@ -1,4 +1,18 @@ # CI/CD Test +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} + # ECS 인스턴스가 사용할 IAM 역할 생성 resource "aws_iam_role" "ecs_instance_role" { name = "${var.project_name}-ecs-instance-role" @@ -57,14 +71,6 @@ resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_attachment" { policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" } -# ECS 작업 실행 역할의 ARN 출력 -# 다른 리소스에서 이 ARN을 참조할 수 있도록 출력 변수 설정 -output "ecs_task_execution_role_arn" { - description = "ARN of the ECS Task Execution Role" - value = aws_iam_role.ecs_task_execution_role.arn -} - - # CodeDeploy를 위한 IAM 역할 resource "aws_iam_role" "codedeploy_role" { name = "${var.project_name}-codedeploy-role" diff --git a/prod-team-account/modules/iam/outputs.tf b/prod-team-account/iam/outputs.tf similarity index 100% rename from prod-team-account/modules/iam/outputs.tf rename to prod-team-account/iam/outputs.tf diff --git a/prod-team-account/iam/variables.tf b/prod-team-account/iam/variables.tf new file mode 100644 index 0000000..3890190 --- /dev/null +++ b/prod-team-account/iam/variables.tf @@ -0,0 +1,4 @@ +variable "project_name" { + description = "The name of the project for resource naming" + type = string +} \ No newline at end of file diff --git a/prod-team-account/main.tf b/prod-team-account/main.tf deleted file mode 100644 index 5047eba..0000000 --- a/prod-team-account/main.tf +++ /dev/null @@ -1,67 +0,0 @@ -# CI/CD Test -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.0" - } - } - -} - -provider "aws" { - region = "ap-northeast-2" -} - - -module "vpc" { - source = "./modules/vpc" - vpc_cide = var.vpc_cidr - project_name = var.project_name -} - - -module "alb" { - source = "./modules/alb" - project_name = var.project_name - vpc_id = module.vpc.vpc_id - public_subnet_ids = module.vpc.public_subnet_ids - alb_security_group_id = module.vpc.alb_security_group_id -} - -module "codedeploy" { - source = "./modules/codedeploy" - project_name = var.project_name - ecs_cluster_name = module.ecs.cluster_name - ecs_service_name = module.ecs.service_name - alb_listener_arn = module.alb.listener_arn - blue_target_group_name = module.alb.blue_target_group_name - green_target_group_name = module.alb.green_target_group_name - ecs_task_execution_role_arn = module.iam.ecs_task_execution_role_arn -} - -module "ecs" { - source = "./modules/ecs" - project_name = var.project_name - ecs_task_execution_role_arn = module.iam.ecs_task_execution_role_arn - ecs_instance_profile_name = module.iam.ecs_instance_profile_name - private_subnet_ids = module.vpc.private_subnet_ids - ecs_security_group_id = module.vpc.ecs_security_group_id - blue_target_group_arn = module.alb.blue_target_group_arn - green_target_group_arn = module.alb.green_target_group_arn - golden_ami = data.aws_ami.latest_shared_ami.id - ecr_image_url = data.terraform_remote_state.operation_account.outputs.ecr_repository_url - -} - -module "iam" { - source = "./modules/iam" - project_name = var.project_name -} - - - -output "github_actions_role_arn" { - description = "The ARN of the IAM role for GitHub Actions in prod-team-account" - value = module.iam.github_actions_role_arn -} \ No newline at end of file diff --git a/prod-team-account/modules/alb/variables.tf b/prod-team-account/modules/alb/variables.tf deleted file mode 100644 index 5cb7b64..0000000 --- a/prod-team-account/modules/alb/variables.tf +++ /dev/null @@ -1,19 +0,0 @@ -variable "project_name" { - description = "The name of the project" - type = string -} - -variable "vpc_id" { - description = "The ID of the VPC" - type = string -} - -variable "public_subnet_ids" { - description = "A list of public subnet IDs for the ALB" - type = list(string) -} - -variable "alb_security_group_id" { - description = "The ID of the security group for the ALB" - type = string -} \ No newline at end of file diff --git a/prod-team-account/modules/codedeploy/codedeploy.tf b/prod-team-account/modules/codedeploy/codedeploy.tf deleted file mode 100644 index 76c7ce1..0000000 --- a/prod-team-account/modules/codedeploy/codedeploy.tf +++ /dev/null @@ -1,52 +0,0 @@ -# CI/CD Test -# CodeDeploy -resource "aws_codedeploy_app" "ecs_app" { - name = "${var.project_name}-ecs-app" - compute_platform = "ECS" -} - -resource "aws_codedeploy_deployment_group" "ecs_deployment_group" { - app_name = aws_codedeploy_app.ecs_app.name - deployment_group_name = "${var.project_name}-ecs-deployment-group" - service_role_arn = var.codedeploy_service_role_arn - - deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" - - ecs_service { - cluster_name = var.ecs_cluster_name - service_name = var.ecs_service_name - } - - deployment_style { - deployment_type = "BLUE_GREEN" - deployment_option = "WITH_TRAFFIC_CONTROL" - } - - blue_green_deployment_config { - deployment_ready_option { - action_on_timeout = "CONTINUE_DEPLOYMENT" - } - terminate_blue_instances_on_deployment_success { - action = "TERMINATE" - termination_wait_time_in_minutes = 5 - } - } - load_balancer_info { - target_group_pair_info { - target_group { - name = var.blue_target_group_name - } - target_group { - name = var.green_target_group_name - } - prod_traffic_route { - listener_arns = [var.alb_listener_arn] - } - } - } - auto_rollback_configuration { - enabled = true - events = ["DEPLOYMENT_FAILURE"] - } - -} \ No newline at end of file diff --git a/prod-team-account/modules/ecs/variables.tf b/prod-team-account/modules/ecs/variables.tf deleted file mode 100644 index 631536a..0000000 --- a/prod-team-account/modules/ecs/variables.tf +++ /dev/null @@ -1,43 +0,0 @@ -variable "project_name" { - description = "The name of the project for resource naming" - type = string -} - -# --- IAM 모듈로부터 받을 정보 --- -variable "ecs_task_execution_role_arn" { - description = "The ARN of the ECS Task Execution Role for tasks to pull images, etc." - type = string -} - -variable "ecs_instance_profile_name" { - description = "The name of the IAM instance profile for ECS container instances" - type = string -} - -# --- VPC 모듈로부터 받을 정보 --- -variable "private_subnet_ids" { - description = "A list of private subnet IDs for the ECS instances" - type = list(string) -} - -variable "ecs_security_group_id" { - description = "The ID of the security group for the ECS instances" - type = string -} - -# --- 외부 데이터 소스로부터 받을 정보 --- -variable "golden_ami" { - description = "The ID of the AMI to use for the ECS instances" - type = string -} - -variable "ecr_image_url" { - description = "The full URL of the Docker image in ECR" - type = string -} - -# --- ALB 모듈로부터 받을 정보 --- -variable "blue_target_group_arn" { - description = "The ARN of the blue target group from the ALB" - type = string -} \ No newline at end of file diff --git a/prod-team-account/modules/vpc/security_group.tf b/prod-team-account/modules/vpc/security_group.tf deleted file mode 100644 index cf0e89a..0000000 --- a/prod-team-account/modules/vpc/security_group.tf +++ /dev/null @@ -1,45 +0,0 @@ -# CI/CD Test -# ALB -resource "aws_security_group" "alb_sg" { - name = "${var.project_name}-alb-sg" - description = "Security group for ALB" - vpc_id = aws_vpc.vpc.id - - ingress { - from_port = 80 - to_port = 80 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - description = "Allow HTTP" - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - -} - - # ECS - resource "aws_security_group" "ecs_sg" { - name = "${var.project_name}-ecs-sg" - description = "Security group for ECS tasks" - vpc_id = aws_vpc.vpc.id - - ingress { - from_port = 80 - to_port = 80 - protocol = "tcp" - security_groups = [aws_security_group.alb_sg.id] - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - -} \ No newline at end of file diff --git a/prod-team-account/outputs.tf b/prod-team-account/outputs.tf deleted file mode 100644 index ad696ed..0000000 --- a/prod-team-account/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "vpc_id" { - description = "생성된 VPC의 ID" - value = aws_vpc.vpc.id -} - -output "public_subnet_ids" { - description = "생성된 Public Subnet들의 ID 목록" - value = [aws_subnet.public1.id, aws_subnet.public2.id] -} - -output "private_subnet_ids" { - description = "생성된 Private Subnet들의 ID 목록" - value = [aws_subnet.private1.id, aws_subnet.private2.id] -} - -output "alb_dns_name" { - description = "ALB의 DNS 이름" - value = aws_lb.alb.dns_name -} \ No newline at end of file diff --git a/prod-team-account/variables.tf b/prod-team-account/variables.tf deleted file mode 100644 index 4875d35..0000000 --- a/prod-team-account/variables.tf +++ /dev/null @@ -1,23 +0,0 @@ -variable "project_name" { - description = "프로젝트를 식별하는 이름" - type = string - default = "cloudfence" -} - -variable "vpc_cidr" { - description = "VPC에 할당할 IP 주소 범위" - type = string - default = "10.0.0.0/16" -} - -variable "instance_type" { - description = "ECS 인스턴스의 타입" - type = string - default = "t3.micro" -} - -variable "ami_owner_id" { - description = "ECS 인스턴스에 사용할 AMI의 소유자 ID" - type = string - default = "502676416967" # operation-team-account -} \ No newline at end of file diff --git a/prod-team-account/vpc/backend.tf b/prod-team-account/vpc/backend.tf new file mode 100644 index 0000000..1662372 --- /dev/null +++ b/prod-team-account/vpc/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-prod-state" + key = "prod-team-account/vpc/terraform.tfstate" + region = "ap-northeast-2" + dynamodb_table = "s3-prod-lock" + encrypt = true + } +} \ No newline at end of file diff --git a/prod-team-account/modules/vpc/network.tf b/prod-team-account/vpc/main.tf similarity index 72% rename from prod-team-account/modules/vpc/network.tf rename to prod-team-account/vpc/main.tf index c3b50cf..30d3c68 100644 --- a/prod-team-account/modules/vpc/network.tf +++ b/prod-team-account/vpc/main.tf @@ -1,4 +1,18 @@ # CI/CD Test +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} + # VPC resource "aws_vpc" "vpc" { cidr_block = var.vpc_cidr @@ -119,4 +133,49 @@ resource "aws_route_table_association" "private1" { resource "aws_route_table_association" "private2" { subnet_id = aws_subnet.private2.id route_table_id = aws_route_table.private.id +} + +# security_group +resource "aws_security_group" "alb_sg" { + name = "${var.project_name}-alb-sg" + description = "Security group for ALB" + vpc_id = aws_vpc.vpc.id + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + description = "Allow HTTP" + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + +} + +# ECS +resource "aws_security_group" "ecs_sg" { + name = "${var.project_name}-ecs-sg" + description = "Security group for ECS tasks" + vpc_id = aws_vpc.vpc.id + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + security_groups = [aws_security_group.alb_sg.id] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + } \ No newline at end of file diff --git a/prod-team-account/modules/vpc/outputs.tf b/prod-team-account/vpc/outputs.tf similarity index 100% rename from prod-team-account/modules/vpc/outputs.tf rename to prod-team-account/vpc/outputs.tf diff --git a/prod-team-account/modules/vpc/variables.tf b/prod-team-account/vpc/variables.tf similarity index 100% rename from prod-team-account/modules/vpc/variables.tf rename to prod-team-account/vpc/variables.tf From 1d0cceb10cfc219774b96aed82142d14c0643e25 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 7 Jul 2025 04:11:01 +0900 Subject: [PATCH 15/66] CI/CD Test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI/CD Test CI/CD Test CI/CD Test CI/CD Test 의존성 기반 CD 수정 --- .github/workflows/cd.yml | 80 ++++++++++++++++++--------- operation-team-account/iam/backend.tf | 9 --- operation-team-account/iam/main.tf | 29 ---------- operation-team-account/iam/outputs.tf | 5 -- prod-team-account/iam/main.tf | 2 +- 5 files changed, 55 insertions(+), 70 deletions(-) delete mode 100644 operation-team-account/iam/backend.tf delete mode 100644 operation-team-account/iam/main.tf delete mode 100644 operation-team-account/iam/outputs.tf diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 60ed96c..bcee80a 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -58,38 +58,65 @@ jobs: ["management"]="ROLE_ARN_MANAGEMENT" ) - MATRIX_ITEMS=() + declare -A DEPENDENCY_MAP=( + ["operation-team-account/ecr"]="" + ["prod-team-account/vpc"]="" + ["prod-team-account/iam"]="" + ["prod-team-account/alb"]="prod-team-account/vpc" + ["prod-team-account/ecs"]="prod-team-account/vpc prod-team-account/iam prod-team-account/alb operation-team-account/ecr" + ["prod-team-account/codedeploy"]="prod-team-account/ecs" + ) - for KEY in "${!ROLE_MAP[@]}"; do - VAR_NAME="FILTER_OUTPUTS_${KEY}" - VALUE="${!VAR_NAME}" - - if [ "$VALUE" = "true" ]; then - BASE_DIR="${KEY}-team-account" - - # 루트 디렉터리 검사 - TF_COUNT_ROOT=$(find "$BASE_DIR" -maxdepth 1 -name '*.tf' | wc -l) - if [ "$TF_COUNT_ROOT" -gt 0 ]; then - MATRIX_ITEMS+=("{\"dir\":\"$BASE_DIR\",\"role_key\":\"${ROLE_MAP[$KEY]}\"}") - fi - # 하위 디렉터리 검사 - - for DIR in $(find $BASE_DIR -type d -mindepth 1); do - if [[ "$DIR" != *".terraform"* && "$DIR" != "$BASE_DIR/modules" ]]; then - TF_COUNT=$(find "$DIR" -maxdepth 1 -name '*.tf' | wc -l) - if [ "$TF_COUNT" -gt 0 ]; then - MATRIX_ITEMS+=("{\"dir\":\"$DIR\",\"role_key\":\"${ROLE_MAP[$KEY]}\"}") - fi - fi - done + # Push 이벤트에 포함된 변경된 파일 목록을 호출 + echo "Comparing changes between ${{ github.event.before }} and ${{ github.event.after }}" + CHANGED_FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.event.after }}) + + # 변경된 파일이 속한 서비스 폴더(backend.tf가 있는 폴더) 목록 검색 + CHANGED_DIRS=() + for file in $CHANGED_FILES; do + dir=$(dirname "$file") + while [ "$dir" != "." ]; do + if [ -f "$dir/backend.tf" ]; then + CHANGED_DIRS+=("$dir"); break; + fi; + dir=$(dirname "$dir"); + done + done + CHANGED_DIRS=($(echo "${CHANGED_DIRS[@]}" | tr ' ' '\n' | sort -u)) + + if [ ${#CHANGED_DIRS[@]} -eq 0 ]; then + echo "No terraform project directories with changes found."; echo "matrix=[]" >> $GITHUB_OUTPUT; exit 0; + fi + echo "Changed project directories: ${CHANGED_DIRS[@]}" + + # 변경된 폴더와 정의된 의존성을 기반으로 배포 순서를 결정 + TSORT_INPUT="" + ALL_DIRS_TO_CONSIDER="${CHANGED_DIRS[@]}" + for DIR in "${CHANGED_DIRS[@]}"; do + dependencies=${DEPENDENCY_MAP[$DIR]} + for DEP in $dependencies; do + TSORT_INPUT+="$DEP $DIR\n"; ALL_DIRS_TO_CONSIDER+=" $DEP"; + done + done + ALL_DIRS_TO_CONSIDER=($(echo "$ALL_DIRS_TO_CONSIDER" | tr ' ' '\n' | sort -u)) + + ORDERED_DIRS=$(echo -e "$TSORT_INPUT" | tsort 2>/dev/null || echo "$ALL_DIRS_TO_CONSIDER") + echo "Calculated execution order: $ORDERED_DIRS" + + # 실행할 최종 매트릭스를 JSON 형식으로 생성 + MATRIX_ITEMS=() + for DIR in $ORDERED_DIRS; do + if [[ " ${CHANGED_DIRS[@]} " =~ " ${DIR} " ]]; then + ACCOUNT_PREFIX=$(echo $DIR | cut -d- -f1) + ROLE_KEY="${ROLE_MAP[$ACCOUNT_PREFIX]}" + MATRIX_ITEMS+=("{\"dir\":\"$DIR\",\"role_key\":\"$ROLE_KEY\"}") fi done if [ ${#MATRIX_ITEMS[@]} -eq 0 ]; then echo "matrix=[]" >> $GITHUB_OUTPUT else - JSON="[$(IFS=,; echo "${MATRIX_ITEMS[*]}")]" - echo "matrix=$JSON" >> $GITHUB_OUTPUT + JSON="[$(IFS=,; echo "${MATRIX_ITEMS[*]}")]"; echo "matrix=$JSON" >> $GITHUB_OUTPUT; fi terraform-apply: @@ -98,9 +125,10 @@ jobs: runs-on: ubuntu-latest strategy: + fail-fast: true + max-parallel: 1 matrix: include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} - fail-fast: false steps: - name: Checkout repository diff --git a/operation-team-account/iam/backend.tf b/operation-team-account/iam/backend.tf deleted file mode 100644 index 7d140de..0000000 --- a/operation-team-account/iam/backend.tf +++ /dev/null @@ -1,9 +0,0 @@ -terraform { - backend "s3" { - bucket = "cloudfence-operation-state" - key = "operation-team-account/iam/terraform.tfstate" - region = "ap-northeast-2" - dynamodb_table = "s3-operation-lock" - encrypt = true - } -} \ No newline at end of file diff --git a/operation-team-account/iam/main.tf b/operation-team-account/iam/main.tf deleted file mode 100644 index 48a22dc..0000000 --- a/operation-team-account/iam/main.tf +++ /dev/null @@ -1,29 +0,0 @@ -# CI/CD Test -# prod-team-account의 역할 ARN을 변수로 받기 -variable "prod_github_actions_role_arn" { - type = string - description = "The ARN of the IAM role for GitHub Actions in prod-team-account" -} - -# ECR 리포지토리 정책을 생성하기 위한 IAM 정책 문서 데이터 소스 -data "aws_iam_policy_document" "ecr_repo_policy_document" { - statement { - sid = "AllowCrossAccountPush" - effect = "Allow" - principals { - type = "AWS" - identifiers = [var.prod_github_actions_role_arn] - } - actions = [ - "ecr:GetDownloadUrlForLayer", - "ecr:BatchGetImage", - "ecr:BatchCheckLayerAvailability", - "ecr:PutImage", - "ecr:InitiateLayerUpload", - "ecr:UploadLayerPart", - "ecr:CompleteLayerUpload" - "ecr:GetAuthorizationToken" - ] - } -} - diff --git a/operation-team-account/iam/outputs.tf b/operation-team-account/iam/outputs.tf deleted file mode 100644 index 1c12136..0000000 --- a/operation-team-account/iam/outputs.tf +++ /dev/null @@ -1,5 +0,0 @@ -# 생성된 정책 문서(JSON)를 output으로 출력 -output "ecr_policy_json" { - description = "The JSON policy document for the ECR repository" - value = data.aws_iam_policy_document.ecr_repo_policy_document.json -} \ No newline at end of file diff --git a/prod-team-account/iam/main.tf b/prod-team-account/iam/main.tf index 3d42d4f..b1eaaea 100644 --- a/prod-team-account/iam/main.tf +++ b/prod-team-account/iam/main.tf @@ -1,4 +1,4 @@ -# CI/CD Test +# CI/CD workflowTest terraform { required_providers { aws = { From 90d152d4377a28d5a8bcbdff7deb73bc6d118600 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 7 Jul 2025 05:29:40 +0900 Subject: [PATCH 16/66] =?UTF-8?q?arn=20=EC=97=B0=EB=8F=99=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit project_name 설정 arn 연동 설정 CI/CD test --- .github/workflows/cd.yml | 2 +- operation-team-account/ecr/main.tf | 13 +++++++++++-- operation-team-account/ecr/variables.tf | 1 + prod-team-account/alb/main.tf | 2 +- prod-team-account/alb/variables.tf | 1 + prod-team-account/codedeploy/main.tf | 2 +- prod-team-account/codedeploy/variables.tf | 1 + prod-team-account/ecs/main.tf | 2 +- prod-team-account/ecs/variables.tf | 1 + prod-team-account/iam/main.tf | 6 +++++- prod-team-account/iam/outputs.tf | 5 +++++ prod-team-account/iam/variables.tf | 1 + prod-team-account/vpc/main.tf | 2 +- prod-team-account/vpc/variables.tf | 1 + 14 files changed, 32 insertions(+), 8 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index bcee80a..9ede438 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -59,9 +59,9 @@ jobs: ) declare -A DEPENDENCY_MAP=( - ["operation-team-account/ecr"]="" ["prod-team-account/vpc"]="" ["prod-team-account/iam"]="" + ["operation-team-account/ecr"]="prod-team-account/iam" ["prod-team-account/alb"]="prod-team-account/vpc" ["prod-team-account/ecs"]="prod-team-account/vpc prod-team-account/iam prod-team-account/alb operation-team-account/ecr" ["prod-team-account/codedeploy"]="prod-team-account/ecs" diff --git a/operation-team-account/ecr/main.tf b/operation-team-account/ecr/main.tf index 0722bc2..f8cf101 100644 --- a/operation-team-account/ecr/main.tf +++ b/operation-team-account/ecr/main.tf @@ -1,4 +1,4 @@ -# CI/CD Test +# CI/CD workflow Test terraform { required_providers { aws = { @@ -13,6 +13,15 @@ provider "aws" { region = "ap-northeast-2" } +data "terraform_remote_state" "iam" { + backend = "s3" + config = { + bucket = "cloudfence-tfstate-app" + key = "prod-team-account/iam/terraform.tfstate" + region = "ap-northeast-2" + } +} + # operation-team-account의 ECR 리포지토리 생성 및 정책 설정 data "aws_iam_policy_document" "ecr_repo_policy_document" { statement { @@ -21,7 +30,7 @@ data "aws_iam_policy_document" "ecr_repo_policy_document" { principals { type = "AWS" # prod 계정의 역할 ARN은 변수로 전달 - identifiers = [var.prod_github_actions_role_arn] + identifiers = [data.terraform_remote_state.iam.outputs.github_actions_role_arn] } actions = [ "ecr:GetDownloadUrlForLayer", diff --git a/operation-team-account/ecr/variables.tf b/operation-team-account/ecr/variables.tf index 0666894..c0095cb 100644 --- a/operation-team-account/ecr/variables.tf +++ b/operation-team-account/ecr/variables.tf @@ -1,6 +1,7 @@ variable "project_name" { description = "The name of the project" type = string + default = "cloudfence" } variable "prod_github_actions_role_arn" { diff --git a/prod-team-account/alb/main.tf b/prod-team-account/alb/main.tf index 418275e..ad20232 100644 --- a/prod-team-account/alb/main.tf +++ b/prod-team-account/alb/main.tf @@ -1,4 +1,4 @@ -# CI/CD Test +# CI/CD workflow Test terraform { required_providers { aws = { diff --git a/prod-team-account/alb/variables.tf b/prod-team-account/alb/variables.tf index d6d5c96..7c839dd 100644 --- a/prod-team-account/alb/variables.tf +++ b/prod-team-account/alb/variables.tf @@ -1,4 +1,5 @@ variable "project_name" { description = "The name of the project" type = string + default = "cloudfence" } \ No newline at end of file diff --git a/prod-team-account/codedeploy/main.tf b/prod-team-account/codedeploy/main.tf index e503be0..c4a9959 100644 --- a/prod-team-account/codedeploy/main.tf +++ b/prod-team-account/codedeploy/main.tf @@ -1,4 +1,4 @@ -# CI/CD Test +# CI/CD workflow Test terraform { required_providers { aws = { diff --git a/prod-team-account/codedeploy/variables.tf b/prod-team-account/codedeploy/variables.tf index 7c30610..e42256f 100644 --- a/prod-team-account/codedeploy/variables.tf +++ b/prod-team-account/codedeploy/variables.tf @@ -1,6 +1,7 @@ variable "project_name" { description = "The name of the project" type = string + default = "cloudfence" } variable "ecs_cluster_name" { diff --git a/prod-team-account/ecs/main.tf b/prod-team-account/ecs/main.tf index 1b37b2a..2018c65 100644 --- a/prod-team-account/ecs/main.tf +++ b/prod-team-account/ecs/main.tf @@ -1,4 +1,4 @@ -# CI/CD Test +# CI/CD workflow Test terraform { required_providers { aws = { diff --git a/prod-team-account/ecs/variables.tf b/prod-team-account/ecs/variables.tf index dd3c33a..c765a7b 100644 --- a/prod-team-account/ecs/variables.tf +++ b/prod-team-account/ecs/variables.tf @@ -1,6 +1,7 @@ variable "project_name" { description = "The name of the project for resource naming" type = string + default = "cloudfence" } variable "ami_owner_account_id" { diff --git a/prod-team-account/iam/main.tf b/prod-team-account/iam/main.tf index b1eaaea..40ff246 100644 --- a/prod-team-account/iam/main.tf +++ b/prod-team-account/iam/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflowTest +# CI/CD workflow Test terraform { required_providers { aws = { @@ -13,6 +13,10 @@ provider "aws" { region = "ap-northeast-2" } +data "aws_iam_role" "github_actions_role" { + name = "Application-Deployment-role2" +} + # ECS 인스턴스가 사용할 IAM 역할 생성 resource "aws_iam_role" "ecs_instance_role" { name = "${var.project_name}-ecs-instance-role" diff --git a/prod-team-account/iam/outputs.tf b/prod-team-account/iam/outputs.tf index 497971b..7f8b0bf 100644 --- a/prod-team-account/iam/outputs.tf +++ b/prod-team-account/iam/outputs.tf @@ -11,4 +11,9 @@ output "codedeploy_service_role_arn" { output "ecs_instance_profile_name" { description = "The name of the IAM instance profile for ECS container instances" value = aws_iam_instance_profile.ecs_instance_profile.name +} + +output "github_actions_role_arn" { + description = "The ARN of the existing IAM role for GitHub Actions" + value = data.aws_iam_role.github_actions_role.arn } \ No newline at end of file diff --git a/prod-team-account/iam/variables.tf b/prod-team-account/iam/variables.tf index 3890190..0704056 100644 --- a/prod-team-account/iam/variables.tf +++ b/prod-team-account/iam/variables.tf @@ -1,4 +1,5 @@ variable "project_name" { description = "The name of the project for resource naming" type = string + default = "cloudfence" } \ No newline at end of file diff --git a/prod-team-account/vpc/main.tf b/prod-team-account/vpc/main.tf index 30d3c68..881a540 100644 --- a/prod-team-account/vpc/main.tf +++ b/prod-team-account/vpc/main.tf @@ -1,4 +1,4 @@ -# CI/CD Test +# CI/CD workflow Test terraform { required_providers { aws = { diff --git a/prod-team-account/vpc/variables.tf b/prod-team-account/vpc/variables.tf index ad775e0..bca080c 100644 --- a/prod-team-account/vpc/variables.tf +++ b/prod-team-account/vpc/variables.tf @@ -1,6 +1,7 @@ variable "project_name" { description = "The name of the project" type = string + default = "cloudfence" } variable "vpc_id" { From 8b1d54aeafe867ae865b3b7325758622326becf7 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 7 Jul 2025 05:52:44 +0900 Subject: [PATCH 17/66] =?UTF-8?q?=EC=A4=91=EB=B3=B5=20=EB=B3=80=EC=88=98?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- operation-team-account/ecr/main.tf | 2 +- operation-team-account/ecr/variables.tf | 6 ----- prod-team-account/alb/main.tf | 2 +- prod-team-account/codedeploy/main.tf | 2 +- prod-team-account/codedeploy/variables.tf | 30 ----------------------- prod-team-account/ecs/main.tf | 2 +- prod-team-account/iam/main.tf | 2 +- prod-team-account/vpc/main.tf | 2 +- prod-team-account/vpc/variables.tf | 1 + 9 files changed, 7 insertions(+), 42 deletions(-) diff --git a/operation-team-account/ecr/main.tf b/operation-team-account/ecr/main.tf index f8cf101..78b3026 100644 --- a/operation-team-account/ecr/main.tf +++ b/operation-team-account/ecr/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow Test +# CI/CD workflow test terraform { required_providers { aws = { diff --git a/operation-team-account/ecr/variables.tf b/operation-team-account/ecr/variables.tf index c0095cb..155f378 100644 --- a/operation-team-account/ecr/variables.tf +++ b/operation-team-account/ecr/variables.tf @@ -3,9 +3,3 @@ variable "project_name" { type = string default = "cloudfence" } - -variable "prod_github_actions_role_arn" { - description = "The ARN of the IAM role for GitHub Actions in prod-team-account to grant push access" - type = string - sensitive = true -} \ No newline at end of file diff --git a/prod-team-account/alb/main.tf b/prod-team-account/alb/main.tf index ad20232..c9f4f4e 100644 --- a/prod-team-account/alb/main.tf +++ b/prod-team-account/alb/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow Test +# CI/CD workflow test terraform { required_providers { aws = { diff --git a/prod-team-account/codedeploy/main.tf b/prod-team-account/codedeploy/main.tf index c4a9959..b0fa8be 100644 --- a/prod-team-account/codedeploy/main.tf +++ b/prod-team-account/codedeploy/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow Test +# CI/CD workflow test terraform { required_providers { aws = { diff --git a/prod-team-account/codedeploy/variables.tf b/prod-team-account/codedeploy/variables.tf index e42256f..155f378 100644 --- a/prod-team-account/codedeploy/variables.tf +++ b/prod-team-account/codedeploy/variables.tf @@ -3,33 +3,3 @@ variable "project_name" { type = string default = "cloudfence" } - -variable "ecs_cluster_name" { - description = "The name of the ECS cluster" - type = string -} - -variable "ecs_service_name" { - description = "The name of the ECS service" - type = string -} - -variable "codedeploy_service_role_arn" { - description = "The ARN of the IAM role for CodeDeploy to use" - type = string -} - -variable "alb_listener_arn" { - description = "The ARN of the ALB listener for traffic shifting" - type = string -} - -variable "blue_target_group_name" { - description = "The name of the blue target group" - type = string -} - -variable "green_target_group_name" { - description = "The name of the green target group" - type = string -} \ No newline at end of file diff --git a/prod-team-account/ecs/main.tf b/prod-team-account/ecs/main.tf index 2018c65..7cf2565 100644 --- a/prod-team-account/ecs/main.tf +++ b/prod-team-account/ecs/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow Test +# CI/CD workflow test terraform { required_providers { aws = { diff --git a/prod-team-account/iam/main.tf b/prod-team-account/iam/main.tf index 40ff246..c65258c 100644 --- a/prod-team-account/iam/main.tf +++ b/prod-team-account/iam/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow Test +# CI/CD workflow test terraform { required_providers { aws = { diff --git a/prod-team-account/vpc/main.tf b/prod-team-account/vpc/main.tf index 881a540..d7932cf 100644 --- a/prod-team-account/vpc/main.tf +++ b/prod-team-account/vpc/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow Test +# CI/CD workflow test terraform { required_providers { aws = { diff --git a/prod-team-account/vpc/variables.tf b/prod-team-account/vpc/variables.tf index bca080c..fdc217a 100644 --- a/prod-team-account/vpc/variables.tf +++ b/prod-team-account/vpc/variables.tf @@ -7,4 +7,5 @@ variable "project_name" { variable "vpc_id" { description = "The ID of the VPC where the ECS cluster will be deployed" type = string + default = "cloudfence-vpc" } \ No newline at end of file From 7af192ced240439292c7a6c5b6c3f2a1b7670e15 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 7 Jul 2025 05:58:06 +0900 Subject: [PATCH 18/66] =?UTF-8?q?vpc/variables.tf=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- operation-team-account/ecr/main.tf | 2 +- prod-team-account/alb/main.tf | 2 +- prod-team-account/codedeploy/main.tf | 2 +- prod-team-account/ecs/main.tf | 4 ++-- prod-team-account/iam/main.tf | 2 +- prod-team-account/vpc/main.tf | 2 +- prod-team-account/vpc/variables.tf | 6 ++++++ 7 files changed, 13 insertions(+), 7 deletions(-) diff --git a/operation-team-account/ecr/main.tf b/operation-team-account/ecr/main.tf index 78b3026..f8cf101 100644 --- a/operation-team-account/ecr/main.tf +++ b/operation-team-account/ecr/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow test +# CI/CD workflow Test terraform { required_providers { aws = { diff --git a/prod-team-account/alb/main.tf b/prod-team-account/alb/main.tf index c9f4f4e..ad20232 100644 --- a/prod-team-account/alb/main.tf +++ b/prod-team-account/alb/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow test +# CI/CD workflow Test terraform { required_providers { aws = { diff --git a/prod-team-account/codedeploy/main.tf b/prod-team-account/codedeploy/main.tf index b0fa8be..c4a9959 100644 --- a/prod-team-account/codedeploy/main.tf +++ b/prod-team-account/codedeploy/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow test +# CI/CD workflow Test terraform { required_providers { aws = { diff --git a/prod-team-account/ecs/main.tf b/prod-team-account/ecs/main.tf index 7cf2565..24f09c0 100644 --- a/prod-team-account/ecs/main.tf +++ b/prod-team-account/ecs/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow test +# CI/CD workflow Test terraform { required_providers { aws = { @@ -75,7 +75,7 @@ resource "aws_ecs_cluster" "ecs_cluster" { # ECS Launch Template resource "aws_launch_template" "ecs_launch_template" { name_prefix = "${var.project_name}-ecs-launch-template-" - image_id = "${var.golden_ami}" + image_id = "${data.aws_ami.latest_shared_ami.id}" instance_type = "t2.micro" iam_instance_profile { diff --git a/prod-team-account/iam/main.tf b/prod-team-account/iam/main.tf index c65258c..40ff246 100644 --- a/prod-team-account/iam/main.tf +++ b/prod-team-account/iam/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow test +# CI/CD workflow Test terraform { required_providers { aws = { diff --git a/prod-team-account/vpc/main.tf b/prod-team-account/vpc/main.tf index d7932cf..881a540 100644 --- a/prod-team-account/vpc/main.tf +++ b/prod-team-account/vpc/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow test +# CI/CD workflow Test terraform { required_providers { aws = { diff --git a/prod-team-account/vpc/variables.tf b/prod-team-account/vpc/variables.tf index fdc217a..083bd85 100644 --- a/prod-team-account/vpc/variables.tf +++ b/prod-team-account/vpc/variables.tf @@ -8,4 +8,10 @@ variable "vpc_id" { description = "The ID of the VPC where the ECS cluster will be deployed" type = string default = "cloudfence-vpc" +} + +variable "vpc_cidr" { + description = "The CIDR block for the VPC" + type = string + default = "10.0.0.0/16" } \ No newline at end of file From cebfbdbdb927f17a7a645d86d53875fe8a6fdced Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 7 Jul 2025 06:12:30 +0900 Subject: [PATCH 19/66] =?UTF-8?q?data=20=EB=B8=94=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit data 블록 오류 수정 data 블록 중복 수정 --- operation-team-account/ecr/main.tf | 4 ++-- prod-team-account/alb/main.tf | 6 ++---- prod-team-account/codedeploy/main.tf | 8 ++++---- prod-team-account/ecs/main.tf | 21 ++++++--------------- prod-team-account/iam/main.tf | 2 +- prod-team-account/vpc/main.tf | 2 +- 6 files changed, 16 insertions(+), 27 deletions(-) diff --git a/operation-team-account/ecr/main.tf b/operation-team-account/ecr/main.tf index f8cf101..1d7b3e7 100644 --- a/operation-team-account/ecr/main.tf +++ b/operation-team-account/ecr/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow Test +# CI/CD Test terraform { required_providers { aws = { @@ -16,7 +16,7 @@ provider "aws" { data "terraform_remote_state" "iam" { backend = "s3" config = { - bucket = "cloudfence-tfstate-app" + bucket = "cloudfence-prod-state" key = "prod-team-account/iam/terraform.tfstate" region = "ap-northeast-2" } diff --git a/prod-team-account/alb/main.tf b/prod-team-account/alb/main.tf index ad20232..d2372d5 100644 --- a/prod-team-account/alb/main.tf +++ b/prod-team-account/alb/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow Test +# CI/CD Test terraform { required_providers { aws = { @@ -16,11 +16,9 @@ provider "aws" { data "terraform_remote_state" "vpc" { backend = "s3" config = { - bucket = "cloudfence-tfstate-app" + bucket = "cloudfence-prod-state" key = "prod-team-account/vpc/terraform.tfstate" region = "ap-northeast-2" - dynamodb_table = "tfstate-lock-app" - encrypt = true } } diff --git a/prod-team-account/codedeploy/main.tf b/prod-team-account/codedeploy/main.tf index c4a9959..3e3c06e 100644 --- a/prod-team-account/codedeploy/main.tf +++ b/prod-team-account/codedeploy/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow Test +# CI/CD Test terraform { required_providers { aws = { @@ -16,7 +16,7 @@ provider "aws" { data "terraform_remote_state" "alb" { backend = "s3" config = { - bucket = "cloudfence-tfstate-app" + bucket = "cloudfence-prod-state" key = "prod-team-account/alb/terraform.tfstate" region = "ap-northeast-2" } @@ -25,7 +25,7 @@ data "terraform_remote_state" "alb" { data "terraform_remote_state" "iam" { backend = "s3" config = { - bucket = "cloudfence-tfstate-app" + bucket = "cloudfence-prod-state" key = "prod-team-account/iam/terraform.tfstate" region = "ap-northeast-2" } @@ -34,7 +34,7 @@ data "terraform_remote_state" "iam" { data "terraform_remote_state" "ecs" { backend = "s3" config = { - bucket = "cloudfence-tfstate-app" + bucket = "cloudfence-prod-state" key = "prod-team-account/ecs/terraform.tfstate" region = "ap-northeast-2" } diff --git a/prod-team-account/ecs/main.tf b/prod-team-account/ecs/main.tf index 24f09c0..a926fcb 100644 --- a/prod-team-account/ecs/main.tf +++ b/prod-team-account/ecs/main.tf @@ -16,7 +16,7 @@ provider "aws" { data "terraform_remote_state" "vpc" { backend = "s3" config = { - bucket = "cloudfence-tfstate-app" + bucket = "cloudfence-prod-state" key = "prod-team-account/vpc/terraform.tfstate" region = "ap-northeast-2" } @@ -25,7 +25,7 @@ data "terraform_remote_state" "vpc" { data "terraform_remote_state" "alb" { backend = "s3" config = { - bucket = "cloudfence-tfstate-app" + bucket = "cloudfence-prod-state" key = "prod-team-account/alb/terraform.tfstate" region = "ap-northeast-2" } @@ -34,17 +34,17 @@ data "terraform_remote_state" "alb" { data "terraform_remote_state" "iam" { backend = "s3" config = { - bucket = "cloudfence-tfstate-app" + bucket = "cloudfence-prod-state" key = "prod-team-account/iam/terraform.tfstate" region = "ap-northeast-2" } } -data "terraform_remote_state" "ecs" { +data "terraform_remote_state" "ecr" { backend = "s3" config = { - bucket = "cloudfence-tfstate-app" - key = "prod-team-account/ecs/terraform.tfstate" + bucket = "cloudfence-operation-state" + key = "operation-team-account/ecr/terraform.tfstate" region = "ap-northeast-2" } } @@ -58,15 +58,6 @@ data "aws_ami" "latest_shared_ami" { } } -data "terraform_remote_state" "ecr" { - backend = "s3" - config = { - bucket = "cloudfence-tfstate-app" - key = "operation-team-account/ecr/terraform.tfstate" - region = "ap-northeast-2" - } -} - # ECS 클러스터 생성 resource "aws_ecs_cluster" "ecs_cluster" { name = "${var.project_name}-ecs-cluster" diff --git a/prod-team-account/iam/main.tf b/prod-team-account/iam/main.tf index 40ff246..08c56c9 100644 --- a/prod-team-account/iam/main.tf +++ b/prod-team-account/iam/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow Test +# CI/CD Test terraform { required_providers { aws = { diff --git a/prod-team-account/vpc/main.tf b/prod-team-account/vpc/main.tf index 881a540..30d3c68 100644 --- a/prod-team-account/vpc/main.tf +++ b/prod-team-account/vpc/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow Test +# CI/CD Test terraform { required_providers { aws = { From 52002445d1aa72609b47b93261c5de5a42c126c1 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 7 Jul 2025 06:38:51 +0900 Subject: [PATCH 20/66] =?UTF-8?q?=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 오타 수정 오타 수정 --- prod-team-account/ecs/main.tf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prod-team-account/ecs/main.tf b/prod-team-account/ecs/main.tf index a926fcb..07faaf2 100644 --- a/prod-team-account/ecs/main.tf +++ b/prod-team-account/ecs/main.tf @@ -1,4 +1,4 @@ -# CI/CD workflow Test +# CI/CD Test terraform { required_providers { aws = { @@ -51,7 +51,7 @@ data "terraform_remote_state" "ecr" { data "aws_ami" "latest_shared_ami" { most_recent = true - owners = [var.ami_owner_id] # operation-team-account의 AMI + owners = [var.ami_owner_account_id] # operation-team-account의 AMI filter { name = "name" values = ["WHS-CloudFence-*"] @@ -145,7 +145,7 @@ resource "aws_ecs_task_definition" "ecs_task_definition" { container_definitions = jsonencode([ { name = "${var.project_name}-container" - image = "${data.terraform_remote_state.ecr.outputs.image_url}:latest" + image = "${data.terraform_remote_state.ecr.outputs.repository_url}:latest" cpu = 256 memory = 512 essential = true From be1a5ab37914f7cc6cfb7d67caab37400273ed3c Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 7 Jul 2025 06:57:21 +0900 Subject: [PATCH 21/66] =?UTF-8?q?EC2=20=EC=9D=B8=EC=8A=A4=ED=84=B4?= =?UTF-8?q?=EC=8A=A4=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- operation-team-account/ecr/main.tf | 2 +- prod-team-account/alb/main.tf | 2 +- prod-team-account/codedeploy/main.tf | 2 +- prod-team-account/ecs/main.tf | 2 +- prod-team-account/iam/main.tf | 2 +- prod-team-account/vpc/main.tf | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/operation-team-account/ecr/main.tf b/operation-team-account/ecr/main.tf index 1d7b3e7..29e45e5 100644 --- a/operation-team-account/ecr/main.tf +++ b/operation-team-account/ecr/main.tf @@ -1,4 +1,4 @@ -# CI/CD Test +# CI/CD Test. terraform { required_providers { aws = { diff --git a/prod-team-account/alb/main.tf b/prod-team-account/alb/main.tf index d2372d5..2e2b323 100644 --- a/prod-team-account/alb/main.tf +++ b/prod-team-account/alb/main.tf @@ -1,4 +1,4 @@ -# CI/CD Test +# CI/CD Test. terraform { required_providers { aws = { diff --git a/prod-team-account/codedeploy/main.tf b/prod-team-account/codedeploy/main.tf index 3e3c06e..78eee35 100644 --- a/prod-team-account/codedeploy/main.tf +++ b/prod-team-account/codedeploy/main.tf @@ -1,4 +1,4 @@ -# CI/CD Test +# CI/CD Test. terraform { required_providers { aws = { diff --git a/prod-team-account/ecs/main.tf b/prod-team-account/ecs/main.tf index 07faaf2..05b631b 100644 --- a/prod-team-account/ecs/main.tf +++ b/prod-team-account/ecs/main.tf @@ -67,7 +67,7 @@ resource "aws_ecs_cluster" "ecs_cluster" { resource "aws_launch_template" "ecs_launch_template" { name_prefix = "${var.project_name}-ecs-launch-template-" image_id = "${data.aws_ami.latest_shared_ami.id}" - instance_type = "t2.micro" + instance_type = "t3.micro" iam_instance_profile { name = data.terraform_remote_state.iam.outputs.ecs_instance_profile_name diff --git a/prod-team-account/iam/main.tf b/prod-team-account/iam/main.tf index 08c56c9..b9f4869 100644 --- a/prod-team-account/iam/main.tf +++ b/prod-team-account/iam/main.tf @@ -1,4 +1,4 @@ -# CI/CD Test +# CI/CD Test. terraform { required_providers { aws = { diff --git a/prod-team-account/vpc/main.tf b/prod-team-account/vpc/main.tf index 30d3c68..e8d1516 100644 --- a/prod-team-account/vpc/main.tf +++ b/prod-team-account/vpc/main.tf @@ -1,4 +1,4 @@ -# CI/CD Test +# CI/CD Test. terraform { required_providers { aws = { From 0cc4fa96a0bbc373bb5c8edc9e6b74c79838e64f Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 7 Jul 2025 07:05:42 +0900 Subject: [PATCH 22/66] =?UTF-8?q?remote=20state=20=EC=98=A4=ED=83=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- operation-team-account/ecr/main.tf | 1 - prod-team-account/alb/main.tf | 1 - prod-team-account/codedeploy/main.tf | 5 ++--- prod-team-account/ecs/main.tf | 1 - prod-team-account/iam/main.tf | 1 - prod-team-account/vpc/main.tf | 1 - 6 files changed, 2 insertions(+), 8 deletions(-) diff --git a/operation-team-account/ecr/main.tf b/operation-team-account/ecr/main.tf index 29e45e5..41fe5d0 100644 --- a/operation-team-account/ecr/main.tf +++ b/operation-team-account/ecr/main.tf @@ -1,4 +1,3 @@ -# CI/CD Test. terraform { required_providers { aws = { diff --git a/prod-team-account/alb/main.tf b/prod-team-account/alb/main.tf index 2e2b323..0785516 100644 --- a/prod-team-account/alb/main.tf +++ b/prod-team-account/alb/main.tf @@ -1,4 +1,3 @@ -# CI/CD Test. terraform { required_providers { aws = { diff --git a/prod-team-account/codedeploy/main.tf b/prod-team-account/codedeploy/main.tf index 78eee35..2b10f70 100644 --- a/prod-team-account/codedeploy/main.tf +++ b/prod-team-account/codedeploy/main.tf @@ -1,4 +1,3 @@ -# CI/CD Test. terraform { required_providers { aws = { @@ -54,8 +53,8 @@ resource "aws_codedeploy_deployment_group" "ecs_deployment_group" { deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" ecs_service { - cluster_name = data.terraform_remote_state.ecs.outputs.ecs_cluster_name - service_name = data.terraform_remote_state.ecs.outputs.ecs_service_name + cluster_name = data.terraform_remote_state.ecs.outputs.cluster_name + service_name = data.terraform_remote_state.ecs.outputs.service_name } deployment_style { diff --git a/prod-team-account/ecs/main.tf b/prod-team-account/ecs/main.tf index 05b631b..0ab7208 100644 --- a/prod-team-account/ecs/main.tf +++ b/prod-team-account/ecs/main.tf @@ -1,4 +1,3 @@ -# CI/CD Test terraform { required_providers { aws = { diff --git a/prod-team-account/iam/main.tf b/prod-team-account/iam/main.tf index b9f4869..8746c8a 100644 --- a/prod-team-account/iam/main.tf +++ b/prod-team-account/iam/main.tf @@ -1,4 +1,3 @@ -# CI/CD Test. terraform { required_providers { aws = { diff --git a/prod-team-account/vpc/main.tf b/prod-team-account/vpc/main.tf index e8d1516..4c2972e 100644 --- a/prod-team-account/vpc/main.tf +++ b/prod-team-account/vpc/main.tf @@ -1,4 +1,3 @@ -# CI/CD Test. terraform { required_providers { aws = { From d0292ece70b93e7332127461d242a0d2149320d7 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 7 Jul 2025 11:01:02 +0900 Subject: [PATCH 23/66] =?UTF-8?q?HTTPS=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prod-team-account/acm/backend.tf | 8 ++++++ prod-team-account/acm/main.tf | 44 ++++++++++++++++++++++++++++++ prod-team-account/acm/outputs.tf | 4 +++ prod-team-account/acm/variables.tf | 11 ++++++++ prod-team-account/alb/main.tf | 32 ++++++++++++++++++++-- prod-team-account/ecs/main.tf | 6 ++++ 6 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 prod-team-account/acm/backend.tf create mode 100644 prod-team-account/acm/main.tf create mode 100644 prod-team-account/acm/outputs.tf create mode 100644 prod-team-account/acm/variables.tf diff --git a/prod-team-account/acm/backend.tf b/prod-team-account/acm/backend.tf new file mode 100644 index 0000000..d8f9527 --- /dev/null +++ b/prod-team-account/acm/backend.tf @@ -0,0 +1,8 @@ +terraform { + backend "s3" { + bucket = "cloudfence-prod-state" + key = "prod-team-account/acm/terraform.tfstate" + region = "ap-northeast-2" + dynamodb_table = "s3-prod-lock" + } +} \ No newline at end of file diff --git a/prod-team-account/acm/main.tf b/prod-team-account/acm/main.tf new file mode 100644 index 0000000..f3f22fb --- /dev/null +++ b/prod-team-account/acm/main.tf @@ -0,0 +1,44 @@ +terraform { + required_providers { + aws = { source = "hashicorp/aws", version = "~> 5.0" } + } +} + +provider "aws" { + region = "ap-northeast-2" +} + +# ACM 인증서 요청 +resource "aws_acm_certificate" "cert" { + domain_name = var.domain_name + subject_alternative_names = ["*.${var.domain_name}"] + validation_method = "DNS" + + lifecycle { + create_before_destroy = true + } +} + +# DNS 검증을 위한 Route 53 레코드 생성 +resource "aws_route53_record" "cert_validation" { + for_each = { + for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name => { + name = dvo.resource_record_name + record = dvo.resource_record_value + type = dvo.resource_record_type + } + } + + allow_overwrite = true + name = each.value.name + records = [each.value.record] + ttl = 60 + type = each.value.type + zone_id = var.zone_id +} + +# DNS 검증이 완료될 때까지 대기하고 인증서 발급 완료 +resource "aws_acm_certificate_validation" "cert" { + certificate_arn = aws_acm_certificate.cert.arn + validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn] +} \ No newline at end of file diff --git a/prod-team-account/acm/outputs.tf b/prod-team-account/acm/outputs.tf new file mode 100644 index 0000000..b168a46 --- /dev/null +++ b/prod-team-account/acm/outputs.tf @@ -0,0 +1,4 @@ +output "certificate_arn" { + description = "The ARN of the validated ACM certificate" + value = aws_acm_certificate_validation.cert.certificate_arn +} \ No newline at end of file diff --git a/prod-team-account/acm/variables.tf b/prod-team-account/acm/variables.tf new file mode 100644 index 0000000..a97510b --- /dev/null +++ b/prod-team-account/acm/variables.tf @@ -0,0 +1,11 @@ +variable "domain_name" { + description = "The domain name for the SSL certificate" + type = string + default = "cloudfence.cloud" +} + +variable "zone_id" { + description = "The Route 53 Hosted Zone ID for the domain" + type = string + default = "Z0324594CRM7IYDEWX83" +} \ No newline at end of file diff --git a/prod-team-account/alb/main.tf b/prod-team-account/alb/main.tf index 0785516..51289b6 100644 --- a/prod-team-account/alb/main.tf +++ b/prod-team-account/alb/main.tf @@ -12,6 +12,15 @@ provider "aws" { region = "ap-northeast-2" } +data "terraform_remote_state" "acm" { + backend = "s3" + config = { + bucket = "cloudfence-prod-state" + key = "prod-team-account/acm/terraform.tfstate" + region = "ap-northeast-2" + } +} + data "terraform_remote_state" "vpc" { backend = "s3" config = { @@ -69,6 +78,7 @@ resource "aws_lb" "alb" { security_groups = [data.terraform_remote_state.vpc.outputs.alb_security_group_id] subnets = data.terraform_remote_state.vpc.outputs.public_subnet_ids + drop_invalid_header_fields = true enable_deletion_protection = true tags = { @@ -116,16 +126,32 @@ resource "aws_lb_target_group" "green" { } # ALB 리스너 -resource "aws_lb_listener" "http" { +resource "aws_lb_listener" "https" { load_balancer_arn = aws_lb.alb.arn - port = 80 - protocol = "HTTP" + port = 443 + protocol = "HTTPS" + ssl_policy = "ELBSecurityPolicy-2016-08" + certificate_arn = data.terraform_remote_state.acm.outputs.certificate_arn default_action { type = "forward" target_group_arn = aws_lb_target_group.blue.arn } } +resource "aws_lb_listener" "http_redirect" { + load_balancer_arn = aws_lb.alb.arn + port = 80 + protocol = "HTTP" + default_action { + type = "redirect" + redirect { + port = "443" + protocol = "HTTPS" + status_code = "HTTP_301" + } + } +} + # WAF와 ALB 연결 resource "aws_wafv2_web_acl_association" "alb_association" { resource_arn = aws_lb.alb.arn diff --git a/prod-team-account/ecs/main.tf b/prod-team-account/ecs/main.tf index 0ab7208..a7966ff 100644 --- a/prod-team-account/ecs/main.tf +++ b/prod-team-account/ecs/main.tf @@ -71,6 +71,12 @@ resource "aws_launch_template" "ecs_launch_template" { iam_instance_profile { name = data.terraform_remote_state.iam.outputs.ecs_instance_profile_name } + + metadata_options { + http_tokens = "required" # 토큰 기반의 IMDSv2만 허용하도록 설정 + http_endpoint = "enabled" + } + network_interfaces { associate_public_ip_address = false security_groups = [data.terraform_remote_state.vpc.outputs.ecs_security_group_id] From 641e70804429823870a761e75b56c3eee0938fe4 Mon Sep 17 00:00:00 2001 From: imyourhopeee <144752063+imyourhopeee@users.noreply.github.com> Date: Mon, 7 Jul 2025 10:30:50 +0900 Subject: [PATCH 24/66] fix fmt --- operation-team-account/ecr/main.tf | 14 +-- prod-team-account/alb/main.tf | 110 ++++++++-------- prod-team-account/codedeploy/main.tf | 30 ++--- prod-team-account/ecs/main.tf | 182 +++++++++++++-------------- prod-team-account/iam/main.tf | 40 +++--- prod-team-account/vpc/main.tf | 182 +++++++++++++-------------- prod-team-account/vpc/outputs.tf | 2 +- 7 files changed, 280 insertions(+), 280 deletions(-) diff --git a/operation-team-account/ecr/main.tf b/operation-team-account/ecr/main.tf index 41fe5d0..b40792e 100644 --- a/operation-team-account/ecr/main.tf +++ b/operation-team-account/ecr/main.tf @@ -1,11 +1,11 @@ terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.0" - } + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" } - + } + } provider "aws" { @@ -27,7 +27,7 @@ data "aws_iam_policy_document" "ecr_repo_policy_document" { sid = "AllowCrossAccountPush" effect = "Allow" principals { - type = "AWS" + type = "AWS" # prod 계정의 역할 ARN은 변수로 전달 identifiers = [data.terraform_remote_state.iam.outputs.github_actions_role_arn] } diff --git a/prod-team-account/alb/main.tf b/prod-team-account/alb/main.tf index 51289b6..3115e61 100644 --- a/prod-team-account/alb/main.tf +++ b/prod-team-account/alb/main.tf @@ -1,11 +1,11 @@ terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.0" - } + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" } - + } + } provider "aws" { @@ -24,9 +24,9 @@ data "terraform_remote_state" "acm" { data "terraform_remote_state" "vpc" { backend = "s3" config = { - bucket = "cloudfence-prod-state" - key = "prod-team-account/vpc/terraform.tfstate" - region = "ap-northeast-2" + bucket = "cloudfence-prod-state" + key = "prod-team-account/vpc/terraform.tfstate" + region = "ap-northeast-2" } } @@ -65,64 +65,64 @@ resource "aws_wafv2_web_acl" "alb_waf" { } } - tags = { - Name = "${var.project_name}-alb-waf" - } + tags = { + Name = "${var.project_name}-alb-waf" + } } # ALB resource "aws_lb" "alb" { - name = "${var.project_name}-alb" - internal = false - load_balancer_type = "application" - security_groups = [data.terraform_remote_state.vpc.outputs.alb_security_group_id] - subnets = data.terraform_remote_state.vpc.outputs.public_subnet_ids + name = "${var.project_name}-alb" + internal = false + load_balancer_type = "application" + security_groups = [data.terraform_remote_state.vpc.outputs.alb_security_group_id] + subnets = data.terraform_remote_state.vpc.outputs.public_subnet_ids drop_invalid_header_fields = true enable_deletion_protection = true - tags = { - Name = "${var.project_name}-alb" - } + tags = { + Name = "${var.project_name}-alb" + } } # Target Group resource "aws_lb_target_group" "blue" { - name = "${var.project_name}-blue-tg" - port = 80 - protocol = "HTTP" - vpc_id = data.terraform_remote_state.vpc.outputs.vpc_id - target_type = "instance" - health_check { - path = "/" - protocol = "HTTP" - interval = 30 - timeout = 5 - healthy_threshold = 2 - unhealthy_threshold = 2 - } - tags = { - Name = "${var.project_name}-blue-tg" - } + name = "${var.project_name}-blue-tg" + port = 80 + protocol = "HTTP" + vpc_id = data.terraform_remote_state.vpc.outputs.vpc_id + target_type = "instance" + health_check { + path = "/" + protocol = "HTTP" + interval = 30 + timeout = 5 + healthy_threshold = 2 + unhealthy_threshold = 2 + } + tags = { + Name = "${var.project_name}-blue-tg" + } } resource "aws_lb_target_group" "green" { - name = "${var.project_name}-green-tg" - port = 80 - protocol = "HTTP" - vpc_id = data.terraform_remote_state.vpc.outputs.vpc_id - target_type = "instance" - health_check { - path = "/" - protocol = "HTTP" - interval = 30 - timeout = 5 - healthy_threshold = 2 - unhealthy_threshold = 2 - } - tags = { - Name = "${var.project_name}-green-tg" - } + name = "${var.project_name}-green-tg" + port = 80 + protocol = "HTTP" + vpc_id = data.terraform_remote_state.vpc.outputs.vpc_id + target_type = "instance" + health_check { + path = "/" + protocol = "HTTP" + interval = 30 + timeout = 5 + healthy_threshold = 2 + unhealthy_threshold = 2 + } + tags = { + Name = "${var.project_name}-green-tg" + } } # ALB 리스너 @@ -154,7 +154,7 @@ resource "aws_lb_listener" "http_redirect" { # WAF와 ALB 연결 resource "aws_wafv2_web_acl_association" "alb_association" { - resource_arn = aws_lb.alb.arn - web_acl_arn = aws_wafv2_web_acl.alb_waf.arn - depends_on = [aws_lb.alb] + resource_arn = aws_lb.alb.arn + web_acl_arn = aws_wafv2_web_acl.alb_waf.arn + depends_on = [aws_lb.alb] } \ No newline at end of file diff --git a/prod-team-account/codedeploy/main.tf b/prod-team-account/codedeploy/main.tf index 2b10f70..3dd3f78 100644 --- a/prod-team-account/codedeploy/main.tf +++ b/prod-team-account/codedeploy/main.tf @@ -1,11 +1,11 @@ terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.0" - } + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" } - + } + } provider "aws" { @@ -41,34 +41,34 @@ data "terraform_remote_state" "ecs" { # CodeDeploy resource "aws_codedeploy_app" "ecs_app" { - name = "${var.project_name}-ecs-app" + name = "${var.project_name}-ecs-app" compute_platform = "ECS" } resource "aws_codedeploy_deployment_group" "ecs_deployment_group" { - app_name = aws_codedeploy_app.ecs_app.name + app_name = aws_codedeploy_app.ecs_app.name deployment_group_name = "${var.project_name}-ecs-deployment-group" - service_role_arn = data.terraform_remote_state.iam.outputs.codedeploy_service_role_arn + service_role_arn = data.terraform_remote_state.iam.outputs.codedeploy_service_role_arn deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" - + ecs_service { cluster_name = data.terraform_remote_state.ecs.outputs.cluster_name service_name = data.terraform_remote_state.ecs.outputs.service_name } deployment_style { - deployment_type = "BLUE_GREEN" + deployment_type = "BLUE_GREEN" deployment_option = "WITH_TRAFFIC_CONTROL" } blue_green_deployment_config { deployment_ready_option { - action_on_timeout = "CONTINUE_DEPLOYMENT" + action_on_timeout = "CONTINUE_DEPLOYMENT" } terminate_blue_instances_on_deployment_success { - action = "TERMINATE" - termination_wait_time_in_minutes = 5 + action = "TERMINATE" + termination_wait_time_in_minutes = 5 } } load_balancer_info { @@ -86,7 +86,7 @@ resource "aws_codedeploy_deployment_group" "ecs_deployment_group" { } auto_rollback_configuration { enabled = true - events = ["DEPLOYMENT_FAILURE"] + events = ["DEPLOYMENT_FAILURE"] } } \ No newline at end of file diff --git a/prod-team-account/ecs/main.tf b/prod-team-account/ecs/main.tf index a7966ff..da59f75 100644 --- a/prod-team-account/ecs/main.tf +++ b/prod-team-account/ecs/main.tf @@ -1,11 +1,11 @@ terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.0" - } + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" } - + } + } provider "aws" { @@ -49,12 +49,12 @@ data "terraform_remote_state" "ecr" { } data "aws_ami" "latest_shared_ami" { - most_recent = true - owners = [var.ami_owner_account_id] # operation-team-account의 AMI - filter { - name = "name" - values = ["WHS-CloudFence-*"] - } + most_recent = true + owners = [var.ami_owner_account_id] # operation-team-account의 AMI + filter { + name = "name" + values = ["WHS-CloudFence-*"] + } } # ECS 클러스터 생성 @@ -65,9 +65,9 @@ resource "aws_ecs_cluster" "ecs_cluster" { # ECS Launch Template resource "aws_launch_template" "ecs_launch_template" { name_prefix = "${var.project_name}-ecs-launch-template-" - image_id = "${data.aws_ami.latest_shared_ami.id}" + image_id = data.aws_ami.latest_shared_ami.id instance_type = "t3.micro" - + iam_instance_profile { name = data.terraform_remote_state.iam.outputs.ecs_instance_profile_name } @@ -82,15 +82,15 @@ resource "aws_launch_template" "ecs_launch_template" { security_groups = [data.terraform_remote_state.vpc.outputs.ecs_security_group_id] } - user_data = base64encode(<<-EOF + user_data = base64encode(<<-EOF #!/bin/bash echo ECS_CLUSTER=${aws_ecs_cluster.ecs_cluster.name} >> /etc/ecs/ecs.config EOF - ) + ) - tags = { - Name = "${var.project_name}-ecs-launch-template" - } + tags = { + Name = "${var.project_name}-ecs-launch-template" + } } # ECS Auto Scaling Group @@ -99,103 +99,103 @@ resource "aws_autoscaling_group" "ecs_auto_scaling_group" { id = aws_launch_template.ecs_launch_template.id version = "$Latest" } - - min_size = 1 - max_size = 4 - desired_capacity = 2 - vpc_zone_identifier = [for subnet in data.terraform_remote_state.vpc.outputs.private_subnet_ids : subnet] - health_check_type = "EC2" - force_delete = true - protect_from_scale_in = true - + + min_size = 1 + max_size = 4 + desired_capacity = 2 + vpc_zone_identifier = [for subnet in data.terraform_remote_state.vpc.outputs.private_subnet_ids : subnet] + health_check_type = "EC2" + force_delete = true + protect_from_scale_in = true + tag { - key = "ECS_Manage" - value = "${var.project_name}-ecs-auto-scaling-group" - propagate_at_launch = true - } + key = "ECS_Manage" + value = "${var.project_name}-ecs-auto-scaling-group" + propagate_at_launch = true + } } # ECS capacity provider resource "aws_ecs_capacity_provider" "ecs_capacity_provider" { - name = "${var.project_name}-ecs-capacity-provider" - auto_scaling_group_provider { - auto_scaling_group_arn = aws_autoscaling_group.ecs_auto_scaling_group.arn - managed_termination_protection = "ENABLED" - managed_scaling { - status = "ENABLED" - target_capacity = 100 - } + name = "${var.project_name}-ecs-capacity-provider" + auto_scaling_group_provider { + auto_scaling_group_arn = aws_autoscaling_group.ecs_auto_scaling_group.arn + managed_termination_protection = "ENABLED" + managed_scaling { + status = "ENABLED" + target_capacity = 100 } + } } # Capacity provider association resource "aws_ecs_cluster_capacity_providers" "ecs_cluster_capacity_providers" { - cluster_name = aws_ecs_cluster.ecs_cluster.name - capacity_providers = [aws_ecs_capacity_provider.ecs_capacity_provider.name] - default_capacity_provider_strategy { - capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name - weight = 100 - base = 1 - } + cluster_name = aws_ecs_cluster.ecs_cluster.name + capacity_providers = [aws_ecs_capacity_provider.ecs_capacity_provider.name] + default_capacity_provider_strategy { + capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name + weight = 100 + base = 1 + } } # ECS Task Definition resource "aws_ecs_task_definition" "ecs_task_definition" { - family = "${var.project_name}-ecs-task" - network_mode = "bridge" - requires_compatibilities = ["EC2"] - execution_role_arn = data.terraform_remote_state.iam.outputs.ecs_task_execution_role_arn - - container_definitions = jsonencode([ + family = "${var.project_name}-ecs-task" + network_mode = "bridge" + requires_compatibilities = ["EC2"] + execution_role_arn = data.terraform_remote_state.iam.outputs.ecs_task_execution_role_arn + + container_definitions = jsonencode([ + { + name = "${var.project_name}-container" + image = "${data.terraform_remote_state.ecr.outputs.repository_url}:latest" + cpu = 256 + memory = 512 + essential = true + portMappings = [ { - name = "${var.project_name}-container" - image = "${data.terraform_remote_state.ecr.outputs.repository_url}:latest" - cpu = 256 - memory = 512 - essential = true - portMappings = [ - { - containerPort = 80 - hostPort = 80 - protocol = "tcp" - } - ] + containerPort = 80 + hostPort = 80 + protocol = "tcp" } - ]) + ] + } + ]) } # ECS Service resource "aws_ecs_service" "ecs_service" { - name = "${var.project_name}-ecs-service" - cluster = aws_ecs_cluster.ecs_cluster.id - task_definition = aws_ecs_task_definition.ecs_task_definition.arn - desired_count = 2 - - - capacity_provider_strategy { - capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name - weight = 100 - } + name = "${var.project_name}-ecs-service" + cluster = aws_ecs_cluster.ecs_cluster.id + task_definition = aws_ecs_task_definition.ecs_task_definition.arn + desired_count = 2 - load_balancer { - target_group_arn = data.terraform_remote_state.alb.outputs.blue_target_group_arn - container_name = "${var.project_name}-container" - container_port = 80 - } - deployment_controller { - type = "CODE_DEPLOY" - } + capacity_provider_strategy { + capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name + weight = 100 + } - lifecycle { - ignore_changes = [task_definition, desired_count] - } + load_balancer { + target_group_arn = data.terraform_remote_state.alb.outputs.blue_target_group_arn + container_name = "${var.project_name}-container" + container_port = 80 + } - health_check_grace_period_seconds = 60 + deployment_controller { + type = "CODE_DEPLOY" + } - tags = { - Name = "${var.project_name}-ecs-service" - } + lifecycle { + ignore_changes = [task_definition, desired_count] + } + + health_check_grace_period_seconds = 60 + + tags = { + Name = "${var.project_name}-ecs-service" + } } \ No newline at end of file diff --git a/prod-team-account/iam/main.tf b/prod-team-account/iam/main.tf index 8746c8a..878016c 100644 --- a/prod-team-account/iam/main.tf +++ b/prod-team-account/iam/main.tf @@ -1,11 +1,11 @@ terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.0" - } + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" } - + } + } provider "aws" { @@ -13,7 +13,7 @@ provider "aws" { } data "aws_iam_role" "github_actions_role" { - name = "Application-Deployment-role2" + name = "Application-Deployment-role2" } # ECS 인스턴스가 사용할 IAM 역할 생성 @@ -57,14 +57,14 @@ resource "aws_iam_instance_profile" "ecs_instance_profile" { resource "aws_iam_role" "ecs_task_execution_role" { name = "${var.project_name}-ecs-task-execution-role" assume_role_policy = jsonencode({ - Version = "2012-10-17", + Version = "2012-10-17", Statement = [ - { - Action = "sts:AssumeRole", - Effect = "Allow", - Principal = { - Service = "ecs-tasks.amazonaws.com" - } + { + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "ecs-tasks.amazonaws.com" + } } ] }) @@ -78,14 +78,14 @@ resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_attachment" { resource "aws_iam_role" "codedeploy_role" { name = "${var.project_name}-codedeploy-role" assume_role_policy = jsonencode({ - Version = "2012-10-17", + Version = "2012-10-17", Statement = [ { - Action = "sts:AssumeRole", - Effect = "Allow", - Principal = { - Service = "codedeploy.amazonaws.com" - } + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "codedeploy.amazonaws.com" + } } ] }) diff --git a/prod-team-account/vpc/main.tf b/prod-team-account/vpc/main.tf index 4c2972e..1f5e919 100644 --- a/prod-team-account/vpc/main.tf +++ b/prod-team-account/vpc/main.tf @@ -1,11 +1,11 @@ terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.0" - } + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" } - + } + } provider "aws" { @@ -14,8 +14,8 @@ provider "aws" { # VPC resource "aws_vpc" "vpc" { - cidr_block = var.vpc_cidr - enable_dns_support = true + cidr_block = var.vpc_cidr + enable_dns_support = true enable_dns_hostnames = true tags = { Name = "${var.project_name}-vpc" @@ -24,114 +24,114 @@ resource "aws_vpc" "vpc" { # subnet(public) resource "aws_subnet" "public1" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.1.0/24" - availability_zone = "ap-northeast-2a" - map_public_ip_on_launch = true - tags = { - Name = "public_subnet1" - } + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.1.0/24" + availability_zone = "ap-northeast-2a" + map_public_ip_on_launch = true + tags = { + Name = "public_subnet1" + } } resource "aws_subnet" "public2" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.2.0/24" - availability_zone = "ap-northeast-2b" - map_public_ip_on_launch = true - tags = { - Name = "public_subnet2" - } + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.2.0/24" + availability_zone = "ap-northeast-2b" + map_public_ip_on_launch = true + tags = { + Name = "public_subnet2" + } } # subnet(private) resource "aws_subnet" "private1" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.101.0/24" - availability_zone = "ap-northeast-2a" - tags = { - Name = "private_subnet1" - } -} + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.101.0/24" + availability_zone = "ap-northeast-2a" + tags = { + Name = "private_subnet1" + } +} resource "aws_subnet" "private2" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.102.0/24" - availability_zone = "ap-northeast-2b" - tags = { - Name = "private_subnet2" - } -} + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.102.0/24" + availability_zone = "ap-northeast-2b" + tags = { + Name = "private_subnet2" + } +} # Internet Gateway resource "aws_internet_gateway" "igw" { - vpc_id = aws_vpc.vpc.id - tags = { - Name = "${var.project_name}-igw" - } + vpc_id = aws_vpc.vpc.id + tags = { + Name = "${var.project_name}-igw" + } } # Public Route Table resource "aws_route_table" "public" { - vpc_id = aws_vpc.vpc.id - route { - cidr_block = "0.0.0.0/0" - gateway_id = aws_internet_gateway.igw.id - } - tags = { - Name = "${var.project_name}-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 = { + Name = "${var.project_name}-public-route-table" + } } # Associate Public Subnets with Route Table resource "aws_route_table_association" "public1" { - subnet_id = aws_subnet.public1.id - route_table_id = aws_route_table.public.id + subnet_id = aws_subnet.public1.id + route_table_id = aws_route_table.public.id } resource "aws_route_table_association" "public2" { - subnet_id = aws_subnet.public2.id - route_table_id = aws_route_table.public.id + subnet_id = aws_subnet.public2.id + route_table_id = aws_route_table.public.id } # NAT Gateway resource "aws_eip" "nat" { - domain = "vpc" - tags = { - Name = "${var.project_name}-nat-eip" - } + domain = "vpc" + tags = { + Name = "${var.project_name}-nat-eip" + } } resource "aws_nat_gateway" "nat" { - allocation_id = aws_eip.nat.id - subnet_id = aws_subnet.public1.id - tags = { - Name = "${var.project_name}-nat-gateway" - } - depends_on = [aws_internet_gateway.igw] + allocation_id = aws_eip.nat.id + subnet_id = aws_subnet.public1.id + tags = { + Name = "${var.project_name}-nat-gateway" + } + depends_on = [aws_internet_gateway.igw] } # Private Route Table resource "aws_route_table" "private" { - vpc_id = aws_vpc.vpc.id - route { - cidr_block = "0.0.0.0/0" - nat_gateway_id = aws_nat_gateway.nat.id - } - tags = { - Name = "${var.project_name}-private-route-table" - } + vpc_id = aws_vpc.vpc.id + route { + cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.nat.id + } + tags = { + Name = "${var.project_name}-private-route-table" + } } # Associate Private Subnets with Route Table resource "aws_route_table_association" "private1" { - subnet_id = aws_subnet.private1.id - route_table_id = aws_route_table.private.id + subnet_id = aws_subnet.private1.id + route_table_id = aws_route_table.private.id } resource "aws_route_table_association" "private2" { - subnet_id = aws_subnet.private2.id - route_table_id = aws_route_table.private.id + subnet_id = aws_subnet.private2.id + route_table_id = aws_route_table.private.id } # security_group @@ -151,7 +151,7 @@ resource "aws_security_group" "alb_sg" { egress { from_port = 0 to_port = 0 - protocol = "-1" + protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } @@ -159,22 +159,22 @@ resource "aws_security_group" "alb_sg" { # ECS resource "aws_security_group" "ecs_sg" { - name = "${var.project_name}-ecs-sg" - description = "Security group for ECS tasks" - vpc_id = aws_vpc.vpc.id - - ingress { - from_port = 80 - to_port = 80 - protocol = "tcp" - security_groups = [aws_security_group.alb_sg.id] - } + name = "${var.project_name}-ecs-sg" + description = "Security group for ECS tasks" + vpc_id = aws_vpc.vpc.id - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + security_groups = [aws_security_group.alb_sg.id] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } } \ No newline at end of file diff --git a/prod-team-account/vpc/outputs.tf b/prod-team-account/vpc/outputs.tf index 257c1a3..0468288 100644 --- a/prod-team-account/vpc/outputs.tf +++ b/prod-team-account/vpc/outputs.tf @@ -9,7 +9,7 @@ output "vpc_id" { output "public_subnet_ids" { description = "A list of public subnet IDs" # network.tf 파일에 정의된 public 서브넷 리소스들의 id를 리스트로 묶어서 출력 - value = [aws_subnet.public1.id, aws_subnet.public2.id] + value = [aws_subnet.public1.id, aws_subnet.public2.id] } # Private Subnet들의 ID 목록을 출력 From a93055fd6e37bcbaa3708bc5dbf2c5edfaa67d19 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 7 Jul 2025 11:19:31 +0900 Subject: [PATCH 25/66] =?UTF-8?q?vpc=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd.yml | 3 ++- prod-team-account/alb/main.tf | 3 ++- prod-team-account/vpc/main.tf | 8 ++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 9ede438..cd947c2 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -61,8 +61,9 @@ jobs: declare -A DEPENDENCY_MAP=( ["prod-team-account/vpc"]="" ["prod-team-account/iam"]="" + ["prod-team-account/acm"]="" ["operation-team-account/ecr"]="prod-team-account/iam" - ["prod-team-account/alb"]="prod-team-account/vpc" + ["prod-team-account/alb"]="prod-team-account/vpc prod-team-account/acm" ["prod-team-account/ecs"]="prod-team-account/vpc prod-team-account/iam prod-team-account/alb operation-team-account/ecr" ["prod-team-account/codedeploy"]="prod-team-account/ecs" ) diff --git a/prod-team-account/alb/main.tf b/prod-team-account/alb/main.tf index 3115e61..f2b02e6 100644 --- a/prod-team-account/alb/main.tf +++ b/prod-team-account/alb/main.tf @@ -71,6 +71,7 @@ resource "aws_wafv2_web_acl" "alb_waf" { } # ALB +#tfsec:ignore:aws-alb-not-public resource "aws_lb" "alb" { name = "${var.project_name}-alb" internal = false @@ -130,7 +131,7 @@ resource "aws_lb_listener" "https" { load_balancer_arn = aws_lb.alb.arn port = 443 protocol = "HTTPS" - ssl_policy = "ELBSecurityPolicy-2016-08" + ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06" certificate_arn = data.terraform_remote_state.acm.outputs.certificate_arn default_action { type = "forward" diff --git a/prod-team-account/vpc/main.tf b/prod-team-account/vpc/main.tf index 1f5e919..d2d1cd9 100644 --- a/prod-team-account/vpc/main.tf +++ b/prod-team-account/vpc/main.tf @@ -23,6 +23,7 @@ resource "aws_vpc" "vpc" { } # subnet(public) +#tfsec:ignore:aws-ec2-subnet-public-ip resource "aws_subnet" "public1" { vpc_id = aws_vpc.vpc.id cidr_block = "10.0.1.0/24" @@ -33,6 +34,7 @@ resource "aws_subnet" "public1" { } } +#tfsec:ignore:aws-ec2-subnet-public-ip resource "aws_subnet" "public2" { vpc_id = aws_vpc.vpc.id cidr_block = "10.0.2.0/24" @@ -135,14 +137,15 @@ resource "aws_route_table_association" "private2" { } # security_group +#tfsec:ignore:aws-ec2-security-group-public-ip-ingress resource "aws_security_group" "alb_sg" { name = "${var.project_name}-alb-sg" description = "Security group for ALB" vpc_id = aws_vpc.vpc.id ingress { - from_port = 80 - to_port = 80 + from_port = 443 + to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] description = "Allow HTTP" @@ -158,6 +161,7 @@ resource "aws_security_group" "alb_sg" { } # ECS +#tfsec:ignore:aws-ec2-security-group-public-ip-ingress resource "aws_security_group" "ecs_sg" { name = "${var.project_name}-ecs-sg" description = "Security group for ECS tasks" From 0eca6c5b4f1b86285713c3a3fadc229a2dc0d507 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 7 Jul 2025 11:26:05 +0900 Subject: [PATCH 26/66] =?UTF-8?q?=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prod-team-account/alb/outputs.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prod-team-account/alb/outputs.tf b/prod-team-account/alb/outputs.tf index 6f12284..db59ab3 100644 --- a/prod-team-account/alb/outputs.tf +++ b/prod-team-account/alb/outputs.tf @@ -5,7 +5,7 @@ output "dns_name" { output "listener_arn" { description = "The ARN of the ALB listener" - value = aws_lb_listener.http.arn + value = aws_lb_listener.https.arn } output "blue_target_group_name" { @@ -21,4 +21,4 @@ output "green_target_group_name" { output "blue_target_group_arn" { description = "The ARN of the blue target group" value = aws_lb_target_group.blue.arn -} \ No newline at end of file +} From ca4ab9e034ee3e1efc54c8b23655878482f8d62a Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 7 Jul 2025 11:28:37 +0900 Subject: [PATCH 27/66] =?UTF-8?q?acm=20backend=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prod-team-account/acm/backend.tf | 1 + 1 file changed, 1 insertion(+) diff --git a/prod-team-account/acm/backend.tf b/prod-team-account/acm/backend.tf index d8f9527..ea45e0d 100644 --- a/prod-team-account/acm/backend.tf +++ b/prod-team-account/acm/backend.tf @@ -4,5 +4,6 @@ terraform { key = "prod-team-account/acm/terraform.tfstate" region = "ap-northeast-2" dynamodb_table = "s3-prod-lock" + encrypt = true } } \ No newline at end of file From 0bbd880fe3121a75e796328902996b90ba0aa310 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Tue, 8 Jul 2025 23:04:24 +0900 Subject: [PATCH 28/66] =?UTF-8?q?tfsec=20scan=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prod-team-account/acm/backend.tf | 2 +- prod-team-account/acm/main.tf | 10 +++++----- prod-team-account/alb/main.tf | 33 +++++++++++++++++++------------- prod-team-account/vpc/main.tf | 13 ++++++++----- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/prod-team-account/acm/backend.tf b/prod-team-account/acm/backend.tf index ea45e0d..7e1e3a0 100644 --- a/prod-team-account/acm/backend.tf +++ b/prod-team-account/acm/backend.tf @@ -1,7 +1,7 @@ terraform { backend "s3" { bucket = "cloudfence-prod-state" - key = "prod-team-account/acm/terraform.tfstate" + key = "prod-team-account/acm/terraform.tfstate" region = "ap-northeast-2" dynamodb_table = "s3-prod-lock" encrypt = true diff --git a/prod-team-account/acm/main.tf b/prod-team-account/acm/main.tf index f3f22fb..bd352b1 100644 --- a/prod-team-account/acm/main.tf +++ b/prod-team-account/acm/main.tf @@ -10,9 +10,9 @@ provider "aws" { # ACM 인증서 요청 resource "aws_acm_certificate" "cert" { - domain_name = var.domain_name + domain_name = var.domain_name subject_alternative_names = ["*.${var.domain_name}"] - validation_method = "DNS" + validation_method = "DNS" lifecycle { create_before_destroy = true @@ -23,9 +23,9 @@ resource "aws_acm_certificate" "cert" { resource "aws_route53_record" "cert_validation" { for_each = { for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name => { - name = dvo.resource_record_name - record = dvo.resource_record_value - type = dvo.resource_record_type + name = dvo.resource_record_name + record = dvo.resource_record_value + type = dvo.resource_record_type } } diff --git a/prod-team-account/alb/main.tf b/prod-team-account/alb/main.tf index f2b02e6..024130a 100644 --- a/prod-team-account/alb/main.tf +++ b/prod-team-account/alb/main.tf @@ -15,9 +15,9 @@ provider "aws" { data "terraform_remote_state" "acm" { backend = "s3" config = { - bucket = "cloudfence-prod-state" - key = "prod-team-account/acm/terraform.tfstate" - region = "ap-northeast-2" + bucket = "cloudfence-prod-state" + key = "prod-team-account/acm/terraform.tfstate" + region = "ap-northeast-2" } } @@ -71,7 +71,8 @@ resource "aws_wafv2_web_acl" "alb_waf" { } # ALB -#tfsec:ignore:aws-alb-not-public +# 외부 사용자를 위한 로드 밸런서이므로 외부에 노출해야해서 tfsec 경고 무시 +#tfsec:ignore:aws-elb-alb-not-public resource "aws_lb" "alb" { name = "${var.project_name}-alb" internal = false @@ -79,8 +80,14 @@ resource "aws_lb" "alb" { security_groups = [data.terraform_remote_state.vpc.outputs.alb_security_group_id] subnets = data.terraform_remote_state.vpc.outputs.public_subnet_ids +<<<<<<< HEAD drop_invalid_header_fields = true enable_deletion_protection = true +======= + + drop_invalid_header_fields = true + enable_deletion_protection = true +>>>>>>> 90ca48c (tfsec scan 문제 해결) tags = { Name = "${var.project_name}-alb" @@ -128,15 +135,15 @@ resource "aws_lb_target_group" "green" { # ALB 리스너 resource "aws_lb_listener" "https" { - load_balancer_arn = aws_lb.alb.arn - port = 443 - protocol = "HTTPS" - ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06" - certificate_arn = data.terraform_remote_state.acm.outputs.certificate_arn - default_action { - type = "forward" - target_group_arn = aws_lb_target_group.blue.arn - } + load_balancer_arn = aws_lb.alb.arn + port = 443 + protocol = "HTTPS" + ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06" + certificate_arn = data.terraform_remote_state.acm.outputs.certificate_arn + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.blue.arn + } } resource "aws_lb_listener" "http_redirect" { diff --git a/prod-team-account/vpc/main.tf b/prod-team-account/vpc/main.tf index d2d1cd9..378c705 100644 --- a/prod-team-account/vpc/main.tf +++ b/prod-team-account/vpc/main.tf @@ -23,7 +23,8 @@ resource "aws_vpc" "vpc" { } # subnet(public) -#tfsec:ignore:aws-ec2-subnet-public-ip +# public 서브넷은 외부에서 접근 가능하도록 tfsec 경고 무시 +#tfsec:ignore:aws-ec2-no-public-ip-subnet resource "aws_subnet" "public1" { vpc_id = aws_vpc.vpc.id cidr_block = "10.0.1.0/24" @@ -34,7 +35,7 @@ resource "aws_subnet" "public1" { } } -#tfsec:ignore:aws-ec2-subnet-public-ip +#tfsec:ignore:aws-ec2-no-public-ip-subnet resource "aws_subnet" "public2" { vpc_id = aws_vpc.vpc.id cidr_block = "10.0.2.0/24" @@ -137,7 +138,8 @@ resource "aws_route_table_association" "private2" { } # security_group -#tfsec:ignore:aws-ec2-security-group-public-ip-ingress +# ALB를 위한 security group에서는 외부 사용자를위해 HTTP(443) 포트만 열고 이후 tfsec 경고 무시 +#tfsec:ignore:aws-ec2-no-public-ingress-sgr resource "aws_security_group" "alb_sg" { name = "${var.project_name}-alb-sg" description = "Security group for ALB" @@ -155,13 +157,14 @@ resource "aws_security_group" "alb_sg" { from_port = 0 to_port = 0 protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] + destination_security_group_id = aws_security_group.ecs_sg.id } } # ECS -#tfsec:ignore:aws-ec2-security-group-public-ip-ingress +# ECS의 security group은 ALB에서 오는 트래픽만 허용하고, 외부로의 모든 트래픽을 허용하므로 tfsec 경고 무시 +#tfsec:ignore:aws-ec2-no-public-egress-sgr resource "aws_security_group" "ecs_sg" { name = "${var.project_name}-ecs-sg" description = "Security group for ECS tasks" From d826715de60602496863473df95019e943e4cec0 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Tue, 8 Jul 2025 23:25:32 +0900 Subject: [PATCH 29/66] =?UTF-8?q?security=5Fgroup=20=EC=A2=85=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit security_group egress 설정 오류 수정 security_group 종속성 문제 해결결 security_group 오류 해결 --- prod-team-account/alb/main.tf | 2 +- prod-team-account/vpc/main.tf | 22 ++++++++++------------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/prod-team-account/alb/main.tf b/prod-team-account/alb/main.tf index 024130a..a964e07 100644 --- a/prod-team-account/alb/main.tf +++ b/prod-team-account/alb/main.tf @@ -146,7 +146,7 @@ resource "aws_lb_listener" "https" { } } -resource "aws_lb_listener" "http_redirect" { +resource "aws_lb_listener" "https_redirect" { load_balancer_arn = aws_lb.alb.arn port = 80 protocol = "HTTP" diff --git a/prod-team-account/vpc/main.tf b/prod-team-account/vpc/main.tf index 378c705..59a1e78 100644 --- a/prod-team-account/vpc/main.tf +++ b/prod-team-account/vpc/main.tf @@ -138,7 +138,7 @@ resource "aws_route_table_association" "private2" { } # security_group -# ALB를 위한 security group에서는 외부 사용자를위해 HTTP(443) 포트만 열고 이후 tfsec 경고 무시 +# ALB를 위한 security group에서는 외부 사용자를위해 HTTPS(443) 포트만 열고 이후 tfsec 경고 무시 #tfsec:ignore:aws-ec2-no-public-ingress-sgr resource "aws_security_group" "alb_sg" { name = "${var.project_name}-alb-sg" @@ -150,16 +150,15 @@ resource "aws_security_group" "alb_sg" { to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] - description = "Allow HTTP" + description = "Allow HTTPS" } egress { - from_port = 0 - to_port = 0 - protocol = "-1" - destination_security_group_id = aws_security_group.ecs_sg.id + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] } - } # ECS @@ -171,9 +170,9 @@ resource "aws_security_group" "ecs_sg" { vpc_id = aws_vpc.vpc.id ingress { - from_port = 80 - to_port = 80 - protocol = "tcp" + from_port = 80 + to_port = 80 + protocol = "tcp" security_groups = [aws_security_group.alb_sg.id] } @@ -183,5 +182,4 @@ resource "aws_security_group" "ecs_sg" { protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } - -} \ No newline at end of file +} From 70cbfad0e36521205fcfdfcf48adf453ada9cee0 Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Sun, 6 Jul 2025 17:13:57 +0900 Subject: [PATCH 30/66] =?UTF-8?q?=EB=A3=A8=ED=8A=B8=20=EB=94=94=EB=A0=89?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EC=B0=B8=EC=A1=B0,=20.tf=20=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=ED=8F=B4=EB=8D=94=EB=8A=94=20=EC=8B=A4=ED=96=89=20?= =?UTF-8?q?X?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd.yml | 26 ++++++++++++++++++++++++++ .github/workflows/ci.yml | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index cd947c2..bbb9427 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -106,11 +106,37 @@ jobs: # 실행할 최종 매트릭스를 JSON 형식으로 생성 MATRIX_ITEMS=() +<<<<<<< HEAD for DIR in $ORDERED_DIRS; do if [[ " ${CHANGED_DIRS[@]} " =~ " ${DIR} " ]]; then ACCOUNT_PREFIX=$(echo $DIR | cut -d- -f1) ROLE_KEY="${ROLE_MAP[$ACCOUNT_PREFIX]}" MATRIX_ITEMS+=("{\"dir\":\"$DIR\",\"role_key\":\"$ROLE_KEY\"}") +======= + + for KEY in "${!ROLE_MAP[@]}"; do + VAR_NAME="FILTER_OUTPUTS_${KEY}" + VALUE="${!VAR_NAME}" + + if [ "$VALUE" = "true" ]; then + BASE_DIR="${KEY}-team-account" + + # 루트 디렉터리 검사 + TF_COUNT_ROOT=$(find "$BASE_DIR" -maxdepth 1 -name '*.tf' | wc -l) + if [ "$TF_COUNT_ROOT" -gt 0 ]; then + MATRIX_ITEMS+=("{\"dir\":\"$BASE_DIR\",\"role_key\":\"${ROLE_MAP[$KEY]}\"}") + fi + + # 하위 디렉터리 검사 + for DIR in $(find $BASE_DIR -type d -mindepth 1); do + if [[ "$DIR" != *".terraform"* && "$DIR" != "$BASE_DIR/modules" ]]; then + TF_COUNT=$(find "$DIR" -maxdepth 1 -name '*.tf' | wc -l) + if [ "$TF_COUNT" -gt 0 ]; then + MATRIX_ITEMS+=("{\"dir\":\"$DIR\",\"role_key\":\"${ROLE_MAP[$KEY]}\"}") + fi + fi + done +>>>>>>> bbb3fbd (루트 디렉토리 참조, .tf 없는 폴더는 실행 X) fi done diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ddd41c7..a6c72f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: echo "Changed files:" echo "$FILES" - declare -A ROLE_MAP=( + declare -A ROLE_MAP=( ["operation-team-account"]="ROLE_ARN_OPERATION" ["identity-team-account"]="ROLE_ARN_IDENTITY" ["prod-team-account"]="ROLE_ARN_PROD" From 08b12c64d61d9275551b6153d63f0719ad32a09f Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Wed, 9 Jul 2025 00:31:45 +0900 Subject: [PATCH 31/66] =?UTF-8?q?alb=5Fsecurity=5Fgroup=20egress=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit alb_security_group egress 규칙 설정 terraform format check --- prod-team-account/vpc/main.tf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/prod-team-account/vpc/main.tf b/prod-team-account/vpc/main.tf index 59a1e78..3811c80 100644 --- a/prod-team-account/vpc/main.tf +++ b/prod-team-account/vpc/main.tf @@ -157,7 +157,7 @@ resource "aws_security_group" "alb_sg" { from_port = 80 to_port = 80 protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] + cidr_blocks = ["10.0.0.0/16"] } } @@ -170,9 +170,9 @@ resource "aws_security_group" "ecs_sg" { vpc_id = aws_vpc.vpc.id ingress { - from_port = 80 - to_port = 80 - protocol = "tcp" + from_port = 80 + to_port = 80 + protocol = "tcp" security_groups = [aws_security_group.alb_sg.id] } From b3b12f1323ada65a852fad055fd45ee4cef48650 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Wed, 9 Jul 2025 02:02:48 +0900 Subject: [PATCH 32/66] =?UTF-8?q?=ED=8F=B4=EB=8D=94=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=20=EA=B0=9C=ED=8E=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 폴더 구조 개편 구조 개선 폴더 구조 변경 --- .github/workflows/cd.yml | 10 ++++---- .gitignore | 3 ++- .terraform.lock.hcl | 25 ------------------- .../{ => deploy}/ecr/backend.tf | 2 +- .../{ => deploy}/ecr/main.tf | 2 +- .../{ => deploy}/ecr/outputs.tf | 0 .../{ => deploy}/ecr/variables.tf | 0 .../{codedeploy => deploy/acm}/backend.tf | 2 +- prod-team-account/{ => deploy}/acm/main.tf | 0 prod-team-account/{ => deploy}/acm/outputs.tf | 0 .../{ => deploy}/acm/variables.tf | 0 prod-team-account/{ => deploy}/alb/backend.tf | 0 prod-team-account/{ => deploy}/alb/main.tf | 4 +-- prod-team-account/{ => deploy}/alb/outputs.tf | 0 .../{ => deploy}/alb/variables.tf | 0 .../deploy/codedeploy/backend.tf | 9 +++++++ .../{ => deploy}/codedeploy/main.tf | 6 ++--- .../{ => deploy}/codedeploy/outputs.tf | 0 .../{ => deploy}/codedeploy/variables.tf | 0 .../{iam => deploy/ecs}/backend.tf | 2 +- prod-team-account/{ => deploy}/ecs/main.tf | 8 +++--- prod-team-account/{ => deploy}/ecs/outputs.tf | 0 .../{ => deploy}/ecs/variables.tf | 0 .../{ecs => deploy/iam}/backend.tf | 2 +- prod-team-account/{ => deploy}/iam/main.tf | 0 prod-team-account/{ => deploy}/iam/outputs.tf | 0 .../{ => deploy}/iam/variables.tf | 0 .../{acm => deploy/vpc}/backend.tf | 2 +- prod-team-account/{ => deploy}/vpc/main.tf | 0 prod-team-account/{ => deploy}/vpc/outputs.tf | 0 .../{ => deploy}/vpc/variables.tf | 0 prod-team-account/vpc/backend.tf | 9 ------- 32 files changed, 31 insertions(+), 55 deletions(-) delete mode 100644 .terraform.lock.hcl rename operation-team-account/{ => deploy}/ecr/backend.tf (71%) rename operation-team-account/{ => deploy}/ecr/main.tf (95%) rename operation-team-account/{ => deploy}/ecr/outputs.tf (100%) rename operation-team-account/{ => deploy}/ecr/variables.tf (100%) rename prod-team-account/{codedeploy => deploy/acm}/backend.tf (74%) rename prod-team-account/{ => deploy}/acm/main.tf (100%) rename prod-team-account/{ => deploy}/acm/outputs.tf (100%) rename prod-team-account/{ => deploy}/acm/variables.tf (100%) rename prod-team-account/{ => deploy}/alb/backend.tf (100%) rename prod-team-account/{ => deploy}/alb/main.tf (96%) rename prod-team-account/{ => deploy}/alb/outputs.tf (100%) rename prod-team-account/{ => deploy}/alb/variables.tf (100%) create mode 100644 prod-team-account/deploy/codedeploy/backend.tf rename prod-team-account/{ => deploy}/codedeploy/main.tf (91%) rename prod-team-account/{ => deploy}/codedeploy/outputs.tf (100%) rename prod-team-account/{ => deploy}/codedeploy/variables.tf (100%) rename prod-team-account/{iam => deploy/ecs}/backend.tf (71%) rename prod-team-account/{ => deploy}/ecs/main.tf (95%) rename prod-team-account/{ => deploy}/ecs/outputs.tf (100%) rename prod-team-account/{ => deploy}/ecs/variables.tf (100%) rename prod-team-account/{ecs => deploy/iam}/backend.tf (71%) rename prod-team-account/{ => deploy}/iam/main.tf (100%) rename prod-team-account/{ => deploy}/iam/outputs.tf (100%) rename prod-team-account/{ => deploy}/iam/variables.tf (100%) rename prod-team-account/{acm => deploy/vpc}/backend.tf (71%) rename prod-team-account/{ => deploy}/vpc/main.tf (100%) rename prod-team-account/{ => deploy}/vpc/outputs.tf (100%) rename prod-team-account/{ => deploy}/vpc/variables.tf (100%) delete mode 100644 prod-team-account/vpc/backend.tf diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index bbb9427..87da2d4 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -62,11 +62,11 @@ jobs: ["prod-team-account/vpc"]="" ["prod-team-account/iam"]="" ["prod-team-account/acm"]="" - ["operation-team-account/ecr"]="prod-team-account/iam" - ["prod-team-account/alb"]="prod-team-account/vpc prod-team-account/acm" - ["prod-team-account/ecs"]="prod-team-account/vpc prod-team-account/iam prod-team-account/alb operation-team-account/ecr" - ["prod-team-account/codedeploy"]="prod-team-account/ecs" - ) + ["operation-team-account/ecr"]="prod-team-account/deploy/iam" + ["prod-team-account/alb"]="prod-team-account/deploy/vpc prod-team-account/deploy/acm" + ["prod-team-account/ecs"]="prod-team-account/deploy/vpc prod-team-account/deploy/iam prod-team-account/deploy/alb operation-team-account/deploy/ecr" + ["prod-team-account/codedeploy"]="prod-team-account/deploy/ecs" + )ㅜ # Push 이벤트에 포함된 변경된 파일 목록을 호출 echo "Comparing changes between ${{ github.event.before }} and ${{ github.event.after }}" diff --git a/.gitignore b/.gitignore index c22a6c3..1668f42 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ *.tfstate.backup *.tfstate.backup.* *.tfstate.backup.json -*.tfstate.backup.json.* \ No newline at end of file +*.tfstate.backup.json.* +.terraform.lock.hcl \ No newline at end of file diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl deleted file mode 100644 index cdc1668..0000000 --- a/.terraform.lock.hcl +++ /dev/null @@ -1,25 +0,0 @@ -# This file is maintained automatically by "terraform init". -# Manual edits may be lost in future updates. - -provider "registry.terraform.io/hashicorp/aws" { - version = "5.100.0" - constraints = "~> 5.0" - hashes = [ - "h1:Ijt7pOlB7Tr7maGQIqtsLFbl7pSMIj06TVdkoSBcYOw=", - "zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644", - "zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2", - "zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274", - "zh:6330766f1d85f01ae6ea90d1b214b8b74cc8c1badc4696b165b36ddd4cc15f7b", - "zh:7c8c2e30d8e55291b86fcb64bdf6c25489d538688545eb48fd74ad622e5d3862", - "zh:99b1003bd9bd32ee323544da897148f46a527f622dc3971af63ea3e251596342", - "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", - "zh:9f8b909d3ec50ade83c8062290378b1ec553edef6a447c56dadc01a99f4eaa93", - "zh:aaef921ff9aabaf8b1869a86d692ebd24fbd4e12c21205034bb679b9caf883a2", - "zh:ac882313207aba00dd5a76dbd572a0ddc818bb9cbf5c9d61b28fe30efaec951e", - "zh:bb64e8aff37becab373a1a0cc1080990785304141af42ed6aa3dd4913b000421", - "zh:dfe495f6621df5540d9c92ad40b8067376350b005c637ea6efac5dc15028add4", - "zh:f0ddf0eaf052766cfe09dea8200a946519f653c384ab4336e2a4a64fdd6310e9", - "zh:f1b7e684f4c7ae1eed272b6de7d2049bb87a0275cb04dbb7cda6636f600699c9", - "zh:ff461571e3f233699bf690db319dfe46aec75e58726636a0d97dd9ac6e32fb70", - ] -} diff --git a/operation-team-account/ecr/backend.tf b/operation-team-account/deploy/ecr/backend.tf similarity index 71% rename from operation-team-account/ecr/backend.tf rename to operation-team-account/deploy/ecr/backend.tf index 11bb7aa..37bc4a0 100644 --- a/operation-team-account/ecr/backend.tf +++ b/operation-team-account/deploy/ecr/backend.tf @@ -1,7 +1,7 @@ terraform { backend "s3" { bucket = "cloudfence-operation-state" - key = "operation-team-account/ecr/terraform.tfstate" + key = "operation-team-account/deploy/ecr/terraform.tfstate" region = "ap-northeast-2" dynamodb_table = "s3-operation-lock" encrypt = true diff --git a/operation-team-account/ecr/main.tf b/operation-team-account/deploy/ecr/main.tf similarity index 95% rename from operation-team-account/ecr/main.tf rename to operation-team-account/deploy/ecr/main.tf index b40792e..76a9acf 100644 --- a/operation-team-account/ecr/main.tf +++ b/operation-team-account/deploy/ecr/main.tf @@ -16,7 +16,7 @@ data "terraform_remote_state" "iam" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/iam/terraform.tfstate" + key = "prod-team-account/deploy/iam/terraform.tfstate" region = "ap-northeast-2" } } diff --git a/operation-team-account/ecr/outputs.tf b/operation-team-account/deploy/ecr/outputs.tf similarity index 100% rename from operation-team-account/ecr/outputs.tf rename to operation-team-account/deploy/ecr/outputs.tf diff --git a/operation-team-account/ecr/variables.tf b/operation-team-account/deploy/ecr/variables.tf similarity index 100% rename from operation-team-account/ecr/variables.tf rename to operation-team-account/deploy/ecr/variables.tf diff --git a/prod-team-account/codedeploy/backend.tf b/prod-team-account/deploy/acm/backend.tf similarity index 74% rename from prod-team-account/codedeploy/backend.tf rename to prod-team-account/deploy/acm/backend.tf index da9681d..5a17e15 100644 --- a/prod-team-account/codedeploy/backend.tf +++ b/prod-team-account/deploy/acm/backend.tf @@ -1,7 +1,7 @@ terraform { backend "s3" { bucket = "cloudfence-prod-state" - key = "prod-team-account/codedeploy/terraform.tfstate" + key = "prod-team-account/deploy/acm/terraform.tfstate" region = "ap-northeast-2" dynamodb_table = "s3-prod-lock" encrypt = true diff --git a/prod-team-account/acm/main.tf b/prod-team-account/deploy/acm/main.tf similarity index 100% rename from prod-team-account/acm/main.tf rename to prod-team-account/deploy/acm/main.tf diff --git a/prod-team-account/acm/outputs.tf b/prod-team-account/deploy/acm/outputs.tf similarity index 100% rename from prod-team-account/acm/outputs.tf rename to prod-team-account/deploy/acm/outputs.tf diff --git a/prod-team-account/acm/variables.tf b/prod-team-account/deploy/acm/variables.tf similarity index 100% rename from prod-team-account/acm/variables.tf rename to prod-team-account/deploy/acm/variables.tf diff --git a/prod-team-account/alb/backend.tf b/prod-team-account/deploy/alb/backend.tf similarity index 100% rename from prod-team-account/alb/backend.tf rename to prod-team-account/deploy/alb/backend.tf diff --git a/prod-team-account/alb/main.tf b/prod-team-account/deploy/alb/main.tf similarity index 96% rename from prod-team-account/alb/main.tf rename to prod-team-account/deploy/alb/main.tf index a964e07..9b53110 100644 --- a/prod-team-account/alb/main.tf +++ b/prod-team-account/deploy/alb/main.tf @@ -16,7 +16,7 @@ data "terraform_remote_state" "acm" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/acm/terraform.tfstate" + key = "prod-team-account/deploy/acm/terraform.tfstate" region = "ap-northeast-2" } } @@ -25,7 +25,7 @@ data "terraform_remote_state" "vpc" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/vpc/terraform.tfstate" + key = "prod-team-account/deploy/vpc/terraform.tfstate" region = "ap-northeast-2" } } diff --git a/prod-team-account/alb/outputs.tf b/prod-team-account/deploy/alb/outputs.tf similarity index 100% rename from prod-team-account/alb/outputs.tf rename to prod-team-account/deploy/alb/outputs.tf diff --git a/prod-team-account/alb/variables.tf b/prod-team-account/deploy/alb/variables.tf similarity index 100% rename from prod-team-account/alb/variables.tf rename to prod-team-account/deploy/alb/variables.tf diff --git a/prod-team-account/deploy/codedeploy/backend.tf b/prod-team-account/deploy/codedeploy/backend.tf new file mode 100644 index 0000000..ceb5543 --- /dev/null +++ b/prod-team-account/deploy/codedeploy/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-prod-state" + key = "prod-team-account/deploy/codedeploy/terraform.tfstate" + region = "ap-northeast-2" + dynamodb_table = "s3-prod-lock" + encrypt = true + } +} \ No newline at end of file diff --git a/prod-team-account/codedeploy/main.tf b/prod-team-account/deploy/codedeploy/main.tf similarity index 91% rename from prod-team-account/codedeploy/main.tf rename to prod-team-account/deploy/codedeploy/main.tf index 3dd3f78..83d2859 100644 --- a/prod-team-account/codedeploy/main.tf +++ b/prod-team-account/deploy/codedeploy/main.tf @@ -16,7 +16,7 @@ data "terraform_remote_state" "alb" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/alb/terraform.tfstate" + key = "prod-team-account/deploy/alb/terraform.tfstate" region = "ap-northeast-2" } } @@ -25,7 +25,7 @@ data "terraform_remote_state" "iam" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/iam/terraform.tfstate" + key = "prod-team-account/deploy/iam/terraform.tfstate" region = "ap-northeast-2" } } @@ -34,7 +34,7 @@ data "terraform_remote_state" "ecs" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/ecs/terraform.tfstate" + key = "prod-team-account/deploy/ecs/terraform.tfstate" region = "ap-northeast-2" } } diff --git a/prod-team-account/codedeploy/outputs.tf b/prod-team-account/deploy/codedeploy/outputs.tf similarity index 100% rename from prod-team-account/codedeploy/outputs.tf rename to prod-team-account/deploy/codedeploy/outputs.tf diff --git a/prod-team-account/codedeploy/variables.tf b/prod-team-account/deploy/codedeploy/variables.tf similarity index 100% rename from prod-team-account/codedeploy/variables.tf rename to prod-team-account/deploy/codedeploy/variables.tf diff --git a/prod-team-account/iam/backend.tf b/prod-team-account/deploy/ecs/backend.tf similarity index 71% rename from prod-team-account/iam/backend.tf rename to prod-team-account/deploy/ecs/backend.tf index 6c16478..094ca0b 100644 --- a/prod-team-account/iam/backend.tf +++ b/prod-team-account/deploy/ecs/backend.tf @@ -1,7 +1,7 @@ terraform { backend "s3" { bucket = "cloudfence-prod-state" - key = "prod-team-account/iam/terraform.tfstate" + key = "prod-team-account/deploy/ecs/terraform.tfstate" region = "ap-northeast-2" dynamodb_table = "s3-prod-lock" encrypt = true diff --git a/prod-team-account/ecs/main.tf b/prod-team-account/deploy/ecs/main.tf similarity index 95% rename from prod-team-account/ecs/main.tf rename to prod-team-account/deploy/ecs/main.tf index da59f75..a96b027 100644 --- a/prod-team-account/ecs/main.tf +++ b/prod-team-account/deploy/ecs/main.tf @@ -16,7 +16,7 @@ data "terraform_remote_state" "vpc" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/vpc/terraform.tfstate" + key = "prod-team-account/deploy/vpc/terraform.tfstate" region = "ap-northeast-2" } } @@ -25,7 +25,7 @@ data "terraform_remote_state" "alb" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/alb/terraform.tfstate" + key = "prod-team-account/deploy/alb/terraform.tfstate" region = "ap-northeast-2" } } @@ -34,7 +34,7 @@ data "terraform_remote_state" "iam" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/iam/terraform.tfstate" + key = "prod-team-account/deploy/iam/terraform.tfstate" region = "ap-northeast-2" } } @@ -43,7 +43,7 @@ data "terraform_remote_state" "ecr" { backend = "s3" config = { bucket = "cloudfence-operation-state" - key = "operation-team-account/ecr/terraform.tfstate" + key = "operation-team-account/deploy/ecr/terraform.tfstate" region = "ap-northeast-2" } } diff --git a/prod-team-account/ecs/outputs.tf b/prod-team-account/deploy/ecs/outputs.tf similarity index 100% rename from prod-team-account/ecs/outputs.tf rename to prod-team-account/deploy/ecs/outputs.tf diff --git a/prod-team-account/ecs/variables.tf b/prod-team-account/deploy/ecs/variables.tf similarity index 100% rename from prod-team-account/ecs/variables.tf rename to prod-team-account/deploy/ecs/variables.tf diff --git a/prod-team-account/ecs/backend.tf b/prod-team-account/deploy/iam/backend.tf similarity index 71% rename from prod-team-account/ecs/backend.tf rename to prod-team-account/deploy/iam/backend.tf index a149188..69d9393 100644 --- a/prod-team-account/ecs/backend.tf +++ b/prod-team-account/deploy/iam/backend.tf @@ -1,7 +1,7 @@ terraform { backend "s3" { bucket = "cloudfence-prod-state" - key = "prod-team-account/ecs/terraform.tfstate" + key = "prod-team-account/deploy/iam/terraform.tfstate" region = "ap-northeast-2" dynamodb_table = "s3-prod-lock" encrypt = true diff --git a/prod-team-account/iam/main.tf b/prod-team-account/deploy/iam/main.tf similarity index 100% rename from prod-team-account/iam/main.tf rename to prod-team-account/deploy/iam/main.tf diff --git a/prod-team-account/iam/outputs.tf b/prod-team-account/deploy/iam/outputs.tf similarity index 100% rename from prod-team-account/iam/outputs.tf rename to prod-team-account/deploy/iam/outputs.tf diff --git a/prod-team-account/iam/variables.tf b/prod-team-account/deploy/iam/variables.tf similarity index 100% rename from prod-team-account/iam/variables.tf rename to prod-team-account/deploy/iam/variables.tf diff --git a/prod-team-account/acm/backend.tf b/prod-team-account/deploy/vpc/backend.tf similarity index 71% rename from prod-team-account/acm/backend.tf rename to prod-team-account/deploy/vpc/backend.tf index 7e1e3a0..828b119 100644 --- a/prod-team-account/acm/backend.tf +++ b/prod-team-account/deploy/vpc/backend.tf @@ -1,7 +1,7 @@ terraform { backend "s3" { bucket = "cloudfence-prod-state" - key = "prod-team-account/acm/terraform.tfstate" + key = "prod-team-account/deploy/vpc/terraform.tfstate" region = "ap-northeast-2" dynamodb_table = "s3-prod-lock" encrypt = true diff --git a/prod-team-account/vpc/main.tf b/prod-team-account/deploy/vpc/main.tf similarity index 100% rename from prod-team-account/vpc/main.tf rename to prod-team-account/deploy/vpc/main.tf diff --git a/prod-team-account/vpc/outputs.tf b/prod-team-account/deploy/vpc/outputs.tf similarity index 100% rename from prod-team-account/vpc/outputs.tf rename to prod-team-account/deploy/vpc/outputs.tf diff --git a/prod-team-account/vpc/variables.tf b/prod-team-account/deploy/vpc/variables.tf similarity index 100% rename from prod-team-account/vpc/variables.tf rename to prod-team-account/deploy/vpc/variables.tf diff --git a/prod-team-account/vpc/backend.tf b/prod-team-account/vpc/backend.tf deleted file mode 100644 index 1662372..0000000 --- a/prod-team-account/vpc/backend.tf +++ /dev/null @@ -1,9 +0,0 @@ -terraform { - backend "s3" { - bucket = "cloudfence-prod-state" - key = "prod-team-account/vpc/terraform.tfstate" - region = "ap-northeast-2" - dynamodb_table = "s3-prod-lock" - encrypt = true - } -} \ No newline at end of file From 86d76be043583f7d3d3929d9a197756b3f93cfd1 Mon Sep 17 00:00:00 2001 From: Choi Yunho <151824752+yunhoch0i@users.noreply.github.com> Date: Sat, 12 Jul 2025 22:31:35 +0900 Subject: [PATCH 33/66] Update main.tf --- prod-team-account/deploy/alb/main.tf | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/prod-team-account/deploy/alb/main.tf b/prod-team-account/deploy/alb/main.tf index 9b53110..73d5d54 100644 --- a/prod-team-account/deploy/alb/main.tf +++ b/prod-team-account/deploy/alb/main.tf @@ -80,14 +80,10 @@ resource "aws_lb" "alb" { security_groups = [data.terraform_remote_state.vpc.outputs.alb_security_group_id] subnets = data.terraform_remote_state.vpc.outputs.public_subnet_ids -<<<<<<< HEAD + drop_invalid_header_fields = true enable_deletion_protection = true -======= - drop_invalid_header_fields = true - enable_deletion_protection = true ->>>>>>> 90ca48c (tfsec scan 문제 해결) tags = { Name = "${var.project_name}-alb" @@ -165,4 +161,4 @@ resource "aws_wafv2_web_acl_association" "alb_association" { resource_arn = aws_lb.alb.arn web_acl_arn = aws_wafv2_web_acl.alb_waf.arn depends_on = [aws_lb.alb] -} \ No newline at end of file +} From 955eec6fe556bd2b0b0c71180e0ab847aa3221c8 Mon Sep 17 00:00:00 2001 From: Choi Yunho <151824752+yunhoch0i@users.noreply.github.com> Date: Sat, 12 Jul 2025 22:33:39 +0900 Subject: [PATCH 34/66] Update cd.yml --- .github/workflows/cd.yml | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 87da2d4..7845c8e 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -66,7 +66,7 @@ jobs: ["prod-team-account/alb"]="prod-team-account/deploy/vpc prod-team-account/deploy/acm" ["prod-team-account/ecs"]="prod-team-account/deploy/vpc prod-team-account/deploy/iam prod-team-account/deploy/alb operation-team-account/deploy/ecr" ["prod-team-account/codedeploy"]="prod-team-account/deploy/ecs" - )ㅜ + ) # Push 이벤트에 포함된 변경된 파일 목록을 호출 echo "Comparing changes between ${{ github.event.before }} and ${{ github.event.after }}" @@ -106,37 +106,13 @@ jobs: # 실행할 최종 매트릭스를 JSON 형식으로 생성 MATRIX_ITEMS=() -<<<<<<< HEAD + for DIR in $ORDERED_DIRS; do if [[ " ${CHANGED_DIRS[@]} " =~ " ${DIR} " ]]; then ACCOUNT_PREFIX=$(echo $DIR | cut -d- -f1) ROLE_KEY="${ROLE_MAP[$ACCOUNT_PREFIX]}" MATRIX_ITEMS+=("{\"dir\":\"$DIR\",\"role_key\":\"$ROLE_KEY\"}") -======= - - for KEY in "${!ROLE_MAP[@]}"; do - VAR_NAME="FILTER_OUTPUTS_${KEY}" - VALUE="${!VAR_NAME}" - - if [ "$VALUE" = "true" ]; then - BASE_DIR="${KEY}-team-account" - - # 루트 디렉터리 검사 - TF_COUNT_ROOT=$(find "$BASE_DIR" -maxdepth 1 -name '*.tf' | wc -l) - if [ "$TF_COUNT_ROOT" -gt 0 ]; then - MATRIX_ITEMS+=("{\"dir\":\"$BASE_DIR\",\"role_key\":\"${ROLE_MAP[$KEY]}\"}") - fi - - # 하위 디렉터리 검사 - for DIR in $(find $BASE_DIR -type d -mindepth 1); do - if [[ "$DIR" != *".terraform"* && "$DIR" != "$BASE_DIR/modules" ]]; then - TF_COUNT=$(find "$DIR" -maxdepth 1 -name '*.tf' | wc -l) - if [ "$TF_COUNT" -gt 0 ]; then - MATRIX_ITEMS+=("{\"dir\":\"$DIR\",\"role_key\":\"${ROLE_MAP[$KEY]}\"}") - fi - fi - done ->>>>>>> bbb3fbd (루트 디렉토리 참조, .tf 없는 폴더는 실행 X) + fi done From 8ac1608eda4e9c31b64518750092029dcc4030d2 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 14 Jul 2025 16:23:33 +0900 Subject: [PATCH 35/66] =?UTF-8?q?backend=20key=20=EA=B0=92=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- operation-team-account/deploy/ecr/backend.tf | 2 +- operation-team-account/deploy/ecr/main.tf | 2 +- prod-team-account/deploy/acm/backend.tf | 2 +- prod-team-account/deploy/alb/backend.tf | 2 +- prod-team-account/deploy/alb/main.tf | 9 ++++----- prod-team-account/deploy/codedeploy/backend.tf | 2 +- prod-team-account/deploy/codedeploy/main.tf | 6 +++--- prod-team-account/deploy/ecs/backend.tf | 2 +- prod-team-account/deploy/ecs/main.tf | 8 ++++---- prod-team-account/deploy/iam/backend.tf | 2 +- prod-team-account/deploy/vpc/backend.tf | 2 +- 11 files changed, 19 insertions(+), 20 deletions(-) diff --git a/operation-team-account/deploy/ecr/backend.tf b/operation-team-account/deploy/ecr/backend.tf index 37bc4a0..54d2205 100644 --- a/operation-team-account/deploy/ecr/backend.tf +++ b/operation-team-account/deploy/ecr/backend.tf @@ -1,7 +1,7 @@ terraform { backend "s3" { bucket = "cloudfence-operation-state" - key = "operation-team-account/deploy/ecr/terraform.tfstate" + key = "deploy/ecr.tfstate" region = "ap-northeast-2" dynamodb_table = "s3-operation-lock" encrypt = true diff --git a/operation-team-account/deploy/ecr/main.tf b/operation-team-account/deploy/ecr/main.tf index 76a9acf..b80ba7a 100644 --- a/operation-team-account/deploy/ecr/main.tf +++ b/operation-team-account/deploy/ecr/main.tf @@ -16,7 +16,7 @@ data "terraform_remote_state" "iam" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/deploy/iam/terraform.tfstate" + key = "deploy/iam.tfstate" region = "ap-northeast-2" } } diff --git a/prod-team-account/deploy/acm/backend.tf b/prod-team-account/deploy/acm/backend.tf index 5a17e15..029da1b 100644 --- a/prod-team-account/deploy/acm/backend.tf +++ b/prod-team-account/deploy/acm/backend.tf @@ -1,7 +1,7 @@ terraform { backend "s3" { bucket = "cloudfence-prod-state" - key = "prod-team-account/deploy/acm/terraform.tfstate" + key = "deploy/acm.tfstate" region = "ap-northeast-2" dynamodb_table = "s3-prod-lock" encrypt = true diff --git a/prod-team-account/deploy/alb/backend.tf b/prod-team-account/deploy/alb/backend.tf index db6c068..467f717 100644 --- a/prod-team-account/deploy/alb/backend.tf +++ b/prod-team-account/deploy/alb/backend.tf @@ -1,7 +1,7 @@ terraform { backend "s3" { bucket = "cloudfence-prod-state" - key = "prod-team-account/alb/terraform.tfstate" + key = "deploy/alb.tfstate" region = "ap-northeast-2" dynamodb_table = "s3-prod-lock" encrypt = true diff --git a/prod-team-account/deploy/alb/main.tf b/prod-team-account/deploy/alb/main.tf index 73d5d54..ea36832 100644 --- a/prod-team-account/deploy/alb/main.tf +++ b/prod-team-account/deploy/alb/main.tf @@ -16,7 +16,7 @@ data "terraform_remote_state" "acm" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/deploy/acm/terraform.tfstate" + key = "deploy/acm.tfstate" region = "ap-northeast-2" } } @@ -25,7 +25,7 @@ data "terraform_remote_state" "vpc" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/deploy/vpc/terraform.tfstate" + key = "deploy/vpc.tfstate" region = "ap-northeast-2" } } @@ -81,9 +81,8 @@ resource "aws_lb" "alb" { subnets = data.terraform_remote_state.vpc.outputs.public_subnet_ids - drop_invalid_header_fields = true - enable_deletion_protection = true - + drop_invalid_header_fields = true + enable_deletion_protection = true tags = { Name = "${var.project_name}-alb" diff --git a/prod-team-account/deploy/codedeploy/backend.tf b/prod-team-account/deploy/codedeploy/backend.tf index ceb5543..449e823 100644 --- a/prod-team-account/deploy/codedeploy/backend.tf +++ b/prod-team-account/deploy/codedeploy/backend.tf @@ -1,7 +1,7 @@ terraform { backend "s3" { bucket = "cloudfence-prod-state" - key = "prod-team-account/deploy/codedeploy/terraform.tfstate" + key = "deploy/codedeploy.tfstate" region = "ap-northeast-2" dynamodb_table = "s3-prod-lock" encrypt = true diff --git a/prod-team-account/deploy/codedeploy/main.tf b/prod-team-account/deploy/codedeploy/main.tf index 83d2859..b8708ca 100644 --- a/prod-team-account/deploy/codedeploy/main.tf +++ b/prod-team-account/deploy/codedeploy/main.tf @@ -16,7 +16,7 @@ data "terraform_remote_state" "alb" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/deploy/alb/terraform.tfstate" + key = "deploy/alb.tfstate" region = "ap-northeast-2" } } @@ -25,7 +25,7 @@ data "terraform_remote_state" "iam" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/deploy/iam/terraform.tfstate" + key = "deploy/iam.tfstate" region = "ap-northeast-2" } } @@ -34,7 +34,7 @@ data "terraform_remote_state" "ecs" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/deploy/ecs/terraform.tfstate" + key = "deploy/ecs.tfstate" region = "ap-northeast-2" } } diff --git a/prod-team-account/deploy/ecs/backend.tf b/prod-team-account/deploy/ecs/backend.tf index 094ca0b..7486722 100644 --- a/prod-team-account/deploy/ecs/backend.tf +++ b/prod-team-account/deploy/ecs/backend.tf @@ -1,7 +1,7 @@ terraform { backend "s3" { bucket = "cloudfence-prod-state" - key = "prod-team-account/deploy/ecs/terraform.tfstate" + key = "deploy/ecs.tfstate" region = "ap-northeast-2" dynamodb_table = "s3-prod-lock" encrypt = true diff --git a/prod-team-account/deploy/ecs/main.tf b/prod-team-account/deploy/ecs/main.tf index a96b027..f576105 100644 --- a/prod-team-account/deploy/ecs/main.tf +++ b/prod-team-account/deploy/ecs/main.tf @@ -16,7 +16,7 @@ data "terraform_remote_state" "vpc" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/deploy/vpc/terraform.tfstate" + key = "deploy/vpc.tfstate" region = "ap-northeast-2" } } @@ -25,7 +25,7 @@ data "terraform_remote_state" "alb" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/deploy/alb/terraform.tfstate" + key = "deploy/alb.tfstate" region = "ap-northeast-2" } } @@ -34,7 +34,7 @@ data "terraform_remote_state" "iam" { backend = "s3" config = { bucket = "cloudfence-prod-state" - key = "prod-team-account/deploy/iam/terraform.tfstate" + key = "deploy/iam.tfstate" region = "ap-northeast-2" } } @@ -43,7 +43,7 @@ data "terraform_remote_state" "ecr" { backend = "s3" config = { bucket = "cloudfence-operation-state" - key = "operation-team-account/deploy/ecr/terraform.tfstate" + key = "deploy/ecr.tfstate" region = "ap-northeast-2" } } diff --git a/prod-team-account/deploy/iam/backend.tf b/prod-team-account/deploy/iam/backend.tf index 69d9393..4c3ee40 100644 --- a/prod-team-account/deploy/iam/backend.tf +++ b/prod-team-account/deploy/iam/backend.tf @@ -1,7 +1,7 @@ terraform { backend "s3" { bucket = "cloudfence-prod-state" - key = "prod-team-account/deploy/iam/terraform.tfstate" + key = "deploy/iam.tfstate" region = "ap-northeast-2" dynamodb_table = "s3-prod-lock" encrypt = true diff --git a/prod-team-account/deploy/vpc/backend.tf b/prod-team-account/deploy/vpc/backend.tf index 828b119..b0113bd 100644 --- a/prod-team-account/deploy/vpc/backend.tf +++ b/prod-team-account/deploy/vpc/backend.tf @@ -1,7 +1,7 @@ terraform { backend "s3" { bucket = "cloudfence-prod-state" - key = "prod-team-account/deploy/vpc/terraform.tfstate" + key = "deploy/vpc.tfstate" region = "ap-northeast-2" dynamodb_table = "s3-prod-lock" encrypt = true From 41c62349a268d6dab2e6c9b2f3775c17e968876d Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Thu, 10 Jul 2025 00:04:36 +0900 Subject: [PATCH 36/66] =?UTF-8?q?OIDC=20=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80,=20ci/cd=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd.yml | 29 ++++---- .github/workflows/ci.yml | 79 +++++++++++++--------- dev-team-account/OIDC/iam/backend.tf | 0 dev-team-account/OIDC/iam/main.tf | 41 +++++++++++ identity-team-account/OIDC/iam/backend.tf | 9 +++ identity-team-account/OIDC/iam/main.tf | 74 ++++++++++++++++++++ modules/github_oidc/main.tf | 47 +++++++++++++ modules/github_oidc/outputs.tf | 4 ++ modules/github_oidc/variables.tf | 20 ++++++ operation-team-account/OIDC/iam/backend.tf | 9 +++ operation-team-account/OIDC/iam/main.tf | 75 ++++++++++++++++++++ prod-team-account/OIDC/iam/backend.tf | 11 +++ prod-team-account/OIDC/iam/main.tf | 44 ++++++++++++ stage-team-account/OIDC/iam/backend.tf | 9 +++ stage-team-account/OIDC/iam/main.tf | 44 ++++++++++++ 15 files changed, 451 insertions(+), 44 deletions(-) create mode 100644 dev-team-account/OIDC/iam/backend.tf create mode 100644 dev-team-account/OIDC/iam/main.tf create mode 100644 identity-team-account/OIDC/iam/backend.tf create mode 100644 identity-team-account/OIDC/iam/main.tf create mode 100644 modules/github_oidc/main.tf create mode 100644 modules/github_oidc/outputs.tf create mode 100644 modules/github_oidc/variables.tf create mode 100644 operation-team-account/OIDC/iam/backend.tf create mode 100644 operation-team-account/OIDC/iam/main.tf create mode 100644 prod-team-account/OIDC/iam/backend.tf create mode 100644 prod-team-account/OIDC/iam/main.tf create mode 100644 stage-team-account/OIDC/iam/backend.tf create mode 100644 stage-team-account/OIDC/iam/main.tf diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 499224e..a10678c 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -2,24 +2,24 @@ name: Terraform Apply on: push: - branches: [main] + branches: [main] # main 브랜치에 push될 때 실행 permissions: - contents: read - id-token: write + contents: read # 코드 리포지토리 읽기 권한 + id-token: write # OIDC 인증을 위한 ID 토큰 발급 권한 jobs: detect-changes: runs-on: ubuntu-latest outputs: - matrix: ${{ steps.set.outputs.matrix }} + matrix: ${{ steps.set.outputs.matrix }} # 다음 job에 전달할 matrix 출력 steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v3 # 현재 리포지토리 코드 체크아웃 - name: Filter Paths id: filter - uses: dorny/paths-filter@v3 + uses: dorny/paths-filter@v3 # 어떤 디렉토리에 변경이 있는지 필터링 with: filters: | operation: @@ -40,6 +40,7 @@ jobs: - name: Build Matrix from Filter (with subdirs) id: set env: + # 필터링된 결과를 환경변수로 받아옴 FILTER_OUTPUTS_operation: ${{ steps.filter.outputs.operation }} FILTER_OUTPUTS_identity: ${{ steps.filter.outputs.identity }} FILTER_OUTPUTS_prod: ${{ steps.filter.outputs.prod }} @@ -48,6 +49,7 @@ jobs: FILTER_OUTPUTS_stage: ${{ steps.filter.outputs.stage }} FILTER_OUTPUTS_management: ${{ steps.filter.outputs.management }} run: | + # 계정 별 IAM Role Key 매핑 declare -A ROLE_MAP=( ["operation"]="ROLE_ARN_OPERATION" ["identity"]="ROLE_ARN_IDENTITY" @@ -133,6 +135,7 @@ jobs: fi done + # 최종 matrix JSON 출력 if [ ${#MATRIX_ITEMS[@]} -eq 0 ]; then echo "matrix=[]" >> $GITHUB_OUTPUT else @@ -140,8 +143,8 @@ jobs: fi terraform-apply: - needs: detect-changes - if: ${{ needs.detect-changes.outputs.matrix != '[]' }} + needs: detect-changes # detect-changes job 이후 실행 + if: ${{ needs.detect-changes.outputs.matrix != '[]' }} # 변경사항이 있을 경우에만 실행 runs-on: ubuntu-latest strategy: @@ -158,17 +161,17 @@ jobs: uses: aws-actions/configure-aws-credentials@v4 with: aws-region: ap-northeast-2 - role-to-assume: ${{ secrets[matrix.role_key] }} + role-to-assume: ${{ secrets[matrix.role_key] }} # OIDC 기반으로 계정별 IAM Role Assume - name: Setup Terraform uses: hashicorp/setup-terraform@v1 with: - terraform_version: 1.4.0 + terraform_version: 1.4.0 # Terraform 버전 명시 - name: Terraform Init - run: terraform init - working-directory: ${{ matrix.dir }} + run: terraform init # Terraform 초기화: 백엔드 설정 및 provider 다운로드 + working-directory: ${{ matrix.dir }} # matrix로 전달된 디렉토리에서 실행 - name: Terraform Apply - run: terraform apply -auto-approve + run: terraform apply -auto-approve # 사용자 승인 없이 자동 적용 working-directory: ${{ matrix.dir }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17e4911..af35bcf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,51 +2,48 @@ name: Application-Deployment CI on: - pull_request: + pull_request: # PR이 main 브랜치로 올 때 실행 branches: [main] paths: - "operation-team-account/**" - "identity-team-account/**" - "prod-team-account/**" - "dev-team-account/**" - - "security-team-account/**" - "stage-team-account/**" - - "management-team-account/**" permissions: - contents: read - pull-requests: write - id-token: write + contents: read # 코드 읽을 수 있는 권한 + pull-requests: write # PR 코멘트 작성 등 가능 + id-token: write # OIDC기반 인증에 필요 jobs: detect-changes: runs-on: ubuntu-latest outputs: - matrix: ${{ steps.set.outputs.matrix }} + matrix: ${{ steps.set.outputs.matrix }} # 다음 job에서 사용할 matrix 값 steps: - name: Checkout Code uses: actions/checkout@v3 with: - fetch-depth: 0 + fetch-depth: 0 # git diff가 정상 동작하려면 전체 커밋 히스토리를 가져와야 함 - - name: Fetch origin/main + - name: Fetch origin/main # main 브랜치 히스토리 가져오기 run: git fetch origin main - name: Detect Changed Directories & Build Matrix id: set run: | - FILES=$(git diff --name-only origin/main...${{ github.sha }}) + FILES=$(git diff --name-only origin/main...${{ github.sha }}) # 변경된 파일 목록 echo "Changed files:" echo "$FILES" - declare -A ROLE_MAP=( + # 각 account별 디렉토리에 해당하는 IAM Role 매핑 + declare -A ROLE_MAP=( ["operation-team-account"]="ROLE_ARN_OPERATION" ["identity-team-account"]="ROLE_ARN_IDENTITY" ["prod-team-account"]="ROLE_ARN_PROD" ["dev-team-account"]="ROLE_ARN_DEV" - ["security-team-account"]="ROLE_ARN_SECURITY" ["stage-team-account"]="ROLE_ARN_STAGE" - ["management-team-account"]="ROLE_ARN_MANAGEMENT" ) TMP_FILE=$(mktemp) @@ -75,9 +72,7 @@ jobs: fi done - # 중복 제거 - UNIQUE_LINES=$(sort $TMP_FILE | uniq) - + # 중복 제거 및 JSON 포맷 생성 MATRIX_JSON="[" FIRST=1 @@ -103,13 +98,13 @@ jobs: terraform-ci: needs: detect-changes - if: ${{ needs.detect-changes.outputs.matrix != '[]' }} + if: ${{ needs.detect-changes.outputs.matrix != '[]' }} # 변경사항이 있을 때만 실행 runs-on: ubuntu-latest strategy: matrix: - include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} - fail-fast: false + include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} # 변경된 디렉토리 목록 기반 실행 + fail-fast: false # 하나 실패해도 나머지는 계속 실행 env: INFRACOST_API_KEY: ${{ secrets.INFRACOST_API_KEY }} @@ -119,14 +114,16 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v3 + with: + fetch-depth: 0 - - name: Configure AWS Credentials + - name: Configure AWS Credentials # 각 matrix.role_key에 해당하는 AWS Role Assume uses: aws-actions/configure-aws-credentials@v4 with: aws-region: ap-northeast-2 role-to-assume: ${{ secrets[matrix.role_key] }} - - name: Install tfsec + - name: Install tfsec # 보안 취약점 스캐너 설치 run: | curl -sSL https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash @@ -138,24 +135,34 @@ jobs: with: terraform_version: 1.4.0 - - name: Terraform Init + - name: Terraform Init # Terraform 초기화: backend 구성 및 provider 다운로드 run: terraform init working-directory: ${{ matrix.dir }} - name: Terraform Format Check - run: terraform fmt -check -recursive + run: terraform fmt -check -recursive # 코드 스타일 검사 working-directory: ${{ matrix.dir }} - name: Terraform Validate - run: terraform validate + run: terraform validate # Terraform 구성 파일이 유효한지 문법 및 구조 검사 working-directory: ${{ matrix.dir }} - name: Terraform Plan id: plan + continue-on-error: true #plan 실패해도 워크플로우 진행 run: | - terraform plan -no-color -out=tfplan.binary - terraform show -no-color tfplan.binary > plan.txt - terraform show -json tfplan.binary > plan.json + START_TIME=$(date -u +"%Y-%m-%d %H:%M:%S UTC") + echo "START_TIME=$START_TIME" >> $GITHUB_ENV + + PLAN_FILE=tfplan.binary + PLAN_TXT=plan.txt + + terraform plan -no-color -out=$PLAN_FILE || echo "PLAN_FAILED=true" >> $GITHUB_ENV + terraform show -no-color $PLAN_FILE > $PLAN_TXT || echo "Plan failed" > $PLAN_TXT + + # plan 출력값을 멀티라인 환경 변수에 저장 + PLAN_CONTENT=$(cat $PLAN_TXT | sed 's/`/\\`/g') + echo 'PLAN<> $GITHUB_OUTPUT cat plan.txt >> $GITHUB_OUTPUT echo 'EOF' >> $GITHUB_OUTPUT @@ -167,12 +174,22 @@ jobs: with: issue-number: ${{ github.event.pull_request.number }} body: | - ### Terraform Plan Output for `${{ matrix.dir }}` + ## [Terraform Plan Summary] + | 항목 | 값 | + |-----------------|-----| + | **Status** | `${{ steps.plan.outcome }}` | + | **Directory** | `${{ matrix.dir }}` | + | **Executed At** | `${{ env.START_TIME }}` | + + + --- + + ### Plan Output ```hcl - ${{ steps.plan.outputs.PLAN }} + ${{ steps.plan.outputs.PLAN_CONTENT }} ``` - - name: Setup Infracost + - name: Setup Infracost # 비용 예측 도구 세팅 uses: infracost/actions/setup@v2 - name: Infracost Breakdown @@ -187,4 +204,4 @@ jobs: uses: infracost/actions/comment@v1 with: path: ${{ matrix.dir }}/infracost.json - behavior: update + behavior: update # 기존 코멘트 업데이트 diff --git a/dev-team-account/OIDC/iam/backend.tf b/dev-team-account/OIDC/iam/backend.tf new file mode 100644 index 0000000..e69de29 diff --git a/dev-team-account/OIDC/iam/main.tf b/dev-team-account/OIDC/iam/main.tf new file mode 100644 index 0000000..d1c174f --- /dev/null +++ b/dev-team-account/OIDC/iam/main.tf @@ -0,0 +1,41 @@ +# modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 +# +module "github_oidc" { + source = "../../../modules/github_oidc" + + role_name = "application-deployment-role1" + + # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 + sub_condition = "repo:WHS-DevSecOps-infra/Application-Deployment:*" + + + + # 이 role에 연결할 정책들(IAM 정책 ARN) + policy_arns = [ + "arn:aws:iam::aws:policy/AdministratorAccess" + ] +} + +#tfsec:ignore:aws-iam-no-policy-wildcards +resource "aws_iam_role_policy" "custom_inline_policy" { + name = "dev-role" + role = module.github_oidc.oidc_role_name # 모듈에서 출력된 role이름 참조 + + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Sid" : "VisualEditor0", + "Effect" : "Allow", + "Action" : [ + "rds:*", + "s3:*", + "ec2:*", + "kms:*", + "dynamodb:*" + ], + "Resource" : "*" + } + ] + }) +} diff --git a/identity-team-account/OIDC/iam/backend.tf b/identity-team-account/OIDC/iam/backend.tf new file mode 100644 index 0000000..7f4b07a --- /dev/null +++ b/identity-team-account/OIDC/iam/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-identity-state" + key = "OIDC/iam.tfstate" + region = "ap-northeast-2" + encrypt = true + dynamodb_table = "s3-identity-lock" + } +} \ No newline at end of file diff --git a/identity-team-account/OIDC/iam/main.tf b/identity-team-account/OIDC/iam/main.tf new file mode 100644 index 0000000..97b6f44 --- /dev/null +++ b/identity-team-account/OIDC/iam/main.tf @@ -0,0 +1,74 @@ +# identity-team-account의 main.tf +# modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 + +module "github_oidc" { + source = "../../../modules/github_oidc" + + role_name = "Organization-role" + + # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 + sub_condition = "repo:WHS-DevSecOps-infra/Application-Deployment:*" + + + # 이 role에 연결할 정책들(IAM 정책 ARN) + policy_arns = [ + "arn:aws:iam::aws:policy/AdministratorAccess" + ] +} + +#tfsec:ignore:aws-iam-no-policy-wildcards +resource "aws_iam_role_policy" "custom_inline_policy" { + name = "org-role" + role = module.github_oidc.oidc_role_name # 모듈에서 출력된 role이름 참조 + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Action = [ + "sso:CreatePermissionSet", + "sso:DescribePermissionSet", + "sso:UpdatePermissionSet", + "sso:DeletePermissionSet", + "sso:AttachManagedPolicyToPermissionSet", + "sso:ListPermissionSets", + "sso:ListInstances", + "sso:ProvisionPermissionSet", + "sso:PutInlinePolicyToPermissionSet", + "sso:DeleteInlinePolicyFromPermissionSet", + "sso:ListPermissionSetProvisioningStatus", + "organizations:*", + "identitystore:*" + ], + Resource = "*" + }, + { + "Sid" : "S3Access", + "Effect" : "Allow", + "Action" : [ + "s3:*" + ], + "Resource" : [ + "*" + ] + }, + { + "Sid" : "KMSAccess", + "Effect" : "Allow", + "Action" : [ + "kms:*" + ], + "Resource" : "*" + }, + { + "Sid" : "DynamoDBAccess", + "Effect" : "Allow", + "Action" : [ + "dynamodb:*" + ], + "Resource" : "*" + } + ] + }) +} diff --git a/modules/github_oidc/main.tf b/modules/github_oidc/main.tf new file mode 100644 index 0000000..d2cdcf6 --- /dev/null +++ b/modules/github_oidc/main.tf @@ -0,0 +1,47 @@ +#현재 계정정보를 가져옴 +data "aws_caller_identity" "current" {} + + +provider "aws" { + region = "ap-northeast-2" + +} + + +# GitHub Actions용 OIDC provider 설정 +resource "aws_iam_openid_connect_provider" "github" { + url = "https://token.actions.githubusercontent.com" + + client_id_list = [ + "sts.amazonaws.com" # OIDC에서 사용할 클라이언트 ID + ] + + thumbprint_list = [ + "6938fd4d98bab03faadb97b34396831e3780aea1" # GitHub 공식 인증서 지문 thumbprint(공식값) + ] +} + +# oidc_role 이라는 이름의 IAM Role +resource "aws_iam_role" "oidc_role" { + name = var.role_name # 생성할 Role 이름 + + # GitHub에서 이 역할을 assume할 수 있게 설정 + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Principal = { + Federated = aws_iam_openid_connect_provider.github.arn # 위에서 만든 OIDC Provider의 ARN + }, + Action = "sts:AssumeRoleWithWebIdentity", + Condition = { + StringLike = { + # 어떤 GitHub repo에서만 이 Role을 사용할 수 있는지 제어 + "token.actions.githubusercontent.com:sub" : var.sub_condition + } + } + } + ] + }) +} \ No newline at end of file diff --git a/modules/github_oidc/outputs.tf b/modules/github_oidc/outputs.tf new file mode 100644 index 0000000..56e363e --- /dev/null +++ b/modules/github_oidc/outputs.tf @@ -0,0 +1,4 @@ +output "oidc_role_name" { + description = "OIDC로 생성된 IAM Role 이름" + value = aws_iam_role.oidc_role.name +} \ No newline at end of file diff --git a/modules/github_oidc/variables.tf b/modules/github_oidc/variables.tf new file mode 100644 index 0000000..d5dd127 --- /dev/null +++ b/modules/github_oidc/variables.tf @@ -0,0 +1,20 @@ +variable "role_name" { + type = string + description = "OIDC 역할 이름" +} + +variable "sub_condition" { + type = string + description = "OIDC Subject (sub) 조건" +} + +variable "policy_arns" { + type = list(string) + description = "Attach할 IAM 정책 목록" +} + +resource "aws_iam_role_policy_attachment" "attach_policy" { + for_each = toset(var.policy_arns) + role = aws_iam_role.oidc_role.name + policy_arn = each.value +} diff --git a/operation-team-account/OIDC/iam/backend.tf b/operation-team-account/OIDC/iam/backend.tf new file mode 100644 index 0000000..2b14d75 --- /dev/null +++ b/operation-team-account/OIDC/iam/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-operation-state" + key = "OIDC/iam.tfstate" + region = "ap-northeast-2" + encrypt = true + dynamodb_table = "s3-operation-lock" + } +} \ No newline at end of file diff --git a/operation-team-account/OIDC/iam/main.tf b/operation-team-account/OIDC/iam/main.tf new file mode 100644 index 0000000..081b464 --- /dev/null +++ b/operation-team-account/OIDC/iam/main.tf @@ -0,0 +1,75 @@ +# modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 + + + + +module "github_oidc" { + source = "../../../modules/github_oidc" + + role_name = "operation-cicd" + + # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 + sub_condition = "repo:WHS-DevSecOps-infra/Application-Deployment:*" + + + # 이 role에 연결할 정책들(IAM 정책 ARN) + policy_arns = [ + "arn:aws:iam::aws:policy/AmazonEC2FullAccess", + "arn:aws:iam::aws:policy/AmazonECS_FullAccess", + "arn:aws:iam::aws:policy/AmazonElasticContainerRegistryPublicFullAccess", + "arn:aws:iam::aws:policy/AmazonVPCFullAccess", + "arn:aws:iam::aws:policy/AWSCodeDeployFullAccess", + "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS", + "arn:aws:iam::aws:policy/AWSWAFFullAccess", + "arn:aws:iam::aws:policy/IAMFullAccess" + ] +} + +#tfsec:ignore:aws-iam-no-policy-wildcards +resource "aws_iam_role_policy" "custom_inline_policy" { + name = "operation-cicd" + role = module.github_oidc.oidc_role_name # 모듈에서 출력된 role이름 참조 + + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Effect" : "Allow", + "Action" : [ + "s3:ListBucket", + "s3:GetObject", + "s3:PutObject", + "s3:*", + "sts:AssumeRole" + ], + "Resource" : [ + "*" + ] + }, + { + "Effect" : "Allow", + "Action" : [ + "cloudwatch:*", + "cloudtrail:*" + ], + "Resource" : "*" + }, + { + "Sid" : "KMSAccess", + "Effect" : "Allow", + "Action" : [ + "kms:*" + ], + "Resource" : "*" + }, + { + "Sid" : "DynamoDBAccess", + "Effect" : "Allow", + "Action" : [ + "dynamodb:*" + ], + "Resource" : "*" + } + ] + }) +} diff --git a/prod-team-account/OIDC/iam/backend.tf b/prod-team-account/OIDC/iam/backend.tf new file mode 100644 index 0000000..ec45978 --- /dev/null +++ b/prod-team-account/OIDC/iam/backend.tf @@ -0,0 +1,11 @@ + +terraform { + backend "s3" { + bucket = "cloudfence-prod-state" + key = "OIDC/iam.tfstate" + region = "ap-northeast-2" + encrypt = true + dynamodb_table = "s3-prod-lock" + + } +} \ No newline at end of file diff --git a/prod-team-account/OIDC/iam/main.tf b/prod-team-account/OIDC/iam/main.tf new file mode 100644 index 0000000..e7e8bd4 --- /dev/null +++ b/prod-team-account/OIDC/iam/main.tf @@ -0,0 +1,44 @@ +# prod-team-account의 main.tf +# modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 + +module "github_oidc" { + source = "../../../modules/github_oidc" + + role_name = "Application-Deployment-role2" + + # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 + sub_condition = "repo:WHS-DevSecOps-infra/Application-Deployment:*" + + + # 이 role에 연결할 정책들(IAM 정책 ARN) + policy_arns = [ + "arn:aws:iam::aws:policy/AdministratorAccess" + ] +} + +data "aws_caller_identity" "current" {} + + +#tfsec:ignore:aws-iam-no-policy-wildcards +resource "aws_iam_role_policy" "custom_inline_policy" { + name = "prod-role" + role = module.github_oidc.oidc_role_name # 모듈에서 출력된 role이름 참조 + + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Sid" : "VisualEditor0", + "Effect" : "Allow", + "Action" : [ + "rds:*", + "s3:*", + "ec2:*", + "dynamodb:*", + "kms:*" + ], + "Resource" : "*" + } + ] + }) +} \ No newline at end of file diff --git a/stage-team-account/OIDC/iam/backend.tf b/stage-team-account/OIDC/iam/backend.tf new file mode 100644 index 0000000..1b3bf8a --- /dev/null +++ b/stage-team-account/OIDC/iam/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-stage-state" + key = "OIDC/iam.tfstate" + region = "ap-northeast-2" + encrypt = true + dynamodb_table = "s3-stage-lock" + } +} \ No newline at end of file diff --git a/stage-team-account/OIDC/iam/main.tf b/stage-team-account/OIDC/iam/main.tf new file mode 100644 index 0000000..7a5954a --- /dev/null +++ b/stage-team-account/OIDC/iam/main.tf @@ -0,0 +1,44 @@ +# stage-team-account의 main.tf +# modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 + +module "github_oidc" { + source = "../../../modules/github_oidc" + + role_name = "Application-deployment-role3" + + # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 + sub_condition = "repo:WHS-DevSecOps-infra/Application-Deployment:*" + + + # 이 role에 연결할 정책들(IAM 정책 ARN) + policy_arns = [ + "arn:aws:iam::aws:policy/AdministratorAccess" + ] +} + + + + +#tfsec:ignore:aws-iam-no-policy-wildcards +resource "aws_iam_role_policy" "custom_inline_policy" { + name = "stage-role" + role = module.github_oidc.oidc_role_name # 모듈에서 출력된 role이름 참조 + + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Sid" : "VisualEditor0", + "Effect" : "Allow", + "Action" : [ + "s3:*", + "ec2:*", + "rds:*", + "dynamodb:*", + "kms:*" + ], + "Resource" : "*" + } + ] + }) +} \ No newline at end of file From 80c54c09d9aa3dcef2e5539abb77fe0f8e0a0678 Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Thu, 10 Jul 2025 00:19:53 +0900 Subject: [PATCH 37/66] =?UTF-8?q?ci=20=EC=A4=91=EB=B3=B5=EC=A0=9C=EA=B1=B0?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 1 + dev-team-account/OIDC/iam/backend.tf | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index af35bcf..6285860 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,6 +73,7 @@ jobs: done # 중복 제거 및 JSON 포맷 생성 + UNIQUE_LINES=$(sort $TMP_FILE | uniq) MATRIX_JSON="[" FIRST=1 diff --git a/dev-team-account/OIDC/iam/backend.tf b/dev-team-account/OIDC/iam/backend.tf index e69de29..d0d6476 100644 --- a/dev-team-account/OIDC/iam/backend.tf +++ b/dev-team-account/OIDC/iam/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-dev-state" + key = "OIDC/iam.tfstate" + region = "ap-northeast-2" + encrypt = true + dynamodb_table = "s3-dev-lock" + } +} \ No newline at end of file From 8e506be62a127cde4daf22c9ece2651231264a2a Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Thu, 10 Jul 2025 00:24:37 +0900 Subject: [PATCH 38/66] =?UTF-8?q?=ED=8F=AC=EB=A7=B7=20=EC=B2=B4=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/github_oidc/main.tf | 4 ++-- prod-team-account/OIDC/iam/backend.tf | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/github_oidc/main.tf b/modules/github_oidc/main.tf index d2cdcf6..dfe8a3f 100644 --- a/modules/github_oidc/main.tf +++ b/modules/github_oidc/main.tf @@ -3,8 +3,8 @@ data "aws_caller_identity" "current" {} provider "aws" { - region = "ap-northeast-2" - + region = "ap-northeast-2" + } diff --git a/prod-team-account/OIDC/iam/backend.tf b/prod-team-account/OIDC/iam/backend.tf index ec45978..006fcd1 100644 --- a/prod-team-account/OIDC/iam/backend.tf +++ b/prod-team-account/OIDC/iam/backend.tf @@ -6,6 +6,6 @@ terraform { region = "ap-northeast-2" encrypt = true dynamodb_table = "s3-prod-lock" - + } } \ No newline at end of file From 6c48f96899112bf12cf144d9ca3357042022c490 Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Thu, 10 Jul 2025 01:21:06 +0900 Subject: [PATCH 39/66] =?UTF-8?q?iam=20=EC=A0=95=EC=B1=85=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dev-team-account/OIDC/iam/main.tf | 3 ++- identity-team-account/OIDC/iam/main.tf | 12 +++++++++++- stage-team-account/OIDC/iam/main.tf | 3 ++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/dev-team-account/OIDC/iam/main.tf b/dev-team-account/OIDC/iam/main.tf index d1c174f..fa681a5 100644 --- a/dev-team-account/OIDC/iam/main.tf +++ b/dev-team-account/OIDC/iam/main.tf @@ -32,7 +32,8 @@ resource "aws_iam_role_policy" "custom_inline_policy" { "s3:*", "ec2:*", "kms:*", - "dynamodb:*" + "dynamodb:*", + "iam:*" ], "Resource" : "*" } diff --git a/identity-team-account/OIDC/iam/main.tf b/identity-team-account/OIDC/iam/main.tf index 97b6f44..5aafec3 100644 --- a/identity-team-account/OIDC/iam/main.tf +++ b/identity-team-account/OIDC/iam/main.tf @@ -68,7 +68,17 @@ resource "aws_iam_role_policy" "custom_inline_policy" { "dynamodb:*" ], "Resource" : "*" - } + }, + { + "Sid": "Statement3", + "Effect": "Allow", + "Action": [ + "iam:*" + ], + "Resource": [ + "*" + ] + } ] }) } diff --git a/stage-team-account/OIDC/iam/main.tf b/stage-team-account/OIDC/iam/main.tf index 7a5954a..97141c0 100644 --- a/stage-team-account/OIDC/iam/main.tf +++ b/stage-team-account/OIDC/iam/main.tf @@ -35,7 +35,8 @@ resource "aws_iam_role_policy" "custom_inline_policy" { "ec2:*", "rds:*", "dynamodb:*", - "kms:*" + "kms:*", + "iam:*" ], "Resource" : "*" } From 73e5af70e4e6619f93a38533289690eda2bcf6a2 Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Thu, 10 Jul 2025 01:23:09 +0900 Subject: [PATCH 40/66] =?UTF-8?q?=ED=8F=AC=EB=A7=B7=20=EC=B2=B4=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- identity-team-account/OIDC/iam/main.tf | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/identity-team-account/OIDC/iam/main.tf b/identity-team-account/OIDC/iam/main.tf index 5aafec3..ea798fb 100644 --- a/identity-team-account/OIDC/iam/main.tf +++ b/identity-team-account/OIDC/iam/main.tf @@ -70,15 +70,15 @@ resource "aws_iam_role_policy" "custom_inline_policy" { "Resource" : "*" }, { - "Sid": "Statement3", - "Effect": "Allow", - "Action": [ - "iam:*" - ], - "Resource": [ - "*" - ] - } + "Sid" : "Statement3", + "Effect" : "Allow", + "Action" : [ + "iam:*" + ], + "Resource" : [ + "*" + ] + } ] }) } From 4c35fb947396a4d1cc3c3563e3ad79b8f2b2516f Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Sun, 13 Jul 2025 16:28:06 +0900 Subject: [PATCH 41/66] =?UTF-8?q?OIDC=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20PR=20output=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd.yml | 10 +-- .github/workflows/ci.yml | 45 ++++++++-- dev-team-account/OIDC/iam/main.tf | 11 +-- identity-team-account/OIDC/iam/main.tf | 51 +++++++----- modules/github_oidc/main.tf | 57 +++++++------ modules/github_oidc/variables.tf | 20 ++++- operation-team-account/OIDC/iam/main.tf | 75 +++++++++++++---- prod-team-account/OIDC/iam/main.tf | 104 ++++++++++++++++++++++-- stage-team-account/OIDC/iam/main.tf | 10 +-- 9 files changed, 285 insertions(+), 98 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index a10678c..9e756d5 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -30,12 +30,9 @@ jobs: - 'prod-team-account/**' dev: - 'dev-team-account/**' - security: - - 'security-team-account/**' stage: - 'stage-team-account/**' - management: - - 'management-team-account/**' + - name: Build Matrix from Filter (with subdirs) id: set @@ -45,9 +42,7 @@ jobs: FILTER_OUTPUTS_identity: ${{ steps.filter.outputs.identity }} FILTER_OUTPUTS_prod: ${{ steps.filter.outputs.prod }} FILTER_OUTPUTS_dev: ${{ steps.filter.outputs.dev }} - FILTER_OUTPUTS_security: ${{ steps.filter.outputs.security }} FILTER_OUTPUTS_stage: ${{ steps.filter.outputs.stage }} - FILTER_OUTPUTS_management: ${{ steps.filter.outputs.management }} run: | # 계정 별 IAM Role Key 매핑 declare -A ROLE_MAP=( @@ -55,9 +50,8 @@ jobs: ["identity"]="ROLE_ARN_IDENTITY" ["prod"]="ROLE_ARN_PROD" ["dev"]="ROLE_ARN_DEV" - ["security"]="ROLE_ARN_SECURITY" ["stage"]="ROLE_ARN_STAGE" - ["management"]="ROLE_ARN_MANAGEMENT" + ) declare -A DEPENDENCY_MAP=( diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6285860..157750b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,7 @@ on: - "dev-team-account/**" - "stage-team-account/**" + permissions: contents: read # 코드 읽을 수 있는 권한 pull-requests: write # PR 코멘트 작성 등 가능 @@ -44,6 +45,7 @@ jobs: ["prod-team-account"]="ROLE_ARN_PROD" ["dev-team-account"]="ROLE_ARN_DEV" ["stage-team-account"]="ROLE_ARN_STAGE" + ) TMP_FILE=$(mktemp) @@ -150,7 +152,7 @@ jobs: - name: Terraform Plan id: plan - continue-on-error: true #plan 실패해도 워크플로우 진행 + continue-on-error: true run: | START_TIME=$(date -u +"%Y-%m-%d %H:%M:%S UTC") echo "START_TIME=$START_TIME" >> $GITHUB_ENV @@ -158,15 +160,40 @@ jobs: PLAN_FILE=tfplan.binary PLAN_TXT=plan.txt - terraform plan -no-color -out=$PLAN_FILE || echo "PLAN_FAILED=true" >> $GITHUB_ENV - terraform show -no-color $PLAN_FILE > $PLAN_TXT || echo "Plan failed" > $PLAN_TXT + # 기본 실패 메시지 + PLAN_CONTENT="Terraform plan failed." - # plan 출력값을 멀티라인 환경 변수에 저장 - PLAN_CONTENT=$(cat $PLAN_TXT | sed 's/`/\\`/g') + # Terraform Plan 실행 + if terraform plan -no-color -out=$PLAN_FILE; then + echo "[INFO] terraform plan succeeded." + + # show 및 no change 여부 판단 + terraform show -no-color $PLAN_FILE > $PLAN_TXT - echo 'PLAN<> $GITHUB_OUTPUT - cat plan.txt >> $GITHUB_OUTPUT - echo 'EOF' >> $GITHUB_OUTPUT + IS_EMPTY=$(terraform show -json $PLAN_FILE 2>/dev/null | jq -e '.resource_changes == []' 2>/dev/null || echo "false") + + if [ "$IS_EMPTY" = "true" ]; then + PLAN_CONTENT="(plan success) No changes to apply. Everything looks good!" + else + # 길이 제한 없이 전체 내용 출력 (출력 너무 클 경우 GitHub가 거부할 수 있음 — 감수한 상태) + PLAN_CONTENT=$( + cat $PLAN_TXT | \ + sed 's/`/\\`/g' | \ + sed 's/::/%::%/g' | \ + tr -d '\r' | \ + sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" + ) + fi + else + echo "[ERROR] terraform plan failed. Skipping show." + echo "Terraform plan failed." > $PLAN_TXT + fi + + { + echo "PLAN_CONTENT<> "$GITHUB_OUTPUT" working-directory: ${{ matrix.dir }} - name: Comment Terraform Plan on PR @@ -205,4 +232,4 @@ jobs: uses: infracost/actions/comment@v1 with: path: ${{ matrix.dir }}/infracost.json - behavior: update # 기존 코멘트 업데이트 + behavior: update # 기존 코멘트 업데이트 \ No newline at end of file diff --git a/dev-team-account/OIDC/iam/main.tf b/dev-team-account/OIDC/iam/main.tf index fa681a5..591f5cb 100644 --- a/dev-team-account/OIDC/iam/main.tf +++ b/dev-team-account/OIDC/iam/main.tf @@ -6,14 +6,15 @@ module "github_oidc" { role_name = "application-deployment-role1" # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 - sub_condition = "repo:WHS-DevSecOps-infra/Application-Deployment:*" - + sub_condition = ["repo:WHS-DevSecOps-infra/Organization:*", + "repo:WHS-DevSecOps-infra/Application-Deployment:*"] + thumbprint_list = [ + "d89e3bd43d5d909b47a18977aa9d5ce36cee184c" + ] # 이 role에 연결할 정책들(IAM 정책 ARN) - policy_arns = [ - "arn:aws:iam::aws:policy/AdministratorAccess" - ] + policy_arns = [] } #tfsec:ignore:aws-iam-no-policy-wildcards diff --git a/identity-team-account/OIDC/iam/main.tf b/identity-team-account/OIDC/iam/main.tf index ea798fb..55cd384 100644 --- a/identity-team-account/OIDC/iam/main.tf +++ b/identity-team-account/OIDC/iam/main.tf @@ -7,12 +7,17 @@ module "github_oidc" { role_name = "Organization-role" # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 - sub_condition = "repo:WHS-DevSecOps-infra/Application-Deployment:*" + sub_condition = ["repo:WHS-DevSecOps-infra/Organization:*", + "repo:WHS-DevSecOps-infra/Application-Deployment:*", + "repo:WHS-DevSecOps-infra/Monitoring:*"] # 이 role에 연결할 정책들(IAM 정책 ARN) policy_arns = [ - "arn:aws:iam::aws:policy/AdministratorAccess" + + ] + thumbprint_list = [ + "d89e3bd43d5d909b47a18977aa9d5ce36cee184c" ] } @@ -22,26 +27,16 @@ resource "aws_iam_role_policy" "custom_inline_policy" { role = module.github_oidc.oidc_role_name # 모듈에서 출력된 role이름 참조 policy = jsonencode({ - Version = "2012-10-17", - Statement = [ + "Version" : "2012-10-17", + "Statement" : [ { - Effect = "Allow", - Action = [ - "sso:CreatePermissionSet", - "sso:DescribePermissionSet", - "sso:UpdatePermissionSet", - "sso:DeletePermissionSet", - "sso:AttachManagedPolicyToPermissionSet", - "sso:ListPermissionSets", - "sso:ListInstances", - "sso:ProvisionPermissionSet", - "sso:PutInlinePolicyToPermissionSet", - "sso:DeleteInlinePolicyFromPermissionSet", - "sso:ListPermissionSetProvisioningStatus", - "organizations:*", - "identitystore:*" + "Sid" : "SSOAccess", + "Effect" : "Allow", + "Action" : [ + "sso:*", + "sso:CreateAccountAssignment" ], - Resource = "*" + "Resource" : "*" }, { "Sid" : "S3Access", @@ -69,6 +64,22 @@ resource "aws_iam_role_policy" "custom_inline_policy" { ], "Resource" : "*" }, + { + "Sid" : "Statement1", + "Effect" : "Allow", + "Action" : [ + "organizations:*" + ], + "Resource" : "*" + }, + { + "Sid" : "Statement2", + "Effect" : "Allow", + "Action" : [ + "identitystore:*" + ], + "Resource" : "*" + }, { "Sid" : "Statement3", "Effect" : "Allow", diff --git a/modules/github_oidc/main.tf b/modules/github_oidc/main.tf index dfe8a3f..1e5482f 100644 --- a/modules/github_oidc/main.tf +++ b/modules/github_oidc/main.tf @@ -1,14 +1,8 @@ -#현재 계정정보를 가져옴 -data "aws_caller_identity" "current" {} - provider "aws" { region = "ap-northeast-2" } - - -# GitHub Actions용 OIDC provider 설정 resource "aws_iam_openid_connect_provider" "github" { url = "https://token.actions.githubusercontent.com" @@ -16,32 +10,43 @@ resource "aws_iam_openid_connect_provider" "github" { "sts.amazonaws.com" # OIDC에서 사용할 클라이언트 ID ] - thumbprint_list = [ - "6938fd4d98bab03faadb97b34396831e3780aea1" # GitHub 공식 인증서 지문 thumbprint(공식값) - ] + thumbprint_list = var.thumbprint_list } -# oidc_role 이라는 이름의 IAM Role resource "aws_iam_role" "oidc_role" { - name = var.role_name # 생성할 Role 이름 + name = var.role_name + description = "cicd" - # GitHub에서 이 역할을 assume할 수 있게 설정 assume_role_policy = jsonencode({ Version = "2012-10-17", - Statement = [ - { - Effect = "Allow", - Principal = { - Federated = aws_iam_openid_connect_provider.github.arn # 위에서 만든 OIDC Provider의 ARN - }, - Action = "sts:AssumeRoleWithWebIdentity", - Condition = { - StringLike = { - # 어떤 GitHub repo에서만 이 Role을 사용할 수 있는지 제어 - "token.actions.githubusercontent.com:sub" : var.sub_condition + Statement = concat( + [ + { + Effect = "Allow", + Principal = { + Federated = aws_iam_openid_connect_provider.github.arn + }, + Action = "sts:AssumeRoleWithWebIdentity", + Condition = { + StringEquals = { + "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com" + }, + StringLike = { + "token.actions.githubusercontent.com:sub" = var.sub_condition + } } } - } - ] + ], + var.add_root_trust ? [ + { + Effect = "Allow", + Principal = { + AWS = "arn:aws:iam::${var.account_id}:root" + }, + Action = "sts:AssumeRole" + } + ] : [] + ) + }) -} \ No newline at end of file +} diff --git a/modules/github_oidc/variables.tf b/modules/github_oidc/variables.tf index d5dd127..d2f17a5 100644 --- a/modules/github_oidc/variables.tf +++ b/modules/github_oidc/variables.tf @@ -4,7 +4,7 @@ variable "role_name" { } variable "sub_condition" { - type = string + type = list(string) description = "OIDC Subject (sub) 조건" } @@ -18,3 +18,21 @@ resource "aws_iam_role_policy_attachment" "attach_policy" { role = aws_iam_role.oidc_role.name policy_arn = each.value } + +variable "thumbprint_list" { + description = "OIDC provider thumbprint list" + type = list(string) + default = ["6938fd4d98bab03faadb97b34396831e3780aea1"] +} + +variable "add_root_trust" { + description = "Whether to add root account trust" + type = bool + default = false +} + +variable "account_id" { + description = "AWS account ID for root trust" + type = string + default = "" +} diff --git a/operation-team-account/OIDC/iam/main.tf b/operation-team-account/OIDC/iam/main.tf index 081b464..fa5ea6b 100644 --- a/operation-team-account/OIDC/iam/main.tf +++ b/operation-team-account/OIDC/iam/main.tf @@ -6,23 +6,23 @@ module "github_oidc" { source = "../../../modules/github_oidc" - role_name = "operation-cicd" + role_name = "operation-cicd" + account_id = "502676416967" + add_root_trust = true - # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 - sub_condition = "repo:WHS-DevSecOps-infra/Application-Deployment:*" + # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 + sub_condition = ["repo:WHS-DevSecOps-infra/Organization:*", + "repo:WHS-DevSecOps-infra/Monitoring:*", + "repo:WHS-DevSecOps-infra/Application-Deployment:*", + "repo:yunhoch0i/Application-Deployment:*"] + thumbprint_list = ["d89e3bd43d5d909b47a18977aa9d5ce36cee184c"] # 이 role에 연결할 정책들(IAM 정책 ARN) - policy_arns = [ - "arn:aws:iam::aws:policy/AmazonEC2FullAccess", - "arn:aws:iam::aws:policy/AmazonECS_FullAccess", - "arn:aws:iam::aws:policy/AmazonElasticContainerRegistryPublicFullAccess", - "arn:aws:iam::aws:policy/AmazonVPCFullAccess", - "arn:aws:iam::aws:policy/AWSCodeDeployFullAccess", - "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS", - "arn:aws:iam::aws:policy/AWSWAFFullAccess", - "arn:aws:iam::aws:policy/IAMFullAccess" - ] + policy_arns = [] + + + } #tfsec:ignore:aws-iam-no-policy-wildcards @@ -42,9 +42,7 @@ resource "aws_iam_role_policy" "custom_inline_policy" { "s3:*", "sts:AssumeRole" ], - "Resource" : [ - "*" - ] + "Resource" : ["*"] }, { "Effect" : "Allow", @@ -69,7 +67,50 @@ resource "aws_iam_role_policy" "custom_inline_policy" { "dynamodb:*" ], "Resource" : "*" + }, + { + "Sid" : "TerraformBackendOperationState", + "Effect" : "Allow", + "Action" : [ + "s3:GetObject", + "s3:PutObject", + "s3:ListBucket" + ], + "Resource" : [ + "arn:aws:s3:::cloudfence-operation-state", + "arn:aws:s3:::cloudfence-operation-state/*" + ] + }, + { + "Sid" : "TerraformDynamoDBLock", + "Effect" : "Allow", + "Action" : [ + "dynamodb:GetItem", + "dynamodb:PutItem", + "dynamodb:DeleteItem" + ], + "Resource" : "arn:aws:dynamodb:*:*:table/s3-operation-lock" + }, + { + "Sid" : "KMSAccessForState", + "Effect" : "Allow", + "Action" : [ + "kms:Decrypt", + "kms:Encrypt", + "kms:GenerateDataKey" + ], + "Resource" : "arn:aws:kms:ap-northeast-2:502676416967:key/9901c9d1-8b00-47a9-bd7a-53cfc1f70d25" + }, + { + "Sid" : "ECRAndIAMManagement", + "Effect" : "Allow", + "Action" : [ + "ecr:*", + "iam:CreateServiceLinkedRole" + ], + "Resource" : "*" } ] - }) + } + ) } diff --git a/prod-team-account/OIDC/iam/main.tf b/prod-team-account/OIDC/iam/main.tf index e7e8bd4..c4163a5 100644 --- a/prod-team-account/OIDC/iam/main.tf +++ b/prod-team-account/OIDC/iam/main.tf @@ -4,16 +4,15 @@ module "github_oidc" { source = "../../../modules/github_oidc" - role_name = "Application-Deployment-role2" - + role_name = "Application-Deployment-role2" + add_root_trust = false # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 - sub_condition = "repo:WHS-DevSecOps-infra/Application-Deployment:*" - + sub_condition = ["repo:WHS-DevSecOps-infra/Organization:*", + "repo:WHS-DevSecOps-infra/Application-Deployment:*"] + thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"] # 이 role에 연결할 정책들(IAM 정책 ARN) - policy_arns = [ - "arn:aws:iam::aws:policy/AdministratorAccess" - ] + policy_arns = [] } data "aws_caller_identity" "current" {} @@ -38,6 +37,97 @@ resource "aws_iam_role_policy" "custom_inline_policy" { "kms:*" ], "Resource" : "*" + }, + { + "Sid" : "TerraformBackendProdState", + "Effect" : "Allow", + "Action" : [ + "s3:GetObject", + "s3:PutObject", + "s3:ListBucket" + ], + "Resource" : [ + "arn:aws:s3:::cloudfence-prod-state", + "arn:aws:s3:::cloudfence-prod-state/*" + ] + }, + { + "Sid" : "TerraformBackendOperationState", + "Effect" : "Allow", + "Action" : [ + "s3:GetObject", + "s3:ListBucket" + ], + "Resource" : [ + "arn:aws:s3:::cloudfence-operation-state", + "arn:aws:s3:::cloudfence-operation-state/*" + ] + }, + { + "Sid" : "TerraformDynamoDBLock", + "Effect" : "Allow", + "Action" : [ + "dynamodb:GetItem", + "dynamodb:PutItem", + "dynamodb:DeleteItem" + ], + "Resource" : "arn:aws:dynamodb:*:*:table/s3-operation-lock" + }, + { + "Sid" : "KMSDecryptForStateFiles", + "Effect" : "Allow", + "Action" : [ + "kms:Decrypt", + "kms:Encrypt", + "kms:GenerateDataKey" + ], + "Resource" : "arn:aws:kms:ap-northeast-2:243359234795:key/c2c5da76-b55b-4bcc-a240-10cc6d6e9940" + }, + { + "Sid" : "AllProdResourceManagement", + "Effect" : "Allow", + "Action" : [ + "ec2:*", + "ecs:*", + "iam:*", + "elasticloadbalancing:*", + "codedeploy:*", + "autoscaling:*", + "cloudwatch:*", + "wafv2:*" + ], + "Resource" : "*" + }, + { + "Sid" : "AllowACMCertificateManagement", + "Effect" : "Allow", + "Action" : [ + "acm:RequestCertificate", + "acm:DescribeCertificate", + "acm:DeleteCertificate", + "acm:ListTagsForCertificate", + "acm:AddTagsToCertificate", + "acm:RemoveTagsFromCertificate" + ], + "Resource" : "*" + }, + { + "Sid" : "AllowRoute53DNSValidation", + "Effect" : "Allow", + "Action" : [ + "route53:GetChange", + "route53:ChangeResourceRecordSets", + "route53:ListHostedZonesByName", + "route53:GetHostedZone", + "route53:ListResourceRecordSets" + ], + "Resource" : "arn:aws:route53:::hostedzone/*" + }, + { + "Sid" : "AllowRoute53GetChange", + "Effect" : "Allow", + "Action" : "route53:GetChange", + "Resource" : "arn:aws:route53:::change/*" } ] }) diff --git a/stage-team-account/OIDC/iam/main.tf b/stage-team-account/OIDC/iam/main.tf index 97141c0..338f4b7 100644 --- a/stage-team-account/OIDC/iam/main.tf +++ b/stage-team-account/OIDC/iam/main.tf @@ -6,16 +6,16 @@ module "github_oidc" { role_name = "Application-deployment-role3" + thumbprint_list = ["d89e3bd43d5d909b47a18977aa9d5ce36cee184c"] # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 - sub_condition = "repo:WHS-DevSecOps-infra/Application-Deployment:*" + sub_condition = ["repo:WHS-DevSecOps-infra/Organization:*", + "repo:WHS-DevSecOps-infra/Application-Deployment:*"] # 이 role에 연결할 정책들(IAM 정책 ARN) - policy_arns = [ - "arn:aws:iam::aws:policy/AdministratorAccess" - ] -} + policy_arns = [] +} From a1481b81d8ecf5b2727f9bc47f9ebe7077768f46 Mon Sep 17 00:00:00 2001 From: imyourhopeee <144752063+imyourhopeee@users.noreply.github.com> Date: Mon, 14 Jul 2025 22:47:34 +0900 Subject: [PATCH 42/66] change pr output line --- .github/workflows/ci.yml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 157750b..7b4e5a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,6 @@ on: - "dev-team-account/**" - "stage-team-account/**" - permissions: contents: read # 코드 읽을 수 있는 권한 pull-requests: write # PR 코멘트 작성 등 가능 @@ -170,20 +169,16 @@ jobs: # show 및 no change 여부 판단 terraform show -no-color $PLAN_FILE > $PLAN_TXT + terraform show -json $PLAN_FILE > plan.json + IS_EMPTY=$(terraform show -json $PLAN_FILE 2>/dev/null | jq -e '.resource_changes == []' 2>/dev/null || echo "false") if [ "$IS_EMPTY" = "true" ]; then PLAN_CONTENT="(plan success) No changes to apply. Everything looks good!" else # 길이 제한 없이 전체 내용 출력 (출력 너무 클 경우 GitHub가 거부할 수 있음 — 감수한 상태) - PLAN_CONTENT=$( - cat $PLAN_TXT | \ - sed 's/`/\\`/g' | \ - sed 's/::/%::%/g' | \ - tr -d '\r' | \ - sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" - ) - fi + PLAN_CONTENT=$(cat $PLAN_TXT) + fi else echo "[ERROR] terraform plan failed. Skipping show." echo "Terraform plan failed." > $PLAN_TXT @@ -232,4 +227,4 @@ jobs: uses: infracost/actions/comment@v1 with: path: ${{ matrix.dir }}/infracost.json - behavior: update # 기존 코멘트 업데이트 \ No newline at end of file + behavior: update # 기존 코멘트 업데이트 From 48a14b9f7b843e62e240f38d4a77c18dbc3b1580 Mon Sep 17 00:00:00 2001 From: imyourhopeee <144752063+imyourhopeee@users.noreply.github.com> Date: Mon, 14 Jul 2025 23:01:01 +0900 Subject: [PATCH 43/66] change pr output line_2 --- .github/workflows/ci.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b4e5a2..0d3e25b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -159,16 +159,12 @@ jobs: PLAN_FILE=tfplan.binary PLAN_TXT=plan.txt - # 기본 실패 메시지 PLAN_CONTENT="Terraform plan failed." - # Terraform Plan 실행 if terraform plan -no-color -out=$PLAN_FILE; then echo "[INFO] terraform plan succeeded." - - # show 및 no change 여부 판단 - terraform show -no-color $PLAN_FILE > $PLAN_TXT + terraform show -no-color $PLAN_FILE > $PLAN_TXT terraform show -json $PLAN_FILE > plan.json IS_EMPTY=$(terraform show -json $PLAN_FILE 2>/dev/null | jq -e '.resource_changes == []' 2>/dev/null || echo "false") @@ -176,8 +172,14 @@ jobs: if [ "$IS_EMPTY" = "true" ]; then PLAN_CONTENT="(plan success) No changes to apply. Everything looks good!" else - # 길이 제한 없이 전체 내용 출력 (출력 너무 클 경우 GitHub가 거부할 수 있음 — 감수한 상태) - PLAN_CONTENT=$(cat $PLAN_TXT) + # Escape 처리 추가됨 + PLAN_CONTENT=$( + cat $PLAN_TXT | \ + sed 's/`/\\`/g' | \ + sed 's/::/%::%/g' | \ + tr -d '\r' | \ + sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" + ) fi else echo "[ERROR] terraform plan failed. Skipping show." From 4e08b5c1048572c6b30092e4d13632944a62d5e1 Mon Sep 17 00:00:00 2001 From: imyourhopeee <144752063+imyourhopeee@users.noreply.github.com> Date: Mon, 14 Jul 2025 23:16:30 +0900 Subject: [PATCH 44/66] change pr output line_3 --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d3e25b..96f1f00 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -187,9 +187,9 @@ jobs: fi { - echo "PLAN_CONTENT<> "$GITHUB_OUTPUT" working-directory: ${{ matrix.dir }} From f18141fe9cf83ca2a568ee3173133b5e02af1d9d Mon Sep 17 00:00:00 2001 From: imyourhopeee <144752063+imyourhopeee@users.noreply.github.com> Date: Mon, 14 Jul 2025 23:33:17 +0900 Subject: [PATCH 45/66] change pr output line_3 --- .github/workflows/ci.yml | 53 ++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96f1f00..37e4eeb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -158,61 +158,50 @@ jobs: PLAN_FILE=tfplan.binary PLAN_TXT=plan.txt - - PLAN_CONTENT="Terraform plan failed." + PLAN_TXT_CLEAN=plan_content.txt if terraform plan -no-color -out=$PLAN_FILE; then echo "[INFO] terraform plan succeeded." - terraform show -no-color $PLAN_FILE > $PLAN_TXT terraform show -json $PLAN_FILE > plan.json IS_EMPTY=$(terraform show -json $PLAN_FILE 2>/dev/null | jq -e '.resource_changes == []' 2>/dev/null || echo "false") if [ "$IS_EMPTY" = "true" ]; then - PLAN_CONTENT="(plan success) No changes to apply. Everything looks good!" + echo "(plan success) No changes to apply. Everything looks good!" > $PLAN_TXT_CLEAN else - # Escape 처리 추가됨 - PLAN_CONTENT=$( - cat $PLAN_TXT | \ + cat $PLAN_TXT | \ sed 's/`/\\`/g' | \ sed 's/::/%::%/g' | \ tr -d '\r' | \ - sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" - ) + sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" > $PLAN_TXT_CLEAN fi else - echo "[ERROR] terraform plan failed. Skipping show." - echo "Terraform plan failed." > $PLAN_TXT + echo "[ERROR] terraform plan failed. Skipping show." > $PLAN_TXT_CLEAN fi - { - printf "PLAN_CONTENT<> "$GITHUB_OUTPUT" + # PR comment용 텍스트 조립 + echo "## [Terraform Plan Summary]" > comment.txt + echo "| 항목 | 값 |" >> comment.txt + echo "|-----------------|-----|" >> comment.txt + echo "| **Status** | ${{ steps.plan.outcome }} |" >> comment.txt + echo "| **Directory** | ${{ matrix.dir }} |" >> comment.txt + echo "| **Executed At** | $START_TIME |" >> comment.txt + echo "" >> comment.txt + echo "---" >> comment.txt + echo "" >> comment.txt + echo "### Plan Output" >> comment.txt + echo '```hcl' >> comment.txt + cat $PLAN_TXT_CLEAN >> comment.txt + echo '```' >> comment.txt working-directory: ${{ matrix.dir }} - - name: Comment Terraform Plan on PR + - name: Post Terraform Plan Comment if: github.event.pull_request.number != '' uses: peter-evans/create-or-update-comment@v4 with: issue-number: ${{ github.event.pull_request.number }} - body: | - ## [Terraform Plan Summary] - | 항목 | 값 | - |-----------------|-----| - | **Status** | `${{ steps.plan.outcome }}` | - | **Directory** | `${{ matrix.dir }}` | - | **Executed At** | `${{ env.START_TIME }}` | - - - --- - - ### Plan Output - ```hcl - ${{ steps.plan.outputs.PLAN_CONTENT }} - ``` + body-path: ${{ matrix.dir }}/comment.txt - name: Setup Infracost # 비용 예측 도구 세팅 uses: infracost/actions/setup@v2 From 0667ce05004e5b6753d962d4e34f6d1cc430c093 Mon Sep 17 00:00:00 2001 From: imyourhopeee <144752063+imyourhopeee@users.noreply.github.com> Date: Mon, 14 Jul 2025 23:41:48 +0900 Subject: [PATCH 46/66] change pr output line_4 --- .github/workflows/ci.yml | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 37e4eeb..fab06e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -160,15 +160,18 @@ jobs: PLAN_TXT=plan.txt PLAN_TXT_CLEAN=plan_content.txt + PLAN_CONTENT="Terraform plan failed." + if terraform plan -no-color -out=$PLAN_FILE; then echo "[INFO] terraform plan succeeded." + terraform show -no-color $PLAN_FILE > $PLAN_TXT terraform show -json $PLAN_FILE > plan.json IS_EMPTY=$(terraform show -json $PLAN_FILE 2>/dev/null | jq -e '.resource_changes == []' 2>/dev/null || echo "false") if [ "$IS_EMPTY" = "true" ]; then - echo "(plan success) No changes to apply. Everything looks good!" > $PLAN_TXT_CLEAN + PLAN_CONTENT="(plan success) No changes to apply. Everything looks good!" else cat $PLAN_TXT | \ sed 's/`/\\`/g' | \ @@ -177,32 +180,31 @@ jobs: sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" > $PLAN_TXT_CLEAN fi else - echo "[ERROR] terraform plan failed. Skipping show." > $PLAN_TXT_CLEAN + echo "[ERROR] terraform plan failed. Skipping show." + echo "Terraform plan failed." > $PLAN_TXT fi - - # PR comment용 텍스트 조립 - echo "## [Terraform Plan Summary]" > comment.txt - echo "| 항목 | 값 |" >> comment.txt - echo "|-----------------|-----|" >> comment.txt - echo "| **Status** | ${{ steps.plan.outcome }} |" >> comment.txt - echo "| **Directory** | ${{ matrix.dir }} |" >> comment.txt - echo "| **Executed At** | $START_TIME |" >> comment.txt - echo "" >> comment.txt - echo "---" >> comment.txt - echo "" >> comment.txt - echo "### Plan Output" >> comment.txt - echo '```hcl' >> comment.txt - cat $PLAN_TXT_CLEAN >> comment.txt - echo '```' >> comment.txt working-directory: ${{ matrix.dir }} - - name: Post Terraform Plan Comment + - name: Comment Terraform Plan on PR if: github.event.pull_request.number != '' uses: peter-evans/create-or-update-comment@v4 with: issue-number: ${{ github.event.pull_request.number }} - body-path: ${{ matrix.dir }}/comment.txt + body: | + ## [Terraform Plan Summary] + | 항목 | 값 | + |-----------------|-----| + | **Status** | `${{ steps.plan.outcome }}` | + | **Directory** | `${{ matrix.dir }}` | + | **Executed At** | `${{ env.START_TIME }}` | + + + --- + ### Plan Output + ```hcl + ${{ steps.plan.outputs.PLAN_CONTENT }} + ``` - name: Setup Infracost # 비용 예측 도구 세팅 uses: infracost/actions/setup@v2 From 9e2d21dfe3a31bb40d7368499dd529b659fb6016 Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Mon, 14 Jul 2025 23:35:45 +0900 Subject: [PATCH 47/66] =?UTF-8?q?=EB=AA=A8=EB=93=88=20=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=20=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dev-team-account/OIDC/iam/main.tf | 2 +- identity-team-account/OIDC/iam/main.tf | 2 +- modules/{github_oidc => iam_OIDC}/main.tf | 0 modules/{github_oidc => iam_OIDC}/outputs.tf | 0 modules/{github_oidc => iam_OIDC}/variables.tf | 0 operation-team-account/OIDC/iam/main.tf | 2 +- prod-team-account/OIDC/iam/main.tf | 2 +- stage-team-account/OIDC/iam/main.tf | 2 +- 8 files changed, 5 insertions(+), 5 deletions(-) rename modules/{github_oidc => iam_OIDC}/main.tf (100%) rename modules/{github_oidc => iam_OIDC}/outputs.tf (100%) rename modules/{github_oidc => iam_OIDC}/variables.tf (100%) diff --git a/dev-team-account/OIDC/iam/main.tf b/dev-team-account/OIDC/iam/main.tf index 591f5cb..818f749 100644 --- a/dev-team-account/OIDC/iam/main.tf +++ b/dev-team-account/OIDC/iam/main.tf @@ -1,7 +1,7 @@ # modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 # module "github_oidc" { - source = "../../../modules/github_oidc" + source = "../../../modules/iam_OIDC" role_name = "application-deployment-role1" diff --git a/identity-team-account/OIDC/iam/main.tf b/identity-team-account/OIDC/iam/main.tf index 55cd384..ab27c6e 100644 --- a/identity-team-account/OIDC/iam/main.tf +++ b/identity-team-account/OIDC/iam/main.tf @@ -2,7 +2,7 @@ # modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 module "github_oidc" { - source = "../../../modules/github_oidc" + source = "../../../modules/iam_OIDC" role_name = "Organization-role" diff --git a/modules/github_oidc/main.tf b/modules/iam_OIDC/main.tf similarity index 100% rename from modules/github_oidc/main.tf rename to modules/iam_OIDC/main.tf diff --git a/modules/github_oidc/outputs.tf b/modules/iam_OIDC/outputs.tf similarity index 100% rename from modules/github_oidc/outputs.tf rename to modules/iam_OIDC/outputs.tf diff --git a/modules/github_oidc/variables.tf b/modules/iam_OIDC/variables.tf similarity index 100% rename from modules/github_oidc/variables.tf rename to modules/iam_OIDC/variables.tf diff --git a/operation-team-account/OIDC/iam/main.tf b/operation-team-account/OIDC/iam/main.tf index fa5ea6b..c8d9493 100644 --- a/operation-team-account/OIDC/iam/main.tf +++ b/operation-team-account/OIDC/iam/main.tf @@ -4,7 +4,7 @@ module "github_oidc" { - source = "../../../modules/github_oidc" + source = "../../../modules/iam_OIDC" role_name = "operation-cicd" account_id = "502676416967" diff --git a/prod-team-account/OIDC/iam/main.tf b/prod-team-account/OIDC/iam/main.tf index c4163a5..efcc1ff 100644 --- a/prod-team-account/OIDC/iam/main.tf +++ b/prod-team-account/OIDC/iam/main.tf @@ -2,7 +2,7 @@ # modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 module "github_oidc" { - source = "../../../modules/github_oidc" + source = "../../../modules/iam_OIDC" role_name = "Application-Deployment-role2" add_root_trust = false diff --git a/stage-team-account/OIDC/iam/main.tf b/stage-team-account/OIDC/iam/main.tf index 338f4b7..d9c0edd 100644 --- a/stage-team-account/OIDC/iam/main.tf +++ b/stage-team-account/OIDC/iam/main.tf @@ -2,7 +2,7 @@ # modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 module "github_oidc" { - source = "../../../modules/github_oidc" + source = "../../../modules/iam_OIDC" role_name = "Application-deployment-role3" From a1306454c15abcc714adcac171c82b5256c76469 Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Tue, 15 Jul 2025 00:00:38 +0900 Subject: [PATCH 48/66] =?UTF-8?q?ci=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fab06e6..82d7655 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,8 +44,7 @@ jobs: ["prod-team-account"]="ROLE_ARN_PROD" ["dev-team-account"]="ROLE_ARN_DEV" ["stage-team-account"]="ROLE_ARN_STAGE" - - ) + ) TMP_FILE=$(mktemp) @@ -158,31 +157,41 @@ jobs: PLAN_FILE=tfplan.binary PLAN_TXT=plan.txt - PLAN_TXT_CLEAN=plan_content.txt + # 기본 실패 메시지 PLAN_CONTENT="Terraform plan failed." + # Terraform Plan 실행 if terraform plan -no-color -out=$PLAN_FILE; then echo "[INFO] terraform plan succeeded." - + + # show 및 no change 여부 판단 terraform show -no-color $PLAN_FILE > $PLAN_TXT - terraform show -json $PLAN_FILE > plan.json IS_EMPTY=$(terraform show -json $PLAN_FILE 2>/dev/null | jq -e '.resource_changes == []' 2>/dev/null || echo "false") if [ "$IS_EMPTY" = "true" ]; then PLAN_CONTENT="(plan success) No changes to apply. Everything looks good!" else + # 길이 제한 없이 전체 내용 출력 (출력 너무 클 경우 GitHub가 거부할 수 있음 — 감수한 상태) + PLAN_CONTENT=$( cat $PLAN_TXT | \ - sed 's/`/\\`/g' | \ - sed 's/::/%::%/g' | \ - tr -d '\r' | \ - sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" > $PLAN_TXT_CLEAN - fi + sed 's/`/\\`/g' | \ + sed 's/::/%::%/g' | \ + tr -d '\r' | \ + sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" + ) + fi else echo "[ERROR] terraform plan failed. Skipping show." echo "Terraform plan failed." > $PLAN_TXT fi + + { + echo "PLAN_CONTENT<> "$GITHUB_OUTPUT" working-directory: ${{ matrix.dir }} - name: Comment Terraform Plan on PR @@ -205,6 +214,7 @@ jobs: ```hcl ${{ steps.plan.outputs.PLAN_CONTENT }} ``` + - name: Setup Infracost # 비용 예측 도구 세팅 uses: infracost/actions/setup@v2 From d42a1d190f366c84c17c2ee6c4bc1c4c5fa23e7f Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Tue, 15 Jul 2025 00:12:19 +0900 Subject: [PATCH 49/66] =?UTF-8?q?ci=20=EA=B8=80=EC=9E=90=20=EA=B9=A8?= =?UTF-8?q?=EC=A7=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 45 ++++++++++------------------------------ 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82d7655..54572d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,7 @@ on: - "identity-team-account/**" - "prod-team-account/**" - "dev-team-account/**" + - "security-team-account/**" - "stage-team-account/**" permissions: @@ -43,8 +44,10 @@ jobs: ["identity-team-account"]="ROLE_ARN_IDENTITY" ["prod-team-account"]="ROLE_ARN_PROD" ["dev-team-account"]="ROLE_ARN_DEV" + ["security-team-account"]="ROLE_ARN_SECURITY" ["stage-team-account"]="ROLE_ARN_STAGE" - ) + ["management-team-account"]="ROLE_ARN_MANAGEMENT" + ) TMP_FILE=$(mktemp) @@ -157,41 +160,15 @@ jobs: PLAN_FILE=tfplan.binary PLAN_TXT=plan.txt + PLAN_JSON=plan.json - # 기본 실패 메시지 - PLAN_CONTENT="Terraform plan failed." + terraform plan -no-color -out=$PLAN_FILE || echo "PLAN_FAILED=true" >> $GITHUB_ENV + terraform show -no-color $PLAN_FILE > $PLAN_TXT || echo "Plan failed" > $PLAN_TXT + terraform show -json $PLAN_FILE > $PLAN_JSON || true - # Terraform Plan 실행 - if terraform plan -no-color -out=$PLAN_FILE; then - echo "[INFO] terraform plan succeeded." - - # show 및 no change 여부 판단 - terraform show -no-color $PLAN_FILE > $PLAN_TXT - - IS_EMPTY=$(terraform show -json $PLAN_FILE 2>/dev/null | jq -e '.resource_changes == []' 2>/dev/null || echo "false") - - if [ "$IS_EMPTY" = "true" ]; then - PLAN_CONTENT="(plan success) No changes to apply. Everything looks good!" - else - # 길이 제한 없이 전체 내용 출력 (출력 너무 클 경우 GitHub가 거부할 수 있음 — 감수한 상태) - PLAN_CONTENT=$( - cat $PLAN_TXT | \ - sed 's/`/\\`/g' | \ - sed 's/::/%::%/g' | \ - tr -d '\r' | \ - sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" - ) - fi - else - echo "[ERROR] terraform plan failed. Skipping show." - echo "Terraform plan failed." > $PLAN_TXT - fi - - { - echo "PLAN_CONTENT<> "$GITHUB_OUTPUT" + echo "PLAN_CONTENT<> $GITHUB_OUTPUT + cat $PLAN_TXT >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT working-directory: ${{ matrix.dir }} - name: Comment Terraform Plan on PR From 3fe4c91dc743a072f07a83b10a8c6cb08b5a9ef7 Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Tue, 15 Jul 2025 00:15:39 +0900 Subject: [PATCH 50/66] =?UTF-8?q?ci=20=EA=B8=80=EC=9E=90=20=EA=B9=A8?= =?UTF-8?q?=EC=A7=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 80 +++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54572d0..6ff904f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,51 +2,47 @@ name: Application-Deployment CI on: - pull_request: # PR이 main 브랜치로 올 때 실행 + pull_request: branches: [main] paths: - "operation-team-account/**" - "identity-team-account/**" - "prod-team-account/**" - "dev-team-account/**" - - "security-team-account/**" - "stage-team-account/**" permissions: - contents: read # 코드 읽을 수 있는 권한 - pull-requests: write # PR 코멘트 작성 등 가능 - id-token: write # OIDC기반 인증에 필요 + contents: read + pull-requests: write + id-token: write jobs: detect-changes: runs-on: ubuntu-latest outputs: - matrix: ${{ steps.set.outputs.matrix }} # 다음 job에서 사용할 matrix 값 + matrix: ${{ steps.set.outputs.matrix }} steps: - name: Checkout Code uses: actions/checkout@v3 with: - fetch-depth: 0 # git diff가 정상 동작하려면 전체 커밋 히스토리를 가져와야 함 + fetch-depth: 0 - - name: Fetch origin/main # main 브랜치 히스토리 가져오기 + - name: Fetch origin/main run: git fetch origin main - name: Detect Changed Directories & Build Matrix id: set run: | - FILES=$(git diff --name-only origin/main...${{ github.sha }}) # 변경된 파일 목록 + FILES=$(git diff --name-only origin/main...${{ github.sha }}) echo "Changed files:" echo "$FILES" - # 각 account별 디렉토리에 해당하는 IAM Role 매핑 declare -A ROLE_MAP=( ["operation-team-account"]="ROLE_ARN_OPERATION" ["identity-team-account"]="ROLE_ARN_IDENTITY" ["prod-team-account"]="ROLE_ARN_PROD" ["dev-team-account"]="ROLE_ARN_DEV" - ["security-team-account"]="ROLE_ARN_SECURITY" ["stage-team-account"]="ROLE_ARN_STAGE" - ["management-team-account"]="ROLE_ARN_MANAGEMENT" ) TMP_FILE=$(mktemp) @@ -57,15 +53,12 @@ jobs: ROLE_KEY="${ROLE_MAP[$TOP_DIR]}" if [ -n "$ROLE_KEY" ]; then - # 루트 디렉터리 - if [ "$DIR" == "$TOP_DIR" ]; then TF_COUNT=$(find "$DIR" -maxdepth 1 -name '*.tf' | wc -l) if [ "$TF_COUNT" -gt 0 ]; then echo "$DIR|$ROLE_KEY" >> $TMP_FILE fi else - # 하위 디렉터리 TF_COUNT=$(find "$DIR" -maxdepth 1 -name '*.tf' | wc -l) if [ "$TF_COUNT" -gt 0 ]; then echo "$DIR|$ROLE_KEY" >> $TMP_FILE @@ -75,7 +68,6 @@ jobs: fi done - # 중복 제거 및 JSON 포맷 생성 UNIQUE_LINES=$(sort $TMP_FILE | uniq) MATRIX_JSON="[" FIRST=1 @@ -102,13 +94,13 @@ jobs: terraform-ci: needs: detect-changes - if: ${{ needs.detect-changes.outputs.matrix != '[]' }} # 변경사항이 있을 때만 실행 + if: ${{ needs.detect-changes.outputs.matrix != '[]' }} runs-on: ubuntu-latest strategy: matrix: - include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} # 변경된 디렉토리 목록 기반 실행 - fail-fast: false # 하나 실패해도 나머지는 계속 실행 + include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} + fail-fast: false env: INFRACOST_API_KEY: ${{ secrets.INFRACOST_API_KEY }} @@ -121,13 +113,13 @@ jobs: with: fetch-depth: 0 - - name: Configure AWS Credentials # 각 matrix.role_key에 해당하는 AWS Role Assume + - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-region: ap-northeast-2 role-to-assume: ${{ secrets[matrix.role_key] }} - - name: Install tfsec # 보안 취약점 스캐너 설치 + - name: Install tfsec run: | curl -sSL https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash @@ -139,16 +131,16 @@ jobs: with: terraform_version: 1.4.0 - - name: Terraform Init # Terraform 초기화: backend 구성 및 provider 다운로드 + - name: Terraform Init run: terraform init working-directory: ${{ matrix.dir }} - name: Terraform Format Check - run: terraform fmt -check -recursive # 코드 스타일 검사 + run: terraform fmt -check -recursive working-directory: ${{ matrix.dir }} - name: Terraform Validate - run: terraform validate # Terraform 구성 파일이 유효한지 문법 및 구조 검사 + run: terraform validate working-directory: ${{ matrix.dir }} - name: Terraform Plan @@ -160,15 +152,36 @@ jobs: PLAN_FILE=tfplan.binary PLAN_TXT=plan.txt - PLAN_JSON=plan.json - terraform plan -no-color -out=$PLAN_FILE || echo "PLAN_FAILED=true" >> $GITHUB_ENV - terraform show -no-color $PLAN_FILE > $PLAN_TXT || echo "Plan failed" > $PLAN_TXT - terraform show -json $PLAN_FILE > $PLAN_JSON || true + PLAN_CONTENT="Terraform plan failed." + + if terraform plan -no-color -out=$PLAN_FILE; then + echo "[INFO] terraform plan succeeded." + + terraform show -no-color $PLAN_FILE > $PLAN_TXT - echo "PLAN_CONTENT<> $GITHUB_OUTPUT - cat $PLAN_TXT >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT + IS_EMPTY=$(terraform show -json $PLAN_FILE 2>/dev/null | jq -e '.resource_changes == []' 2>/dev/null || echo "false") + + if [ "$IS_EMPTY" = "true" ]; then + PLAN_CONTENT="(plan success) No changes to apply. Everything looks good!" + else + PLAN_CONTENT=$( + cat $PLAN_TXT | \ + sed 's/`/\\`/g' | \ + tr -d '\r' | \ + sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" + ) + fi + else + echo "[ERROR] terraform plan failed. Skipping show." + echo "Terraform plan failed." > $PLAN_TXT + fi + + { + echo "PLAN_CONTENT<> "$GITHUB_OUTPUT" working-directory: ${{ matrix.dir }} - name: Comment Terraform Plan on PR @@ -184,7 +197,6 @@ jobs: | **Directory** | `${{ matrix.dir }}` | | **Executed At** | `${{ env.START_TIME }}` | - --- ### Plan Output @@ -192,7 +204,7 @@ jobs: ${{ steps.plan.outputs.PLAN_CONTENT }} ``` - - name: Setup Infracost # 비용 예측 도구 세팅 + - name: Setup Infracost uses: infracost/actions/setup@v2 - name: Infracost Breakdown @@ -207,4 +219,4 @@ jobs: uses: infracost/actions/comment@v1 with: path: ${{ matrix.dir }}/infracost.json - behavior: update # 기존 코멘트 업데이트 + behavior: update From d89905a220dd33ee706bfeeb7bc79a35a79d0d03 Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Tue, 15 Jul 2025 00:25:07 +0900 Subject: [PATCH 51/66] =?UTF-8?q?ci=20=EA=B8=80=EC=9E=90=20=EA=B9=A8?= =?UTF-8?q?=EC=A7=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ff904f..f284017 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,10 @@ on: - "dev-team-account/**" - "stage-team-account/**" +env: + ACTIONS_STEP_DEBUG: false + + permissions: contents: read pull-requests: write From 62c5d789a9dca463dd96bf45cb3c0a80a40be180 Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Tue, 15 Jul 2025 00:27:35 +0900 Subject: [PATCH 52/66] =?UTF-8?q?ci=20=EA=B8=80=EC=9E=90=20=EA=B9=A8?= =?UTF-8?q?=EC=A7=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 83 ++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 50 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f284017..0d6f628 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,51 +2,51 @@ name: Application-Deployment CI on: - pull_request: + pull_request: # PR이 main 브랜치로 올 때 실행 branches: [main] paths: - "operation-team-account/**" - "identity-team-account/**" - "prod-team-account/**" - "dev-team-account/**" + - "security-team-account/**" - "stage-team-account/**" -env: - ACTIONS_STEP_DEBUG: false - - permissions: - contents: read - pull-requests: write - id-token: write + contents: read # 코드 읽을 수 있는 권한 + pull-requests: write # PR 코멘트 작성 등 가능 + id-token: write # OIDC기반 인증에 필요 jobs: detect-changes: runs-on: ubuntu-latest outputs: - matrix: ${{ steps.set.outputs.matrix }} + matrix: ${{ steps.set.outputs.matrix }} # 다음 job에서 사용할 matrix 값 steps: - name: Checkout Code uses: actions/checkout@v3 with: - fetch-depth: 0 + fetch-depth: 0 # git diff가 정상 동작하려면 전체 커밋 히스토리를 가져와야 함 - - name: Fetch origin/main + - name: Fetch origin/main # main 브랜치 히스토리 가져오기 run: git fetch origin main - name: Detect Changed Directories & Build Matrix id: set run: | - FILES=$(git diff --name-only origin/main...${{ github.sha }}) + FILES=$(git diff --name-only origin/main...${{ github.sha }}) # 변경된 파일 목록 echo "Changed files:" echo "$FILES" + # 각 account별 디렉토리에 해당하는 IAM Role 매핑 declare -A ROLE_MAP=( ["operation-team-account"]="ROLE_ARN_OPERATION" ["identity-team-account"]="ROLE_ARN_IDENTITY" ["prod-team-account"]="ROLE_ARN_PROD" ["dev-team-account"]="ROLE_ARN_DEV" + ["security-team-account"]="ROLE_ARN_SECURITY" ["stage-team-account"]="ROLE_ARN_STAGE" + ["management-team-account"]="ROLE_ARN_MANAGEMENT" ) TMP_FILE=$(mktemp) @@ -57,12 +57,14 @@ jobs: ROLE_KEY="${ROLE_MAP[$TOP_DIR]}" if [ -n "$ROLE_KEY" ]; then + # 루트 디렉터리 if [ "$DIR" == "$TOP_DIR" ]; then TF_COUNT=$(find "$DIR" -maxdepth 1 -name '*.tf' | wc -l) if [ "$TF_COUNT" -gt 0 ]; then echo "$DIR|$ROLE_KEY" >> $TMP_FILE fi else + # 하위 디렉터리 TF_COUNT=$(find "$DIR" -maxdepth 1 -name '*.tf' | wc -l) if [ "$TF_COUNT" -gt 0 ]; then echo "$DIR|$ROLE_KEY" >> $TMP_FILE @@ -72,6 +74,7 @@ jobs: fi done + # 중복 제거 및 JSON 포맷 생성 UNIQUE_LINES=$(sort $TMP_FILE | uniq) MATRIX_JSON="[" FIRST=1 @@ -98,13 +101,13 @@ jobs: terraform-ci: needs: detect-changes - if: ${{ needs.detect-changes.outputs.matrix != '[]' }} + if: ${{ needs.detect-changes.outputs.matrix != '[]' }} # 변경사항이 있을 때만 실행 runs-on: ubuntu-latest strategy: matrix: - include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} - fail-fast: false + include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} # 변경된 디렉토리 목록 기반 실행 + fail-fast: false # 하나 실패해도 나머지는 계속 실행 env: INFRACOST_API_KEY: ${{ secrets.INFRACOST_API_KEY }} @@ -117,13 +120,13 @@ jobs: with: fetch-depth: 0 - - name: Configure AWS Credentials + - name: Configure AWS Credentials # 각 matrix.role_key에 해당하는 AWS Role Assume uses: aws-actions/configure-aws-credentials@v4 with: aws-region: ap-northeast-2 role-to-assume: ${{ secrets[matrix.role_key] }} - - name: Install tfsec + - name: Install tfsec # 보안 취약점 스캐너 설치 run: | curl -sSL https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash @@ -135,16 +138,16 @@ jobs: with: terraform_version: 1.4.0 - - name: Terraform Init + - name: Terraform Init # Terraform 초기화: backend 구성 및 provider 다운로드 run: terraform init working-directory: ${{ matrix.dir }} - name: Terraform Format Check - run: terraform fmt -check -recursive + run: terraform fmt -check -recursive # 코드 스타일 검사 working-directory: ${{ matrix.dir }} - name: Terraform Validate - run: terraform validate + run: terraform validate # Terraform 구성 파일이 유효한지 문법 및 구조 검사 working-directory: ${{ matrix.dir }} - name: Terraform Plan @@ -156,36 +159,15 @@ jobs: PLAN_FILE=tfplan.binary PLAN_TXT=plan.txt + PLAN_JSON=plan.json - PLAN_CONTENT="Terraform plan failed." - - if terraform plan -no-color -out=$PLAN_FILE; then - echo "[INFO] terraform plan succeeded." - - terraform show -no-color $PLAN_FILE > $PLAN_TXT + terraform plan -no-color -out=$PLAN_FILE > /dev/null 2>&1 || echo "PLAN_FAILED=true" >> $GITHUB_ENV + terraform show -no-color $PLAN_FILE > $PLAN_TXT 2>/dev/null || echo "Plan failed" > $PLAN_TXT + terraform show -json $PLAN_FILE > $PLAN_JSON || true - IS_EMPTY=$(terraform show -json $PLAN_FILE 2>/dev/null | jq -e '.resource_changes == []' 2>/dev/null || echo "false") - - if [ "$IS_EMPTY" = "true" ]; then - PLAN_CONTENT="(plan success) No changes to apply. Everything looks good!" - else - PLAN_CONTENT=$( - cat $PLAN_TXT | \ - sed 's/`/\\`/g' | \ - tr -d '\r' | \ - sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" - ) - fi - else - echo "[ERROR] terraform plan failed. Skipping show." - echo "Terraform plan failed." > $PLAN_TXT - fi - - { - echo "PLAN_CONTENT<> "$GITHUB_OUTPUT" + echo "PLAN_CONTENT<> $GITHUB_OUTPUT + cat $PLAN_TXT >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT working-directory: ${{ matrix.dir }} - name: Comment Terraform Plan on PR @@ -201,6 +183,7 @@ jobs: | **Directory** | `${{ matrix.dir }}` | | **Executed At** | `${{ env.START_TIME }}` | + --- ### Plan Output @@ -208,7 +191,7 @@ jobs: ${{ steps.plan.outputs.PLAN_CONTENT }} ``` - - name: Setup Infracost + - name: Setup Infracost # 비용 예측 도구 세팅 uses: infracost/actions/setup@v2 - name: Infracost Breakdown @@ -223,4 +206,4 @@ jobs: uses: infracost/actions/comment@v1 with: path: ${{ matrix.dir }}/infracost.json - behavior: update + behavior: update # 기존 코멘트 업데이트 From 62d4044167f945274c4f9ecb060e96abac375fde Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Tue, 15 Jul 2025 00:29:25 +0900 Subject: [PATCH 53/66] =?UTF-8?q?ci=20=EA=B8=80=EC=9E=90=20=EA=B9=A8?= =?UTF-8?q?=EC=A7=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d6f628..83f5bc8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -152,7 +152,7 @@ jobs: - name: Terraform Plan id: plan - continue-on-error: true + continue-on-error: false run: | START_TIME=$(date -u +"%Y-%m-%d %H:%M:%S UTC") echo "START_TIME=$START_TIME" >> $GITHUB_ENV From 7df3f7d776cda5f7519f4779e7a974f0f69a9b90 Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Tue, 15 Jul 2025 00:33:38 +0900 Subject: [PATCH 54/66] =?UTF-8?q?ci=20=EA=B8=80=EC=9E=90=20=EA=B9=A8?= =?UTF-8?q?=EC=A7=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 78 +++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83f5bc8..11a0d91 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: Application-Deployment CI on: - pull_request: # PR이 main 브랜치로 올 때 실행 + pull_request: branches: [main] paths: - "operation-team-account/**" @@ -13,32 +13,31 @@ on: - "stage-team-account/**" permissions: - contents: read # 코드 읽을 수 있는 권한 - pull-requests: write # PR 코멘트 작성 등 가능 - id-token: write # OIDC기반 인증에 필요 + contents: read + pull-requests: write + id-token: write jobs: detect-changes: runs-on: ubuntu-latest outputs: - matrix: ${{ steps.set.outputs.matrix }} # 다음 job에서 사용할 matrix 값 + matrix: ${{ steps.set.outputs.matrix }} steps: - name: Checkout Code uses: actions/checkout@v3 with: - fetch-depth: 0 # git diff가 정상 동작하려면 전체 커밋 히스토리를 가져와야 함 + fetch-depth: 0 - - name: Fetch origin/main # main 브랜치 히스토리 가져오기 + - name: Fetch origin/main run: git fetch origin main - name: Detect Changed Directories & Build Matrix id: set run: | - FILES=$(git diff --name-only origin/main...${{ github.sha }}) # 변경된 파일 목록 + FILES=$(git diff --name-only origin/main...${{ github.sha }}) echo "Changed files:" echo "$FILES" - # 각 account별 디렉토리에 해당하는 IAM Role 매핑 declare -A ROLE_MAP=( ["operation-team-account"]="ROLE_ARN_OPERATION" ["identity-team-account"]="ROLE_ARN_IDENTITY" @@ -57,14 +56,12 @@ jobs: ROLE_KEY="${ROLE_MAP[$TOP_DIR]}" if [ -n "$ROLE_KEY" ]; then - # 루트 디렉터리 if [ "$DIR" == "$TOP_DIR" ]; then TF_COUNT=$(find "$DIR" -maxdepth 1 -name '*.tf' | wc -l) if [ "$TF_COUNT" -gt 0 ]; then echo "$DIR|$ROLE_KEY" >> $TMP_FILE fi else - # 하위 디렉터리 TF_COUNT=$(find "$DIR" -maxdepth 1 -name '*.tf' | wc -l) if [ "$TF_COUNT" -gt 0 ]; then echo "$DIR|$ROLE_KEY" >> $TMP_FILE @@ -74,7 +71,6 @@ jobs: fi done - # 중복 제거 및 JSON 포맷 생성 UNIQUE_LINES=$(sort $TMP_FILE | uniq) MATRIX_JSON="[" FIRST=1 @@ -101,13 +97,13 @@ jobs: terraform-ci: needs: detect-changes - if: ${{ needs.detect-changes.outputs.matrix != '[]' }} # 변경사항이 있을 때만 실행 + if: ${{ needs.detect-changes.outputs.matrix != '[]' }} runs-on: ubuntu-latest strategy: matrix: - include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} # 변경된 디렉토리 목록 기반 실행 - fail-fast: false # 하나 실패해도 나머지는 계속 실행 + include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} + fail-fast: false env: INFRACOST_API_KEY: ${{ secrets.INFRACOST_API_KEY }} @@ -120,39 +116,39 @@ jobs: with: fetch-depth: 0 - - name: Configure AWS Credentials # 각 matrix.role_key에 해당하는 AWS Role Assume + - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-region: ap-northeast-2 role-to-assume: ${{ secrets[matrix.role_key] }} - - name: Install tfsec # 보안 취약점 스캐너 설치 + - name: Install Terraform + run: | + curl -LO https://releases.hashicorp.com/terraform/1.4.0/terraform_1.4.0_linux_amd64.zip + unzip terraform_1.4.0_linux_amd64.zip + sudo mv terraform /usr/local/bin/ + + - name: Install tfsec run: | curl -sSL https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash - name: Run tfsec (fail on HIGH+) run: tfsec --minimum-severity HIGH --no-color ${{ matrix.dir }} - - name: Setup Terraform - uses: hashicorp/setup-terraform@v1 - with: - terraform_version: 1.4.0 - - - name: Terraform Init # Terraform 초기화: backend 구성 및 provider 다운로드 + - name: Terraform Init run: terraform init working-directory: ${{ matrix.dir }} - name: Terraform Format Check - run: terraform fmt -check -recursive # 코드 스타일 검사 + run: terraform fmt -check -recursive working-directory: ${{ matrix.dir }} - name: Terraform Validate - run: terraform validate # Terraform 구성 파일이 유효한지 문법 및 구조 검사 + run: terraform validate working-directory: ${{ matrix.dir }} - name: Terraform Plan id: plan - continue-on-error: false run: | START_TIME=$(date -u +"%Y-%m-%d %H:%M:%S UTC") echo "START_TIME=$START_TIME" >> $GITHUB_ENV @@ -161,13 +157,30 @@ jobs: PLAN_TXT=plan.txt PLAN_JSON=plan.json - terraform plan -no-color -out=$PLAN_FILE > /dev/null 2>&1 || echo "PLAN_FAILED=true" >> $GITHUB_ENV + # Run terraform plan + terraform plan -no-color -out=$PLAN_FILE > /dev/null 2> plan_error.txt || echo "PLAN_FAILED=true" >> $GITHUB_ENV + + # Show plan text output terraform show -no-color $PLAN_FILE > $PLAN_TXT 2>/dev/null || echo "Plan failed" > $PLAN_TXT + + # Remove ANSI color codes + cat $PLAN_TXT | \ + sed 's/`/\\`/g' | \ + tr -d '\r' | \ + sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" \ + > cleaned_plan.txt + + PLAN_CONTENT=$(cat cleaned_plan.txt) + + # Save JSON plan for infracost terraform show -json $PLAN_FILE > $PLAN_JSON || true - echo "PLAN_CONTENT<> $GITHUB_OUTPUT - cat $PLAN_TXT >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT + # Output plan content for PR comment + { + echo "PLAN_CONTENT<> $GITHUB_OUTPUT working-directory: ${{ matrix.dir }} - name: Comment Terraform Plan on PR @@ -183,7 +196,6 @@ jobs: | **Directory** | `${{ matrix.dir }}` | | **Executed At** | `${{ env.START_TIME }}` | - --- ### Plan Output @@ -191,7 +203,7 @@ jobs: ${{ steps.plan.outputs.PLAN_CONTENT }} ``` - - name: Setup Infracost # 비용 예측 도구 세팅 + - name: Setup Infracost uses: infracost/actions/setup@v2 - name: Infracost Breakdown @@ -206,4 +218,4 @@ jobs: uses: infracost/actions/comment@v1 with: path: ${{ matrix.dir }}/infracost.json - behavior: update # 기존 코멘트 업데이트 + behavior: update From ff63f4becaff34deb844b4b0efcad238c5804eda Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Sat, 5 Jul 2025 18:54:11 +0900 Subject: [PATCH 55/66] =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 7 +- .terraform.lock.hcl | 25 ++++++ operation-team-account/ecr.tf | 14 +++ prod-team-account/alb.tf | 111 +++++++++++++++++++++++ prod-team-account/codedeploy.tf | 51 +++++++++++ prod-team-account/ecs.tf | 135 ++++++++++++++++++++++++++++ prod-team-account/iam.tf | 80 +++++++++++++++++ prod-team-account/main.tf | 13 +++ prod-team-account/network.tf | 121 +++++++++++++++++++++++++ prod-team-account/output.tf | 121 +++++++++++++++++++++++++ prod-team-account/security_group.tf | 44 +++++++++ prod-team-account/variables.tf | 24 +++++ 12 files changed, 745 insertions(+), 1 deletion(-) create mode 100644 .terraform.lock.hcl create mode 100644 operation-team-account/ecr.tf create mode 100644 prod-team-account/alb.tf create mode 100644 prod-team-account/codedeploy.tf create mode 100644 prod-team-account/ecs.tf create mode 100644 prod-team-account/iam.tf create mode 100644 prod-team-account/main.tf create mode 100644 prod-team-account/network.tf create mode 100644 prod-team-account/output.tf create mode 100644 prod-team-account/security_group.tf create mode 100644 prod-team-account/variables.tf diff --git a/.gitignore b/.gitignore index 1668f42..ab1d334 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store .idea +<<<<<<< HEAD # tfstate files *.tfstate @@ -16,4 +17,8 @@ *.tfstate.backup.* *.tfstate.backup.json *.tfstate.backup.json.* -.terraform.lock.hcl \ No newline at end of file +.terraform.lock.hcl +======= +*.tfstate +*.tfstate.* +>>>>>>> bf11ac5 (코드 구조 리팩토링) diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl new file mode 100644 index 0000000..cdc1668 --- /dev/null +++ b/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.100.0" + constraints = "~> 5.0" + hashes = [ + "h1:Ijt7pOlB7Tr7maGQIqtsLFbl7pSMIj06TVdkoSBcYOw=", + "zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644", + "zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2", + "zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274", + "zh:6330766f1d85f01ae6ea90d1b214b8b74cc8c1badc4696b165b36ddd4cc15f7b", + "zh:7c8c2e30d8e55291b86fcb64bdf6c25489d538688545eb48fd74ad622e5d3862", + "zh:99b1003bd9bd32ee323544da897148f46a527f622dc3971af63ea3e251596342", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9f8b909d3ec50ade83c8062290378b1ec553edef6a447c56dadc01a99f4eaa93", + "zh:aaef921ff9aabaf8b1869a86d692ebd24fbd4e12c21205034bb679b9caf883a2", + "zh:ac882313207aba00dd5a76dbd572a0ddc818bb9cbf5c9d61b28fe30efaec951e", + "zh:bb64e8aff37becab373a1a0cc1080990785304141af42ed6aa3dd4913b000421", + "zh:dfe495f6621df5540d9c92ad40b8067376350b005c637ea6efac5dc15028add4", + "zh:f0ddf0eaf052766cfe09dea8200a946519f653c384ab4336e2a4a64fdd6310e9", + "zh:f1b7e684f4c7ae1eed272b6de7d2049bb87a0275cb04dbb7cda6636f600699c9", + "zh:ff461571e3f233699bf690db319dfe46aec75e58726636a0d97dd9ac6e32fb70", + ] +} diff --git a/operation-team-account/ecr.tf b/operation-team-account/ecr.tf new file mode 100644 index 0000000..d3a5272 --- /dev/null +++ b/operation-team-account/ecr.tf @@ -0,0 +1,14 @@ +resource "aws_ecr_repository" "app" { + name = var.project_name + + image_tag_mutability = "IMMUTABLE" + + image_scanning_configuration { + scan_on_push = true + } + + tags = { + Name = "${var.project_name}-repo" + Project = var.project_name + } +} \ No newline at end of file diff --git a/prod-team-account/alb.tf b/prod-team-account/alb.tf new file mode 100644 index 0000000..31a1ece --- /dev/null +++ b/prod-team-account/alb.tf @@ -0,0 +1,111 @@ +# WAF +resource "aws_wafv2_web_acl" "alb_waf" { + name = "${var.project_name}-alb-waf" + description = "WAF for ALB" + scope = "REGIONAL" + + default_action { + allow {} + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "waf-alb-metric" + sampled_requests_enabled = true + } + + rule { + name = "AWS-AWSManagedRulesCommonRuleSet" + priority = 1 + override_action { + none {} + } + statement { + managed_rule_group_statement { + vendor_name = "AWS" + name = "AWSManagedRulesCommonRuleSet" + } + } + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "AWSManagedRulesCommonRuleSet" + sampled_requests_enabled = true + } + } + + tags = { + Name = "${var.project_name}-alb-waf" + } +} + +# ALB +resource "aws_lb" "alb" { + name = "${var.project_name}-alb" + internal = false + load_balancer_type = "application" + security_groups = [aws_security_group.alb_sg.id] + subnets = [aws_subnet.public1.id, aws_subnet.public2.id] + + enable_deletion_protection = true + + tags = { + Name = "${var.project_name}-alb" + } +} + +# Target Group +resource "aws_lb_target_group" "blue" { + name = "${var.project_name}-blue-tg" + port = 80 + protocol = "HTTP" + vpc_id = aws_vpc.vpc.id + target_type = "instance" + health_check { + path = "/" + protocol = "HTTP" + interval = 30 + timeout = 5 + healthy_threshold = 2 + unhealthy_threshold = 2 + } + tags = { + Name = "${var.project_name}-blue-tg" + } +} + +resource "aws_lb_target_group" "green" { + name = "${var.project_name}-green-tg" + port = 80 + protocol = "HTTP" + vpc_id = aws_vpc.vpc.id + target_type = "instance" + health_check { + path = "/" + protocol = "HTTP" + interval = 30 + timeout = 5 + healthy_threshold = 2 + unhealthy_threshold = 2 + } + tags = { + Name = "${var.project_name}-green-tg" + } +} + +# ALB 리스너 +resource "aws_lb_listener" "http" { + load_balancer_arn = aws_lb.alb.arn + port = 80 + protocol = "HTTP" + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.blue.arn + } +} + +# WAF와 ALB 연결 +resource "aws_wafv2_web_acl_association" "alb_association" { + resource_arn = aws_lb.alb.arn + web_acl_arn = aws_wafv2_web_acl.alb_waf.arn + depends_on = [aws_lb.alb] +} \ No newline at end of file diff --git a/prod-team-account/codedeploy.tf b/prod-team-account/codedeploy.tf new file mode 100644 index 0000000..9c6301b --- /dev/null +++ b/prod-team-account/codedeploy.tf @@ -0,0 +1,51 @@ +# CodeDeploy +resource "aws_codedeploy_app" "ecs_app" { + name = "${var.project_name}-ecs-app" + compute_platform = "ECS" +} + +resource "aws_codedeploy_deployment_group" "ecs_deployment_group" { + app_name = aws_codedeploy_app.ecs_app.name + deployment_group_name = "${var.project_name}-ecs-deployment-group" + service_role_arn = aws_iam_role.codedeploy_role.arn + + deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" + + ecs_service { + cluster_name = aws_ecs_cluster.ecs_cluster.name + service_name = aws_ecs_service.ecs_service.name + } + + deployment_style { + deployment_type = "BLUE_GREEN" + deployment_option = "WITH_TRAFFIC_CONTROL" + } + + blue_green_deployment_config { + deployment_ready_option { + action_on_timeout = "CONTINUE_DEPLOYMENT" + } + terminate_blue_instances_on_deployment_success { + action = "TERMINATE" + termination_wait_time_in_minutes = 5 + } + } + load_balancer_info { + target_group_pair_info { + target_group { + name = aws_lb_target_group.blue.name + } + target_group { + name = aws_lb_target_group.green.name + } + prod_traffic_route { + listener_arns = [aws_lb_listener.http.arn] + } + } + } + auto_rollback_configuration { + enabled = true + events = ["DEPLOYMENT_FAILURE"] + } + +} \ No newline at end of file diff --git a/prod-team-account/ecs.tf b/prod-team-account/ecs.tf new file mode 100644 index 0000000..68bf3b4 --- /dev/null +++ b/prod-team-account/ecs.tf @@ -0,0 +1,135 @@ +# ECS 클러스터 생성 +resource "aws_ecs_cluster" "ecs_cluster" { + name = "${var.project_name}-ecs-cluster" +} + +# ECS Launch Template +resource "aws_launch_template" "ecs_launch_template" { + name_prefix = "${var.project_name}-ecs-launch-template-" + image_id = "ami-0bc365768d185847c" + instance_type = "t2.micro" + + iam_instance_profile { + name = aws_iam_instance_profile.ecs_instance_profile.name + } + network_interfaces { + associate_public_ip_address = false + security_groups = [aws_security_group.ecs_sg.id] + } + + user_data = base64encode(<<-EOF + #!/bin/bash + echo ECS_CLUSTER=${aws_ecs_cluster.ecs_cluster.name} >> /etc/ecs/ecs.config + EOF + ) + + tags = { + Name = "${var.project_name}-ecs-launch-template" + } +} + +# ECS Auto Scaling Group +resource "aws_autoscaling_group" "ecs_auto_scaling_group" { + launch_template { + id = aws_launch_template.ecs_launch_template.id + version = "$Latest" + } + + min_size = 1 + max_size = 4 + desired_capacity = 2 + vpc_zone_identifier = [aws_subnet.private1.id, aws_subnet.private2.id] + health_check_type = "EC2" + force_delete = true + protect_from_scale_in = true + + tag { + key = "ECS_Manage" + value = "${var.project_name}-ecs-auto-scaling-group" + propagate_at_launch = true + } + +} + +# ECS capacity provider +resource "aws_ecs_capacity_provider" "ecs_capacity_provider" { + name = "${var.project_name}-ecs-capacity-provider" + auto_scaling_group_provider { + auto_scaling_group_arn = aws_autoscaling_group.ecs_auto_scaling_group.arn + managed_termination_protection = "ENABLED" + managed_scaling { + status = "ENABLED" + target_capacity = 100 + } + } +} + +# Capacity provider association +resource "aws_ecs_cluster_capacity_providers" "ecs_cluster_capacity_providers" { + cluster_name = aws_ecs_cluster.ecs_cluster.name + capacity_providers = [aws_ecs_capacity_provider.ecs_capacity_provider.name] + default_capacity_provider_strategy { + capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name + weight = 100 + base = 1 + } +} + +# ECS Task Definition +resource "aws_ecs_task_definition" "ecs_task_definition" { + family = "${var.project_name}-ecs-task" + network_mode = "bridge" + requires_compatibilities = ["EC2"] + + container_definitions = jsonencode([ + { + name = "${var.project_name}-container" + image = "${aws_ecr_repository.app.repository_url}:latest" + cpu = 256 + memory = 512 + essential = true + portMappings = [ + { + containerPort = 80 + hostPort = 80 + protocol = "tcp" + } + ] + } + ]) +} + +# ECS Service +resource "aws_ecs_service" "ecs_service" { + name = "${var.project_name}-ecs-service" + cluster = aws_ecs_cluster.ecs_cluster.id + task_definition = aws_ecs_task_definition.ecs_task_definition.arn + desired_count = 2 + + + capacity_provider_strategy { + capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name + weight = 100 + } + + load_balancer { + target_group_arn = aws_lb_target_group.blue.arn + container_name = "${var.project_name}-container" + container_port = 80 + } + + deployment_controller { + type = "CODE_DEPLOY" + } + + lifecycle { + ignore_changes = [task_definition, desired_count] + } + + health_check_grace_period_seconds = 60 + + tags = { + Name = "${var.project_name}-ecs-service" + } +} + \ No newline at end of file diff --git a/prod-team-account/iam.tf b/prod-team-account/iam.tf new file mode 100644 index 0000000..0b281a0 --- /dev/null +++ b/prod-team-account/iam.tf @@ -0,0 +1,80 @@ +# ECS 인스턴스가 사용할 IAM 역할 생성 +resource "aws_iam_role" "ecs_instance_role" { + name = "${var.project_name}-ecs-instance-role" + + # 이 역할을 EC2 인스턴스가 사용할 수 있도록 신뢰 정책 설정 + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "ec2.amazonaws.com" + } + } + ] + }) + + tags = { + Name = "${var.project_name}-ecs-instance-role" + } +} + +# AWS에서 관리하는 정책을 위에서 만든 역할에 연결 +resource "aws_iam_role_policy_attachment" "ecs_instance_role_attachment" { + role = aws_iam_role.ecs_instance_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" +} + +# EC2 인스턴스에 역할을 부여하기 위한 인스턴스 프로파일 생성 +resource "aws_iam_instance_profile" "ecs_instance_profile" { + name = "${var.project_name}-ecs-instance-profile" + role = aws_iam_role.ecs_instance_role.name +} + + + +# ECS 작업 실행 역할 +resource "aws_iam_role" "ecs_task_execution_role" { + name = "${var.project_name}-ecs-task-execution-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "ecs-tasks.amazonaws.com" + } + } + ] + }) +} +resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_attachment" { + role = aws_iam_role.ecs_task_execution_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" +} + + + +# CodeDeploy를 위한 IAM 역할 +resource "aws_iam_role" "codedeploy_role" { + name = "${var.project_name}-codedeploy-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "codedeploy.amazonaws.com" + } + } + ] + }) +} +resource "aws_iam_role_policy_attachment" "codedeploy_role_attachment" { + role = aws_iam_role.codedeploy_role.name + policy_arn = "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS" +} \ No newline at end of file diff --git a/prod-team-account/main.tf b/prod-team-account/main.tf new file mode 100644 index 0000000..1b0717f --- /dev/null +++ b/prod-team-account/main.tf @@ -0,0 +1,13 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} \ No newline at end of file diff --git a/prod-team-account/network.tf b/prod-team-account/network.tf new file mode 100644 index 0000000..0f7fbc9 --- /dev/null +++ b/prod-team-account/network.tf @@ -0,0 +1,121 @@ +# VPC +resource "aws_vpc" "vpc" { + cidr_block = var.vpc_cidr + enable_dns_support = true + enable_dns_hostnames = true + tags = { + Name = "${var.project_name}-vpc" + } +} + +# subnet(public) +resource "aws_subnet" "public1" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.1.0/24" + availability_zone = "ap-northeast-2a" + map_public_ip_on_launch = true + tags = { + Name = "public_subnet1" + } +} + +resource "aws_subnet" "public2" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.2.0/24" + availability_zone = "ap-northeast-2b" + map_public_ip_on_launch = true + tags = { + Name = "public_subnet2" + } +} + +# subnet(private) + +resource "aws_subnet" "private1" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.101.0/24" + availability_zone = "ap-northeast-2a" + tags = { + Name = "private_subnet1" + } +} + +resource "aws_subnet" "private2" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.102.0/24" + availability_zone = "ap-northeast-2b" + tags = { + Name = "private_subnet2" + } +} + +# Internet Gateway +resource "aws_internet_gateway" "igw" { + vpc_id = aws_vpc.vpc.id + tags = { + Name = "${var.project_name}-igw" + } +} + +# Public Route Table +resource "aws_route_table" "public" { + vpc_id = aws_vpc.vpc.id + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.igw.id + } + tags = { + Name = "${var.project_name}-public-route-table" + } +} + +# Associate Public Subnets with Route Table +resource "aws_route_table_association" "public1" { + subnet_id = aws_subnet.public1.id + route_table_id = aws_route_table.public.id +} + +resource "aws_route_table_association" "public2" { + subnet_id = aws_subnet.public2.id + route_table_id = aws_route_table.public.id +} + +# NAT Gateway +resource "aws_eip" "nat" { + domain = "vpc" + tags = { + Name = "${var.project_name}-nat-eip" + } +} + +resource "aws_nat_gateway" "nat" { + allocation_id = aws_eip.nat.id + subnet_id = aws_subnet.public1.id + tags = { + Name = "${var.project_name}-nat-gateway" + } + depends_on = [aws_internet_gateway.igw] +} + +# Private Route Table +resource "aws_route_table" "private" { + vpc_id = aws_vpc.vpc.id + route { + cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.nat.id + } + tags = { + Name = "${var.project_name}-private-route-table" + } +} + +# Associate Private Subnets with Route Table +resource "aws_route_table_association" "private1" { + subnet_id = aws_subnet.private1.id + route_table_id = aws_route_table.private.id +} + +resource "aws_route_table_association" "private2" { + subnet_id = aws_subnet.private2.id + route_table_id = aws_route_table.private.id +} \ No newline at end of file diff --git a/prod-team-account/output.tf b/prod-team-account/output.tf new file mode 100644 index 0000000..0f7fbc9 --- /dev/null +++ b/prod-team-account/output.tf @@ -0,0 +1,121 @@ +# VPC +resource "aws_vpc" "vpc" { + cidr_block = var.vpc_cidr + enable_dns_support = true + enable_dns_hostnames = true + tags = { + Name = "${var.project_name}-vpc" + } +} + +# subnet(public) +resource "aws_subnet" "public1" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.1.0/24" + availability_zone = "ap-northeast-2a" + map_public_ip_on_launch = true + tags = { + Name = "public_subnet1" + } +} + +resource "aws_subnet" "public2" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.2.0/24" + availability_zone = "ap-northeast-2b" + map_public_ip_on_launch = true + tags = { + Name = "public_subnet2" + } +} + +# subnet(private) + +resource "aws_subnet" "private1" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.101.0/24" + availability_zone = "ap-northeast-2a" + tags = { + Name = "private_subnet1" + } +} + +resource "aws_subnet" "private2" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.102.0/24" + availability_zone = "ap-northeast-2b" + tags = { + Name = "private_subnet2" + } +} + +# Internet Gateway +resource "aws_internet_gateway" "igw" { + vpc_id = aws_vpc.vpc.id + tags = { + Name = "${var.project_name}-igw" + } +} + +# Public Route Table +resource "aws_route_table" "public" { + vpc_id = aws_vpc.vpc.id + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.igw.id + } + tags = { + Name = "${var.project_name}-public-route-table" + } +} + +# Associate Public Subnets with Route Table +resource "aws_route_table_association" "public1" { + subnet_id = aws_subnet.public1.id + route_table_id = aws_route_table.public.id +} + +resource "aws_route_table_association" "public2" { + subnet_id = aws_subnet.public2.id + route_table_id = aws_route_table.public.id +} + +# NAT Gateway +resource "aws_eip" "nat" { + domain = "vpc" + tags = { + Name = "${var.project_name}-nat-eip" + } +} + +resource "aws_nat_gateway" "nat" { + allocation_id = aws_eip.nat.id + subnet_id = aws_subnet.public1.id + tags = { + Name = "${var.project_name}-nat-gateway" + } + depends_on = [aws_internet_gateway.igw] +} + +# Private Route Table +resource "aws_route_table" "private" { + vpc_id = aws_vpc.vpc.id + route { + cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.nat.id + } + tags = { + Name = "${var.project_name}-private-route-table" + } +} + +# Associate Private Subnets with Route Table +resource "aws_route_table_association" "private1" { + subnet_id = aws_subnet.private1.id + route_table_id = aws_route_table.private.id +} + +resource "aws_route_table_association" "private2" { + subnet_id = aws_subnet.private2.id + route_table_id = aws_route_table.private.id +} \ No newline at end of file diff --git a/prod-team-account/security_group.tf b/prod-team-account/security_group.tf new file mode 100644 index 0000000..fc6e37c --- /dev/null +++ b/prod-team-account/security_group.tf @@ -0,0 +1,44 @@ +# ALB +resource "aws_security_group" "alb_sg" { + name = "${var.project_name}-alb-sg" + description = "Security group for ALB" + vpc_id = aws_vpc.vpc.id + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + description = "Allow HTTP" + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + +} + + # ECS + resource "aws_security_group" "ecs_sg" { + name = "${var.project_name}-ecs-sg" + description = "Security group for ECS tasks" + vpc_id = aws_vpc.vpc.id + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + security_groups = [aws_security_group.alb_sg.id] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + +} \ No newline at end of file diff --git a/prod-team-account/variables.tf b/prod-team-account/variables.tf new file mode 100644 index 0000000..6fb2432 --- /dev/null +++ b/prod-team-account/variables.tf @@ -0,0 +1,24 @@ +variable "project_name" { + description = "프로젝트를 식별하는 이름" + type = string + default = "cloudfence" +} + +variable "vpc_cidr" { + description = "VPC에 할당할 IP 주소 범위" + type = string + default = "10.0.0.0/16" +} + +variable "custom_ami_id" { + description = "ECS 인스턴스에 사용할 사용자 정의 AMI ID" + type = string + # 중요: 반드시 본인의 리전에 맞는 최신 ECS 최적화 AMI ID로 변경하세요. + default = "ami-0c55b159cbfafe1f0" +} + +variable "instance_type" { + description = "ECS 인스턴스의 타입" + type = string + default = "t3.micro" +} \ No newline at end of file From 6b49e629bacc9b6c11e37340b029eb882b418cf9 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Sat, 5 Jul 2025 23:57:44 +0900 Subject: [PATCH 56/66] =?UTF-8?q?terraform=20state=EB=A5=BC=20=ED=86=B5?= =?UTF-8?q?=ED=95=B4=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=A1=9C=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 9 ++++--- operation-team-account/ecr.tf | 19 ++++++++----- operation-team-account/modules/iam/iam.tf | 33 +++++++++++++++++++++++ prod-team-account/data.tf | 8 ++++++ prod-team-account/ecs.tf | 2 +- 5 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 operation-team-account/modules/iam/iam.tf create mode 100644 prod-team-account/data.tf diff --git a/.gitignore b/.gitignore index ab1d334..5b42b4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ .DS_Store .idea <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> ccd1a9a (terraform state를 통해 이미지 로드) # tfstate files *.tfstate @@ -18,7 +21,5 @@ *.tfstate.backup.json *.tfstate.backup.json.* .terraform.lock.hcl -======= -*.tfstate -*.tfstate.* ->>>>>>> bf11ac5 (코드 구조 리팩토링) + + diff --git a/operation-team-account/ecr.tf b/operation-team-account/ecr.tf index d3a5272..8c84270 100644 --- a/operation-team-account/ecr.tf +++ b/operation-team-account/ecr.tf @@ -1,14 +1,21 @@ -resource "aws_ecr_repository" "app" { - name = var.project_name +# iam.tf에서 만든 정책 JSON을 받을 변수 선언 +variable "ecr_policy_json" { + type = string + description = "The JSON policy to attach to the ECR repository" +} +# ECR 리포지토리 생성 +resource "aws_ecr_repository" "app_ecr_repo" { + name = var.project_name image_tag_mutability = "IMMUTABLE" image_scanning_configuration { scan_on_push = true } +} - tags = { - Name = "${var.project_name}-repo" - Project = var.project_name - } +# 정책을 리포지토리에 연결 +resource "aws_ecr_repository_policy" "app_ecr_repo_policy" { + repository = aws_ecr_repository.app_ecr_repo.name + policy = var.ecr_policy_json # 변수로 받은 정책을 사용 } \ No newline at end of file diff --git a/operation-team-account/modules/iam/iam.tf b/operation-team-account/modules/iam/iam.tf new file mode 100644 index 0000000..9f50db1 --- /dev/null +++ b/operation-team-account/modules/iam/iam.tf @@ -0,0 +1,33 @@ +# prod-team-account의 역할 ARN을 변수로 받기 +variable "prod_github_actions_role_arn" { + type = string + description = "The ARN of the IAM role for GitHub Actions in prod-team-account" +} + +# ECR 리포지토리 정책을 생성하기 위한 IAM 정책 문서 데이터 소스 +data "aws_iam_policy_document" "ecr_repo_policy_document" { + statement { + sid = "AllowCrossAccountPush" + effect = "Allow" + principals { + type = "AWS" + identifiers = [var.prod_github_actions_role_arn] + } + actions = [ + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + "ecr:BatchCheckLayerAvailability", + "ecr:PutImage", + "ecr:InitiateLayerUpload", + "ecr:UploadLayerPart", + "ecr:CompleteLayerUpload" + "ecr:GetAuthorizationToken" + ] + } +} + +# 생성된 정책 문서(JSON)를 output으로 출력 +output "ecr_policy_json" { + description = "The JSON policy document for the ECR repository" + value = data.aws_iam_policy_document.ecr_repo_policy_document.json +} \ No newline at end of file diff --git a/prod-team-account/data.tf b/prod-team-account/data.tf new file mode 100644 index 0000000..72a643e --- /dev/null +++ b/prod-team-account/data.tf @@ -0,0 +1,8 @@ +data "terraform_remote_state" "operation_account" { + backend = "s3" # operation-team-account의 state가 저장된 백엔드 + config = { + bucket = "cloudfence-tfstate-app" + key = "operation-team/terraform.tfstate" + region = "ap-northeast-2" + } +} diff --git a/prod-team-account/ecs.tf b/prod-team-account/ecs.tf index 68bf3b4..ca543c7 100644 --- a/prod-team-account/ecs.tf +++ b/prod-team-account/ecs.tf @@ -84,7 +84,7 @@ resource "aws_ecs_task_definition" "ecs_task_definition" { container_definitions = jsonencode([ { name = "${var.project_name}-container" - image = "${aws_ecr_repository.app.repository_url}:latest" + image = "${data.terraform_remote_state.operation_account.outputs.ecr_repository_url}:latest" cpu = 256 memory = 512 essential = true From 8164cd943b8cb21f454a43b4490956d3c631c285 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Fri, 20 Jun 2025 12:55:28 +0900 Subject: [PATCH 57/66] Init Deployment --- .github/workflows/deploy.yml | 0 .github/workflows/terraform-apply.yml | 0 .gitignore | 4 - alb.tf | 111 +++++++++++++++++++++ codedeploy.tf | 51 ++++++++++ ecs.tf | 135 ++++++++++++++++++++++++++ iam.tf | 61 ++++++++++++ main.tf | 14 +++ network.tf | 121 +++++++++++++++++++++++ output.tf | 20 ++++ security_group.tf | 44 +++++++++ variables.tf | 24 +++++ 12 files changed, 581 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/terraform-apply.yml create mode 100644 alb.tf create mode 100644 codedeploy.tf create mode 100644 ecs.tf create mode 100644 iam.tf create mode 100644 main.tf create mode 100644 network.tf create mode 100644 output.tf create mode 100644 security_group.tf create mode 100644 variables.tf diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..e69de29 diff --git a/.github/workflows/terraform-apply.yml b/.github/workflows/terraform-apply.yml new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore index 5b42b4a..feafccb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,5 @@ .DS_Store .idea -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> ccd1a9a (terraform state를 통해 이미지 로드) # tfstate files *.tfstate diff --git a/alb.tf b/alb.tf new file mode 100644 index 0000000..31a1ece --- /dev/null +++ b/alb.tf @@ -0,0 +1,111 @@ +# WAF +resource "aws_wafv2_web_acl" "alb_waf" { + name = "${var.project_name}-alb-waf" + description = "WAF for ALB" + scope = "REGIONAL" + + default_action { + allow {} + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "waf-alb-metric" + sampled_requests_enabled = true + } + + rule { + name = "AWS-AWSManagedRulesCommonRuleSet" + priority = 1 + override_action { + none {} + } + statement { + managed_rule_group_statement { + vendor_name = "AWS" + name = "AWSManagedRulesCommonRuleSet" + } + } + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "AWSManagedRulesCommonRuleSet" + sampled_requests_enabled = true + } + } + + tags = { + Name = "${var.project_name}-alb-waf" + } +} + +# ALB +resource "aws_lb" "alb" { + name = "${var.project_name}-alb" + internal = false + load_balancer_type = "application" + security_groups = [aws_security_group.alb_sg.id] + subnets = [aws_subnet.public1.id, aws_subnet.public2.id] + + enable_deletion_protection = true + + tags = { + Name = "${var.project_name}-alb" + } +} + +# Target Group +resource "aws_lb_target_group" "blue" { + name = "${var.project_name}-blue-tg" + port = 80 + protocol = "HTTP" + vpc_id = aws_vpc.vpc.id + target_type = "instance" + health_check { + path = "/" + protocol = "HTTP" + interval = 30 + timeout = 5 + healthy_threshold = 2 + unhealthy_threshold = 2 + } + tags = { + Name = "${var.project_name}-blue-tg" + } +} + +resource "aws_lb_target_group" "green" { + name = "${var.project_name}-green-tg" + port = 80 + protocol = "HTTP" + vpc_id = aws_vpc.vpc.id + target_type = "instance" + health_check { + path = "/" + protocol = "HTTP" + interval = 30 + timeout = 5 + healthy_threshold = 2 + unhealthy_threshold = 2 + } + tags = { + Name = "${var.project_name}-green-tg" + } +} + +# ALB 리스너 +resource "aws_lb_listener" "http" { + load_balancer_arn = aws_lb.alb.arn + port = 80 + protocol = "HTTP" + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.blue.arn + } +} + +# WAF와 ALB 연결 +resource "aws_wafv2_web_acl_association" "alb_association" { + resource_arn = aws_lb.alb.arn + web_acl_arn = aws_wafv2_web_acl.alb_waf.arn + depends_on = [aws_lb.alb] +} \ No newline at end of file diff --git a/codedeploy.tf b/codedeploy.tf new file mode 100644 index 0000000..9c6301b --- /dev/null +++ b/codedeploy.tf @@ -0,0 +1,51 @@ +# CodeDeploy +resource "aws_codedeploy_app" "ecs_app" { + name = "${var.project_name}-ecs-app" + compute_platform = "ECS" +} + +resource "aws_codedeploy_deployment_group" "ecs_deployment_group" { + app_name = aws_codedeploy_app.ecs_app.name + deployment_group_name = "${var.project_name}-ecs-deployment-group" + service_role_arn = aws_iam_role.codedeploy_role.arn + + deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" + + ecs_service { + cluster_name = aws_ecs_cluster.ecs_cluster.name + service_name = aws_ecs_service.ecs_service.name + } + + deployment_style { + deployment_type = "BLUE_GREEN" + deployment_option = "WITH_TRAFFIC_CONTROL" + } + + blue_green_deployment_config { + deployment_ready_option { + action_on_timeout = "CONTINUE_DEPLOYMENT" + } + terminate_blue_instances_on_deployment_success { + action = "TERMINATE" + termination_wait_time_in_minutes = 5 + } + } + load_balancer_info { + target_group_pair_info { + target_group { + name = aws_lb_target_group.blue.name + } + target_group { + name = aws_lb_target_group.green.name + } + prod_traffic_route { + listener_arns = [aws_lb_listener.http.arn] + } + } + } + auto_rollback_configuration { + enabled = true + events = ["DEPLOYMENT_FAILURE"] + } + +} \ No newline at end of file diff --git a/ecs.tf b/ecs.tf new file mode 100644 index 0000000..22bc2b7 --- /dev/null +++ b/ecs.tf @@ -0,0 +1,135 @@ +# ECS 클러스터 생성 +resource "aws_ecs_cluster" "ecs_cluster" { + name = "${var.project_name}-ecs-cluster" +} + +# ECS Launch Template +resource "aws_launch_template" "ecs_launch_template" { + name_prefix = "${var.project_name}-ecs-launch-template-" + image_id = "ami-0bc365768d185847c" + instance_type = "t3.micro" + + iam_instance_profile { + name = aws_iam_instance_profile.ecs_instance_profile.name + } + network_interfaces { + associate_public_ip_address = false + security_groups = [aws_security_group.ecs_sg.id] + } + + user_data = base64encode(<<-EOF + #!/bin/bash + echo ECS_CLUSTER=${aws_ecs_cluster.ecs_cluster.name} >> /etc/ecs/ecs.config + EOF + ) + + tags = { + Name = "${var.project_name}-ecs-launch-template" + } +} + +# ECS Auto Scaling Group +resource "aws_autoscaling_group" "ecs_auto_scaling_group" { + launch_template { + id = aws_launch_template.ecs_launch_template.id + version = "$Latest" + } + + min_size = 1 + max_size = 4 + desired_capacity = 2 + vpc_zone_identifier = [aws_subnet.private1.id, aws_subnet.private2.id] + health_check_type = "EC2" + force_delete = true + protect_from_scale_in = true + + tag { + key = "ECS_Manage" + value = "${var.project_name}-ecs-auto-scaling-group" + propagate_at_launch = true + } + +} + +# ECS capacity provider +resource "aws_ecs_capacity_provider" "ecs_capacity_provider" { + name = "${var.project_name}-ecs-capacity-provider" + auto_scaling_group_provider { + auto_scaling_group_arn = aws_autoscaling_group.ecs_auto_scaling_group.arn + managed_termination_protection = "ENABLED" + managed_scaling { + status = "ENABLED" + target_capacity = 100 + } + } +} + +# Capacity provider association +resource "aws_ecs_cluster_capacity_providers" "ecs_cluster_capacity_providers" { + cluster_name = aws_ecs_cluster.ecs_cluster.name + capacity_providers = [aws_ecs_capacity_provider.ecs_capacity_provider.name] + default_capacity_provider_strategy { + capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name + weight = 100 + base = 1 + } +} + +# ECS Task Definition +resource "aws_ecs_task_definition" "ecs_task_definition" { + family = "${var.project_name}-ecs-task" + network_mode = "bridge" + requires_compatibilities = ["EC2"] + + container_definitions = jsonencode([ + { + name = "${var.project_name}-container" + image = "sh1220/mnrader" + cpu = 256 + memory = 512 + essential = true + portMappings = [ + { + containerPort = 80 + hostPort = 80 + protocol = "tcp" + } + ] + } + ]) +} + +# ECS Service +resource "aws_ecs_service" "ecs_service" { + name = "${var.project_name}-ecs-service" + cluster = aws_ecs_cluster.ecs_cluster.id + task_definition = aws_ecs_task_definition.ecs_task_definition.arn + desired_count = 2 + + + capacity_provider_strategy { + capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name + weight = 100 + } + + load_balancer { + target_group_arn = aws_lb_target_group.blue.arn + container_name = "${var.project_name}-container" + container_port = 80 + } + + deployment_controller { + type = "CODE_DEPLOY" + } + + lifecycle { + ignore_changes = [task_definition, desired_count] + } + + health_check_grace_period_seconds = 60 + + tags = { + Name = "${var.project_name}-ecs-service" + } +} + diff --git a/iam.tf b/iam.tf new file mode 100644 index 0000000..4599c65 --- /dev/null +++ b/iam.tf @@ -0,0 +1,61 @@ +# ECS 인스턴스가 사용할 IAM 역할 생성 +resource "aws_iam_role" "ecs_instance_role" { + name = "${var.project_name}-ecs-instance-role" + + # 이 역할을 EC2 인스턴스가 사용할 수 있도록 신뢰 정책 설정 + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "ec2.amazonaws.com" + } + } + ] + }) + + tags = { + Name = "${var.project_name}-ecs-instance-role" + } +} + +# AWS에서 관리하는 정책을 위에서 만든 역할에 연결 +resource "aws_iam_role_policy_attachment" "ecs_instance_role_attachment" { + role = aws_iam_role.ecs_instance_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" +} + +# EC2 인스턴스에 역할을 부여하기 위한 인스턴스 프로파일 생성 +resource "aws_iam_instance_profile" "ecs_instance_profile" { + name = "${var.project_name}-ecs-instance-profile" + role = aws_iam_role.ecs_instance_role.name +} + +# ECS 작업 실행 역할 +resource "aws_iam_role" "ecs_task_execution_role" { + name = "${var.project_name}-ecs-task-execution-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [{ Action = "sts:AssumeRole", Effect = "Allow", Principal = { Service = "ecs-tasks.amazonaws.com" } }] + }) +} +resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_attachment" { + role = aws_iam_role.ecs_task_execution_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" +} + +# CodeDeploy를 위한 IAM 역할 +resource "aws_iam_role" "codedeploy_role" { + name = "${var.project_name}-codedeploy-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [{ Action = "sts:AssumeRole", Effect = "Allow", Principal = { Service = "codedeploy.amazonaws.com" } }] + }) +} +resource "aws_iam_role_policy_attachment" "codedeploy_role_attachment" { + role = aws_iam_role.codedeploy_role.name + policy_arn = "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS" +} + diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..7c9372f --- /dev/null +++ b/main.tf @@ -0,0 +1,14 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} + diff --git a/network.tf b/network.tf new file mode 100644 index 0000000..0f7fbc9 --- /dev/null +++ b/network.tf @@ -0,0 +1,121 @@ +# VPC +resource "aws_vpc" "vpc" { + cidr_block = var.vpc_cidr + enable_dns_support = true + enable_dns_hostnames = true + tags = { + Name = "${var.project_name}-vpc" + } +} + +# subnet(public) +resource "aws_subnet" "public1" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.1.0/24" + availability_zone = "ap-northeast-2a" + map_public_ip_on_launch = true + tags = { + Name = "public_subnet1" + } +} + +resource "aws_subnet" "public2" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.2.0/24" + availability_zone = "ap-northeast-2b" + map_public_ip_on_launch = true + tags = { + Name = "public_subnet2" + } +} + +# subnet(private) + +resource "aws_subnet" "private1" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.101.0/24" + availability_zone = "ap-northeast-2a" + tags = { + Name = "private_subnet1" + } +} + +resource "aws_subnet" "private2" { + vpc_id = aws_vpc.vpc.id + cidr_block = "10.0.102.0/24" + availability_zone = "ap-northeast-2b" + tags = { + Name = "private_subnet2" + } +} + +# Internet Gateway +resource "aws_internet_gateway" "igw" { + vpc_id = aws_vpc.vpc.id + tags = { + Name = "${var.project_name}-igw" + } +} + +# Public Route Table +resource "aws_route_table" "public" { + vpc_id = aws_vpc.vpc.id + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.igw.id + } + tags = { + Name = "${var.project_name}-public-route-table" + } +} + +# Associate Public Subnets with Route Table +resource "aws_route_table_association" "public1" { + subnet_id = aws_subnet.public1.id + route_table_id = aws_route_table.public.id +} + +resource "aws_route_table_association" "public2" { + subnet_id = aws_subnet.public2.id + route_table_id = aws_route_table.public.id +} + +# NAT Gateway +resource "aws_eip" "nat" { + domain = "vpc" + tags = { + Name = "${var.project_name}-nat-eip" + } +} + +resource "aws_nat_gateway" "nat" { + allocation_id = aws_eip.nat.id + subnet_id = aws_subnet.public1.id + tags = { + Name = "${var.project_name}-nat-gateway" + } + depends_on = [aws_internet_gateway.igw] +} + +# Private Route Table +resource "aws_route_table" "private" { + vpc_id = aws_vpc.vpc.id + route { + cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.nat.id + } + tags = { + Name = "${var.project_name}-private-route-table" + } +} + +# Associate Private Subnets with Route Table +resource "aws_route_table_association" "private1" { + subnet_id = aws_subnet.private1.id + route_table_id = aws_route_table.private.id +} + +resource "aws_route_table_association" "private2" { + subnet_id = aws_subnet.private2.id + route_table_id = aws_route_table.private.id +} \ No newline at end of file diff --git a/output.tf b/output.tf new file mode 100644 index 0000000..79e62d3 --- /dev/null +++ b/output.tf @@ -0,0 +1,20 @@ +# outputs.tf +output "vpc_id" { + description = "생성된 VPC의 ID" + value = aws_vpc.vpc.id +} + +output "public_subnet_ids" { + description = "생성된 Public Subnet들의 ID 목록" + value = [aws_subnet.public1.id, aws_subnet.public2.id] +} + +output "private_subnet_ids" { + description = "생성된 Private Subnet들의 ID 목록" + value = [aws_subnet.private1.id, aws_subnet.private2.id] +} + +output "alb_dns_name" { + description = "ALB의 DNS 이름" + value = aws_lb.alb.dns_name +} \ No newline at end of file diff --git a/security_group.tf b/security_group.tf new file mode 100644 index 0000000..ff9059b --- /dev/null +++ b/security_group.tf @@ -0,0 +1,44 @@ +# ALB +resource "aws_security_group" "alb_sg" { + name = "${var.project_name}-alb-sg" + description = "Security group for ALB" + vpc_id = aws_vpc.vpc.id + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + description = "Allow HTTP" + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + +} + + # ECS + resource "aws_security_group" "ecs_sg" { + name = "${var.project_name}-ecs-sg" + description = "Security group for ECS tasks" + vpc_id = aws_vpc.vpc.id + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + security_groups = [aws_security_group.alb_sg.id] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + +} diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..b96a003 --- /dev/null +++ b/variables.tf @@ -0,0 +1,24 @@ +variable "project_name" { + description = "프로젝트를 식별하는 이름" + type = string + default = "WHS-CloudFence" +} + +variable "vpc_cidr" { + description = "VPC에 할당할 IP 주소 범위" + type = string + default = "10.0.0.0/16" +} + +variable "custom_ami_id" { + description = "ECS 인스턴스에 사용할 사용자 정의 AMI ID" + type = string + # 중요: 반드시 본인의 리전에 맞는 최신 ECS 최적화 AMI ID로 변경하세요. + default = "ami-0c55b159cbfafe1f0" +} + +variable "instance_type" { + description = "ECS 인스턴스의 타입" + type = string + default = "t3.micro" +} \ No newline at end of file From 99039e812550c7cc723ab1d08d82c3bf5b53093e Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Sun, 6 Jul 2025 16:10:23 +0900 Subject: [PATCH 58/66] =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alb.tf | 111 -------------------------------------- codedeploy.tf | 51 ------------------ ecs.tf | 135 ---------------------------------------------- iam.tf | 61 --------------------- main.tf | 14 ----- network.tf | 121 ----------------------------------------- output.tf | 20 ------- security_group.tf | 44 --------------- variables.tf | 24 --------- 9 files changed, 581 deletions(-) delete mode 100644 alb.tf delete mode 100644 codedeploy.tf delete mode 100644 ecs.tf delete mode 100644 iam.tf delete mode 100644 main.tf delete mode 100644 network.tf delete mode 100644 output.tf delete mode 100644 security_group.tf delete mode 100644 variables.tf diff --git a/alb.tf b/alb.tf deleted file mode 100644 index 31a1ece..0000000 --- a/alb.tf +++ /dev/null @@ -1,111 +0,0 @@ -# WAF -resource "aws_wafv2_web_acl" "alb_waf" { - name = "${var.project_name}-alb-waf" - description = "WAF for ALB" - scope = "REGIONAL" - - default_action { - allow {} - } - - visibility_config { - cloudwatch_metrics_enabled = true - metric_name = "waf-alb-metric" - sampled_requests_enabled = true - } - - rule { - name = "AWS-AWSManagedRulesCommonRuleSet" - priority = 1 - override_action { - none {} - } - statement { - managed_rule_group_statement { - vendor_name = "AWS" - name = "AWSManagedRulesCommonRuleSet" - } - } - visibility_config { - cloudwatch_metrics_enabled = true - metric_name = "AWSManagedRulesCommonRuleSet" - sampled_requests_enabled = true - } - } - - tags = { - Name = "${var.project_name}-alb-waf" - } -} - -# ALB -resource "aws_lb" "alb" { - name = "${var.project_name}-alb" - internal = false - load_balancer_type = "application" - security_groups = [aws_security_group.alb_sg.id] - subnets = [aws_subnet.public1.id, aws_subnet.public2.id] - - enable_deletion_protection = true - - tags = { - Name = "${var.project_name}-alb" - } -} - -# Target Group -resource "aws_lb_target_group" "blue" { - name = "${var.project_name}-blue-tg" - port = 80 - protocol = "HTTP" - vpc_id = aws_vpc.vpc.id - target_type = "instance" - health_check { - path = "/" - protocol = "HTTP" - interval = 30 - timeout = 5 - healthy_threshold = 2 - unhealthy_threshold = 2 - } - tags = { - Name = "${var.project_name}-blue-tg" - } -} - -resource "aws_lb_target_group" "green" { - name = "${var.project_name}-green-tg" - port = 80 - protocol = "HTTP" - vpc_id = aws_vpc.vpc.id - target_type = "instance" - health_check { - path = "/" - protocol = "HTTP" - interval = 30 - timeout = 5 - healthy_threshold = 2 - unhealthy_threshold = 2 - } - tags = { - Name = "${var.project_name}-green-tg" - } -} - -# ALB 리스너 -resource "aws_lb_listener" "http" { - load_balancer_arn = aws_lb.alb.arn - port = 80 - protocol = "HTTP" - default_action { - type = "forward" - target_group_arn = aws_lb_target_group.blue.arn - } -} - -# WAF와 ALB 연결 -resource "aws_wafv2_web_acl_association" "alb_association" { - resource_arn = aws_lb.alb.arn - web_acl_arn = aws_wafv2_web_acl.alb_waf.arn - depends_on = [aws_lb.alb] -} \ No newline at end of file diff --git a/codedeploy.tf b/codedeploy.tf deleted file mode 100644 index 9c6301b..0000000 --- a/codedeploy.tf +++ /dev/null @@ -1,51 +0,0 @@ -# CodeDeploy -resource "aws_codedeploy_app" "ecs_app" { - name = "${var.project_name}-ecs-app" - compute_platform = "ECS" -} - -resource "aws_codedeploy_deployment_group" "ecs_deployment_group" { - app_name = aws_codedeploy_app.ecs_app.name - deployment_group_name = "${var.project_name}-ecs-deployment-group" - service_role_arn = aws_iam_role.codedeploy_role.arn - - deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" - - ecs_service { - cluster_name = aws_ecs_cluster.ecs_cluster.name - service_name = aws_ecs_service.ecs_service.name - } - - deployment_style { - deployment_type = "BLUE_GREEN" - deployment_option = "WITH_TRAFFIC_CONTROL" - } - - blue_green_deployment_config { - deployment_ready_option { - action_on_timeout = "CONTINUE_DEPLOYMENT" - } - terminate_blue_instances_on_deployment_success { - action = "TERMINATE" - termination_wait_time_in_minutes = 5 - } - } - load_balancer_info { - target_group_pair_info { - target_group { - name = aws_lb_target_group.blue.name - } - target_group { - name = aws_lb_target_group.green.name - } - prod_traffic_route { - listener_arns = [aws_lb_listener.http.arn] - } - } - } - auto_rollback_configuration { - enabled = true - events = ["DEPLOYMENT_FAILURE"] - } - -} \ No newline at end of file diff --git a/ecs.tf b/ecs.tf deleted file mode 100644 index 22bc2b7..0000000 --- a/ecs.tf +++ /dev/null @@ -1,135 +0,0 @@ -# ECS 클러스터 생성 -resource "aws_ecs_cluster" "ecs_cluster" { - name = "${var.project_name}-ecs-cluster" -} - -# ECS Launch Template -resource "aws_launch_template" "ecs_launch_template" { - name_prefix = "${var.project_name}-ecs-launch-template-" - image_id = "ami-0bc365768d185847c" - instance_type = "t3.micro" - - iam_instance_profile { - name = aws_iam_instance_profile.ecs_instance_profile.name - } - network_interfaces { - associate_public_ip_address = false - security_groups = [aws_security_group.ecs_sg.id] - } - - user_data = base64encode(<<-EOF - #!/bin/bash - echo ECS_CLUSTER=${aws_ecs_cluster.ecs_cluster.name} >> /etc/ecs/ecs.config - EOF - ) - - tags = { - Name = "${var.project_name}-ecs-launch-template" - } -} - -# ECS Auto Scaling Group -resource "aws_autoscaling_group" "ecs_auto_scaling_group" { - launch_template { - id = aws_launch_template.ecs_launch_template.id - version = "$Latest" - } - - min_size = 1 - max_size = 4 - desired_capacity = 2 - vpc_zone_identifier = [aws_subnet.private1.id, aws_subnet.private2.id] - health_check_type = "EC2" - force_delete = true - protect_from_scale_in = true - - tag { - key = "ECS_Manage" - value = "${var.project_name}-ecs-auto-scaling-group" - propagate_at_launch = true - } - -} - -# ECS capacity provider -resource "aws_ecs_capacity_provider" "ecs_capacity_provider" { - name = "${var.project_name}-ecs-capacity-provider" - auto_scaling_group_provider { - auto_scaling_group_arn = aws_autoscaling_group.ecs_auto_scaling_group.arn - managed_termination_protection = "ENABLED" - managed_scaling { - status = "ENABLED" - target_capacity = 100 - } - } -} - -# Capacity provider association -resource "aws_ecs_cluster_capacity_providers" "ecs_cluster_capacity_providers" { - cluster_name = aws_ecs_cluster.ecs_cluster.name - capacity_providers = [aws_ecs_capacity_provider.ecs_capacity_provider.name] - default_capacity_provider_strategy { - capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name - weight = 100 - base = 1 - } -} - -# ECS Task Definition -resource "aws_ecs_task_definition" "ecs_task_definition" { - family = "${var.project_name}-ecs-task" - network_mode = "bridge" - requires_compatibilities = ["EC2"] - - container_definitions = jsonencode([ - { - name = "${var.project_name}-container" - image = "sh1220/mnrader" - cpu = 256 - memory = 512 - essential = true - portMappings = [ - { - containerPort = 80 - hostPort = 80 - protocol = "tcp" - } - ] - } - ]) -} - -# ECS Service -resource "aws_ecs_service" "ecs_service" { - name = "${var.project_name}-ecs-service" - cluster = aws_ecs_cluster.ecs_cluster.id - task_definition = aws_ecs_task_definition.ecs_task_definition.arn - desired_count = 2 - - - capacity_provider_strategy { - capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name - weight = 100 - } - - load_balancer { - target_group_arn = aws_lb_target_group.blue.arn - container_name = "${var.project_name}-container" - container_port = 80 - } - - deployment_controller { - type = "CODE_DEPLOY" - } - - lifecycle { - ignore_changes = [task_definition, desired_count] - } - - health_check_grace_period_seconds = 60 - - tags = { - Name = "${var.project_name}-ecs-service" - } -} - diff --git a/iam.tf b/iam.tf deleted file mode 100644 index 4599c65..0000000 --- a/iam.tf +++ /dev/null @@ -1,61 +0,0 @@ -# ECS 인스턴스가 사용할 IAM 역할 생성 -resource "aws_iam_role" "ecs_instance_role" { - name = "${var.project_name}-ecs-instance-role" - - # 이 역할을 EC2 인스턴스가 사용할 수 있도록 신뢰 정책 설정 - assume_role_policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - { - Action = "sts:AssumeRole", - Effect = "Allow", - Principal = { - Service = "ec2.amazonaws.com" - } - } - ] - }) - - tags = { - Name = "${var.project_name}-ecs-instance-role" - } -} - -# AWS에서 관리하는 정책을 위에서 만든 역할에 연결 -resource "aws_iam_role_policy_attachment" "ecs_instance_role_attachment" { - role = aws_iam_role.ecs_instance_role.name - policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" -} - -# EC2 인스턴스에 역할을 부여하기 위한 인스턴스 프로파일 생성 -resource "aws_iam_instance_profile" "ecs_instance_profile" { - name = "${var.project_name}-ecs-instance-profile" - role = aws_iam_role.ecs_instance_role.name -} - -# ECS 작업 실행 역할 -resource "aws_iam_role" "ecs_task_execution_role" { - name = "${var.project_name}-ecs-task-execution-role" - assume_role_policy = jsonencode({ - Version = "2012-10-17", - Statement = [{ Action = "sts:AssumeRole", Effect = "Allow", Principal = { Service = "ecs-tasks.amazonaws.com" } }] - }) -} -resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_attachment" { - role = aws_iam_role.ecs_task_execution_role.name - policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" -} - -# CodeDeploy를 위한 IAM 역할 -resource "aws_iam_role" "codedeploy_role" { - name = "${var.project_name}-codedeploy-role" - assume_role_policy = jsonencode({ - Version = "2012-10-17", - Statement = [{ Action = "sts:AssumeRole", Effect = "Allow", Principal = { Service = "codedeploy.amazonaws.com" } }] - }) -} -resource "aws_iam_role_policy_attachment" "codedeploy_role_attachment" { - role = aws_iam_role.codedeploy_role.name - policy_arn = "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS" -} - diff --git a/main.tf b/main.tf deleted file mode 100644 index 7c9372f..0000000 --- a/main.tf +++ /dev/null @@ -1,14 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.0" - } - } - -} - -provider "aws" { - region = "ap-northeast-2" -} - diff --git a/network.tf b/network.tf deleted file mode 100644 index 0f7fbc9..0000000 --- a/network.tf +++ /dev/null @@ -1,121 +0,0 @@ -# VPC -resource "aws_vpc" "vpc" { - cidr_block = var.vpc_cidr - enable_dns_support = true - enable_dns_hostnames = true - tags = { - Name = "${var.project_name}-vpc" - } -} - -# subnet(public) -resource "aws_subnet" "public1" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.1.0/24" - availability_zone = "ap-northeast-2a" - map_public_ip_on_launch = true - tags = { - Name = "public_subnet1" - } -} - -resource "aws_subnet" "public2" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.2.0/24" - availability_zone = "ap-northeast-2b" - map_public_ip_on_launch = true - tags = { - Name = "public_subnet2" - } -} - -# subnet(private) - -resource "aws_subnet" "private1" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.101.0/24" - availability_zone = "ap-northeast-2a" - tags = { - Name = "private_subnet1" - } -} - -resource "aws_subnet" "private2" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.102.0/24" - availability_zone = "ap-northeast-2b" - tags = { - Name = "private_subnet2" - } -} - -# Internet Gateway -resource "aws_internet_gateway" "igw" { - vpc_id = aws_vpc.vpc.id - tags = { - Name = "${var.project_name}-igw" - } -} - -# Public Route Table -resource "aws_route_table" "public" { - vpc_id = aws_vpc.vpc.id - route { - cidr_block = "0.0.0.0/0" - gateway_id = aws_internet_gateway.igw.id - } - tags = { - Name = "${var.project_name}-public-route-table" - } -} - -# Associate Public Subnets with Route Table -resource "aws_route_table_association" "public1" { - subnet_id = aws_subnet.public1.id - route_table_id = aws_route_table.public.id -} - -resource "aws_route_table_association" "public2" { - subnet_id = aws_subnet.public2.id - route_table_id = aws_route_table.public.id -} - -# NAT Gateway -resource "aws_eip" "nat" { - domain = "vpc" - tags = { - Name = "${var.project_name}-nat-eip" - } -} - -resource "aws_nat_gateway" "nat" { - allocation_id = aws_eip.nat.id - subnet_id = aws_subnet.public1.id - tags = { - Name = "${var.project_name}-nat-gateway" - } - depends_on = [aws_internet_gateway.igw] -} - -# Private Route Table -resource "aws_route_table" "private" { - vpc_id = aws_vpc.vpc.id - route { - cidr_block = "0.0.0.0/0" - nat_gateway_id = aws_nat_gateway.nat.id - } - tags = { - Name = "${var.project_name}-private-route-table" - } -} - -# Associate Private Subnets with Route Table -resource "aws_route_table_association" "private1" { - subnet_id = aws_subnet.private1.id - route_table_id = aws_route_table.private.id -} - -resource "aws_route_table_association" "private2" { - subnet_id = aws_subnet.private2.id - route_table_id = aws_route_table.private.id -} \ No newline at end of file diff --git a/output.tf b/output.tf deleted file mode 100644 index 79e62d3..0000000 --- a/output.tf +++ /dev/null @@ -1,20 +0,0 @@ -# outputs.tf -output "vpc_id" { - description = "생성된 VPC의 ID" - value = aws_vpc.vpc.id -} - -output "public_subnet_ids" { - description = "생성된 Public Subnet들의 ID 목록" - value = [aws_subnet.public1.id, aws_subnet.public2.id] -} - -output "private_subnet_ids" { - description = "생성된 Private Subnet들의 ID 목록" - value = [aws_subnet.private1.id, aws_subnet.private2.id] -} - -output "alb_dns_name" { - description = "ALB의 DNS 이름" - value = aws_lb.alb.dns_name -} \ No newline at end of file diff --git a/security_group.tf b/security_group.tf deleted file mode 100644 index ff9059b..0000000 --- a/security_group.tf +++ /dev/null @@ -1,44 +0,0 @@ -# ALB -resource "aws_security_group" "alb_sg" { - name = "${var.project_name}-alb-sg" - description = "Security group for ALB" - vpc_id = aws_vpc.vpc.id - - ingress { - from_port = 80 - to_port = 80 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - description = "Allow HTTP" - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - -} - - # ECS - resource "aws_security_group" "ecs_sg" { - name = "${var.project_name}-ecs-sg" - description = "Security group for ECS tasks" - vpc_id = aws_vpc.vpc.id - - ingress { - from_port = 80 - to_port = 80 - protocol = "tcp" - security_groups = [aws_security_group.alb_sg.id] - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - -} diff --git a/variables.tf b/variables.tf deleted file mode 100644 index b96a003..0000000 --- a/variables.tf +++ /dev/null @@ -1,24 +0,0 @@ -variable "project_name" { - description = "프로젝트를 식별하는 이름" - type = string - default = "WHS-CloudFence" -} - -variable "vpc_cidr" { - description = "VPC에 할당할 IP 주소 범위" - type = string - default = "10.0.0.0/16" -} - -variable "custom_ami_id" { - description = "ECS 인스턴스에 사용할 사용자 정의 AMI ID" - type = string - # 중요: 반드시 본인의 리전에 맞는 최신 ECS 최적화 AMI ID로 변경하세요. - default = "ami-0c55b159cbfafe1f0" -} - -variable "instance_type" { - description = "ECS 인스턴스의 타입" - type = string - default = "t3.micro" -} \ No newline at end of file From 08bacc49de425531da2442676806feaa7da51679 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Sun, 6 Jul 2025 21:35:23 +0900 Subject: [PATCH 59/66] =?UTF-8?q?CI/CD=20.tf=EC=97=86=EB=8A=94=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=20=EC=8B=A4=ED=96=89=20X?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 11a0d91..f318ab7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Application-Deployment CI +name: Application-DeploymentCI on: @@ -11,6 +11,7 @@ on: - "dev-team-account/**" - "security-team-account/**" - "stage-team-account/**" + - "management-team-account/**" permissions: contents: read From dce807a643ce10bf2bab877a01b755302e4d3d5e Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Mon, 7 Jul 2025 04:11:01 +0900 Subject: [PATCH 60/66] CI/CD Test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI/CD Test CI/CD Test CI/CD Test CI/CD Test 의존성 기반 CD 수정 --- .github/workflows/cd.yml | 13 +++++++++++++ prod-team-account/iam.tf | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 9e756d5..a126ca3 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -142,10 +142,23 @@ jobs: runs-on: ubuntu-latest strategy: +<<<<<<< HEAD fail-fast: true max-parallel: 1 matrix: include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} +======= +<<<<<<< HEAD + matrix: # matrix 기반 반복 실행 + include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} + fail-fast: false # 하나 실패해도 나머지 job은 계속 진행 +======= + fail-fast: true + max-parallel: 1 + matrix: + include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} +>>>>>>> 1d0cceb (CI/CD Test) +>>>>>>> cac750c (CI/CD Test) steps: - name: Checkout repository diff --git a/prod-team-account/iam.tf b/prod-team-account/iam.tf index 0b281a0..ddd820b 100644 --- a/prod-team-account/iam.tf +++ b/prod-team-account/iam.tf @@ -1,3 +1,21 @@ +<<<<<<< HEAD:prod-team-account/iam.tf +======= +# CI/CD workflowTest +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} + +>>>>>>> cac750c (CI/CD Test):prod-team-account/iam/main.tf # ECS 인스턴스가 사용할 IAM 역할 생성 resource "aws_iam_role" "ecs_instance_role" { name = "${var.project_name}-ecs-instance-role" From d3079cf5e8409367bf2ec4b8d28c0eb3d66b3bbf Mon Sep 17 00:00:00 2001 From: rnjsdbwlsqwer Date: Sun, 6 Jul 2025 17:13:57 +0900 Subject: [PATCH 61/66] =?UTF-8?q?=EB=A3=A8=ED=8A=B8=20=EB=94=94=EB=A0=89?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EC=B0=B8=EC=A1=B0,=20.tf=20=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=ED=8F=B4=EB=8D=94=EB=8A=94=20=EC=8B=A4=ED=96=89=20?= =?UTF-8?q?X?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd.yml | 14 -------------- .github/workflows/ci.yml | 2 +- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index a126ca3..6dfc599 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -109,7 +109,6 @@ jobs: ROLE_KEY="${ROLE_MAP[$ACCOUNT_PREFIX]}" MATRIX_ITEMS+=("{\"dir\":\"$DIR\",\"role_key\":\"$ROLE_KEY\"}") - # 루트 디렉터리 검사 TF_COUNT_ROOT=$(find "$BASE_DIR" -maxdepth 1 -name '*.tf' | wc -l) if [ "$TF_COUNT_ROOT" -gt 0 ]; then @@ -142,23 +141,10 @@ jobs: runs-on: ubuntu-latest strategy: -<<<<<<< HEAD - fail-fast: true - max-parallel: 1 - matrix: - include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} -======= -<<<<<<< HEAD - matrix: # matrix 기반 반복 실행 - include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} - fail-fast: false # 하나 실패해도 나머지 job은 계속 진행 -======= fail-fast: true max-parallel: 1 matrix: include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} ->>>>>>> 1d0cceb (CI/CD Test) ->>>>>>> cac750c (CI/CD Test) steps: - name: Checkout repository diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f318ab7..d6b393a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: echo "Changed files:" echo "$FILES" - declare -A ROLE_MAP=( + declare -A ROLE_MAP=( ["operation-team-account"]="ROLE_ARN_OPERATION" ["identity-team-account"]="ROLE_ARN_IDENTITY" ["prod-team-account"]="ROLE_ARN_PROD" From 630a75793cc637ae45a087f54dd00fd261b21633 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Wed, 9 Jul 2025 02:02:48 +0900 Subject: [PATCH 62/66] =?UTF-8?q?=ED=8F=B4=EB=8D=94=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=20=EA=B0=9C=ED=8E=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 폴더 구조 개편 구조 개선 폴더 구조 변경 --- .github/workflows/cd.yml | 4 ++++ .gitignore | 2 -- .terraform.lock.hcl | 25 --------------------- prod-team-account/deploy/codedeploy/main.tf | 12 ++++++++++ 4 files changed, 16 insertions(+), 27 deletions(-) delete mode 100644 .terraform.lock.hcl diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 6dfc599..2f94eb1 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -62,7 +62,11 @@ jobs: ["prod-team-account/alb"]="prod-team-account/deploy/vpc prod-team-account/deploy/acm" ["prod-team-account/ecs"]="prod-team-account/deploy/vpc prod-team-account/deploy/iam prod-team-account/deploy/alb operation-team-account/deploy/ecr" ["prod-team-account/codedeploy"]="prod-team-account/deploy/ecs" +<<<<<<< HEAD ) +======= + )ㅜ +>>>>>>> fca52e1 (폴더 구조 개편) # Push 이벤트에 포함된 변경된 파일 목록을 호출 echo "Comparing changes between ${{ github.event.before }} and ${{ github.event.after }}" diff --git a/.gitignore b/.gitignore index feafccb..e95ad6e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,3 @@ *.tfstate.backup.json *.tfstate.backup.json.* .terraform.lock.hcl - - diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl deleted file mode 100644 index cdc1668..0000000 --- a/.terraform.lock.hcl +++ /dev/null @@ -1,25 +0,0 @@ -# This file is maintained automatically by "terraform init". -# Manual edits may be lost in future updates. - -provider "registry.terraform.io/hashicorp/aws" { - version = "5.100.0" - constraints = "~> 5.0" - hashes = [ - "h1:Ijt7pOlB7Tr7maGQIqtsLFbl7pSMIj06TVdkoSBcYOw=", - "zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644", - "zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2", - "zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274", - "zh:6330766f1d85f01ae6ea90d1b214b8b74cc8c1badc4696b165b36ddd4cc15f7b", - "zh:7c8c2e30d8e55291b86fcb64bdf6c25489d538688545eb48fd74ad622e5d3862", - "zh:99b1003bd9bd32ee323544da897148f46a527f622dc3971af63ea3e251596342", - "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", - "zh:9f8b909d3ec50ade83c8062290378b1ec553edef6a447c56dadc01a99f4eaa93", - "zh:aaef921ff9aabaf8b1869a86d692ebd24fbd4e12c21205034bb679b9caf883a2", - "zh:ac882313207aba00dd5a76dbd572a0ddc818bb9cbf5c9d61b28fe30efaec951e", - "zh:bb64e8aff37becab373a1a0cc1080990785304141af42ed6aa3dd4913b000421", - "zh:dfe495f6621df5540d9c92ad40b8067376350b005c637ea6efac5dc15028add4", - "zh:f0ddf0eaf052766cfe09dea8200a946519f653c384ab4336e2a4a64fdd6310e9", - "zh:f1b7e684f4c7ae1eed272b6de7d2049bb87a0275cb04dbb7cda6636f600699c9", - "zh:ff461571e3f233699bf690db319dfe46aec75e58726636a0d97dd9ac6e32fb70", - ] -} diff --git a/prod-team-account/deploy/codedeploy/main.tf b/prod-team-account/deploy/codedeploy/main.tf index b8708ca..3bd8aef 100644 --- a/prod-team-account/deploy/codedeploy/main.tf +++ b/prod-team-account/deploy/codedeploy/main.tf @@ -16,7 +16,11 @@ data "terraform_remote_state" "alb" { backend = "s3" config = { bucket = "cloudfence-prod-state" +<<<<<<< HEAD key = "deploy/alb.tfstate" +======= + key = "prod-team-account/deploy/alb/terraform.tfstate" +>>>>>>> fca52e1 (폴더 구조 개편) region = "ap-northeast-2" } } @@ -25,7 +29,11 @@ data "terraform_remote_state" "iam" { backend = "s3" config = { bucket = "cloudfence-prod-state" +<<<<<<< HEAD key = "deploy/iam.tfstate" +======= + key = "prod-team-account/deploy/iam/terraform.tfstate" +>>>>>>> fca52e1 (폴더 구조 개편) region = "ap-northeast-2" } } @@ -34,7 +42,11 @@ data "terraform_remote_state" "ecs" { backend = "s3" config = { bucket = "cloudfence-prod-state" +<<<<<<< HEAD key = "deploy/ecs.tfstate" +======= + key = "prod-team-account/deploy/ecs/terraform.tfstate" +>>>>>>> fca52e1 (폴더 구조 개편) region = "ap-northeast-2" } } From fb1b8a10173fee66ea7c590915128d8ff19f8013 Mon Sep 17 00:00:00 2001 From: Choi Yunho <151824752+yunhoch0i@users.noreply.github.com> Date: Sat, 12 Jul 2025 22:33:39 +0900 Subject: [PATCH 63/66] Update cd.yml --- .github/workflows/cd.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 2f94eb1..b5d6845 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -62,11 +62,8 @@ jobs: ["prod-team-account/alb"]="prod-team-account/deploy/vpc prod-team-account/deploy/acm" ["prod-team-account/ecs"]="prod-team-account/deploy/vpc prod-team-account/deploy/iam prod-team-account/deploy/alb operation-team-account/deploy/ecr" ["prod-team-account/codedeploy"]="prod-team-account/deploy/ecs" -<<<<<<< HEAD ) -======= - )ㅜ ->>>>>>> fca52e1 (폴더 구조 개편) + # Push 이벤트에 포함된 변경된 파일 목록을 호출 echo "Comparing changes between ${{ github.event.before }} and ${{ github.event.after }}" From 82d6e42f3d1fe292fc9c0b26e4ddea57ab5f5e8f Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Wed, 16 Jul 2025 14:06:58 +0900 Subject: [PATCH 64/66] branch rebase --- .github/workflows/cd.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index b5d6845..adf0f76 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -32,6 +32,11 @@ jobs: - 'dev-team-account/**' stage: - 'stage-team-account/**' + security: + - 'security-team-account/**' + management: + - 'management-team-account/**' + - name: Build Matrix from Filter (with subdirs) @@ -51,6 +56,8 @@ jobs: ["prod"]="ROLE_ARN_PROD" ["dev"]="ROLE_ARN_DEV" ["stage"]="ROLE_ARN_STAGE" + ["security"]="ROLE_ARN_SECURITY" + ["management"]="ROLE_ARN_MANAGEMENT" ) From 6d6f7ffe177c4d1d11bf6f5fd2775c56b1913fd1 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Wed, 16 Jul 2025 14:57:01 +0900 Subject: [PATCH 65/66] branch rebase --- operation-team-account/ecr.tf | 21 --- operation-team-account/modules/iam/iam.tf | 33 ----- prod-team-account/alb.tf | 111 ---------------- prod-team-account/codedeploy.tf | 51 -------- prod-team-account/data.tf | 8 -- prod-team-account/deploy/codedeploy/main.tf | 12 -- prod-team-account/ecs.tf | 135 -------------------- prod-team-account/iam.tf | 98 -------------- prod-team-account/main.tf | 13 -- prod-team-account/network.tf | 121 ------------------ prod-team-account/output.tf | 121 ------------------ prod-team-account/security_group.tf | 44 ------- prod-team-account/variables.tf | 24 ---- 13 files changed, 792 deletions(-) delete mode 100644 operation-team-account/ecr.tf delete mode 100644 operation-team-account/modules/iam/iam.tf delete mode 100644 prod-team-account/alb.tf delete mode 100644 prod-team-account/codedeploy.tf delete mode 100644 prod-team-account/data.tf delete mode 100644 prod-team-account/ecs.tf delete mode 100644 prod-team-account/iam.tf delete mode 100644 prod-team-account/main.tf delete mode 100644 prod-team-account/network.tf delete mode 100644 prod-team-account/output.tf delete mode 100644 prod-team-account/security_group.tf delete mode 100644 prod-team-account/variables.tf diff --git a/operation-team-account/ecr.tf b/operation-team-account/ecr.tf deleted file mode 100644 index 8c84270..0000000 --- a/operation-team-account/ecr.tf +++ /dev/null @@ -1,21 +0,0 @@ -# iam.tf에서 만든 정책 JSON을 받을 변수 선언 -variable "ecr_policy_json" { - type = string - description = "The JSON policy to attach to the ECR repository" -} - -# ECR 리포지토리 생성 -resource "aws_ecr_repository" "app_ecr_repo" { - name = var.project_name - image_tag_mutability = "IMMUTABLE" - - image_scanning_configuration { - scan_on_push = true - } -} - -# 정책을 리포지토리에 연결 -resource "aws_ecr_repository_policy" "app_ecr_repo_policy" { - repository = aws_ecr_repository.app_ecr_repo.name - policy = var.ecr_policy_json # 변수로 받은 정책을 사용 -} \ No newline at end of file diff --git a/operation-team-account/modules/iam/iam.tf b/operation-team-account/modules/iam/iam.tf deleted file mode 100644 index 9f50db1..0000000 --- a/operation-team-account/modules/iam/iam.tf +++ /dev/null @@ -1,33 +0,0 @@ -# prod-team-account의 역할 ARN을 변수로 받기 -variable "prod_github_actions_role_arn" { - type = string - description = "The ARN of the IAM role for GitHub Actions in prod-team-account" -} - -# ECR 리포지토리 정책을 생성하기 위한 IAM 정책 문서 데이터 소스 -data "aws_iam_policy_document" "ecr_repo_policy_document" { - statement { - sid = "AllowCrossAccountPush" - effect = "Allow" - principals { - type = "AWS" - identifiers = [var.prod_github_actions_role_arn] - } - actions = [ - "ecr:GetDownloadUrlForLayer", - "ecr:BatchGetImage", - "ecr:BatchCheckLayerAvailability", - "ecr:PutImage", - "ecr:InitiateLayerUpload", - "ecr:UploadLayerPart", - "ecr:CompleteLayerUpload" - "ecr:GetAuthorizationToken" - ] - } -} - -# 생성된 정책 문서(JSON)를 output으로 출력 -output "ecr_policy_json" { - description = "The JSON policy document for the ECR repository" - value = data.aws_iam_policy_document.ecr_repo_policy_document.json -} \ No newline at end of file diff --git a/prod-team-account/alb.tf b/prod-team-account/alb.tf deleted file mode 100644 index 31a1ece..0000000 --- a/prod-team-account/alb.tf +++ /dev/null @@ -1,111 +0,0 @@ -# WAF -resource "aws_wafv2_web_acl" "alb_waf" { - name = "${var.project_name}-alb-waf" - description = "WAF for ALB" - scope = "REGIONAL" - - default_action { - allow {} - } - - visibility_config { - cloudwatch_metrics_enabled = true - metric_name = "waf-alb-metric" - sampled_requests_enabled = true - } - - rule { - name = "AWS-AWSManagedRulesCommonRuleSet" - priority = 1 - override_action { - none {} - } - statement { - managed_rule_group_statement { - vendor_name = "AWS" - name = "AWSManagedRulesCommonRuleSet" - } - } - visibility_config { - cloudwatch_metrics_enabled = true - metric_name = "AWSManagedRulesCommonRuleSet" - sampled_requests_enabled = true - } - } - - tags = { - Name = "${var.project_name}-alb-waf" - } -} - -# ALB -resource "aws_lb" "alb" { - name = "${var.project_name}-alb" - internal = false - load_balancer_type = "application" - security_groups = [aws_security_group.alb_sg.id] - subnets = [aws_subnet.public1.id, aws_subnet.public2.id] - - enable_deletion_protection = true - - tags = { - Name = "${var.project_name}-alb" - } -} - -# Target Group -resource "aws_lb_target_group" "blue" { - name = "${var.project_name}-blue-tg" - port = 80 - protocol = "HTTP" - vpc_id = aws_vpc.vpc.id - target_type = "instance" - health_check { - path = "/" - protocol = "HTTP" - interval = 30 - timeout = 5 - healthy_threshold = 2 - unhealthy_threshold = 2 - } - tags = { - Name = "${var.project_name}-blue-tg" - } -} - -resource "aws_lb_target_group" "green" { - name = "${var.project_name}-green-tg" - port = 80 - protocol = "HTTP" - vpc_id = aws_vpc.vpc.id - target_type = "instance" - health_check { - path = "/" - protocol = "HTTP" - interval = 30 - timeout = 5 - healthy_threshold = 2 - unhealthy_threshold = 2 - } - tags = { - Name = "${var.project_name}-green-tg" - } -} - -# ALB 리스너 -resource "aws_lb_listener" "http" { - load_balancer_arn = aws_lb.alb.arn - port = 80 - protocol = "HTTP" - default_action { - type = "forward" - target_group_arn = aws_lb_target_group.blue.arn - } -} - -# WAF와 ALB 연결 -resource "aws_wafv2_web_acl_association" "alb_association" { - resource_arn = aws_lb.alb.arn - web_acl_arn = aws_wafv2_web_acl.alb_waf.arn - depends_on = [aws_lb.alb] -} \ No newline at end of file diff --git a/prod-team-account/codedeploy.tf b/prod-team-account/codedeploy.tf deleted file mode 100644 index 9c6301b..0000000 --- a/prod-team-account/codedeploy.tf +++ /dev/null @@ -1,51 +0,0 @@ -# CodeDeploy -resource "aws_codedeploy_app" "ecs_app" { - name = "${var.project_name}-ecs-app" - compute_platform = "ECS" -} - -resource "aws_codedeploy_deployment_group" "ecs_deployment_group" { - app_name = aws_codedeploy_app.ecs_app.name - deployment_group_name = "${var.project_name}-ecs-deployment-group" - service_role_arn = aws_iam_role.codedeploy_role.arn - - deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" - - ecs_service { - cluster_name = aws_ecs_cluster.ecs_cluster.name - service_name = aws_ecs_service.ecs_service.name - } - - deployment_style { - deployment_type = "BLUE_GREEN" - deployment_option = "WITH_TRAFFIC_CONTROL" - } - - blue_green_deployment_config { - deployment_ready_option { - action_on_timeout = "CONTINUE_DEPLOYMENT" - } - terminate_blue_instances_on_deployment_success { - action = "TERMINATE" - termination_wait_time_in_minutes = 5 - } - } - load_balancer_info { - target_group_pair_info { - target_group { - name = aws_lb_target_group.blue.name - } - target_group { - name = aws_lb_target_group.green.name - } - prod_traffic_route { - listener_arns = [aws_lb_listener.http.arn] - } - } - } - auto_rollback_configuration { - enabled = true - events = ["DEPLOYMENT_FAILURE"] - } - -} \ No newline at end of file diff --git a/prod-team-account/data.tf b/prod-team-account/data.tf deleted file mode 100644 index 72a643e..0000000 --- a/prod-team-account/data.tf +++ /dev/null @@ -1,8 +0,0 @@ -data "terraform_remote_state" "operation_account" { - backend = "s3" # operation-team-account의 state가 저장된 백엔드 - config = { - bucket = "cloudfence-tfstate-app" - key = "operation-team/terraform.tfstate" - region = "ap-northeast-2" - } -} diff --git a/prod-team-account/deploy/codedeploy/main.tf b/prod-team-account/deploy/codedeploy/main.tf index 3bd8aef..b8708ca 100644 --- a/prod-team-account/deploy/codedeploy/main.tf +++ b/prod-team-account/deploy/codedeploy/main.tf @@ -16,11 +16,7 @@ data "terraform_remote_state" "alb" { backend = "s3" config = { bucket = "cloudfence-prod-state" -<<<<<<< HEAD key = "deploy/alb.tfstate" -======= - key = "prod-team-account/deploy/alb/terraform.tfstate" ->>>>>>> fca52e1 (폴더 구조 개편) region = "ap-northeast-2" } } @@ -29,11 +25,7 @@ data "terraform_remote_state" "iam" { backend = "s3" config = { bucket = "cloudfence-prod-state" -<<<<<<< HEAD key = "deploy/iam.tfstate" -======= - key = "prod-team-account/deploy/iam/terraform.tfstate" ->>>>>>> fca52e1 (폴더 구조 개편) region = "ap-northeast-2" } } @@ -42,11 +34,7 @@ data "terraform_remote_state" "ecs" { backend = "s3" config = { bucket = "cloudfence-prod-state" -<<<<<<< HEAD key = "deploy/ecs.tfstate" -======= - key = "prod-team-account/deploy/ecs/terraform.tfstate" ->>>>>>> fca52e1 (폴더 구조 개편) region = "ap-northeast-2" } } diff --git a/prod-team-account/ecs.tf b/prod-team-account/ecs.tf deleted file mode 100644 index ca543c7..0000000 --- a/prod-team-account/ecs.tf +++ /dev/null @@ -1,135 +0,0 @@ -# ECS 클러스터 생성 -resource "aws_ecs_cluster" "ecs_cluster" { - name = "${var.project_name}-ecs-cluster" -} - -# ECS Launch Template -resource "aws_launch_template" "ecs_launch_template" { - name_prefix = "${var.project_name}-ecs-launch-template-" - image_id = "ami-0bc365768d185847c" - instance_type = "t2.micro" - - iam_instance_profile { - name = aws_iam_instance_profile.ecs_instance_profile.name - } - network_interfaces { - associate_public_ip_address = false - security_groups = [aws_security_group.ecs_sg.id] - } - - user_data = base64encode(<<-EOF - #!/bin/bash - echo ECS_CLUSTER=${aws_ecs_cluster.ecs_cluster.name} >> /etc/ecs/ecs.config - EOF - ) - - tags = { - Name = "${var.project_name}-ecs-launch-template" - } -} - -# ECS Auto Scaling Group -resource "aws_autoscaling_group" "ecs_auto_scaling_group" { - launch_template { - id = aws_launch_template.ecs_launch_template.id - version = "$Latest" - } - - min_size = 1 - max_size = 4 - desired_capacity = 2 - vpc_zone_identifier = [aws_subnet.private1.id, aws_subnet.private2.id] - health_check_type = "EC2" - force_delete = true - protect_from_scale_in = true - - tag { - key = "ECS_Manage" - value = "${var.project_name}-ecs-auto-scaling-group" - propagate_at_launch = true - } - -} - -# ECS capacity provider -resource "aws_ecs_capacity_provider" "ecs_capacity_provider" { - name = "${var.project_name}-ecs-capacity-provider" - auto_scaling_group_provider { - auto_scaling_group_arn = aws_autoscaling_group.ecs_auto_scaling_group.arn - managed_termination_protection = "ENABLED" - managed_scaling { - status = "ENABLED" - target_capacity = 100 - } - } -} - -# Capacity provider association -resource "aws_ecs_cluster_capacity_providers" "ecs_cluster_capacity_providers" { - cluster_name = aws_ecs_cluster.ecs_cluster.name - capacity_providers = [aws_ecs_capacity_provider.ecs_capacity_provider.name] - default_capacity_provider_strategy { - capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name - weight = 100 - base = 1 - } -} - -# ECS Task Definition -resource "aws_ecs_task_definition" "ecs_task_definition" { - family = "${var.project_name}-ecs-task" - network_mode = "bridge" - requires_compatibilities = ["EC2"] - - container_definitions = jsonencode([ - { - name = "${var.project_name}-container" - image = "${data.terraform_remote_state.operation_account.outputs.ecr_repository_url}:latest" - cpu = 256 - memory = 512 - essential = true - portMappings = [ - { - containerPort = 80 - hostPort = 80 - protocol = "tcp" - } - ] - } - ]) -} - -# ECS Service -resource "aws_ecs_service" "ecs_service" { - name = "${var.project_name}-ecs-service" - cluster = aws_ecs_cluster.ecs_cluster.id - task_definition = aws_ecs_task_definition.ecs_task_definition.arn - desired_count = 2 - - - capacity_provider_strategy { - capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name - weight = 100 - } - - load_balancer { - target_group_arn = aws_lb_target_group.blue.arn - container_name = "${var.project_name}-container" - container_port = 80 - } - - deployment_controller { - type = "CODE_DEPLOY" - } - - lifecycle { - ignore_changes = [task_definition, desired_count] - } - - health_check_grace_period_seconds = 60 - - tags = { - Name = "${var.project_name}-ecs-service" - } -} - \ No newline at end of file diff --git a/prod-team-account/iam.tf b/prod-team-account/iam.tf deleted file mode 100644 index ddd820b..0000000 --- a/prod-team-account/iam.tf +++ /dev/null @@ -1,98 +0,0 @@ -<<<<<<< HEAD:prod-team-account/iam.tf -======= -# CI/CD workflowTest -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.0" - } - } - -} - -provider "aws" { - region = "ap-northeast-2" -} - ->>>>>>> cac750c (CI/CD Test):prod-team-account/iam/main.tf -# ECS 인스턴스가 사용할 IAM 역할 생성 -resource "aws_iam_role" "ecs_instance_role" { - name = "${var.project_name}-ecs-instance-role" - - # 이 역할을 EC2 인스턴스가 사용할 수 있도록 신뢰 정책 설정 - assume_role_policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - { - Action = "sts:AssumeRole", - Effect = "Allow", - Principal = { - Service = "ec2.amazonaws.com" - } - } - ] - }) - - tags = { - Name = "${var.project_name}-ecs-instance-role" - } -} - -# AWS에서 관리하는 정책을 위에서 만든 역할에 연결 -resource "aws_iam_role_policy_attachment" "ecs_instance_role_attachment" { - role = aws_iam_role.ecs_instance_role.name - policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" -} - -# EC2 인스턴스에 역할을 부여하기 위한 인스턴스 프로파일 생성 -resource "aws_iam_instance_profile" "ecs_instance_profile" { - name = "${var.project_name}-ecs-instance-profile" - role = aws_iam_role.ecs_instance_role.name -} - - - -# ECS 작업 실행 역할 -resource "aws_iam_role" "ecs_task_execution_role" { - name = "${var.project_name}-ecs-task-execution-role" - assume_role_policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - { - Action = "sts:AssumeRole", - Effect = "Allow", - Principal = { - Service = "ecs-tasks.amazonaws.com" - } - } - ] - }) -} -resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_attachment" { - role = aws_iam_role.ecs_task_execution_role.name - policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" -} - - - -# CodeDeploy를 위한 IAM 역할 -resource "aws_iam_role" "codedeploy_role" { - name = "${var.project_name}-codedeploy-role" - assume_role_policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - { - Action = "sts:AssumeRole", - Effect = "Allow", - Principal = { - Service = "codedeploy.amazonaws.com" - } - } - ] - }) -} -resource "aws_iam_role_policy_attachment" "codedeploy_role_attachment" { - role = aws_iam_role.codedeploy_role.name - policy_arn = "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS" -} \ No newline at end of file diff --git a/prod-team-account/main.tf b/prod-team-account/main.tf deleted file mode 100644 index 1b0717f..0000000 --- a/prod-team-account/main.tf +++ /dev/null @@ -1,13 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.0" - } - } - -} - -provider "aws" { - region = "ap-northeast-2" -} \ No newline at end of file diff --git a/prod-team-account/network.tf b/prod-team-account/network.tf deleted file mode 100644 index 0f7fbc9..0000000 --- a/prod-team-account/network.tf +++ /dev/null @@ -1,121 +0,0 @@ -# VPC -resource "aws_vpc" "vpc" { - cidr_block = var.vpc_cidr - enable_dns_support = true - enable_dns_hostnames = true - tags = { - Name = "${var.project_name}-vpc" - } -} - -# subnet(public) -resource "aws_subnet" "public1" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.1.0/24" - availability_zone = "ap-northeast-2a" - map_public_ip_on_launch = true - tags = { - Name = "public_subnet1" - } -} - -resource "aws_subnet" "public2" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.2.0/24" - availability_zone = "ap-northeast-2b" - map_public_ip_on_launch = true - tags = { - Name = "public_subnet2" - } -} - -# subnet(private) - -resource "aws_subnet" "private1" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.101.0/24" - availability_zone = "ap-northeast-2a" - tags = { - Name = "private_subnet1" - } -} - -resource "aws_subnet" "private2" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.102.0/24" - availability_zone = "ap-northeast-2b" - tags = { - Name = "private_subnet2" - } -} - -# Internet Gateway -resource "aws_internet_gateway" "igw" { - vpc_id = aws_vpc.vpc.id - tags = { - Name = "${var.project_name}-igw" - } -} - -# Public Route Table -resource "aws_route_table" "public" { - vpc_id = aws_vpc.vpc.id - route { - cidr_block = "0.0.0.0/0" - gateway_id = aws_internet_gateway.igw.id - } - tags = { - Name = "${var.project_name}-public-route-table" - } -} - -# Associate Public Subnets with Route Table -resource "aws_route_table_association" "public1" { - subnet_id = aws_subnet.public1.id - route_table_id = aws_route_table.public.id -} - -resource "aws_route_table_association" "public2" { - subnet_id = aws_subnet.public2.id - route_table_id = aws_route_table.public.id -} - -# NAT Gateway -resource "aws_eip" "nat" { - domain = "vpc" - tags = { - Name = "${var.project_name}-nat-eip" - } -} - -resource "aws_nat_gateway" "nat" { - allocation_id = aws_eip.nat.id - subnet_id = aws_subnet.public1.id - tags = { - Name = "${var.project_name}-nat-gateway" - } - depends_on = [aws_internet_gateway.igw] -} - -# Private Route Table -resource "aws_route_table" "private" { - vpc_id = aws_vpc.vpc.id - route { - cidr_block = "0.0.0.0/0" - nat_gateway_id = aws_nat_gateway.nat.id - } - tags = { - Name = "${var.project_name}-private-route-table" - } -} - -# Associate Private Subnets with Route Table -resource "aws_route_table_association" "private1" { - subnet_id = aws_subnet.private1.id - route_table_id = aws_route_table.private.id -} - -resource "aws_route_table_association" "private2" { - subnet_id = aws_subnet.private2.id - route_table_id = aws_route_table.private.id -} \ No newline at end of file diff --git a/prod-team-account/output.tf b/prod-team-account/output.tf deleted file mode 100644 index 0f7fbc9..0000000 --- a/prod-team-account/output.tf +++ /dev/null @@ -1,121 +0,0 @@ -# VPC -resource "aws_vpc" "vpc" { - cidr_block = var.vpc_cidr - enable_dns_support = true - enable_dns_hostnames = true - tags = { - Name = "${var.project_name}-vpc" - } -} - -# subnet(public) -resource "aws_subnet" "public1" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.1.0/24" - availability_zone = "ap-northeast-2a" - map_public_ip_on_launch = true - tags = { - Name = "public_subnet1" - } -} - -resource "aws_subnet" "public2" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.2.0/24" - availability_zone = "ap-northeast-2b" - map_public_ip_on_launch = true - tags = { - Name = "public_subnet2" - } -} - -# subnet(private) - -resource "aws_subnet" "private1" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.101.0/24" - availability_zone = "ap-northeast-2a" - tags = { - Name = "private_subnet1" - } -} - -resource "aws_subnet" "private2" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.0.102.0/24" - availability_zone = "ap-northeast-2b" - tags = { - Name = "private_subnet2" - } -} - -# Internet Gateway -resource "aws_internet_gateway" "igw" { - vpc_id = aws_vpc.vpc.id - tags = { - Name = "${var.project_name}-igw" - } -} - -# Public Route Table -resource "aws_route_table" "public" { - vpc_id = aws_vpc.vpc.id - route { - cidr_block = "0.0.0.0/0" - gateway_id = aws_internet_gateway.igw.id - } - tags = { - Name = "${var.project_name}-public-route-table" - } -} - -# Associate Public Subnets with Route Table -resource "aws_route_table_association" "public1" { - subnet_id = aws_subnet.public1.id - route_table_id = aws_route_table.public.id -} - -resource "aws_route_table_association" "public2" { - subnet_id = aws_subnet.public2.id - route_table_id = aws_route_table.public.id -} - -# NAT Gateway -resource "aws_eip" "nat" { - domain = "vpc" - tags = { - Name = "${var.project_name}-nat-eip" - } -} - -resource "aws_nat_gateway" "nat" { - allocation_id = aws_eip.nat.id - subnet_id = aws_subnet.public1.id - tags = { - Name = "${var.project_name}-nat-gateway" - } - depends_on = [aws_internet_gateway.igw] -} - -# Private Route Table -resource "aws_route_table" "private" { - vpc_id = aws_vpc.vpc.id - route { - cidr_block = "0.0.0.0/0" - nat_gateway_id = aws_nat_gateway.nat.id - } - tags = { - Name = "${var.project_name}-private-route-table" - } -} - -# Associate Private Subnets with Route Table -resource "aws_route_table_association" "private1" { - subnet_id = aws_subnet.private1.id - route_table_id = aws_route_table.private.id -} - -resource "aws_route_table_association" "private2" { - subnet_id = aws_subnet.private2.id - route_table_id = aws_route_table.private.id -} \ No newline at end of file diff --git a/prod-team-account/security_group.tf b/prod-team-account/security_group.tf deleted file mode 100644 index fc6e37c..0000000 --- a/prod-team-account/security_group.tf +++ /dev/null @@ -1,44 +0,0 @@ -# ALB -resource "aws_security_group" "alb_sg" { - name = "${var.project_name}-alb-sg" - description = "Security group for ALB" - vpc_id = aws_vpc.vpc.id - - ingress { - from_port = 80 - to_port = 80 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - description = "Allow HTTP" - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - -} - - # ECS - resource "aws_security_group" "ecs_sg" { - name = "${var.project_name}-ecs-sg" - description = "Security group for ECS tasks" - vpc_id = aws_vpc.vpc.id - - ingress { - from_port = 80 - to_port = 80 - protocol = "tcp" - security_groups = [aws_security_group.alb_sg.id] - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - -} \ No newline at end of file diff --git a/prod-team-account/variables.tf b/prod-team-account/variables.tf deleted file mode 100644 index 6fb2432..0000000 --- a/prod-team-account/variables.tf +++ /dev/null @@ -1,24 +0,0 @@ -variable "project_name" { - description = "프로젝트를 식별하는 이름" - type = string - default = "cloudfence" -} - -variable "vpc_cidr" { - description = "VPC에 할당할 IP 주소 범위" - type = string - default = "10.0.0.0/16" -} - -variable "custom_ami_id" { - description = "ECS 인스턴스에 사용할 사용자 정의 AMI ID" - type = string - # 중요: 반드시 본인의 리전에 맞는 최신 ECS 최적화 AMI ID로 변경하세요. - default = "ami-0c55b159cbfafe1f0" -} - -variable "instance_type" { - description = "ECS 인스턴스의 타입" - type = string - default = "t3.micro" -} \ No newline at end of file From 7b0bef0ca1f5e631cb9389747594e5b233bdeba8 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Wed, 16 Jul 2025 15:00:04 +0900 Subject: [PATCH 66/66] delete empty file --- .github/workflows/deploy.yml | 0 .github/workflows/terraform-apply.yml | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .github/workflows/deploy.yml delete mode 100644 .github/workflows/terraform-apply.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index e69de29..0000000 diff --git a/.github/workflows/terraform-apply.yml b/.github/workflows/terraform-apply.yml deleted file mode 100644 index e69de29..0000000