Azure Resource Manager API bundle
Use this bundle only for ARM resource provider APIs. ARM contracts are not generic REST APIs; they require ARM-specific routing, resource identity, provider namespace, operations, and lifecycle semantics.
Recommended dependencies
{
"type": "module",
"private": true,
"scripts": {
"build": "tsp compile . --warn-as-error",
"watch": "tsp compile . --watch",
"format": "tsp format \"**/*.tsp\""
},
"dependencies": {
"@typespec/compiler": "<verified-compatible-version>",
"@typespec/http": "<verified-compatible-version>",
"@typespec/rest": "<verified-compatible-version>",
"@typespec/versioning": "<verified-compatible-version>",
"@azure-tools/typespec-azure-core": "<verified-compatible-version>",
"@azure-tools/typespec-azure-resource-manager": "<verified-compatible-version>",
"@azure-tools/typespec-autorest": "<verified-compatible-version>"
}
}Recommended start
Prefer the official Azure template for new ARM work:
npx tsp init https://aka.ms/typespec/azure-init
# Select the Azure Resource Manager service project template
npm install
npx tsp compile . --warn-as-errorTracked resource example
import "@azure-tools/typespec-azure-resource-manager";
using Azure.ResourceManager;
@armProviderNamespace
@service(#{ title: "Microsoft.Contoso" })
@versioned(Versions)
namespace Microsoft.Contoso;
enum Versions { v2026_05_01: "2026-05-01" }
model Widget is TrackedResource<WidgetProperties> {
@key("widgetName") @segment("widgets")
@path @maxLength(63) @pattern("^[a-z0-9-]+$")
name: string;
}
model WidgetProperties {
@visibility(Lifecycle.Read) provisioningState?: ResourceProvisioningState;
displayName: string;
}
@armResourceOperations
interface Widgets {
get is ArmResourceRead<Widget>;
createOrUpdate is ArmResourceCreateOrReplaceAsync<Widget>;
update is ArmResourcePatchAsync<Widget, WidgetProperties>;
delete is ArmResourceDeleteWithoutOkAsync<Widget>;
listByResourceGroup is ArmResourceListByParent<Widget>;
listBySubscription is ArmListBySubscription<Widget>;
}provisioningState enum
ARM mandates only the three terminal provisioning-state values - Succeeded, Failed, Canceled - which the ResourceProvisioningState type from Azure.ResourceManager provides. Resource providers almost always also expose non-terminal states (commonly Creating, Updating, Deleting, Accepted); these are RP-defined and recommended, not required by ARM. Add the non-terminal states your RP uses as a union over ResourceProvisioningState:
union WidgetProvisioningState {
ResourceProvisioningState,
"Creating",
"Updating",
"Deleting",
"Accepted",
}Operations API
Every ARM resource provider MUST expose a Microsoft.<RP>/operations endpoint that lists the RP's operations. Use the standard template:
interface Operations extends Azure.ResourceManager.Operations {}Proxy and Extension resources
model ChildThing is ProxyResource<ChildThingProperties>- for child resources that don't have their ownlocation/tags(e.g., per-region configuration under a tracked parent).model Permission is ExtensionResource<PermissionProperties>- for resources that attach to an existing resource of another type (e.g., role assignments, locks, diagnostics settings).
Locations
Never hard-code Azure region enums in a TypeSpec contract - Azure adds new regions continuously, and a frozen enum becomes a contract bug the day a customer deploys to a new region. ARM expects @armResourceIdentifier on cross-resource references and runtime location validation by the RP, not contract-time enumeration.
Common ARM rejections
Reviewer-killers seen repeatedly in Azure API review:
- Missing
systemDataon the resource.TrackedResourceandProxyResourcetemplates auto-include it - verify it appears in emitted OpenAPI output. - Missing
@armProviderNamespacedecorator on the namespace. - PUT operation missing the
201 Createdresponse. Use the*Asyncoperation templates (ArmResourceCreateOrReplaceAsync) rather than hand-rolled ops. - Properties hoisted to the resource root instead of nested inside the
properties: {}envelope. ARM mandates the envelope shape. - Required properties added in a non-breaking version. For ARM this is ALWAYS breaking, even if the property is on a response - bump the API version.
ARM design rules
- Use
@azure-tools/typespec-azure-resource-managerpatterns and templates. - Provider namespace, resource types, scope, and API versions must be explicit.
- ARM resources need
name,type,id,systemData,tags,locationwhere applicable, and standard provisioning state semantics. - Avoid inventing custom control-plane patterns when ARM templates already exist.
- Do not use deprecated location patterns for new work; prefer current ARM location-scoped resource patterns from the Azure TypeSpec docs.
- Generated Swagger/AutoRest output may need to be checked in if the Azure SDK/API review pipeline requires it; otherwise treat it as an artifact.
ARM-specific gotchas
- Resource type names use PascalCase singular in TypeSpec models (e.g.
VirtualMachine) but appear as lowercase plural URL segments (virtualMachines). This pluralization is controlled by the resource template - do not hand-route it. @parentResourceis required on nested resources; without it the emitter generates malformed routes that miss the parent segment.- Tracked resources require
location,tags, andsystemData. The Azure Core / ARM templates wire these in for you - do not redeclare them. - The default API version for ARM is the latest GA. Preview versions live under the
azure-rest-api-specspreview folder, not the GA folder. - The
azure-rest-api-specsrepo requires bothstable/<version>/andpreview/<version>/folder placement for resources that ship in both channels. Confirm both folders are correct before opening a PR.
@parentResource caveat
@parentResource is silently ignored when used with Azure.Core.Legacy.* operation templates, producing flat routes that miss the parent path segment. Use the modern ARM/Core resource templates (ArmResource*, Operations.Resource*) which honor parent resources correctly. Verify this behavior against your pinned library versions before relying on either pattern - the Legacy templates exist only for migration and their quirks are fixed by design.
ARM review checks
- Provider namespace is correct and stable.
- Resource names use ARM-compatible constraints.
- Resource hierarchy and scope are correct: tenant, management group, subscription, resource group, extension, or location-scoped.
- Long-running create/update/delete operations follow ARM LRO rules.
api-versionis present and governed by TypeSpec versioning.- Common ARM operations are present where expected: get, create/update, delete, list by parent/scope, provider operations.
- Examples are complete and match emitted swagger.
- RP registration, permissions, and RBAC implications are documented outside the TypeSpec contract if relevant.