Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9397c15
First emit
dargilco Sep 3, 2026
d981f14
CoPilot Fix to emitted code. Why is this needed?
dargilco Sep 3, 2026
f382af6
Temp update to api-version. Add tests & recording
dargilco Sep 4, 2026
ffb2605
Add examples for create and update
dargilco Sep 4, 2026
5b160ca
Move examples to the correct place
dargilco Sep 4, 2026
2763a51
Fix azdev linter on Modified Extensions
dargilco Sep 4, 2026
ceff52c
Step toward testing attaching cost control to account
dargilco Sep 8, 2026
b6eb7c5
Fixes
dargilco Sep 8, 2026
fa21705
Test passes now
dargilco Sep 8, 2026
7895b54
add temp doc
dargilco Sep 8, 2026
3d20465
rename extension to foundry-cost-control
dargilco Sep 9, 2026
0d027b9
Update package docs
dargilco Sep 9, 2026
acdbf5b
Re-emit with updated long summary
dargilco Sep 9, 2026
0d94087
CoPilot review comments - part 1
dargilco Sep 9, 2026
fa25978
CoPilot review comments - part 2
dargilco Sep 9, 2026
f9bbf8f
Re-emit and run PostEmitter.ps1
dargilco Sep 10, 2026
2d12d05
fix for emitter bug
dargilco Sep 10, 2026
56773a6
Update recordings
dargilco Sep 10, 2026
09dd090
Re-emit
dargilco Sep 14, 2026
5c82be9
override account and deployment commands
dargilco Sep 15, 2026
eb9f20f
rename command group
dargilco Sep 15, 2026
eb38e12
rename unit-test file
dargilco Sep 15, 2026
7f8b766
Update README.md
dargilco Sep 15, 2026
f77c5fc
Add tests for deployments
dargilco Sep 15, 2026
6022b4f
Fix linter errors
dargilco Sep 17, 2026
141130c
azdev style fix
dargilco Sep 17, 2026
9e3a39b
Fix setup.py, update PostEmitter.ps1
dargilco Sep 23, 2026
7b6fc55
Fix pylance error
dargilco Sep 23, 2026
860d287
Cleanup of unused file
dargilco Sep 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/foundry-cost-control/HISTORY.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.. :changelog:

Release History
===============

1.0.0b1
++++++
* Initial release.
23 changes: 23 additions & 0 deletions src/foundry-cost-control/PostEmitter.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#
# Run this script after re-emitting the code:
# powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\PostEmitter.ps1
#

# Change 1:
# I did manual edits to README.md and setup.py files. Make sure you restore
# this version after you re-emit

& git -C $PSScriptRoot restore -- README.md
if ($LASTEXITCODE -ne 0) {
throw "Failed to restore README.md after CLI emission."
}

& git -C $PSScriptRoot restore -- setup.py
if ($LASTEXITCODE -ne 0) {
throw "Failed to restore setup.py after CLI emission."
}

# Change 2:
# Use CoPilot to fix an emitter bug (?). Use this prompt:
# Run "cls & azdev test foundry-cost-control --live --debug" and fix the resulting error. A similar fix was made in the past, so you can look at this commit for reference: https://github.com/Azure/azure-cli-extensions/pull/10298/changes/d981f14597cdcd118c3ab7b17264057944496a97
#
214 changes: 214 additions & 0 deletions src/foundry-cost-control/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
# Azure CLI Foundry Cost Control Extension

This is an extension to Azure CLI to manage Foundry Cost Control policies.

## Install the extension

Add the extension:

```
az extension add --name foundry-cost-control
```

Verify your installation by viewing all extensions:

```
az extension list --output table
```

## Get usage help on affected commands

```
az cognitiveservices account update --help
az cognitiveservices account deployment create --help
az cognitiveservices account deployment update --help
az cognitiveservices account costcontrol --help
```

## Cost control CRUD operations

Cost-control policy commands use `az cognitiveservices account costcontrol`.
The examples below assume that the owning AI Services account already exists.

First, save the following rule definition as `cost-control-rules.json`:

```json
[
{
"name": "per-agent-monthly",
"counterKey": [
{
"type": "agent"
}
],
"unit": "usd",
"amount": 200,
"period": "month",
"recurring": true,
"match": {
"foundryCallerAgentId": [
"agent-123"
]
},
"thresholds": [
{
"type": "percentage",
"value": 80,
"action": "alert"
},
{
"type": "absolute",
"value": 0,
"action": "audit"
}
]
}
]
```

Set values used by the examples:

```powershell
$resourceGroup = "my-resource-group"
$accountName = "my-account"
$costControlName = "production-agents"
$rulesFile = ".\cost-control-rules.json"
```

### Create

Create a cost control and save its resource ID for use when attaching it to an
account or deployment:

```powershell
$costControlId = az cognitiveservices account costcontrol create `
--resource-group $resourceGroup `
--account-name $accountName `
--cost-control-name $costControlName `
--display-name "Production agent monthly budget" `
--rules "@$rulesFile" `
--query id `
--output tsv
```

### List

List all cost controls owned by an account:

```powershell
az cognitiveservices account costcontrol list `
--resource-group $resourceGroup `
--account-name $accountName `
--output table
```

### Show

Get a specific cost control:

```powershell
az cognitiveservices account costcontrol show `
--resource-group $resourceGroup `
--account-name $accountName `
--cost-control-name $costControlName
```

### Update

Update the display name while preserving the existing rules:

```powershell
az cognitiveservices account costcontrol update `
--resource-group $resourceGroup `
--account-name $accountName `
--cost-control-name $costControlName `
--display-name "Updated production agent monthly budget"
```

To replace the rules, also pass `--rules "@$rulesFile"` with the updated rules
file.

### Delete

Detach the cost control from any accounts or deployments that use it, and then
delete it:

```powershell
az cognitiveservices account costcontrol delete `
--resource-group $resourceGroup `
--account-name $accountName `
--cost-control-name $costControlName `
--yes
```

## Attach cost controls to an account

The extension overrides `az cognitiveservices account update` to add preview
arguments for attaching cost controls and configuring account-level telemetry
and event connections:

```powershell
az cognitiveservices account update `
--name my-account `
--resource-group my-resource-group `
--cost-control-ids $costControlId1 $costControlId2 `
--cost-control-connections '{
"appInsightsConnectionId": "/subscriptions/.../connections/application-insights",
"eventGridConnectionId": "/subscriptions/.../connections/event-grid"
}'
```

Remove all cost-control attachments by specifying the argument without values:

```powershell
az cognitiveservices account update `
--name my-account `
--resource-group my-resource-group `
--cost-control-ids
```

Remove the configured account-level connections with:

```powershell
az cognitiveservices account update `
--name my-account `
--resource-group my-resource-group `
--clear-cost-control-connections
```

These arguments use the `2026-09-15-preview` Cognitive Services Management API.

## Attach a cost control to a deployment

Create a deployment with its cost-control attachment in the same request:

```powershell
az cognitiveservices account deployment create `
--name my-account `
--resource-group my-resource-group `
--deployment-name my-deployment `
--model-name gpt-4.1 `
--model-version 2025-04-14 `
--model-format OpenAI `
--sku-name GlobalStandard `
--sku-capacity 10 `
--cost-control-ids $costControlId
```

Update or clear the attachment on an existing deployment:

```powershell
az cognitiveservices account deployment update `
--name my-account `
--resource-group my-resource-group `
--deployment-name my-deployment `
--cost-control-ids $costControlId

az cognitiveservices account deployment update `
--name my-account `
--resource-group my-resource-group `
--deployment-name my-deployment `
--cost-control-ids
```

The current preview supports at most one cost control per deployment.
42 changes: 42 additions & 0 deletions src/foundry-cost-control/azext_foundry_cost_control/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
#
# Code generated by aaz-dev-tools
# --------------------------------------------------------------------------------------------

from azure.cli.core import AzCommandsLoader
from azext_foundry_cost_control._help import helps # pylint: disable=unused-import


class FoundryCostControlCommandsLoader(AzCommandsLoader):

def __init__(self, cli_ctx=None):
from azure.cli.core.commands import CliCommandType
custom_command_type = CliCommandType(
operations_tmpl='azext_foundry_cost_control.custom#{}')
super().__init__(cli_ctx=cli_ctx,
custom_command_type=custom_command_type)

def load_command_table(self, args):
from azext_foundry_cost_control.commands import load_command_table
from azure.cli.core.aaz import load_aaz_command_table
try:
from . import aaz
except ImportError:
aaz = None
if aaz:
load_aaz_command_table(
loader=self,
aaz_pkg_name=aaz.__name__,
args=args
)
load_command_table(self, args)
return self.command_table

def load_arguments(self, command):
from azext_foundry_cost_control._params import load_arguments
load_arguments(self, command)


COMMAND_LOADER_CLS = FoundryCostControlCommandsLoader
48 changes: 48 additions & 0 deletions src/foundry-cost-control/azext_foundry_cost_control/_help.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
#
# Code generated by aaz-dev-tools
# --------------------------------------------------------------------------------------------

# pylint: disable=line-too-long
# pylint: disable=too-many-lines

from knack.help_files import helps # pylint: disable=unused-import


helps["cognitiveservices account update"] = """
type: command
short-summary: Update a Cognitive Services account.
examples:
- name: Attach cost controls and configure telemetry and event connections
text: |-
az cognitiveservices account update --name my-account --resource-group my-resource-group
--cost-control-ids /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-group/providers/Microsoft.CognitiveServices/accounts/my-account/costControls/my-control
--cost-control-connections '{"appInsightsConnectionId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-group/providers/Microsoft.CognitiveServices/accounts/my-account/connections/application-insights","eventGridConnectionId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-group/providers/Microsoft.CognitiveServices/accounts/my-account/connections/event-grid"}'
- name: Remove all cost-control attachments
text: |-
az cognitiveservices account update --name my-account --resource-group my-resource-group
--cost-control-ids
"""

helps["cognitiveservices account deployment create"] = """
type: command
short-summary: Create a Cognitive Services account deployment.
examples:
- name: Create a deployment with a cost-control attachment
text: |-
az cognitiveservices account deployment create --name my-account --resource-group my-resource-group --deployment-name my-deployment --model-name gpt-4.1 --model-version 2025-04-14 --model-format OpenAI --sku-name GlobalStandard --sku-capacity 10 --cost-control-ids /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-group/providers/Microsoft.CognitiveServices/accounts/my-account/costControls/my-control
"""

helps["cognitiveservices account deployment update"] = """
type: command
short-summary: Update a Cognitive Services account deployment.
examples:
- name: Attach a cost control to an existing deployment
text: |-
az cognitiveservices account deployment update --name my-account --resource-group my-resource-group --deployment-name my-deployment --cost-control-ids /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-group/providers/Microsoft.CognitiveServices/accounts/my-account/costControls/my-control
- name: Remove the cost-control attachment
text: |-
az cognitiveservices account deployment update --name my-account --resource-group my-resource-group --deployment-name my-deployment --cost-control-ids
"""
Loading
Loading