-
Notifications
You must be signed in to change notification settings - Fork 53
feat(ai): Add SharePoint Embedded agent skills for autonomous setup #216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| --- | ||
| name: sharepoint-embedded | ||
| description: Entry point for all SharePoint Embedded operations - setup, container management, content operations, and billing. Routes to the appropriate sub-skill. Use when working with SPE, SharePoint Embedded containers, container types, file storage containers, or Microsoft Graph storage APIs. | ||
| --- | ||
|
|
||
| # SharePoint Embedded | ||
|
|
||
| AI agent skills for SharePoint Embedded — from initial setup to day-2 operations. | ||
|
|
||
| ## Skills | ||
|
|
||
| | Skill | When to Use | | ||
| |-------|-------------| | ||
| | [full-setup/](full-setup/SKILL.md) | First-time environment setup (Entra app, container type, container) | | ||
| | [container-management/](container-management/SKILL.md) | Day-2 container operations (list, inspect, archive, delete, permissions) | | ||
| | [content-operations/](content-operations/SKILL.md) | File and folder operations inside containers | | ||
| | [billing-setup/](billing-setup/SKILL.md) | Production billing configuration | | ||
|
|
||
| ## Quick Start | ||
|
|
||
| Give an agent this prompt: | ||
|
|
||
| ``` | ||
| Read Skills/full-setup/SKILL.md and run the SPE setup scripts to set up SharePoint Embedded on my tenant. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Casing of |
||
| ``` | ||
|
|
||
| Or run it yourself: | ||
|
|
||
| ```powershell | ||
| cd Skills/full-setup | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Casing of |
||
| .\spe-setup.ps1 | ||
| ``` | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Azure CLI (`az --version`) | ||
| - PowerShell 5.1+ or 7+ | ||
| - Tenant admin access (Global Admin or Application Admin) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By the way this is worded, it seems that an Application Admin is sufficient for this process. Is that true? |
||
|
|
||
| ## Auth Architecture | ||
|
|
||
| Two-moment auth — see [reference/auth.md](reference/auth.md) for details. | ||
|
|
||
| - **Moment 1 (Bootstrap):** `az login` for admin-level Entra app creation | ||
| - **Moment 2 (SPE Token):** Device code flow for delegated SPE scopes | ||
|
|
||
| ## Reference | ||
|
|
||
| - **Auth flow details + fallbacks:** [reference/auth.md](reference/auth.md) | ||
| - **Graph API endpoints + payloads + errors:** [reference/graph-api-reference.md](reference/graph-api-reference.md) | ||
|
|
||
| ## References | ||
|
|
||
| - [SharePoint Embedded Getting Started](https://learn.microsoft.com/en-us/sharepoint/dev/embedded/getting-started/register-api-documentation) | ||
| - [Graph API: Container Types](https://learn.microsoft.com/en-us/graph/api/resources/filestoragecontainertype?view=graph-rest-beta) | ||
| - [Graph API: Containers](https://learn.microsoft.com/en-us/graph/api/resources/filestoragecontainer?view=graph-rest-beta) | ||
| - [Entra App Registration](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app) | ||
| - [Device Code Flow](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-device-code) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| #requires -Version 5.1 | ||
| <# | ||
| .SYNOPSIS | ||
| Step 1: Azure CLI authentication for SharePoint Embedded setup. | ||
| .DESCRIPTION | ||
| Signs into Azure CLI and saves tenant info to .env.spe. | ||
| If already signed in, reuses the existing session. | ||
| #> | ||
|
|
||
| . "$PSScriptRoot\_common.ps1" | ||
|
|
||
| Write-Host "`n=== Step 1: Azure CLI Authentication ===" -ForegroundColor Cyan | ||
|
|
||
| $azAccount = $null | ||
| try { | ||
| $azAccount = az account show --query "{tenantId:tenantId, user:user.name}" -o json 2>$null | ConvertFrom-Json | ||
| } catch {} | ||
|
|
||
| if (-not $azAccount) { | ||
| Write-Host " Signing into Azure CLI..." -ForegroundColor Yellow | ||
| Write-Host " A browser window will open for authentication." -ForegroundColor Gray | ||
| Write-Host " If no browser opens, use: az login --allow-no-subscriptions --use-device-code" -ForegroundColor Gray | ||
| Write-Host "" | ||
| $prevPref = $ErrorActionPreference | ||
| $ErrorActionPreference = "Continue" | ||
| az login --allow-no-subscriptions 2>&1 | Out-Null | ||
| $ErrorActionPreference = $prevPref | ||
| $azAccount = az account show --query "{tenantId:tenantId, user:user.name}" -o json | ConvertFrom-Json | ||
| if (-not $azAccount) { | ||
| throw "Azure CLI login failed. Please run 'az login --allow-no-subscriptions' manually." | ||
| } | ||
| } | ||
|
|
||
| $tenantId = $azAccount.tenantId | ||
|
|
||
| # Save to .env.spe | ||
| $spe = Read-EnvFile | ||
| $spe["TENANT_ID"] = $tenantId | ||
| $spe["USER_UPN"] = $azAccount.user | ||
| Save-EnvFile $spe | ||
|
|
||
| Write-Host "" | ||
| Write-Host "=== RESULT ===" -ForegroundColor Green | ||
| Write-Host "Status: OK" | ||
| Write-Host "User: $($azAccount.user)" | ||
| Write-Host "Tenant: $tenantId" | ||
| Write-Host "Entra Portal: https://entra.microsoft.com/$tenantId/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade" | ||
| Write-Host "=== END ===" -ForegroundColor Green | ||
| Write-Host "[AGENT] STOP. Present the RESULT block above to the user as a markdown table. Do NOT run the next script until the user replies." -ForegroundColor DarkGray | ||
| Write-Host "" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| #requires -Version 5.1 | ||
| <# | ||
| .SYNOPSIS | ||
| Step 2-3: Create Entra app registration and add SPE API permissions. | ||
| .PARAMETER AppDisplayName | ||
| Display name for the app registration. Default: "My SPE App" | ||
| #> | ||
|
|
||
| param( | ||
| [string]$AppDisplayName = "My SPE App" | ||
| ) | ||
|
|
||
| . "$PSScriptRoot\_common.ps1" | ||
|
|
||
| $spe = Assert-EnvKeys @("TENANT_ID") | ||
| $tenantId = $spe["TENANT_ID"] | ||
| $headers = Get-BootstrapHeaders | ||
| $portalBase = "https://portal.azure.com/$tenantId/#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade" | ||
|
|
||
| # ── Step 2: Create or find Entra app ────────────────────────────────────────── | ||
| Write-Host "`n=== Step 2: Entra App Registration ===" -ForegroundColor Cyan | ||
|
|
||
| $app = $null | ||
|
|
||
| # Check for app from previous run | ||
| if ($spe["CLIENT_ID"]) { | ||
| try { | ||
| $existing = Invoke-GraphRequest -Uri "$GraphBase/v1.0/applications?`$filter=appId eq '$($spe["CLIENT_ID"])'" -Headers $headers | ||
| $app = $existing.value | Select-Object -First 1 | ||
| if ($app) { Write-Host " Found app from previous run: $($app.appId)" -ForegroundColor Gray } | ||
| } catch {} | ||
| } | ||
|
|
||
| # Fall back to displayName lookup | ||
| if (-not $app) { | ||
| $existing = Invoke-GraphRequest -Uri "$GraphBase/v1.0/applications?`$filter=displayName eq '$AppDisplayName'" -Headers $headers | ||
| $app = $existing.value | Select-Object -First 1 | ||
| } | ||
|
|
||
| if ($app) { | ||
| Write-Host " App already exists: $($app.appId)" -ForegroundColor Green | ||
| } else { | ||
| Write-Host " Creating public client app '$AppDisplayName'..." -ForegroundColor Gray | ||
| $appBody = @{ | ||
| displayName = $AppDisplayName | ||
| signInAudience = "AzureADMyOrg" | ||
| isFallbackPublicClient = $true | ||
| publicClient = @{ redirectUris = @("http://localhost:3000") } | ||
| } | ConvertTo-Json -Depth 5 | ||
|
|
||
| $app = Invoke-GraphRequest -Uri "$GraphBase/v1.0/applications" -Method POST -Headers $headers -Body $appBody | ||
| Write-Host " App created: $($app.appId)" -ForegroundColor Green | ||
| } | ||
|
|
||
| $appId = $app.appId | ||
| $appObjectId = $app.id | ||
|
|
||
| # Ensure publicClient redirect URI and isFallbackPublicClient are set (fixes existing apps) | ||
| $needsPatch = $false | ||
| $patchBody = @{} | ||
| if (-not $app.isFallbackPublicClient) { | ||
| $patchBody["isFallbackPublicClient"] = $true | ||
| $needsPatch = $true | ||
| } | ||
| $currentRedirects = @() | ||
| if ($app.publicClient -and $app.publicClient.redirectUris) { | ||
| $currentRedirects = $app.publicClient.redirectUris | ||
| } | ||
| if ("http://localhost:3000" -notin $currentRedirects) { | ||
| $patchBody["publicClient"] = @{ redirectUris = @("http://localhost:3000") } | ||
| $needsPatch = $true | ||
| } | ||
| # Remove SPA redirect if present (causes AADSTS9002327 on server-side token exchange) | ||
| if ($app.spa -and $app.spa.redirectUris -and $app.spa.redirectUris.Count -gt 0) { | ||
| $patchBody["spa"] = @{ redirectUris = @() } | ||
| $needsPatch = $true | ||
| } | ||
| if ($needsPatch) { | ||
| $patchJson = $patchBody | ConvertTo-Json -Depth 5 | ||
| Invoke-GraphRequest -Uri "$GraphBase/v1.0/applications/$($app.id)" -Method PATCH -Headers $headers -Body $patchJson | ||
| Write-Host " Updated app: publicClient redirect URI and settings configured" -ForegroundColor Green | ||
| } | ||
|
|
||
| # ── Step 3: Add API permissions ─────────────────────────────────────────────── | ||
| Write-Host "`n=== Step 3: API Permissions ===" -ForegroundColor Cyan | ||
|
|
||
| $permBody = @{ | ||
| requiredResourceAccess = @( | ||
| @{ | ||
| resourceAppId = "00000003-0000-0000-c000-000000000000" | ||
| resourceAccess = @( | ||
| @{ id = $PERMS.FileStorageContainer_Selected_Delegated; type = "Scope" } | ||
| @{ id = $PERMS.FileStorageContainerType_ManageAll_Delegated; type = "Scope" } | ||
| @{ id = $PERMS.FileStorageContainerTypeReg_ManageAll_Delegated; type = "Scope" } | ||
| ) | ||
| } | ||
| ) | ||
| } | ConvertTo-Json -Depth 5 | ||
|
|
||
| Invoke-GraphRequest -Uri "$GraphBase/v1.0/applications/$appObjectId" -Method PATCH -Headers $headers -Body $permBody | ||
| Write-Host " Permissions configured" -ForegroundColor Green | ||
|
|
||
| # Save state | ||
| $spe["CLIENT_ID"] = $appId | ||
| $spe["APP_OBJECT_ID"] = $appObjectId | ||
| $spe["APP_PORTAL"] = "$portalBase/~/Overview/appId/$appId" | ||
| $spe["PERMISSIONS_PORTAL"] = "$portalBase/~/CallAnAPI/appId/$appId" | ||
| Save-EnvFile $spe | ||
|
|
||
| Write-Host "" | ||
| Write-Host "=== RESULT ===" -ForegroundColor Green | ||
| Write-Host "Status: OK" | ||
| Write-Host "App Name: $AppDisplayName" | ||
| Write-Host "Application ID: $appId" | ||
| Write-Host "Object ID: $appObjectId" | ||
| Write-Host "Public Client: Yes (no secret needed)" | ||
| Write-Host "Permissions: FileStorageContainer.Selected, ContainerType.Manage.All, ContainerTypeReg.Manage.All" | ||
| Write-Host "View App: $portalBase/~/Overview/appId/$appId" | ||
| Write-Host "View Permissions: $portalBase/~/CallAnAPI/appId/$appId" | ||
| Write-Host "=== END ===" -ForegroundColor Green | ||
| Write-Host "[AGENT] STOP. Present the RESULT block above to the user as a markdown table. Do NOT run the next script until the user replies." -ForegroundColor DarkGray | ||
| Write-Host "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These files don't exist.