Microsoft Security Copilot compute capacities (billable SCUs), map-driven through azapi, with first-class overage and data-residency controls and cost checks on every plan.
Microsoft.SecurityCopilot/capacities has no azurerm resource, so the module drives it through
azapi. One map entry per capacity, keyed by resource name.
COST, first-class: the provisioned baseline (number_of_units) bills per SCU-hour for every
hour the capacity EXISTS, used or not. The cost lever is capacity lifetime, not prompt volume:
apply it last, destroy it first. Overage is optional and OMITTED from the request unless
configured; overage units bill only for usage above the baseline, at the higher pay-as-you-go
rate, so they are a throttling safety valve rather than a cost saver. Limited overage requires a
bound; Unlimited overage and cross-geo compute are legal but each trips an informational check
on every plan, so neither is ever a surprise.
Residency, explicit: geo pins where evaluation happens (EU, US or UK);
cross_geo_compute = "NotAllowed" (the default) keeps it strict.
The capacity is the deployment half of Security Copilot only. Runtime prompting is delegated
on-behalf-of by platform design (no application permission exists for the Graph data plane today),
so automation prompts through the first-party securitycopilot Logic Apps connector, not through
Terraform.
module "security_copilot_capacity" {
source = "libre-devops/security-copilot-compute-capacity/azapi"
version = "~> 2.0"
resource_group_id = module.rg.ids["rg-ldo-uks-prd-001"]
location = "uksouth"
tags = module.tags.tags
compute_capacities = {
"sccapldouksprd01" = {
geo = "UK"
number_of_units = 1
overage_state = "Limited"
overage_amount = 1
}
}
}examples/minimal- one capacity on defaults: 1 SCU, strict residency, no overage.examples/complete- limited overage with a bound, module and per-capacity tags, and the exported properties as outputs.
The examples bill when applied. There is deliberately NO live self-test in CI: verification is
the mocked terraform test suite plus the offline gates, and the live proof is a deliberate,
human-attended window in a consuming estate. If you apply an example yourself, destroy it in the
same sitting.
Local work needs PowerShell 7+ and just, because the recipes
wrap the LibreDevOpsHelpers
PowerShell module (the same engine the libre-devops/terraform-azure action runs in CI). Install
just with brew install just, or uv tool add rust-just then uv run just <recipe>.
Run just to list recipes: just update-ldo-pwsh, just validate, just scan,
just pwsh-analyze, just test, and just docs. The plan/apply/destroy/e2e recipes exist
but REMEMBER THE BILL: an applied example provisions real SCUs, so just e2e (apply then always
destroy) is the only sane live shape, run deliberately and attended. Releasing is also just:
just increment-release [patch|minor|major] bumps, tags, and publishes a GitHub release, and the
Terraform Registry picks up the tag.
This module is scanned with Trivy; HIGH and CRITICAL
findings fail the build. Any waiver is a deliberate, reviewed decision, never a way to quiet a
finding that should be fixed. Waivers live in .trivyignore.yaml (the
machine-applied source of truth, passed to Trivy with --ignorefile) and are mirrored in the table
below so the reason is auditable.
| Trivy ID | Resource | Finding | Justification |
|---|---|---|---|
| None |
To add an exception: add an entry to .trivyignore.yaml (id, optional paths to scope it, and a
statement recording why), then add a matching row here. Both the file and this table are reviewed
in the pull request.
The Requirements, Providers, Inputs, Outputs, and Resources below are generated by terraform-docs.
| Name | Version |
|---|---|
| terraform | >= 1.9.0, < 2.0.0 |
| azapi | >= 2.5.0, < 3.0.0 |
| Name | Version |
|---|---|
| azapi | >= 2.5.0, < 3.0.0 |
No modules.
| Name | Type |
|---|---|
| azapi_resource.this | resource |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| api_version | API version for Microsoft.SecurityCopilot/capacities, without the '@' prefix. Preview only today; variablised so it is never pinned to a stale value. | string |
"2024-11-01-preview" |
no |
| compute_capacities | Security Copilot compute capacities (Microsoft.SecurityCopilot/capacities) keyed by resource name. COST: the provisioned baseline (number_of_units) bills per SCU-hour for every hour the capacity EXISTS, used or not; the cost lever is capacity lifetime, not prompt volume. Overage is optional and OMITTED unless overage_state is set: overage units bill only for usage ABOVE the baseline, at the higher pay-as-you-go rate, so they are a throttling safety valve rather than a cost saver. Limited overage needs an overage_amount bound; Unlimited deliberately has none (a check makes that visible on every plan). geo pins data residency for evaluation; cross_geo_compute controls whether evaluation may burst outside it when local capacity is constrained (NotAllowed, the default, keeps residency strict). |
map(object({ |
{} |
no |
| location | Azure region for the capacities. Verify the resource provider's supported regions; the geo (data residency) is set per capacity. | string |
n/a | yes |
| resource_group_id | Resource id of the resource group the capacities deploy into. | string |
n/a | yes |
| schema_validation_enabled | azapi schema validation for the resource body. Off by default: the API is preview and its published schema lags the accepted properties. | bool |
false |
no |
| tags | Tags applied to every capacity; per-capacity tags merge over them by key. | map(string) |
{} |
no |
| Name | Description |
|---|---|
| capacities | Map of capacity name to { id, name, location, units, geo, cross_geo_compute, overage_state, overage_amount, and the raw exported properties }. |
| capacity_ids | Map of capacity name to its resource id. |