eng/packages/http-client-csharp-provisioning/docs/provisioning-library-spec.md
This document describes the structure and shape of provisioning libraries that the provisioning generator must produce. All generated types follow the patterns established by existing Azure.Provisioning.* libraries and map directly to Bicep resource definitions.
Provisioning libraries produce three categories of types:
| Category | Base Class | Purpose | Example |
|---|---|---|---|
| Resource | ProvisionableResource | Top-level ARM resource | AppConfigurationStore |
| Model | ProvisionableConstruct | Nested property object | AppConfigurationKeyVaultProperties |
| Enum | enum | Constrained string/int value | AppConfigurationCreateMode |
Supporting types are also generated:
| Type | Purpose | Example |
|---|---|---|
| BuiltInRole | RBAC role definitions | AppConfigurationBuiltInRole |
| ResourceVersions | Supported API versions | Nested class inside resource |
Provisioning types are Bicep-centric, not REST-centric. They mirror the Bicep resource schema — each property has a bicepPath (e.g., ["properties", "createMode"]) that maps directly to the Bicep template output. Properties use BicepValue<T> wrappers instead of raw .NET types.
Each provisioning library lives alongside its corresponding management library under the same service directory:
sdk/keyvault/
├── Azure.ResourceManager.KeyVault/ ← Management library
│ ├── src/
│ ├── tests/
│ └── tsp-location.yaml
├── Azure.Provisioning.KeyVault/ ← Provisioning library (parallel)
│ ├── src/
│ │ ├── Generated/ ← Generator output
│ │ └── ... ← Hand-written customizations (partial classes)
│ ├── tests/
│ ├── tsp-location.yaml ← Points to same TypeSpec spec, different emitter
│ └── Azure.Provisioning.KeyVault.csproj
└── ci.mgmt.yml
This co-location ensures the provisioning library is discovered and built alongside the management library for the same service. Both libraries point to the same TypeSpec spec in azure-rest-api-specs, but use different emitters (@azure-typespec/http-client-csharp-mgmt vs @azure-typespec/http-client-csharp-provisioning).
Note: Existing provisioning libraries currently live under
sdk/provisioning/Azure.Provisioning.{ServiceName}. New TypeSpec-based provisioning libraries should be placed under the service directory instead.
A resource class:
ProvisionableResourcepublic partial classbicepIdentifier and optional resourceVersionDefineProvisionableProperties() to declare all propertiesResourceVersions class listing supported GA API versionsFromExisting() factory methodGetResourceNameRequirements() for naming validationCreateRoleAssignment() methods and GetKeys() / similar helpers// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// <auto-generated/>
#nullable enable
using Azure.Core;
using Azure.Provisioning;
using Azure.Provisioning.Expressions;
using Azure.Provisioning.Primitives;
using Azure.Provisioning.Resources;
using System;
using System.ComponentModel;
namespace Azure.Provisioning.AppConfiguration;
/// <summary>
/// AppConfigurationStore.
/// </summary>
public partial class AppConfigurationStore : ProvisionableResource
{
// ── Input properties (have setter) ──────────────────────────────
/// <summary>
/// The name of the configuration store.
/// </summary>
public BicepValue<string> Name
{
get { Initialize(); return _name!; }
set { Initialize(); _name!.Assign(value); }
}
private BicepValue<string>? _name;
/// <summary>
/// Gets or sets the Location.
/// </summary>
public BicepValue<AzureLocation> Location
{
get { Initialize(); return _location!; }
set { Initialize(); _location!.Assign(value); }
}
private BicepValue<AzureLocation>? _location;
/// <summary>
/// The SKU name of the configuration store.
/// </summary>
public BicepValue<string> SkuName
{
get { Initialize(); return _skuName!; }
set { Initialize(); _skuName!.Assign(value); }
}
private BicepValue<string>? _skuName;
/// <summary>
/// Disables all authentication methods other than AAD authentication.
/// </summary>
public BicepValue<bool> DisableLocalAuth
{
get { Initialize(); return _disableLocalAuth!; }
set { Initialize(); _disableLocalAuth!.Assign(value); }
}
private BicepValue<bool>? _disableLocalAuth;
/// <summary>
/// Gets or sets the Tags.
/// </summary>
public BicepDictionary<string> Tags
{
get { Initialize(); return _tags!; }
set { Initialize(); _tags!.Assign(value); }
}
private BicepDictionary<string>? _tags;
/// <summary>
/// Key vault properties.
/// </summary>
public AppConfigurationKeyVaultProperties EncryptionKeyVaultProperties
{
get { Initialize(); return _encryptionKeyVaultProperties!; }
set { Initialize(); AssignOrReplace(ref _encryptionKeyVaultProperties, value); }
}
private AppConfigurationKeyVaultProperties? _encryptionKeyVaultProperties;
// ── Output properties (read-only, no setter) ────────────────────
/// <summary>
/// The creation date of configuration store.
/// </summary>
public BicepValue<DateTimeOffset> CreatedOn
{
get { Initialize(); return _createdOn!; }
}
private BicepValue<DateTimeOffset>? _createdOn;
/// <summary>
/// Gets the Id.
/// </summary>
public BicepValue<ResourceIdentifier> Id
{
get { Initialize(); return _id!; }
}
private BicepValue<ResourceIdentifier>? _id;
/// <summary>
/// The provisioning state of the configuration store.
/// </summary>
public BicepValue<AppConfigurationProvisioningState> ProvisioningState
{
get { Initialize(); return _provisioningState!; }
}
private BicepValue<AppConfigurationProvisioningState>? _provisioningState;
// ── Constructor ─────────────────────────────────────────────────
/// <summary>
/// Creates a new AppConfigurationStore.
/// </summary>
/// <param name="bicepIdentifier">
/// The the Bicep identifier name of the AppConfigurationStore resource.
/// This can be used to refer to the resource in expressions, but is not
/// the Azure name of the resource. This value can contain letters,
/// numbers, and underscores.
/// </param>
/// <param name="resourceVersion">Version of the AppConfigurationStore.</param>
public AppConfigurationStore(string bicepIdentifier, string? resourceVersion = default)
: base(bicepIdentifier, "Microsoft.AppConfiguration/configurationStores", resourceVersion ?? "2024-06-01")
{
}
// ── Property definitions (Bicep path mapping) ───────────────────
/// <summary>
/// Define all the provisionable properties of AppConfigurationStore.
/// </summary>
protected override void DefineProvisionableProperties()
{
base.DefineProvisionableProperties();
// Input properties
_name = DefineProperty<string>("Name", ["name"], isRequired: true);
_location = DefineProperty<AzureLocation>("Location", ["location"], isRequired: true);
_skuName = DefineProperty<string>("SkuName", ["sku", "name"]);
_disableLocalAuth = DefineProperty<bool>("DisableLocalAuth", ["properties", "disableLocalAuth"]);
_tags = DefineDictionaryProperty<string>("Tags", ["tags"]);
_encryptionKeyVaultProperties = DefineModelProperty<AppConfigurationKeyVaultProperties>(
"EncryptionKeyVaultProperties", ["properties", "encryption", "keyVaultProperties"]);
// Output properties (server-provided, read-only)
_createdOn = DefineProperty<DateTimeOffset>("CreatedOn", ["properties", "creationDate"], isOutput: true);
_id = DefineProperty<ResourceIdentifier>("Id", ["id"], isOutput: true);
_provisioningState = DefineProperty<AppConfigurationProvisioningState>(
"ProvisioningState", ["properties", "provisioningState"], isOutput: true);
}
// ── ResourceVersions ────────────────────────────────────────────
/// <summary>
/// Supported AppConfigurationStore resource versions.
/// </summary>
public static class ResourceVersions
{
/// <summary>
/// 2024-06-01.
/// </summary>
public static readonly string V2024_06_01 = "2024-06-01";
/// <summary>
/// 2024-05-01.
/// </summary>
public static readonly string V2024_05_01 = "2024-05-01";
}
// ── FromExisting ────────────────────────────────────────────────
/// <summary>
/// Creates a reference to an existing AppConfigurationStore.
/// </summary>
public static AppConfigurationStore FromExisting(string bicepIdentifier, string? resourceVersion = default) =>
new(bicepIdentifier, resourceVersion) { IsExistingResource = true };
// ── Naming requirements ─────────────────────────────────────────
/// <summary>
/// Get the requirements for naming this AppConfigurationStore resource.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public override ResourceNameRequirements GetResourceNameRequirements() =>
new(minLength: 5, maxLength: 50, validCharacters:
ResourceNameCharacters.LowercaseLetters |
ResourceNameCharacters.UppercaseLetters |
ResourceNameCharacters.Numbers |
ResourceNameCharacters.Hyphen);
}
Properties fall into distinct categories based on their Bicep behavior:
| Category | Wrapper | Has Setter? | DefineProperty flags | Example |
|---|---|---|---|---|
| Scalar input | BicepValue<T> | Yes (.Assign()) | — | Name, Location, DisableLocalAuth |
| Required scalar | BicepValue<T> | Yes (.Assign()) | isRequired: true | Name, Location |
| Enum input | BicepValue<TEnum> | Yes (.Assign()) | — | PublicNetworkAccess, CreateMode |
| Model input | TModel (construct) | Yes (AssignOrReplace) | via DefineModelProperty | EncryptionKeyVaultProperties |
| Dictionary input | BicepDictionary<T> | Yes (.Assign()) | via DefineDictionaryProperty | Tags |
| List input | BicepList<T> | Yes (.Assign()) | via DefineListProperty | — |
| Scalar output | BicepValue<T> | No (read-only) | isOutput: true | Id, Endpoint, ProvisioningState |
| Model output | TModel (construct) | No (read-only) | isOutput: true via DefineModelProperty | SystemData |
| List output | BicepList<T> | No (read-only) | isOutput: true via DefineListProperty | PrivateEndpointConnections |
| Secure | BicepValue<T> | Varies | isSecure: true | password / key properties |
The bicepPath parameter in DefineProperty maps the C# property to the Bicep resource schema hierarchy:
Bicep schema C# DefineProperty bicepPath
───────────── ──────────────────────────
name ["name"]
location ["location"]
tags ["tags"]
sku.name ["sku", "name"]
identity ["identity"]
properties.createMode ["properties", "createMode"]
properties.disableLocalAuth ["properties", "disableLocalAuth"]
properties.encryption.keyVaultProperties ["properties", "encryption", "keyVaultProperties"]
properties.provisioningState ["properties", "provisioningState"]
The path directly corresponds to the nesting in the Bicep resource definition. See the Bicep resource template reference for the canonical schema of each resource type.
Input property (writable):
public BicepValue<T> PropertyName
{
get { Initialize(); return _propertyName!; }
set { Initialize(); _propertyName!.Assign(value); }
}
private BicepValue<T>? _propertyName;
Output property (read-only):
public BicepValue<T> PropertyName
{
get { Initialize(); return _propertyName!; }
}
private BicepValue<T>? _propertyName;
Model property (nested construct):
// Input model
public ModelType PropertyName
{
get { Initialize(); return _propertyName!; }
set { Initialize(); AssignOrReplace(ref _propertyName, value); }
}
private ModelType? _propertyName;
// Output model
public ModelType PropertyName
{
get { Initialize(); return _propertyName!; }
}
private ModelType? _propertyName;
public ResourceTypeName(string bicepIdentifier, string? resourceVersion = default)
: base(bicepIdentifier, "Microsoft.Provider/resourceTypes", resourceVersion ?? "YYYY-MM-DD")
{
}
bicepIdentifier: The Bicep symbolic name (used in resource <name> 'type@version')"Microsoft.AppConfiguration/configurationStores")Lists all supported GA-only API versions (no previews) in reverse chronological order:
public static class ResourceVersions
{
public static readonly string V2024_06_01 = "2024-06-01";
public static readonly string V2024_05_01 = "2024-05-01";
public static readonly string V2023_03_01 = "2023-03-01";
}
A model class:
ProvisionableConstructpublic partial classDefineProvisionableProperties() to declare propertiesBicepValue<T>, Initialize(), Assign())// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// <auto-generated/>
#nullable enable
using Azure.Provisioning.Primitives;
namespace Azure.Provisioning.AppConfiguration;
/// <summary>
/// Settings concerning key vault encryption for a configuration store.
/// </summary>
public partial class AppConfigurationKeyVaultProperties : ProvisionableConstruct
{
/// <summary>
/// The URI of the key vault key used to encrypt data.
/// </summary>
public BicepValue<string> KeyIdentifier
{
get { Initialize(); return _keyIdentifier!; }
set { Initialize(); _keyIdentifier!.Assign(value); }
}
private BicepValue<string>? _keyIdentifier;
/// <summary>
/// The client id of the identity which will be used to access key vault.
/// </summary>
public BicepValue<string> IdentityClientId
{
get { Initialize(); return _identityClientId!; }
set { Initialize(); _identityClientId!.Assign(value); }
}
private BicepValue<string>? _identityClientId;
/// <summary>
/// Creates a new AppConfigurationKeyVaultProperties.
/// </summary>
public AppConfigurationKeyVaultProperties()
{
}
/// <summary>
/// Define all the provisionable properties of AppConfigurationKeyVaultProperties.
/// </summary>
protected override void DefineProvisionableProperties()
{
base.DefineProvisionableProperties();
_keyIdentifier = DefineProperty<string>("KeyIdentifier", ["keyIdentifier"]);
_identityClientId = DefineProperty<string>("IdentityClientId", ["identityClientId"]);
}
}
| Aspect | Resource (ProvisionableResource) | Model (ProvisionableConstruct) |
|---|---|---|
| Base class | ProvisionableResource | ProvisionableConstruct |
| Constructor | Takes bicepIdentifier + resourceVersion | Parameterless |
| ARM type string | Passed to base ctor | N/A |
ResourceVersions | Yes (nested class) | No |
FromExisting() | Yes | No |
bicepPath | Full path from root (e.g., ["properties", "encryption", "keyVaultProperties"]) | Relative path within parent (e.g., ["keyIdentifier"]) |
Model properties use relative paths — they represent the property path within the model object, not the full path from the resource root. The parent resource's DefineModelProperty establishes the prefix:
Resource: _kvProps = DefineModelProperty<KVProperties>(..., ["properties", "encryption", "keyVaultProperties"]);
Model: _keyIdentifier = DefineProperty<string>("KeyIdentifier", ["keyIdentifier"]);
Full Bicep path → properties.encryption.keyVaultProperties.keyIdentifier
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// <auto-generated/>
namespace Azure.Provisioning.AppConfiguration;
/// <summary>
/// Indicates whether the configuration store need to be recovered.
/// </summary>
public enum AppConfigurationCreateMode
{
/// <summary>
/// Recover.
/// </summary>
Recover,
/// <summary>
/// Default.
/// </summary>
Default,
}
When the Bicep/ARM value doesn't match a valid C# identifier, use [DataMember(Name = "...")]:
using System.Runtime.Serialization;
namespace Azure.Provisioning.AppConfiguration;
public enum DataPlaneProxyAuthenticationMode
{
Local,
[DataMember(Name = "Pass-through")]
PassThrough,
}
Enums are prefixed with the service name to avoid collisions across provisioning libraries:
AppConfigurationCreateMode (not CreateMode)AppConfigurationProvisioningState (not ProvisioningState)AppConfigurationPublicNetworkAccess (not PublicNetworkAccess)public readonly partial struct AppConfigurationBuiltInRole : IEquatable<AppConfigurationBuiltInRole>
{
private readonly string _value;
private AppConfigurationBuiltInRole(string value) { _value = value; }
/// <summary>
/// Full access to App Configuration data.
/// </summary>
public static AppConfigurationBuiltInRole AppConfigurationDataOwner { get; } =
new("5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b");
// ... more roles ...
public static bool operator ==(AppConfigurationBuiltInRole left, AppConfigurationBuiltInRole right) =>
left.Equals(right);
public static bool operator !=(AppConfigurationBuiltInRole left, AppConfigurationBuiltInRole right) =>
!left.Equals(right);
public static implicit operator AppConfigurationBuiltInRole(string value) => new(value);
public bool Equals(AppConfigurationBuiltInRole other) => string.Equals(_value, other._value, ...);
public override bool Equals(object obj) => obj is AppConfigurationBuiltInRole other && Equals(other);
public override int GetHashCode() => _value?.GetHashCode() ?? 0;
public override string ToString() => _value;
internal static string GetBuiltInRoleName(AppConfigurationBuiltInRole value) =>
value._value switch { "5ae67dd6..." => "AppConfigurationDataOwner", ... };
}
The generator produces provisioning types with the following features:
| Aspect | Details |
|---|---|
| Base class | ProvisionableResource for resources, ProvisionableConstruct for models |
| Properties | BicepValue<T>, BicepList<T>, BicepDictionary<T> wrappers |
| Constructor | (string bicepIdentifier, string? resourceVersion) with base ctor |
DefineProvisionableProperties() | Correct bicep path mapping |
ResourceVersions | Nested class with GA API version constants |
FromExisting() | Static factory method |
| No serialization files | Serialization providers suppressed |
| Post-processor pruning | Unreferenced models/enums auto-removed |
| Property flattening | Decorator-driven via @flattenProperty |
| Discriminator | Polymorphic type hierarchies for resources and models |
| Flat namespace | All types in Azure.Provisioning.{ServiceName} without .Models |
| Parent property | Typed Parent property for child resources |
| Property naming | Names resolved through mgmt visitor pipeline (e.g., NameVisitor) via CreatePropertyCore |
ARM resources often have a properties bag that contains most user-settable fields. In earlier provisioning libraries, these were always flattened into the resource class directly. The new generator only flattens when the @flattenProperty decorator is present in the TypeSpec definition.
With @flattenProperty:
ARM JSON Schema: Provisioning Resource:
{ class AppConfigurationStore
"name": "...", Name → ["name"]
"location": "...", Location → ["location"]
"properties": { DisableLocalAuth → ["properties", "disableLocalAuth"]
"disableLocalAuth": true, SoftDeleteRetentionInDays → ["properties", "softDeleteRetentionInDays"]
"softDeleteRetentionInDays": 7,
}
}
Without @flattenProperty (default):
ARM JSON Schema: Provisioning Resource:
{ class ConfigurationStoreData
"name": "...", Name → ["name"]
"location": "...", Location → ["location"]
"properties": { ... } Properties → ["properties"] (model type)
}
Whether or not @flattenProperty is present depends on the TypeSpec definition. The generator does not hardcode any flattening — it is purely decorator-driven.
Azure.Provisioning.AppConfiguration/
└── src/
└── Generated/
├── AppConfigurationStore.cs # Resource
├── AppConfigurationKeyValue.cs # Child resource
├── AppConfigurationReplica.cs # Child resource
├── AppConfigurationSnapshot.cs # Child resource
├── AppConfigurationPrivateEndpointConnection.cs # Child resource
├── AppConfigurationBuiltInRole.cs # Role definitions
├── AppConfigurationCreateMode.cs # Enum
├── AppConfigurationProvisioningState.cs # Enum
├── AppConfigurationPublicNetworkAccess.cs # Enum
├── DataPlaneProxyAuthenticationMode.cs # Enum
├── AppConfigurationKeyVaultProperties.cs # Model (construct)
├── AppConfigurationDataPlaneProxyProperties.cs # Model (construct)
├── AppConfigurationStoreApiKey.cs # Model (construct)
└── SnapshotKeyValueFilter.cs # Model (construct)
Generated/ root (flat namespace, no Models/ subfolder)Azure.Provisioning.{ServiceName}.Serialization.cs files — serialization is handled by DefineProvisionableProperties()Internal/ directory — no internal helper types needed