IaC

[PaC] HCP Terraform Sentinel을 활용한 배포 정책 적용

cloudraw 2025. 5. 12. 16:57

안녕하세요, Cloudraw입니다!

 

 

오늘은 대표적인 IaC도구인 Terraform의 SaaS 버전인 HCP Terraform에서 정책을 설정하고 적용하는 방법에 대해 다뤄보고자 합니다.

 

HCP Terraform에서의 정책은 Sentinel이라 불리며 PaC(Policy as Code)라는 개념으로 정책을 코드로 작성하고 관리할 수 있습니다.

 

HCP Terraform을 활용한다면 클라우드 인프라 관리시 IaC와 PaC를 모두 만족할 수 있습니다!

 

이번 글의 순서는 다음과 같습니다.

 

1. HCP Terraform 연동

2. 정책(Policy) 등록

3. 정책 적용

4. 정책 적용 Test

 

 

사전 요구사항

 - HCP Terraform 계정(월 500리소스 무료) -> 가입하기

 - CSP 자격증명 발급

1. HCP Terraform 연동

- HCP Terraform 연동을 위해서는 terraform login 명령을 통해 Token을 등록해 주어야 합니다.

- 해당 명령어를 실행하면 웹 브라우저에서 HCP Terraform이 실행되며 로그인 후 Token 발행 페이지로 이동합니다.

terraform login

 

-Token을 발행 후 터미널로 돌아와 입력하면 로그인이 완료됩니다.

 

-해당 토큰은 ~/.terraform.d/credentials.tfrc.json에 저장됩니다.

 

- 다음, terraform 블록에 cloud 블록을 추가해 줍니다.

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.70.0"
    }
  }

  #HCP Terraform 연동을 위한 코드
  cloud {
    organization = "cloudraw" #(required)연동할 organization 이름, HCP Terraform에서 생성해야함.

    workspaces {
      name = "test" #(required)organization내 workspace 이름(tfstate 관리 단위)
      project = "cloudstudio" #(optional) workspace가 속할 project이름, default=Default Project
    }
  }
  
}

 

- 블록 추가 후 terraform init을 실행하면 연동이 완료됩니다.

terraform init

 

- 연동 이후 실행하는 plan, apply, destroy 명령은 HCP Terraform에서 GUI로도 확인할 수 있습니다.

 

2. 정책(Policy) 등록

- 정책은 workspaces > Integrations > Policies 탭에서 생성할 수 있습니다. 

- 해당 탭에서 Create a new policy 버튼을 클릭하여 새로운 정책을 생성합니다.

 

- Sentinel에서는 3가지(Advisory, Soft mandatory, Hard mandatory)레벨의 경고를 제공하고 있습니다.

- 정책 코드 샘플은 https://github.com/hashicorp/terraform-guides/tree/master/governance 에서 제공하고 있습니다.

- 이번 글에서는 ec2인스턴스 타입을 제한하는 정책을 설정해 보겠습니다. (파일 위치: governance/second-generation/aws/restrict-ec2-instance-type.sentinel)

- 아래 Policy code(Sentinel) 부분에 위 코드를 복사하여 붙여 넣습니다.

# This policy uses the Sentinel tfplan import to require that
# all EC2 instances have instance types from an allowed list

##### Imports #####

import "tfplan"
import "strings"

##### Functions #####

# Find all resources of a specific type from all modules using the tfplan import
find_resources_from_plan = func(type) {

  resources = {}

  # Iterate over all modules in the tfplan import
  for tfplan.module_paths as path {
    # Iterate over the named resources of desired type in the module
    for tfplan.module(path).resources[type] else {} as name, instances {
      # Iterate over resource instances
      for instances as index, r {

        # Get the address of the instance
        if length(path) == 0 {
          # root module
          address = type + "." + name + "[" + string(index) + "]"
        } else {
          # non-root module
          address = "module." + strings.join(path, ".module.") + "." +
                    type + "." + name + "[" + string(index) + "]"
        }

        # Add the instance to resources map, setting the key to the address
        resources[address] = r
      }
    }
  }

  return resources
}

# Validate that all instances of a specified resource type being modified have
# a specified top-level attribute in a given list
validate_attribute_in_list = func(type, attribute, allowed_values) {

  validated = true

  # Get all resource instances of the specified type
  resource_instances = find_resources_from_plan(type)

  # Loop through the resource instances
  for resource_instances as address, r {

    # Skip resource instances that are being destroyed
    # to avoid unnecessary policy violations.
    # Used to be: if length(r.diff) == 0
    if r.destroy and not r.requires_new {
      print("Skipping resource", address, "that is being destroyed.")
      continue
    }

    # Determine if the attribute is computed
    if r.diff[attribute].computed else false is true {
      print("Resource", address, "has attribute", attribute,
            "that is computed.")
      # If you want computed values to cause the policy to fail,
      # uncomment the next line.
      # validated = false
    } else {
      # Validate that each instance has allowed value
      if r.applied[attribute] else "" not in allowed_values {
        print("Resource", address, "has attribute", attribute, "with value",
              r.applied[attribute] else "",
              "that is not in the allowed list:", allowed_values)
        validated = false
      }
    }

  }
  return validated
}

##### Lists #####

# Allowed EC2 Instance Types
# We don't include t2.nano or t2.micro to illustrate overriding failed policy
allowed_types = [
  "t2.small",
  "t2.medium",
  "t2.large",
]

##### Rules #####

# Call the validation function
instances_validated = validate_attribute_in_list("aws_instance",
                      "instance_type", allowed_types)

# Main rule
main = rule {
  instances_validated
}

 

- 해당 코드에서는 96라인의 validate_attribute_in_list 함수를 통해 terraform 코드 내 존재하는 aws_instance 리소스의 instance_type을 확인하며,

 

- 87라인의 allowed_types의 3가지(t2.small, t2.medium, t2.large)의 스펙에 대해서만 허용하고 있습니다.

 

- 정책 작성 이후 Create policy 버튼을 클릭하여 생성합니다.

3. 정책 적용

- 정책 생성 후 workspaces > Integrations > Policy sets 탭으로 이동합니다. 

- 해당 탭에서 Connect a new policy set 버튼을 클릭하여 정책을 연결합니다.

 

- 정책 연결은 3가지 소스를 제공하며 Individually managed policies를 선택합니다.

 

- 정책은 Scope설정을 통해 전역(Globally) 또는 Workspace 단위로 적용할 수 있습니다.

 

 

- 이번 글에서는 앞서 코드로 연동한 cloudstudio(Project)의 test(Workspace)에 적용하겠습니다.

 

- 설정이 완료되면 다음을 눌러 정책을 선택합니다.

 

- 생성한 정책을 선택 후 Connect policy set 버튼을 클릭하여 정책을 적용합니다.

 

4. 정책 적용 Test

- 앞서 생성한 정책이 제대로 동작하는지 terraform명령을 통해 Test해 보겠습니다.

- terraform 명령 실행을 위해 Workspace에 미리 발급받은 CSP의 자격증명을 등록합니다. 자격증명을 Variables에 등록함으로써 코드상에서의 노출을 방지할 수 있습니다. 키 등록시 Sensitive를 체크하여 유출을 방지하세요!

 

- 첫번째는 aws_instanceinstance_typet2.micro(허용되지 않은 설정)로 설정하여 terraform plan 명령을 실행해 보겠습니다.

 

- 정책 설정 이전에는 없던 Task가 추가되었으며 정책에 위배되어 plan 동작 이후 Task가 Failed 상태가 되었습니다.

- 해당 Task는 연동한 HCP Terraform의 Workspace의 Runs 탭에서도 확인이 가능합니다.

 

- 다음은 aws_instance instance_type t2.small(허용된 설정)로 설정하여 terraform plan 명령을 실행해 보겠습니다.

 

- 정책을 만족하였기 때문에 plan 동작 이후 Task가 Passed 상태가 되었습니다.

 

 지금까지 HCP Terraform에서 Sentinel을 활용한 PaC와 정책 적용에 대해 알아보았습니다.

Sentinel은 HCP Terraform과 Terraform Enterprise에서 제공되며 위 기능을 활용하여 더욱 안전한 클라우드 환경을 구성하고 활용할 수 있습니다.

 

감사합니다.


 

    

 

Cloudraw는 쉽게 클라우드 인프라를 그리고 사용할 수 있는 서비스를 제공하기 위해 노력하고 있습니다.

 

클라우드가 있는 곳 어디든 Cloudraw가 함께합니다.

 

📨 help@cloudraw.kr