안녕하세요, 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_instance의 instance_type을 t2.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
'IaC' 카테고리의 다른 글
[IaC] Terraform으로 Kubernetes 다루기 - 2/5 (0) | 2025.04.17 |
---|---|
[IaC] Terraform Enterprise AKS에 배포하기 (0) | 2025.02.07 |
[IaC] Terraform Module 작성 및 사용방법 (2) | 2024.12.03 |
[IaC] Terraform으로 Kubernetes 다루기 - 1/5 (0) | 2024.09.19 |
[IaC] Terraform import 개념 및 사용방법 (0) | 2024.06.03 |