AI로 ADFS 운영을 자동화할 수 있을까? — MCP 활용 실험
AI로 ADFS 운영을 자동화할 수 있을까? — MCP 활용 실험
이 글은 ADFS 엔지니어 관점에서 MCP(Model Context Protocol)를 공부하다가 든 의문점을 바탕으로 실험한 내용을 정리한 포스팅임.
실제 운영 환경이 아닌 로컬 테스트 환경 기준이며, 보안 고려사항을 포함함.
MCP가 뭔지 — ADFS 엔지니어 시각으로
MCP를 처음 접했을 때 “또 다른 인증 프로토콜인가?” 싶었음.
ADFS, SAML, OAuth, OIDC만으로도 충분히 복잡한데 하나 더 외워야 하나 싶었음.
결론부터 말하면, MCP는 인증 프로토콜이 아님.
| 구분 | 역할 |
|---|---|
| ADFS | “너 누구야? 맞으면 토큰 줄게” → 인증/인가 처리 |
| MCP | “AI야, 이 도구들 써도 돼” → AI에게 도구/데이터 연결 |
Anthropic이 2024년 11월 발표한 오픈 표준으로, AI 에이전트가 외부 시스템을 표준화된 방식으로 사용할 수 있게 연결해주는 통로(Universal Adapter) 에 가까움.
쉽게 말하면 이러함.
기존: AI ─────── 직접 연결 ──────── 각 시스템 (매번 커스텀 코드)
MCP: AI ── MCP ── 표준 인터페이스 ── 각 시스템 (한 번만 구현)
그래서 ADFS랑 무슨 관계인가
ADFS와 MCP를 억지로 엮을 이유는 없음.
하지만 “AI 에이전트가 ADFS를 관리 도구로 쓴다” 는 관점에서는 이야기가 달라짐.
ADFS 엔지니어라면 이런 작업을 반복함.
- Relying Party Trust 목록 조회 및 설정 확인
- 특정 사용자의 SSO 오류 원인 파악
- 인증서 만료 여부 확인
- 이벤트 로그에서 오류 패턴 찾기
- 토큰 발급 테스트
대부분 PowerShell 명령어 몇 줄이면 되는 작업이지만, 명령어를 외우거나 찾아야 하고, 결과를 해석하는 데 시간이 걸림.
여기에 AI를 붙이면 어떨까? 가 이번 실험의 출발점이었음.
아키텍처 구성
핵심은 MCP Server를 개발 PC에서 돌리고, ADFS 명령은 PowerShell Remoting으로 원격 실행하는 구조임.
ADFS 서버에 Python을 설치할 이유도, 필요도 없음.
[개발 PC] [ADFS 서버]
Claude Desktop Windows Server
│ │
MCP Client PowerShell
│ │
MCP Server (Python/FastMCP) ── WinRM ──▶ Get-AdfsRelyingPartyTrust
Get-AdfsCertificate
Get-AdfsProperties 등
준비 환경
| 항목 | 내용 |
|---|---|
| 개발 PC | Windows 10/11, Python 3.10+, Claude Desktop |
| ADFS 서버 | Windows Server, PowerShell Remoting 활성화 |
| 라이브러리 | fastmcp, pywinrm |
구현: ADFS 운영 자동화 MCP Server
1. 설치
pip install fastmcp pywinrm
2. MCP Server 코드 (adfs_mcp_server.py)
import subprocess
import json
from fastmcp import FastMCP
mcp = FastMCP("ADFS Ops Server")
ADFS_SERVER = "adfs.bwcorp.com" # 실제 ADFS 서버 호스트명으로 변경
ADFS_USER = "BWCORP\\Administrator"
ADFS_PASS = "your_password" # 실제 운영 환경에서는 환경변수 사용 권장
def run_ps_remote(script: str) -> str:
"""PowerShell Remoting으로 ADFS 서버에서 스크립트 실행"""
cmd = [
"powershell", "-Command",
f"""
$pw = ConvertTo-SecureString '{ADFS_PASS}' -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential('{ADFS_USER}', $pw)
Invoke-Command -ComputerName {ADFS_SERVER} -Credential $cred -ScriptBlock {{
{script}
}}
"""
]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
if result.returncode != 0:
return f"오류: {result.stderr}"
return result.stdout.strip()
@mcp.tool()
def get_relying_party_list() -> str:
"""등록된 Relying Party Trust 목록을 조회함."""
script = """
Get-AdfsRelyingPartyTrust |
Select-Object Name, Enabled, Identifier |
ConvertTo-Json
"""
return run_ps_remote(script)
@mcp.tool()
def get_relying_party_detail(name: str) -> str:
"""특정 Relying Party Trust의 상세 정보를 조회함.
Args:
name: Relying Party Trust 이름
"""
script = f"""
Get-AdfsRelyingPartyTrust -Name '{name}' |
Select-Object Name, Enabled, Identifier, ClaimsProviderName,
IssuanceAuthorizationRules, IssuanceTransformRules |
ConvertTo-Json
"""
return run_ps_remote(script)
@mcp.tool()
def check_adfs_certificates() -> str:
"""ADFS 인증서 목록과 만료일을 확인함."""
script = """
Get-AdfsCertificate |
Select-Object CertificateType,
@{N='Subject';E={$_.Certificate.Subject}},
@{N='Thumbprint';E={$_.Certificate.Thumbprint}},
@{N='NotAfter';E={$_.Certificate.NotAfter}},
@{N='DaysRemaining';E={($_.Certificate.NotAfter - (Get-Date)).Days}} |
ConvertTo-Json
"""
return run_ps_remote(script)
@mcp.tool()
def get_recent_adfs_events(count: int = 20) -> str:
"""ADFS 이벤트 로그에서 최근 오류/경고를 조회함.
Args:
count: 조회할 이벤트 수 (기본값 20)
"""
script = f"""
Get-WinEvent -LogName 'AD FS/Admin' -MaxEvents {count} |
Where-Object {{ $_.Level -le 3 }} |
Select-Object TimeCreated, Id, LevelDisplayName, Message |
ConvertTo-Json
"""
return run_ps_remote(script)
@mcp.tool()
def diagnose_user_sso(upn: str) -> str:
"""특정 사용자의 최근 SSO 인증 이벤트를 조회함.
Args:
upn: 사용자 UPN (예: user@bwcorp.com)
"""
script = f"""
Get-WinEvent -LogName 'AD FS/Audit' -MaxEvents 200 -ErrorAction SilentlyContinue |
Where-Object {{ $_.Message -like '*{upn}*' }} |
Select-Object -First 10 TimeCreated, Id, LevelDisplayName, Message |
ConvertTo-Json
"""
return run_ps_remote(script)
@mcp.tool()
def get_adfs_properties() -> str:
"""ADFS 서버의 주요 속성(엔드포인트, 토큰 수명 등)을 조회함."""
script = """
Get-AdfsProperties |
Select-Object HostName, HttpPort, HttpsPort,
TokenLifetime, ProxyTrustTokenLifetime,
WIASupportedUserAgentStrings |
ConvertTo-Json
"""
return run_ps_remote(script)
if __name__ == "__main__":
mcp.run()
3. Claude Desktop 연결 설정
%APPDATA%\Claude\claude_desktop_config.json 파일을 열고 아래 내용을 추가함.
{
"mcpServers": {
"adfs-ops": {
"command": "python",
"args": ["C:/path/to/adfs_mcp_server.py"]
}
}
}
저장 후 Claude Desktop을 재시작하면 연결됨.
4. 실행 결과
Claude Desktop에서 아래처럼 자연어로 질의가 가능함.
"현재 등록된 Relying Party Trust 목록 보여줘"
"ADFS 인증서 중 30일 이내 만료되는 거 있어?"
"user@bwcorp.com 사용자가 오늘 SSO 실패한 이력 있어?"
"최근 ADFS 오류 이벤트 확인해줘"
⚠️ 보안 고려사항 — 가장 중요한 부분
데이터 흐름을 먼저 이해해야 함
[Claude Desktop] ── MCP Tool 결과 ──▶ [Anthropic API 서버] ← 여기가 핵심
↕
ADFS 조회 결과 (RPT 목록, 인증서 정보, 이벤트 로그)
MCP Server ↔ ADFS 서버 구간은 내부망이라 문제없음.
하지만 MCP Tool 실행 결과가 Claude API 요청에 포함되어 Anthropic 서버로 전송됨.
ADFS 구성 정보나 이벤트 로그가 외부 클라우드로 나가는 셈임.
환경별 사용 가이드
| 환경 | 사용 가능 여부 | 이유 |
|---|---|---|
| 로컬 테스트 AD/ADFS | ✅ 가능 | 실 데이터 없음 |
| 개발/스테이징 ADFS | ⚠️ 신중 | 구성 정보 외부 전송 |
| 실 운영 ADFS (인터넷망) | ⚠️ 정책 확인 필요 | 보안 정책에 따라 상이 |
| 폐쇄망 ADFS | ❌ Claude API 불가 | 인터넷 연결 자체가 없음 |
폐쇄망 환경이라면 — Ollama 대안
실제 현장의 ADFS는 대부분 폐쇄망 구성임.
이 경우 Claude API 대신 로컬 LLM(Ollama) 으로 교체하면 외부 통신 없이 동일한 구조를 구현할 수 있음.
[폐쇄망 내부]
Ollama (로컬 LLM)
│
MCP Server (Python) ── WinRM ──▶ ADFS 서버
→ 외부 통신 없음, 모든 데이터 내부에서만 처리
이 구성은 향후 Mac mini M4 + Ollama 환경이 준비되면 별도로 다뤄볼 예정임.
마치며
이번 실험으로 확인한 것은 크게 두 가지임.
첫째, MCP는 ADFS를 대체하거나 경쟁하는 기술이 아님.
ADFS는 여전히 인증/인가를 담당하고, MCP는 AI가 그 ADFS를 도구로 활용하는 통로 역할임.
둘째, 실 운영 투입 전 보안 검토가 필수임.
특히 폐쇄망 환경에서는 Claude API 대신 로컬 LLM으로 대체하는 아키텍처를 고려해야 함.
반복적인 ADFS 운영 작업을 AI로 줄일 수 있다는 가능성은 충분히 확인했음.
다음 포스팅에서는 Ollama를 활용한 폐쇄망 버전을 다뤄볼 예정임.
Comments