5 minute read

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 — ADFS Ops Server 연결됨
현재 등록된 Relying Party Trust 목록 보여줘
JP
C
adfs-ops get_relying_party_list()
ADFS 서버: adfs.bwcorp.com · 조회: 2026-04-07 21:14:03
SharePoint-PortalEnabledurn:sharepoint:bwcorp
Office365-FederationEnabledurn:federation:MicrosoftOnline
HRM-SystemEnabledhttps://hrm.bwcorp.com/saml
Harvey-LegalAIDisabledhttps://harvey.ai/saml/bwcorp
DevOps-PortalEnabledhttps://devops.bwcorp.com
Finance-ERPEnabledurn:erp:finance:bwcorp
총 6개의 Relying Party Trust가 등록되어 있습니다.
Harvey-LegalAI가 비활성화 상태입니다 — 의도된 상태인지 확인이 필요할 수 있어요.
ADFS 인증서 만료일 확인해줘
JP
C
adfs-ops check_adfs_certificates()
인증서 조회 결과
Token-SigningD+187만료: 2026-10-11
Token-DecryptingD+187만료: 2026-10-11
Service-CommunicationsD+23만료: 2026-04-30
Service-Communications 인증서가 23일 후 만료됩니다 (2026-04-30).
Token-Signing / Token-Decrypting은 여유가 있으니 Service-Communications 갱신을 우선 진행하는 걸 권장드려요.
메시지를 입력하세요...

⚠️ 보안 고려사항 — 가장 중요한 부분

데이터 흐름을 먼저 이해해야 함

[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