# Claude-MSP-Access - Automated Tenant Onboarding
# Onboards a customer tenant with full Claude + CIPP permissions
# No manual intervention required after initial admin consent
#
# Usage: .\claude-msp-onboard-tenant.ps1 -TenantDomain "sonorangreenllc.com"
#
# Prerequisites: Admin consent URL must be clicked first by customer/sysadmin:
#   https://login.microsoftonline.com/common/adminconsent?client_id=fabb3421-8b34-484b-bc17-e46de9703418&redirect_uri=https://login.microsoftonline.com/common/oauth2/nativeclient
#
# What this script does after consent:
#   1. Finds the Claude-MSP-Access service principal in the customer tenant
#   2. Activates Exchange Administrator directory role (if not active)
#   3. Assigns Exchange Administrator to Claude's SP (via CIPP Graph proxy)
#   4. Verifies all access: Graph, Exchange, Mail, Security, Intune

param(
    [Parameter(Mandatory=$true)]
    [string]$TenantDomain
)

$ErrorActionPreference = "Stop"

# --- Credentials ---
$cippUrl = "https://cippcanvb.azurewebsites.net"
$cippTenantId = "ce61461e-81a0-4c84-bb4a-7b354a9a356d"
$cippClientId = "420cb849-542d-4374-9cb2-3d8ae0e1835b"
$cippSecret = "MOn8Q~otmxJPLvmL~_aCVTV8Va4t4~SrYrukGbJT"

$claudeAppId = "fabb3421-8b34-484b-bc17-e46de9703418"
$claudeSecret = "~QJ8Q~NyQSs4OcGqHZyPrA2CVnq9KBfKiimntbMO"

Write-Output "========================================="
Write-Output " Claude-MSP-Access - Tenant Onboarding"
Write-Output " Tenant: $TenantDomain"
Write-Output " $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Write-Output "========================================="

# --- STEP 1: Get CIPP API token ---
Write-Output "`n[STEP 1] Getting CIPP API token..."
$tokenBody = @{
    client_id     = $cippClientId
    client_secret = $cippSecret
    scope         = "api://$cippClientId/.default"
    grant_type    = "client_credentials"
}
$cippToken = (Invoke-RestMethod -Uri "https://login.microsoftonline.com/$cippTenantId/oauth2/v2.0/token" -Method POST -Body $tokenBody).access_token
$cippHeaders = @{ "Authorization" = "Bearer $cippToken" }
Write-Output "[OK] CIPP token acquired"

# --- STEP 2: Find Claude SP in customer tenant via CIPP ---
Write-Output "`n[STEP 2] Finding Claude-MSP-Access service principal..."
$spFilter = [uri]::EscapeDataString("appId eq '$claudeAppId'")
$spResult = Invoke-RestMethod -Uri "$cippUrl/api/ListGraphRequest?TenantFilter=$TenantDomain&Endpoint=servicePrincipals&`$filter=$spFilter" -Headers $cippHeaders
$sp = $spResult.Results | Select-Object -First 1

if (-not $sp) {
    Write-Output "[ERROR] Claude-MSP-Access SP not found in $TenantDomain"
    Write-Output "[INFO] Has admin consent been completed? Use this URL:"
    Write-Output "  https://login.microsoftonline.com/common/adminconsent?client_id=$claudeAppId&redirect_uri=https://login.microsoftonline.com/common/oauth2/nativeclient"
    exit 1
}
$spId = $sp.id
Write-Output "[OK] Found SP: $($sp.displayName) (ID: $spId)"

# --- STEP 3: Get Exchange Administrator role ID ---
Write-Output "`n[STEP 3] Finding Exchange Administrator role..."
$rolesResult = Invoke-RestMethod -Uri "$cippUrl/api/ListGraphRequest?TenantFilter=$TenantDomain&Endpoint=directoryRoles" -Headers $cippHeaders
$exoRole = $rolesResult.Results | Where-Object { $_.displayName -eq "Exchange Administrator" }

if (-not $exoRole) {
    Write-Output "[INFO] Exchange Admin role not activated, activating from template..."
    # Exchange Administrator role template ID is always 29232cdf-9323-42fd-ade2-1d097af3e4de
    $activateBody = [uri]::EscapeDataString((@{ roleTemplateId = "29232cdf-9323-42fd-ade2-1d097af3e4de" } | ConvertTo-Json -Compress))
    $activateResult = Invoke-RestMethod -Uri "$cippUrl/api/ListGraphRequest?TenantFilter=$TenantDomain&Endpoint=directoryRoles&type=POST&body=$activateBody" -Headers $cippHeaders

    # Re-fetch roles
    $rolesResult = Invoke-RestMethod -Uri "$cippUrl/api/ListGraphRequest?TenantFilter=$TenantDomain&Endpoint=directoryRoles" -Headers $cippHeaders
    $exoRole = $rolesResult.Results | Where-Object { $_.displayName -eq "Exchange Administrator" }
}

if (-not $exoRole) {
    Write-Output "[ERROR] Could not find or activate Exchange Administrator role"
    exit 1
}
$exoRoleId = $exoRole.id
Write-Output "[OK] Exchange Admin role: $exoRoleId"

# --- STEP 4: Assign Exchange Administrator to Claude SP ---
Write-Output "`n[STEP 4] Assigning Exchange Administrator role..."
$assignEndpoint = [uri]::EscapeDataString("directoryRoles/$exoRoleId/members/`$ref")
$assignBody = [uri]::EscapeDataString((@{ "@odata.id" = "https://graph.microsoft.com/v1.0/servicePrincipals/$spId" } | ConvertTo-Json -Compress))
try {
    $assignResult = Invoke-RestMethod -Uri "$cippUrl/api/ListGraphRequest?TenantFilter=$TenantDomain&Endpoint=$assignEndpoint&type=POST&body=$assignBody" -Headers $cippHeaders
    if ($assignResult.Results.CippStatus -eq "Good") {
        Write-Output "[OK] Exchange Administrator assigned to Claude-MSP-Access"
    } else {
        Write-Output "[INFO] Assignment result: $($assignResult.Results | ConvertTo-Json -Compress)"
    }
} catch {
    $errMsg = $_.Exception.Message
    if ($errMsg -match "already exist") {
        Write-Output "[OK] Exchange Administrator already assigned"
    } else {
        Write-Output "[WARNING] Role assignment: $errMsg"
    }
}

# --- STEP 5: Verify Claude API access ---
Write-Output "`n[STEP 5] Verifying Claude-MSP-Access API connectivity..."

# Get tenant ID from CIPP
$selectFields = [uri]::EscapeDataString("id,displayName")
$orgResult = Invoke-RestMethod -Uri "$cippUrl/api/ListGraphRequest?TenantFilter=$TenantDomain&Endpoint=organization&`$select=$selectFields" -Headers $cippHeaders
$customerTenantId = $orgResult.Results[0].id
Write-Output "[INFO] Tenant ID: $customerTenantId"

# Get Claude token for this tenant
$claudeTokenBody = @{
    client_id     = $claudeAppId
    client_secret = $claudeSecret
    scope         = "https://graph.microsoft.com/.default"
    grant_type    = "client_credentials"
}
try {
    $claudeToken = (Invoke-RestMethod -Uri "https://login.microsoftonline.com/$customerTenantId/oauth2/v2.0/token" -Method POST -Body $claudeTokenBody).access_token
    Write-Output "[OK] Claude Graph token acquired"
} catch {
    Write-Output "[ERROR] Could not get Claude token - admin consent may not be complete"
    Write-Output "  $($_.Exception.Message)"
    exit 1
}
$claudeHeaders = @{ "Authorization" = "Bearer $claudeToken"; "Content-Type" = "application/json" }

# Test endpoints
$tests = @(
    @{ Name = "Users";     Uri = "https://graph.microsoft.com/v1.0/users?`$top=1&`$select=displayName" },
    @{ Name = "Security";  Uri = "https://graph.microsoft.com/v1.0/security/alerts?`$top=1" },
    @{ Name = "AuditLogs"; Uri = "https://graph.microsoft.com/v1.0/auditLogs/signIns?`$top=1" },
    @{ Name = "Policies";  Uri = "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" },
    @{ Name = "Devices";   Uri = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?`$top=1" }
)

foreach ($test in $tests) {
    try {
        $r = Invoke-RestMethod -Uri $test.Uri -Headers $claudeHeaders -ErrorAction Stop
        Write-Output "  [OK] $($test.Name)"
    } catch {
        $code = $_.Exception.Response.StatusCode.value__
        Write-Output "  [FAIL] $($test.Name): HTTP $code"
    }
}

# Test Exchange Online REST
Write-Output "`n  Testing Exchange Online REST API..."
try {
    $exoTokenBody = @{
        client_id     = $claudeAppId
        client_secret = $claudeSecret
        scope         = "https://outlook.office365.com/.default"
        grant_type    = "client_credentials"
    }
    $exoToken = (Invoke-RestMethod -Uri "https://login.microsoftonline.com/$customerTenantId/oauth2/v2.0/token" -Method POST -Body $exoTokenBody).access_token
    $exoHeaders = @{ "Authorization" = "Bearer $exoToken"; "Content-Type" = "application/json" }

    $invokeUrl = "https://outlook.office365.com/adminapi/beta/$customerTenantId/InvokeCommand"
    $getMailbox = @{
        CmdletInput = @{
            CmdletName = "Get-Mailbox"
            Parameters = @{ ResultSize = "1" }
        }
    } | ConvertTo-Json -Depth 5

    $r = Invoke-RestMethod -Uri $invokeUrl -Headers $exoHeaders -Method POST -Body $getMailbox -ErrorAction Stop
    Write-Output "  [OK] Exchange Online (Get-Mailbox)"
} catch {
    Write-Output "  [FAIL] Exchange Online: $($_.Exception.Message)"
}

# --- DONE ---
Write-Output "`n========================================="
Write-Output " ONBOARDING COMPLETE: $TenantDomain"
Write-Output " $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Write-Output "========================================="
Write-Output ""
Write-Output "Claude-MSP-Access is fully operational for this tenant."
Write-Output "Capabilities: User mgmt, mail access, security alerts,"
Write-Output "audit logs, conditional access, Intune, Exchange admin,"
Write-Output "litigation hold, and all CIPP SAM operations."
