# Foundry Caphost Lifecycle

> >

- **Type:** Skill
- **Install:** `agentstack add skill-aiappsgbb-awesome-gbb-foundry-caphost-lifecycle`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [aiappsgbb](https://agentstack.voostack.com/s/aiappsgbb)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [aiappsgbb](https://github.com/aiappsgbb)
- **Source:** https://github.com/aiappsgbb/awesome-gbb/tree/main/skills/foundry-caphost-lifecycle

## Install

```sh
agentstack add skill-aiappsgbb-awesome-gbb-foundry-caphost-lifecycle
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Foundry Capability Host Lifecycle — Day-2 Operations

## 1. Goal

This skill teaches **Day-2 operations** for Microsoft Foundry capability hosts: how to
**inspect**, **idempotently re-create**, **delete**, **soft-delete the parent account**,
**purge**, and **redeploy** without tripping the `Subnet already in use` failure that
blocks every team that has ever torn down a VNet-injected Foundry account.

This is **explicitly not greenfield create** — for first-time BYO-VNet deploys see
[`foundry-vnet-deploy`](../foundry-vnet-deploy/SKILL.md), and for the azd template
flow see [`foundry-hosted-agents`](../foundry-hosted-agents/SKILL.md) or
[`threadlight-deploy`](https://github.com/aiappsgbb/threadlight-skills/blob/main/skills/threadlight-deploy/SKILL.md).
This skill is the field-experience overlay on top of the MS Learn capability-hosts
page ([learn.microsoft.com/azure/foundry/agents/concepts/capability-hosts](https://learn.microsoft.com/azure/foundry/agents/concepts/capability-hosts))
that ships with the operational knowledge MS Learn does not document.

## 2. When to use

Trigger this skill whenever any of these are true:

- You teardown'd a Foundry VNet-injected account and now `az deployment group create`
  fails with `Subnet already in use` (or `SubnetIsFull` referencing a
  `serviceAssociationLink`) when you try to redeploy into the **same** agent subnet.
- A caphost create is stuck in `Creating` for more than 10 minutes (the upstream
  operation has a documented ~5-15 min p99; longer means hung — go to § 9).
- You need to **change the connections** referenced by a project capability host
  (e.g., swap a Cosmos connection). MS Learn is explicit: updates are not supported;
  delete + recreate is the only path.
- You need to fully reclaim the soft-deleted account name within the 48h window
  (so a new account can be created with the **same** custom domain).
- You want a clean teardown that releases the `serviceAssociationLink` on the agent
  subnet AND removes the hidden ACA Managed Environment that the capability host
  provisioned under the covers.
- You have a soft-deleted account that needs to be **recovered** (not purged)
  before the 48h window expires.

If none of those describe your situation and you're doing first-time create, stop
and use [`foundry-vnet-deploy`](../foundry-vnet-deploy/SKILL.md) instead.

## 3. What MS Learn covers vs what this skill adds

MS Learn ([capability-hosts](https://learn.microsoft.com/azure/foundry/agents/concepts/capability-hosts))
is the authoritative source for the REST API surface and create-time semantics.
Use it for the contract. This skill exists because MS Learn does **not** cover the
operational reality of Day-2.

| Concern | MS Learn | This skill |
|---|---|---|
| Account + project caphost REST shape (PUT/GET) | ✅ | references it |
| Idempotency rules (200/400/409 matrix) | ✅ | retry pattern (§ 6) |
| `One caphost per scope` constraint | ✅ | redeploy implications (§ 9) |
| `Updates not supported — delete and recreate` | ✅ | safe-replace flow (§ 7) |
| Account caphost prerequisite for project caphost | ✅ | inspect order (§ 5) |
| DELETE caphost REST endpoint | ✅ | what it does/doesn't free (§ 7) |
| **Deleting caphost releases the SAL on the agent subnet** | ❌ | **FALSE** — only purging the parent account does (§ 8, § 9) |
| **`az cognitiveservices account purge` semantics** | ❌ | full sequence (§ 8) |
| **48h soft-delete window + recovery** | ❌ | when to recover vs purge (§ 10) |
| **Hidden Managed ACA Environment created by caphost** | ❌ | only released on account purge (§ 8) |
| **Redeploy-after-teardown failure mode** | ❌ | the `Subnet already in use` guard (§ 9) |
| Concurrent-op 409 retry (`currently in non creating`) | ✅ pseudocode | runnable retry loop (§ 6) |

> **The single most important field-verified rule:** **deleting a capability host
> does NOT release the `serviceAssociationLink` on the agent subnet.** Only purging
> the parent `Microsoft.CognitiveServices/accounts` resource releases it. If you
> need to redeploy into the same subnet, you MUST purge — soft-delete alone is not
> enough. See § 8 and § 9 for the exact sequence and the symptom-to-fix mapping.

## 4. Constraints recap (from MS Learn)

Read these once. They drive every Day-2 decision below.

| Constraint | Rule | Source |
|---|---|---|
| **One caphost per scope** | Each account, each project: only one active capability host. Second host with different name → 409 Conflict. | [MS Learn § Constraints](https://learn.microsoft.com/azure/foundry/agents/concepts/capability-hosts) |
| **No updates** | There is no PATCH support. Configuration changes require DELETE + recreate. | [MS Learn § Constraints](https://learn.microsoft.com/azure/foundry/agents/concepts/capability-hosts) |
| **Account caphost prerequisite** | You cannot create a project capability host unless an account-level one already exists. | [MS Learn § Constraints](https://learn.microsoft.com/azure/foundry/agents/concepts/capability-hosts) |
| **Idempotency: same-name + same-config** | Returns 200 OK with the existing resource. Safe to retry. | [MS Learn § Idempotent behavior](https://learn.microsoft.com/azure/foundry/agents/concepts/capability-hosts) |
| **Idempotency: same-name + different config** | Returns 400 Bad Request. No silent in-place modification. | [MS Learn § Idempotent behavior](https://learn.microsoft.com/azure/foundry/agents/concepts/capability-hosts) |
| **Idempotency: different name at occupied scope** | Returns 409 Conflict (one-per-scope). | [MS Learn § Idempotent behavior](https://learn.microsoft.com/azure/foundry/agents/concepts/capability-hosts) |
| **Concurrent operation in flight** | Returns 409 `currently in non creating, retry after its complete`. Retry with backoff. | [MS Learn § Concurrent operations](https://learn.microsoft.com/azure/foundry/agents/concepts/capability-hosts) |
| **Permissions to create / delete caphost** | `Contributor` on the Foundry account. | [MS Learn § Prerequisites](https://learn.microsoft.com/azure/foundry/agents/concepts/capability-hosts) |
| **Permissions to wire BYO connections** | `User Access Administrator` or `Owner` (for assigning RBAC on the BYO resources). | [MS Learn § Prerequisites](https://learn.microsoft.com/azure/foundry/agents/concepts/capability-hosts) |
| **API version** | `2025-06-01` is the current canonical version this skill targets. | [MS Learn REST examples](https://learn.microsoft.com/azure/foundry/agents/concepts/capability-hosts) |
| **Project caphost connection refs are by name, not resource ID** | `threadStorageConnections` / `vectorStoreConnections` / `storageConnections` / `aiServicesConnections` are arrays of **connection names** that already exist on the project. | [MS Learn § Required properties](https://learn.microsoft.com/azure/foundry/agents/concepts/capability-hosts) |

## 5. Inspect: query caphost state before any change

> **Always inspect before mutating.** The one-per-scope constraint and the
> no-update rule mean an idempotent PUT only behaves "idempotently" if you know
> what's already there. Read first, decide second.

### 5.1 List account-level capability hosts

```http
GET https://management.azure.com/subscriptions/{subId}/resourceGroups/{rg}/providers/Microsoft.CognitiveServices/accounts/{accountName}/capabilityHosts?api-version=2025-06-01
```

Equivalent `az` (works for any user with `Cognitive Services Contributor`):

```bash
az rest --method get \
  --url "https://management.azure.com/subscriptions/${SUB}/resourceGroups/${RG}/providers/Microsoft.CognitiveServices/accounts/${ACCT}/capabilityHosts?api-version=2025-06-01" \
  --query "value[].{name:name, state:properties.provisioningState, kind:properties.capabilityHostKind}"
```

Expected shapes:

- `value: []` — no account caphost exists. You cannot create a project caphost
  until one does (§ 4 prerequisite). Go to § 6 to create one.
- `value: [{name: "default", state: "Succeeded", kind: "Agents"}]` — healthy.
  Safe to operate on the project caphost.
- `value: [{state: "Creating"}]` — operation in flight. Do NOT issue another PUT;
  poll the operation result (§ 5.3) until terminal.
- `value: [{state: "Failed"}]` — broken. DELETE it (§ 7), then recreate.

### 5.2 List project-level capability hosts

```bash
az rest --method get \
  --url "https://management.azure.com/subscriptions/${SUB}/resourceGroups/${RG}/providers/Microsoft.CognitiveServices/accounts/${ACCT}/projects/${PROJ}/capabilityHosts?api-version=2025-06-01" \
  --query "value[].{name:name, state:properties.provisioningState, thread:properties.threadStorageConnections, vector:properties.vectorStoreConnections, storage:properties.storageConnections, ai:properties.aiServicesConnections}"
```

### 5.3 Poll an in-flight operation

Capability-host PUT/DELETE return an `Azure-AsyncOperation` header with a URL of
this shape:

```
https://management.azure.com/subscriptions/{subId}/providers/Microsoft.CognitiveServices/locations/{location}/operationResults/{operationId}?api-version=2025-06-01
```

Poll it (5-second interval, 15-minute budget — covers documented p99) until
`properties.status` is `Succeeded` or `Failed`. **Do not issue a parallel PUT or
DELETE on the same scope while an operation is `Running`** — you'll get the
`currently in non creating, retry after its complete` 409 (§ 6).

## 6. Create: idempotent PUT pattern with retry

### 6.1 Account capability host

```http
PUT https://management.azure.com/subscriptions/{subId}/resourceGroups/{rg}/providers/Microsoft.CognitiveServices/accounts/{accountName}/capabilityHosts/{name}?api-version=2025-06-01

{
  "properties": {
    "capabilityHostKind": "Agents"
  }
}
```

(For VNet-injected accounts, the `customerSubnet` is set at deploy time by
[`foundry-vnet-deploy`](../foundry-vnet-deploy/SKILL.md). This skill operates on
the caphost after that subnet binding already exists; it does not re-bind the
subnet.)

### 6.2 Project capability host

```http
PUT https://management.azure.com/subscriptions/{subId}/resourceGroups/{rg}/providers/Microsoft.CognitiveServices/accounts/{accountName}/projects/{projectName}/capabilityHosts/{name}?api-version=2025-06-01

{
  "properties": {
    "capabilityHostKind": "Agents",
    "threadStorageConnections": ["my-cosmosdb-conn"],
    "vectorStoreConnections":   ["my-aisearch-conn"],
    "storageConnections":       ["my-storage-conn"],
    "aiServicesConnections":    ["my-azure-openai-conn"]
  }
}
```

The four `*Connections` arrays are **connection names** that already exist on the
project (or are inherited from account-level), per MS Learn § "Project capability
host required properties". Wrong-name → 400. Resource IDs in place of names → 400.

### 6.3 The retry-on-409 contract

Three distinct 409 responses, three different handling rules (per MS Learn
§ "HTTP 409 Conflict errors"):

```python
# Python pseudocode using azure-mgmt-cognitiveservices
from azure.mgmt.cognitiveservices import CognitiveServicesManagementClient
from azure.core.exceptions import HttpResponseError
import time

def put_caphost_idempotent(client, rg, acct, name, body, *, max_retries=6, backoff_s=30):
    """Idempotent caphost PUT with 409-class-aware retry.

    Per MS Learn:
      - 200 on same-name + same-config (returns existing)
      - 400 on same-name + different config (no PATCH; delete + recreate)
      - 409 'existing Capability Host with name ' — different name at occupied scope
      - 409 'currently in non creating' — concurrent op; retry with backoff
    """
    for attempt in range(max_retries):
        try:
            poller = client.capability_hosts.begin_create_or_update(
                resource_group_name=rg,
                account_name=acct,
                capability_host_name=name,
                capability_host=body,
            )
            return poller.result()
        except HttpResponseError as e:
            msg = (e.message or "").lower()
            if "currently in non creating" in msg:
                # Concurrent-op 409 — wait + retry
                if attempt == max_retries - 1:
                    raise RuntimeError(f"caphost {name} still busy after {max_retries} retries") from e
                time.sleep(backoff_s)
                continue
            if "existing capability host with name" in msg:
                # Different-name 409 — this is policy, not transient. Caller decides.
                raise
            if "differs from the current configuration" in msg or "bad request" in msg:
                # 400 same-name + different-config — caller must delete first
                raise
            raise
```

### 6.4 What a 200-on-replay looks like

If you `PUT` the same name with the same body twice, the second call returns
**200 OK** with the existing resource per MS Learn § "Understand idempotent
behavior". This is what makes the retry above safe: if a transient network blip
hides the success of attempt N, attempt N+1 returns 200 against the now-existing
resource. Field-verified in the catalog fixture
(`skills/foundry-caphost-lifecycle/test-fixture/consumer_prompt.md` step 4).

## 7. Delete: caphost-only (lightweight, keeps account)

### 7.1 When to use this path

DELETE the capability host (without deleting the parent account) when:

- You need to **change a connection reference** (Cosmos, Search, Storage, AOAI)
  on a project caphost. MS Learn is explicit that updates are not supported, so
  the only path is DELETE + recreate.
- You're tearing down agents for a project but want to keep the Foundry account,
  models, other projects, etc.
- The caphost is in `Failed` or stuck `Updating` state and you need a clean slate.

### 7.2 DELETE caphost REST

```http
DELETE https://management.azure.com/subscriptions/{subId}/resourceGroups/{rg}/providers/Microsoft.CognitiveServices/accounts/{accountName}/capabilityHosts/{name}?api-version=2025-06-01
```

(For project scope, insert `projects/{projectName}/` between `accounts/{accountName}/`
and `capabilityHosts/`.)

The response is 202 with an `Azure-AsyncOperation` header. Poll per § 5.3 until
`Succeeded` (typical: 30s-2min; p99 ~5min). Then verify with GET — expect 404.

```bash
# verify gone
az rest --method get \
  --url "https://management.azure.com/subscriptions/${SUB}/resourceGroups/${RG}/providers/Microsoft.CognitiveServices/accounts/${ACCT}/capabilityHosts/${NAME}?api-version=2025-06-01"
# expect HTTP 404 ResourceNotFound
```

### 7.3 What DELETE caphost DOES free

- The caphost resource itself (the per-scope `Microsoft.CognitiveServices/accounts/capabilityHosts` object).
- The project's runtime binding to the listed connections.
- After deletion, agents in that project will no longer have access to the BYO
  thread/file/vector resources the caphost pointed to (MS Learn § "Delete
  capability hosts" Warning).

### 7.4 What DELETE caphost DOES NOT free (field-verified)

- The `serviceAssociationLink` on the agent subnet (only account purge does — § 8).
- The hidden Microsoft.App `Managed Environment` resource that the caphost
  provisioned to host agent compute (only account purge does — § 8).
- Any account-level RBAC or connection definitions.
- Soft-delete state on the account if the account itself is soft-deleted.

This asymmetry is the single biggest gotcha in Foundry Day-2 ops. If you delete
the caphost expecting to redeploy a new one into the same agent subnet, you will
hit `Subnet already in use` on the next deploy — see § 9.

## 8. Soft-delete + purge: full account teardown

### 8.1 Why this matters (the rule)

> **Field-verified rule:** the `serviceAssociationLink` (SAL) that a VNet-injected
> Foundry account holds on its agent subnet is **only released when the parent
> `Microsoft.CognitiveServices/accounts` resource is fully purged** — not when
> the capability host is deleted, and not when the account is soft-deleted. If
> you intend to redeploy into the same subnet, you MUST complete the soft-delete
> + purge sequence below.

This is true for **any

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [aiappsgbb](https://github.com/aiappsgbb)
- **Source:** [aiappsgbb/awesome-gbb](https://github.com/aiappsgbb/awesome-gbb)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-aiappsgbb-awesome-gbb-foundry-caphost-lifecycle
- Seller: https://agentstack.voostack.com/s/aiappsgbb
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
