본문 바로가기
IaC

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

by cloudraw 2025. 5. 12.

안녕하세요, 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