Terraform 으로 AWS 인프라를 관리하고 있으며 EKS API Endpoint 접근 설정을 Private로 변경해야 했습니다. Terraform에서 K8S 관련 리소스에 접근할때 EKS API Endpoint를 사용하므로 Terraform Backend 변경이 필요했습니다.

EKS API EndpointPrivate으로 변경하게되면 EKS와 같은 VPC 또는 이와 연결된 VPC에서만 접근 가능합니다. 변경전에는 Terraform Cloud를 사용하고 있었으며 EKS와 연결된 VPC에 존재하는 Bastion에 Terraform CLI를 설치하하였습니다. Terraform BackendS3DynamoDB 조합으로 구성하였습니다. S3tfstate 파일을 저장하고 DynamoDB에서 State Lock 을 실행하는 구조입니다.

운영환경과 개발환경을 AWS 계정을 분리하여 사용중입니다. Terraform Workspaceprodtest로 구분하여 사용하고 있습니다. Public 환경에서 Terraform Cloud를 사용할때는 AWS IAM 설정만으로 편하게 접근 할 수 있었습니다.

그러나 EKS 설정과 State 저장소 S3의 접근을 Private으로 변경해야 하는 상황입니다.

Backend에서 Workspace를 관리하는 구조이지만 Backend를 공통으로 사용할수 없었기에 prod 환경의 Terraform Backend, test 환경의 Terraform Backend 를 다르게 설정하기로 하였습니다.

S3, DynamoDB 추가

Terraform 공식문서를 참고하여 S3Dynamo를 추가합니다. S3Public 접근을 제한하였으며 Terraform만 접근 하도록 정책을 추가하였습니다.

resource "aws_s3_bucket" "tfstate" {
    bucket = "your-terraform-tfstate"
    acl    = "private"

    versioning {
        enabled = true
    }
}

resource "aws_s3_bucket_ownership_controls" "tfstate" {
    bucket = aws_s3_bucket.tfstate.id

    rule {
        object_ownership = "BucketOwnerPreferred"
    }
}

resource "aws_s3_bucket_public_access_block" "tfstate" {
    bucket = aws_s3_bucket.tfstate.id

    block_public_acls       = true
    block_public_policy     = true
    ignore_public_acls      = true
    restrict_public_buckets = true
}

resource "aws_s3_bucket_policy" "tfstate" {
    bucket = aws_s3_bucket.tfstate.id
    policy = jsonencode({
        Version = "2012-10-17",
        Statement = [
        {
            Effect   = "Allow"
            Action   = "s3:ListBucket"
            Resource = "${aws_s3_bucket.tfstate.arn}",
            Principal = {
            AWS = "arn:aws:iam::468446841302:user/terraform"
            }
        },
        {
            Effect   = "Allow",
            Action   = ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
            Resource = "${aws_s3_bucket.tfstate.arn}/test",
            Principal = {
            AWS = "arn:aws:iam::468446841302:user/terraform"
            }
        },
        ]
    })
}

공식문서를 참고하여 state lock에 사용할 DynamoDB도 추가합니다.

resource "aws_dynamodb_table" "terraform_state_lock" {
  name         = "terraform-state-lock"
  hash_key     = "LockID"
  billing_mode = "PAY_PER_REQUEST"

  attribute {
    name = "LockID"
    type = "S"
  }
}

Terraform Backend 설정

Terraform Backend에 사용할 S3DynamoDB 정보를 입력합니다

terraform {
    required_version = ">= 0.13"

    backend "s3" {
        key                  = "terraform.tfstate"
        region               = "ap-northeast-1"
        encrypt              = true
    }
...

Terraform Backend 수정

State 저장소인 Backend가 변경되었으며 기존 State를 새로운 저장소로 복사하도록 초기화 명령어에 옵션을 추가하여 실행합니다. WorkspaceBackend를 구분하기 위해 init 명령어에 -backend-config를 추가하여 정보를 주입받도록 하였습니다.

terraform init -migrate-state  \
    -backend-config="bucket=your-terraform-tfstate-test" \
    -backend-config="dynamodb_table=terraform-state-lock" \
    -backend-config="workspace_key_prefix=your-prefix"

Bastion 정책 추가

Bastion에서 Terraform CLI 사용시 Terraform state lock이 실행되는데 이때 DynamoDB에 접근할 수 있도록 정책을 추가합니다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:DeleteItem",
                "dynamodb:GetItem",
                "dynamodb:PutItem"
            ],
            "Resource": [
                "arn:aws:dynamodb:ap-northeast-1:468446841302:table/terraform-state-lock"
            ]
        }
    ]
}

정책을 추가하고 Bastion이 사용중인 role에 정책을 연결합니다.

Direnv 사용하여 환경변수 설정

Terraform 작업 디렉토리에서만 환경변수가 활성화 되도록 direnv를 사용합니다. 공식문서를 참고하여 설치합니다.

curl -sfL https://direnv.net/install.sh | bash
chmod +x direnv

.bashrceval "$(direnv hook bash)" 을 추가합니다.

작업 디렉토리에 .envrc 파일을 생성합니다. TF_VAR_를 사용하면 Terraform Variable에서 사용할 수 있습니다. AWS 사용자 토큰 정보도 설정하여 인프라 접근이 가능하게 합니다.

export TF_VAR_grafana_google_oauth_client_id='257581726932-.....com'
export TF_VAR_grafana_google_oauth_client_secret='9Y83bQ...AA'
export TF_VAR_sendgrid_api_key='QXiXbA6JQaqh8iEh1...AAA'
export TF_VAR_cloudamqp_apikey='e61d888a...AAAA'
export TF_VAR_env='test'
export AWS_SECRET_ACCESS_KEY='AWS_SECRET_ACCESS_KEY'
export AWS_ACCESS_KEY_ID='AWS_ACCESS_KEY_ID'
export AWS_DEFAULT_REGION='ap-northeast-1'

.envrc 작성 후 direnv allow를 입력하여 디렉토리에서 환경변수를 감지할 수 있도록 설정합니다.