Install
$ agentstack add skill-aiappsgbb-awesome-gbb-citadel-hub-deploy ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Citadel Hub Deploy — Layer 1 Governance Hub
> Status: Public Preview wrapper around the > [Azure-Samples / ai-hub-gateway-solution-accelerator] branch > citadel-v1 (MIT). The accelerator is the canonical source; this skill > never forks or vendors its Bicep — it pins to a known-good commit, ships > 3 curated AZD env profiles, and wires the deployment into the > awesome-gbb conventions (tenant isolation, MCAPS pilot tagging, > spoke-aware networking). > > Pinned upstream: see [references/upstream-pin.md](references/upstream-pin.md). > Live-validated: ✅ Resource & shape audit + APIM smoke calls against > a real rg-citadel-hub-01 in Sweden Central (May 2026, see > [references/live-audit-notes.md](references/live-audit-notes.md)).
[Azure-Samples / ai-hub-gateway-solution-accelerator]: https://github.com/Azure-Samples/ai-hub-gateway-solution-accelerator/tree/citadel-v1
1. Why this matters
Most "Foundry pilot" decks stop at "deploy a Foundry account, run an agent". That works for one team and one use case. The moment a second team needs the same model, you hit five hard problems:
- Cost attribution. Whose subscription pays for which call?
- Quota fairness. One team's batch run starves another's chat.
- Policy uniformity. PII redaction, content safety, model
allow-lists — defined once, enforced everywhere.
- Auditability. Who called what model, when, with which prompt?
- Backend abstraction. Switching a model from PTU → PayAsYouGo
shouldn't require every spoke to re-deploy.
The Citadel Governance Hub is the AI Apps GBB reference design that solves all five at the platform level. APIM in front of every model backend, Cosmos for usage telemetry, Logic App for billing aggregation, Event Hub for streaming events, and a per-team Access Contract (APIM Product) that gives each spoke its own subscription key, scope, and policy bundle.
Without this you end up with N spoke projects each negotiating their own Foundry quota and their own PII policy — and your CISO finds out on day 91.
┌──────────────────────────────────────────────────────────┐
│ Layer 1 — Governance Hub (this skill deploys it) │
│ │
│ ┌────────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ APIM v2 │ ←→ │ Cosmos │ ←→ │ Foundry (×N) │ │
│ │ AI Gateway │ │ Usage DB │ │ Multi-region │ │
│ └────────────┘ └──────────┘ └──────────────────┘ │
│ ↑ ↑ │
│ │ │ │
│ ┌────┴───┐ ┌──────────┴────┐ │
│ │ Event │ │ Logic App │ │
│ │ Hub │ │ Usage │ │
│ └────────┘ │ Aggregation │ │
│ └───────────────┘ │
└──────────────────────────────────────────────────────────┘
↑ ↑ ↑
│ per-team access contract │ │
│ (APIM Product + sub key) │ │
┌────────┴──────┐ ┌─────────┴─────┐ ┌──────┴────┐
│ Spoke project │ │ Spoke project │ │ Foundry │
│ team A │ │ team B │ │ Workspace │
│ (use citadel- │ │ (use citadel- │ │ (use │
│ spoke-onbrd) │ │ spoke-onbrd) │ │ spoke…) │
└───────────────┘ └───────────────┘ └───────────┘
2. What citadel-hub-deploy does (and what it doesn't)
Does
- Captures the upstream
azdtemplate at a pinned SHA (see
references/upstream-pin.md); never silently rolls forward.
- Wraps the deployment in a tenant-isolated, assertion-gated workflow
(see azure-tenant-isolation).
- Ships 3 curated AZD environment profiles in
references/profiles/: pilot-quickstart.env— Developer SKU, cheapest demoenterprise-baseline.env— Standard v2, production-grade, BYO Log Analyticsvnet-isolated-spoke-aware.env— BYO VNet + DNS, pre-wired for
foundry-vnet-deploy spokes
- Documents the 8 upstream validation notebooks and the
recommended execution order.
- Documents the post-deploy hand-off to
citadel-spoke-onboarding
(per-team access contracts) and foundry-agt (in-process governance).
Doesn't
- Doesn't fork or vendor the upstream Bicep. The 55 KB
main.bicep - 3-tier module tree lives at the source repo and is fetched fresh by
azd init --template ... --branch citadel-v1 at the pinned SHA.
- Doesn't onboard spokes. That's
citadel-spoke-onboarding— a
single az deployment sub create against bicep/infra/citadel-access-contracts/main.bicep.
- Doesn't add in-process governance. That's
foundry-agt— runs
inside the agent process, before/after every tool call.
- Doesn't manage post-deploy upgrades. The
bicep/infra/apim-gateway-upgrade/ flow (StandardV2 → newer SKUs, policy fragment refresh) is upstream-owned.
- Doesn't onboard LLM backends. That's the
validation/llm-backend-onboarding-runner.ipynb notebook upstream.
| Want to do this | Use this skill instead | |---|---| | Wire your agent project into a deployed hub | citadel-spoke-onboarding | | Add per-tool-call governance inside MAF/Foundry agents | foundry-agt | | Deploy a single-resource Foundry inside a private VNet (no APIM) | foundry-vnet-deploy | | Switch tenants, isolate az/azd config dirs | azure-tenant-isolation | | Apply MCAPS pilot tagging conventions (SecurityControl: Ignore, AZURE_TAGS) | azd-patterns | | Get App Insights traces from the deployed hub into your spoke | foundry-observability |
3. When NOT to deploy a Citadel Hub
The hub is opinionated: APIM Standard v2 + Foundry control plane + Cosmos
- Event Hub + Logic App + Redis + 13 Private DNS Zones + 4 NSGs + private
endpoints. That's ~$800-2,500/month baseline cost in pilot config (see guides/citadel-sizing-guide.md upstream) and 30-45 minutes of APIM provisioning before the first request can flow.
Don't deploy a hub when:
- Single-team pilot, single-use-case PoC. You don't need APIM
arbitration if there's only one consumer. Use microsoft-foundry
- a direct AOAI/AI Services connection.
- Dev-time Foundry exploration. Engineers spinning up sandbox
Foundry workspaces shouldn't pay for a shared APIM. Use foundry-vnet-deploy for private networking instead.
- Budget below $1k/mo. Even the
pilot-quickstartprofile (Developer
SKU APIM, no SLA) lands around $200-400/mo with realistic usage, before Foundry model burn. If you can't justify that for governance, you probably shouldn't be running production agents on any platform.
- Pure offline / batch workloads. No runtime to govern → no gateway
needed. foundry-evals + direct backend calls suffice.
- You're inside a Landing Zone with a pre-existing hub. Reuse it via
citadel-spoke-onboarding. Don't deploy a parallel hub.
4. Stakeholder TL;DR
- Engineer: "It's
azd init --template Azure-Samples/ai-hub-gateway-solution-accelerator -e --branch citadel-v1thenazd up. Profile picks the SKU/network shape. 30-45 min wall clock. Don't forget tenant isolation." - Architect: "Layer 1 of the 4-layer Citadel platform. APIM is the gateway plane; spokes connect via per-team access contracts (Bicep-driven). Pairs with
foundry-agtfor in-process defence in depth. Telemetry sinks: 3 App Insights workspaces + 1 Log Analytics + Cosmosusage-db." - Compliance: "PII redaction (Azure AI Language) + Content Safety + JWT-enforceable RBAC + per-team subscription keys with audit trail in Cosmos + private endpoints on every backend service. Documented in
guides/pii-masking-apim.mdandguides/jwt-client-identity-permissions.mdupstream." - Seller: "One repeatable Bicep deployment that checks the platform-team's first 5 boxes (cost attribution, quota fairness, policy uniformity, audit, backend abstraction) plus the unified-ai-api wildcard route lets you onboard AOAI, Foundry, and Gemini behind one developer-friendly endpoint. Demo runs against the deployed hub via
validation/citadel-universal-llm-api-all-models-tests.ipynb."
5. Quickstart
> TENANT ISOLATION FIRST. Per azure-tenant-isolation, set both > AZURE_CONFIG_DIR and AZD_CONFIG_DIR to per-tenant directories > before any az / azd command. Then run the two-layer assertion > (az account show --query tenantId / name) before azd up. Without > these, you risk deploying a $1k+/mo hub into the wrong subscription.
Path A — Pilot Quickstart (cheapest demo)
Goal: smallest hub that exercises every API surface. Developer SKU APIM, public access, all feature flags on, greenfield VNet + Log Analytics.
# 0. Set the path to your awesome-gbb checkout (or `~/.copilot/skills`
# user-scope mirror) so the .env profiles below resolve.
SKILL_DIR="$HOME/.copilot/skills/citadel-hub-deploy" # or your repo path
# 1. Tenant isolation (per azure-tenant-isolation skill)
export AZURE_CONFIG_DIR="$HOME/.azure-tenants/"
export AZD_CONFIG_DIR="$HOME/.azd-tenants/"
az login --tenant "$TENANT_ID"
azd auth login --tenant-id "$TENANT_ID"
az account set --subscription "$DEFAULT_SUB"
[ "$(az account show --query name -o tsv)" = "$DEFAULT_SUB" ] || exit 1
# 2. Init template at pinned branch
mkdir my-citadel-hub && cd my-citadel-hub
azd init --template Azure-Samples/ai-hub-gateway-solution-accelerator \
-e citadel-pilot-01 \
--branch citadel-v1
# 3. Apply the pilot-quickstart profile (env-var bundle from the skill)
while IFS='=' read -r k v; do
[[ -z "$k" || "$k" == \#* ]] && continue
azd env set "$k" "$v"
done "
# Apply the enterprise-baseline profile
while IFS='=' read -r k v; do
[[ -z "$k" || "$k" == \#* ]] && continue
azd env set "$k" "$v"
done /resourceGroups/rg-dns-prod/providers/Microsoft.Network/privateDnsZones/privatelink.openai.azure.com"
# … repeat EXISTING_DNS_ZONE_* for the other 12 zones — see
# `$SKILL_DIR/references/profiles/vnet-isolated-spoke-aware.env` for the
# full list of EXISTING_DNS_ZONE_* env vars.
# Apply the vnet-isolated-spoke-aware profile
while IFS='=' read -r k v; do
[[ -z "$k" || "$k" == \#* ]] && continue
azd env set "$k" "$v"
done "
$env:AZD_CONFIG_DIR = "$env:USERPROFILE\.azd-tenants\"
az login --tenant $tenantId
azd auth login --tenant-id $tenantId
az account set --subscription $defaultSub
if ((az account show --query name -o tsv) -ne $defaultSub) { exit 1 }
# Init + apply profile
mkdir my-citadel-hub; cd my-citadel-hub
azd init --template Azure-Samples/ai-hub-gateway-solution-accelerator `
-e citadel-pilot-01 --branch citadel-v1
Get-Content "$skillDir\references\profiles\pilot-quickstart.env" |
Where-Object { $_ -and -not $_.StartsWith('#') } |
ForEach-Object { $k,$v = $_.Split('=',2); azd env set $k $v }
azd up
6. Pre-flight checklist
See [references/customer-checklist.md](references/customer-checklist.md) for the full pre-flight (tenant verified, providers registered, quota requested, RBAC, networking decision, DNS ownership). The TL;DR:
- [ ] Tenant + subscription confirmed via two-layer assertion
- [ ] Resource providers registered:
Microsoft.ApiManagement,
Microsoft.CognitiveServices, Microsoft.DocumentDB, Microsoft.EventHub, Microsoft.Insights, Microsoft.Logic
- [ ] Quota: APIM Standard v2 (1+ unit), Foundry GlobalStandard tokens
for each model in your aiFoundryModelsConfig, Cosmos RU/s
- [ ] RBAC: deployer is Owner or has Contributor + **User Access
Administrator** on the target sub (role assignments are part of the deploy)
- [ ] Networking decision made (greenfield vs BYO VNet vs BYO DNS)
- [ ] If BYO Log Analytics: workspace ID + cross-sub RBAC granted
- [ ] If
entraAuth=truelater: app registration ready (use
bicep/infra/entra-id-setup/setup.ps1 upstream — outside v1.0.0 of this skill)
7. Post-deploy verification
The upstream ships 8 validation notebooks under validation/. The recommended baseline (run all 4 on every new deployment):
| # | Notebook | What it validates | ⭐ Baseline? | |---|----------|-------------------|------------| | 1 | llm-backend-onboarding-runner.ipynb | Register AI backends + deploy routing logic into APIM | ⭐ | | 2 | citadel-universal-llm-api-all-models-tests.ipynb | Validate every gateway-configured model through /models | ⭐ | | 3 | citadel-access-contracts-tests.ipynb | Per-team access contracts with KV + Foundry connection | ⭐ | | 4 | citadel-agent-frameworks-tests.ipynb | MAF + Foundry SDK + LangChain consumption | ⭐ | | 5 | citadel-model-aliases-tests.ipynb | resolve-model-alias policy fragment (priority + weighted) | scenario | | 6 | citadel-pii-processing-tests.ipynb | PII anonymize/deanonymize/block | scenario | | 7 | citadel-unified-ai-api-tests.ipynb | Multi-provider routing through unified-ai wildcard API | scenario | | 8 | citadel-jwt-authentication-tests.ipynb | JWT enforcement + RBAC across endpoints | scenario |
Each notebook auto-loads from your azd env via the init_from_azd = True toggle in cell 0:
init_from_azd = True # auto-pulls AZURE_RESOURCE_GROUP, AZURE_LOCATION, …
# from `azd env get-values` of the active env
Manually-set values (anything not equal to the "REPLACE" sentinel) win over azd values. See validation/README.md upstream for the per-notebook azd env-var map.
Quick smoke (no Jupyter)
If you don't have a Python venv handy, this curl (or Invoke-RestMethod) proves the gateway works:
# Get APIM gateway URL
GW=$(az apim show -g -n --query gatewayUrl -o tsv)
# Get the master subscription key (DEMO ONLY — don't use master in prod;
# create a per-team Access Contract via citadel-spoke-onboarding instead)
KEY=$(az rest --method post \
--url "https://management.azure.com/subscriptions/$(az account show --query id -o tsv)/resourceGroups//providers/Microsoft.ApiManagement/service//subscriptions/master/listSecrets?api-version=2022-08-01" \
--query primaryKey -o tsv)
# Discover models
curl -s "$GW/models/models" -H "api-key: $KEY" | jq '.value[].name'
# Send one chat completion (NOTE: api-key header, NOT Ocp-Apim-Subscription-Key)
curl -s -X POST "$GW/openai/deployments/gpt-5.4-mini/chat/completions?api-version=2024-12-01-preview" \
-H "api-key: $KEY" -H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"ping"}],"max_completion_tokens":10}'
Live-tested round-trip latency from this skill's audit run (Sweden Central, gpt-5.4-mini, warm): ~1 sec end-to-end through APIM. Discovery /models call: ~250 ms warm. See references/live-audit-notes.md for the full numbers + gotchas.
8. Hand-off to citadel-spoke-onboarding
After the hub is deployed, every spoke project needs:
- An APIM Product (the access contract, e.g.,
LLM-Healthcare-PatientAssistant-DEV) — created via bicep/infra/citadel-access-contracts/main.bicep.
- An APIM Subscription scoped to that product (auto-created by the
contract Bicep, named -SUB-01).
- Optional Key Vault secrets (endpoint + API key stored in central
KV — recommended for managed-identity-only spokes).
- Optional Foundry connection (the contract Bicep can create an
APIM connection in a target Foundry project, so agents can client.connections.get(...) their gateway).
Naming: {serviceCode}-{businessUnit}-{useCase}-{environment} (e.g., LLM-Healthcare-PatientAssistant-DEV). One product per spoke, one subscription per product, optional KV secret per subscription.
Use the citadel-spoke-onboarding skill for the per-spoke wiring; it documents the contract .bicepparam, the opti
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: aiappsgbb
- Source: aiappsgbb/awesome-gbb
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.