본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 21. 07:05

Zero-Trust RAG: Azure Terraform의 Shared Private Link 교착 상태 해결하기

요약

Azure 환경에서 AI Search와 OpenAI를 연결할 때 발생하는 Shared Private Link의 'Pending' 상태 문제를 Terraform AzAPI를 통해 자동 승인하는 방법을 다룹니다. 또한 보안 강화를 위해 API 키 대신 시스템 할당 관리 ID(System Managed Identity)를 사용하는 Identity Chaining 기법과 Private DNS 설정의 중요성을 설명합니다.

핵심 포인트

  • Azure AI Search가 OpenAI를 호출할 때 발생하는 Shared Private Link의 승인 대기(Pending) 문제를 Terraform AzAPI로 자동화하여 해결할 수 있습니다.
  • Terraform destroy 시 발생할 수 있는 인덱싱 오류를 방지하기 위해 try() 래퍼를 사용하여 리소스 삭제 안정성을 확보해야 합니다.
  • 보안 및 컴플라이언스 준수를 위해 정적 API 키 대신 System Managed Identity를 활용한 Identity Chaining을 권장합니다.
  • Private Link 환경에서 403 오류를 방지하려면 Private DNS Zone이 VNet에 올바르게 연결되어 있어야 합니다.

Terraform 파이프라인이 초록색(성공)으로 뜹니다. 배포도 오류 없이 완료되었습니다. 커피 한 잔을 마십니다. 10분 후, 새로운 Enterprise RAG 애플리케이션을 테스트합니다. 403 Forbidden 오류가 발생합니다. Azure Portal을 열고 OpenAI Networking 탭을 확인하니, AI Search로부터 생성된 Shared Private Link가 Pending(대기 중) 상태로 놓여 있습니다. Terraform에게 승인하라고 명령한 적이 없습니다. 승인이 필요하다는 말조차 들은 적이 없습니다. 이것이 바로 Azure AI 인프라의 CI/CD 킬러입니다.

왜 이런 일이 발생하는가
AI Search는 데이터를 벡터화(vectorize)하기 위해 OpenAI를 호출해야 합니다. azurerm 프로바이더(provider)는 이 Shared Private Link를 성공적으로 요청할 수 있지만

status == "Pending" ][ 0 ], "" ) body = jsonencode ({ properties = { privateLinkServiceConnectionState = { status = "Approved" description = "Approved via Terraform AzAPI Pipeline" } } }) } try() 래퍼(wrapper)는 선택 사항이 아닙니다. terraform destroy 실행 시, 이 리소스가 평가되기 전에 Shared Private Link가 먼저 삭제됩니다. try()가 없다면, 빈 배열에 대한 [0] 인덱싱으로 인해 destroy 실행이 중단(crash)되고 Azure에 고아 리소스(orphaned resources)가 남게 됩니다. terraform apply 이후, 링크는 30초 이내에 Pending에서 Approved 상태로 전환됩니다. Portal 접속은 필요하지 않습니다.

ID 체이닝 (Identity Chaining) — API 키 제거하기
링크를 자동 승인하면 AI Search가 OpenAI에 접근할 수 있습니다. 하지만 정적 API 키는 컴플라이언스 감사(compliance audit)를 통과하지 못할 것입니다. 키는 유출될 수 있고, Git에 커밋될 수도 있습니다. 로컬 인증(local auth)을 비활성화하고 대신 시스템 할당 관리 ID (System Managed Identity)를 사용하세요:

resource "azurerm_search_service" "search" {
name = var.search_service_name
sku = "standard"
public_network_access_enabled = false
local_authentication_enabled = false
identity {
type = "SystemAssigned"
}
}

resource "azurerm_role_assignment" "search_to_openai" {
scope = azurerm_cognitive_account.openai.id
role_definition_name = "Cognitive Services OpenAI User"
principal_id = azurerm_search_service.search.identity[0].principal_id
}

자격 증명 관리(credential management)가 필요 없습니다. AI Search 인스턴스가 삭제되면, 해당 ID와 권한도 자동으로 파괴됩니다.

Private DNS를 잊지 마세요
만약 your-instance.openai.azure.com이 여전히 퍼블릭 IP로 해석(resolve)된다면, Azure Firewall이 트래픽을 차단하여 불투명한 403 에러를 받게 됩니다. 두 서비스 모두 DNS 존(DNS zones)이 VNet에 연결되어 있어야 합니다:

resource "azurerm_private_dns_zone" "openai_dns" {
name = "privatelink.openai.azure.com"
resource_group_name = var.resource_group_name
}

resource "azurerm_private_dns_zone_virtual_network_link" "openai_vnet_link" {
name = "link-openai-vnet"
resource_group_name = var.resource_group_name
private_dns_zone_name = azurerm_private_dns_zone.openai_dns.name
virtual_network_id = azurerm_virtual_network.vnet.

id registration_enabled = false } registration_enabled = false — 항상 그렇습니다. 자동 등록 (Automatic registration)은 중앙 집중식 허브 앤 스포크 (Hub & Spoke) DNS 관리와 충돌합니다. 무료 베이스라인 (AzAPI 자동 승인 + 기본 네트워킹)은 GitHub에 있습니다. 전체 VNet 인젝션 (VNet injection), 프라이빗 DNS (Private DNS) 자동화, 그리고 RBAC ID 체이닝 (RBAC Identity Chaining)이 포함된 전체 기사는 제 블로그에 있습니다. 👉 woitzik.dev에서 전체 기사 보기 👉 무료 GitHub 리포지토리

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0