Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions src/elastic-san/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

Release History
===============
1.4.0b1
++++++
* `az elastic-san`: Support the 2026-05-01-preview API version and Elastic SAN V2 capacity and performance settings.
* `az elastic-san volume`: Support managed-by resource IDs and permanent deletion of soft-deleted volumes.
* `az elastic-san volume-group`: Support PATCH updates, delete retention, encryption in transit, and QoS reservations.
* `az elastic-san update` and `az elastic-san volume-group update`: Preserve `--add`, `--set`, `--remove`, and `--force-string` support using GET-modify-PATCH.
* `az elastic-san update`: Preserve `--sku` and `--availability-zones` compatibility using GET-modify-PUT when either option is supplied.
* `az elastic-san volume-group update`: Preserve legacy `--identity` syntax, including `--identity {type:None}` to remove managed identities.
* `az elastic-san volume`: Accept the legacy single-resource `--managed-by` object and expose `managedByResourceId` as a compatibility output alias. The alias returns only the first resource ID when `managedBy` contains multiple IDs.
* BREAKING CHANGE: `az elastic-san volume`: The `managedBy` output changes from an object with `resourceId` to an array of objects with `resourceIds`. Existing queries using `managedBy.resourceId` must use `managedByResourceId` for the first resource ID or query the new array for all IDs.

1.3.2
++++++
* `az elastic-san`: Support autoscale, prerestore, and prebackup
Expand Down
9 changes: 9 additions & 0 deletions src/elastic-san/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ This is an extension to Azure CLI to manage ElasticSan resources.
## Elastic San
### Create an Elastic SAN.
`az elastic-san create -n {san_name} -g {rg} --tags "{key1810:aaaa}" -l southcentralusstg --base-size-tib 23 --extended-capacity-size-tib 14 --sku "{name:Premium_LRS,tier:Premium}"`
For V1 (including when `--version` is omitted), omitted base and extended capacities default to 20 and 0 TiB. V2 does not inject these V1 capacity defaults; provision it using `--sku "{name:ElasticSAN_LRS}" --version V2 --total-iops ... --total-mbps ... --total-size-tib ...` and supply autoscale settings (`--auto-scale-policy-enforcement`, `--capacity-unit-scale-up-limit-tib`, `--increase-capacity-unit-by-tib`, and `--unused-size-tib`).
The separate `test_elastic_san_v2_scenarios` integration test covers V2 create, show, list, update, and delete in `eastus2euap`. Record it with `azdev test --live --discover test_elastic_san_v2_scenarios`, then verify playback with `azdev test --discover test_elastic_san_v2_scenarios`. A successful live recording requires V2 access on the target subscription and region; this test does not skip service feature-gate errors.
### Delete an Elastic SAN.
`az elastic-san delete -g {rg} -n {san_name}`
### Get a list of Elastic SANs in a subscription.
Expand All @@ -16,6 +18,9 @@ This is an extension to Azure CLI to manage ElasticSan resources.
`az elastic-san show -g {rg} -n {san_name}`
### Update an Elastic SAN.
`az elastic-san update -n {san_name} -g {rg} --tags "{key1710:bbbb}" --base-size-tib 25 --extended-capacity-size-tib 15`
`--sku` and `--availability-zones` are also accepted for compatibility. When either is supplied, the command reads the existing resource and uses the API's PUT (create-or-replace) operation because these fields are not part of the PATCH update contract.
SAN and volume-group updates support `--add`, `--set`, `--remove`, and `--force-string`. Custom code reads the resource, applies explicit options followed by generic updates, and sends writable PATCH fields (or PUT for the legacy SAN options above). This compatibility flow reuses the generated show operations and survives regeneration of the PATCH commands.
Volume-group generic updates to `encryptionInTransit` and `qualityOfService` are rejected before sending a request because these fields are not supported by the PATCH contract.

## Volume Group
### Create a Volume Group.
Expand All @@ -32,6 +37,7 @@ This is an extension to Azure CLI to manage ElasticSan resources.
## Volume
### Create a Volume.
`az elastic-san volume create -g {rg} -e {san_name} -v {vg_name} -n {volume_name} --size-gib 2`
Volume create and update accept `--managed-by` as a legacy single-resource object or the current array of objects. Specify each field only once per object: aliases such as `resource-id` and `resource-ids` (including camelCase and snake_case spellings) cannot be combined.
### Delete a Volume.
`az elastic-san volume delete -g {rg} -e {san_name} -v {vg_name} -n {volume_name}`
### List Volumes in a Volume Group.
Expand All @@ -42,6 +48,8 @@ This is an extension to Azure CLI to manage ElasticSan resources.
`az elastic-san volume update -g {rg} -e {san_name} -v {vg_name} -n {volume_name} --size-gib 3`

## Snapshot case
Snapshots support create, show, list, delete, and wait. Snapshot update/PATCH is not supported.

### Create an ElasticSAN
`az elastic-san create -n "san_name" -g "rg" --tags '{key1810:aaaa}' -l eastus2euap --base-size-tib 23 --extended-capacity-size-tib 14 --sku '{name:Premium_LRS,tier:Premium}'`
### Create Vnet
Expand Down Expand Up @@ -85,6 +93,7 @@ This is an extension to Azure CLI to manage ElasticSan resources.
`az elastic-san volume-group update -e "san_name" -n "vg_name" -g "rg" --encryption EncryptionAtRestWithCustomerManagedKey --encryption-properties '{key-vault-properties:{key-name:"key_name",key-vault-uri:"vault_uri"}}'`

## Customer Managed Key User Assigned Identity scenario
The integration scenario reconfigures identities through platform-managed encryption: switch to platform-managed keys, clear the old identity, then assign the new identity and re-enable customer-managed encryption. It does not test uninterrupted CMK identity rotation. The identity-clear and reassignment sequence requires live validation against the target service.
#### Create an ElasticSAN
`az elastic-san create -n "san_name" -g "rg" --tags '{key1810:aaaa}' -l eastus2euap --base-size-tib 23 --extended-capacity-size-tib 14 --sku '{name:Premium_LRS,tier:Premium}'`
### 1. Create a user assigned identity and grant it the access to the key vault
Expand Down
55 changes: 43 additions & 12 deletions src/elastic-san/azext_elastic_san/aaz/latest/elastic_san/_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ class Create(AAZCommand):
"""

_aaz_info = {
"version": "2025-09-01",
"version": "2026-05-01-preview",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High Generated change requires durable Azure/aaz source

The structured ownership check found no open or merged Azure/aaz PR containing this command-model change. Make and link that durable source change, then regenerate the downstream output. Verify regeneration from a clean checkout reproduces the diff and the linked source PR is open or merged.

X Engineering Agent · Reviewer · React with +1 or -1 to train the reviewer

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Azure/aaz#1119

This is the PR

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High Link the durable generated command-model source

The deterministic ownership check found generated output changes without an open or merged Azure/aaz PR containing the durable command-model change. Make that source change, link its PR, and regenerate aaz/. Verify from a clean checkout that regeneration reproduces this change and the source PR is open or merged.

X Engineering Agent · Reviewer · React with +1 or -1 to train the reviewer

"resources": [
["mgmt-plane", "/subscriptions/{}/resourcegroups/{}/providers/microsoft.elasticsan/elasticsans/{}", "2025-09-01"],
["mgmt-plane", "/subscriptions/{}/resourcegroups/{}/providers/microsoft.elasticsan/elasticsans/{}", "2026-05-01-preview"],
]
}

Expand Down Expand Up @@ -119,14 +119,12 @@ def _build_arguments_schema(cls, *args, **kwargs):
options=["--base-size-tib"],
arg_group="Properties",
help="Base size of the Elastic San appliance in TiB.",
required=True,
default=20,
)
_args_schema.extended_capacity_size_tib = AAZIntArg(
options=["--extended-size", "--extended-capacity-size-tib"],
arg_group="Properties",
help="Extended size of the Elastic San appliance in TiB.",
required=True,
default=0,
)
_args_schema.public_network_access = AAZStrArg(
Expand All @@ -141,6 +139,27 @@ def _build_arguments_schema(cls, *args, **kwargs):
help="resource sku",
required=True,
)
_args_schema.total_iops = AAZIntArg(
options=["--total-iops"],
arg_group="Properties",
help="Total Provisioned IOPS of the Elastic San appliance. Settable only for ElasticSanVersion V2, where it is required; read-only for V1.",
)
_args_schema.total_mbps = AAZIntArg(
options=["--total-mbps"],
arg_group="Properties",
help="Total Provisioned MBps Elastic San appliance. Settable only for ElasticSanVersion V2, where it is required; read-only for V1.",
)
_args_schema.total_size_tib = AAZIntArg(
options=["--total-size-tib"],
arg_group="Properties",
help="Total size of the Elastic San appliance in TiB. Settable only for ElasticSanVersion V2, where it is required; read-only for V1.",
)
_args_schema.version = AAZStrArg(
options=["--version"],
arg_group="Properties",
help="Elastic San appliance version. Defaults to V1 if not specified.",
enum={"V1": "V1", "V2": "V2"},
Comment thread
amnguye marked this conversation as resolved.
Resolved
)

availability_zones = cls._args_schema.availability_zones
availability_zones.Element = AAZStrArg()
Expand All @@ -150,7 +169,7 @@ def _build_arguments_schema(cls, *args, **kwargs):
options=["name"],
help="The sku name.",
required=True,
enum={"Premium_LRS": "Premium_LRS", "Premium_ZRS": "Premium_ZRS"},
enum={"ElasticSAN_LRS": "ElasticSAN_LRS", "Premium_LRS": "Premium_LRS", "Premium_ZRS": "Premium_ZRS"},
)
sku.tier = AAZStrArg(
options=["tier"],
Expand Down Expand Up @@ -240,7 +259,7 @@ def url_parameters(self):
def query_parameters(self):
parameters = {
**self.serialize_query_param(
"api-version", "2025-09-01",
"api-version", "2026-05-01-preview",
required=True,
),
}
Expand Down Expand Up @@ -273,10 +292,14 @@ def content(self):
if properties is not None:
properties.set_prop("autoScaleProperties", AAZObjectType)
properties.set_prop("availabilityZones", AAZListType, ".availability_zones")
properties.set_prop("baseSizeTiB", AAZIntType, ".base_size_tib", typ_kwargs={"flags": {"required": True}})
properties.set_prop("extendedCapacitySizeTiB", AAZIntType, ".extended_capacity_size_tib", typ_kwargs={"flags": {"required": True}})
properties.set_prop("baseSizeTiB", AAZIntType, ".base_size_tib")
properties.set_prop("extendedCapacitySizeTiB", AAZIntType, ".extended_capacity_size_tib")
properties.set_prop("publicNetworkAccess", AAZStrType, ".public_network_access")
properties.set_prop("sku", AAZObjectType, ".sku", typ_kwargs={"flags": {"required": True}})
properties.set_prop("totalIops", AAZIntType, ".total_iops")
properties.set_prop("totalMBps", AAZIntType, ".total_mbps")
properties.set_prop("totalSizeTiB", AAZIntType, ".total_size_tib")
properties.set_prop("version", AAZStrType, ".version")

auto_scale_properties = _builder.get(".properties.autoScaleProperties")
if auto_scale_properties is not None:
Expand Down Expand Up @@ -353,11 +376,9 @@ def _build_schema_on_200_201(cls):
)
properties.base_size_ti_b = AAZIntType(
serialized_name="baseSizeTiB",
flags={"required": True},
)
properties.extended_capacity_size_ti_b = AAZIntType(
serialized_name="extendedCapacitySizeTiB",
flags={"required": True},
)
properties.private_endpoint_connections = AAZListType(
serialized_name="privateEndpointConnections",
Expand All @@ -375,20 +396,30 @@ def _build_schema_on_200_201(cls):
)
properties.total_iops = AAZIntType(
serialized_name="totalIops",
flags={"read_only": True},
)
properties.total_m_bps = AAZIntType(
serialized_name="totalMBps",
)
properties.total_reserved_iops = AAZIntType(
serialized_name="totalReservedIops",
flags={"read_only": True},
)
properties.total_reserved_m_bps = AAZIntType(
serialized_name="totalReservedMBps",
flags={"read_only": True},
)
properties.total_size_ti_b = AAZIntType(
serialized_name="totalSizeTiB",
flags={"read_only": True},
)
properties.total_volume_size_gi_b = AAZIntType(
serialized_name="totalVolumeSizeGiB",
flags={"read_only": True},
)
properties.used_capacity_gi_b = AAZIntType(
serialized_name="usedCapacityGiB",
flags={"read_only": True},
)
properties.version = AAZStrType()
properties.volume_group_count = AAZIntType(
serialized_name="volumeGroupCount",
flags={"read_only": True},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ class Delete(AAZCommand):
"""

_aaz_info = {
"version": "2025-09-01",
"version": "2026-05-01-preview",
"resources": [
["mgmt-plane", "/subscriptions/{}/resourcegroups/{}/providers/microsoft.elasticsan/elasticsans/{}", "2025-09-01"],
["mgmt-plane", "/subscriptions/{}/resourcegroups/{}/providers/microsoft.elasticsan/elasticsans/{}", "2026-05-01-preview"],
]
}

Expand Down Expand Up @@ -148,7 +148,7 @@ def url_parameters(self):
def query_parameters(self):
parameters = {
**self.serialize_query_param(
"api-version", "2025-09-01",
"api-version", "2026-05-01-preview",
required=True,
),
}
Expand Down
42 changes: 29 additions & 13 deletions src/elastic-san/azext_elastic_san/aaz/latest/elastic_san/_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ class List(AAZCommand):
"""

_aaz_info = {
"version": "2025-09-01",
"version": "2026-05-01-preview",
"resources": [
["mgmt-plane", "/subscriptions/{}/providers/microsoft.elasticsan/elasticsans", "2025-09-01"],
["mgmt-plane", "/subscriptions/{}/resourcegroups/{}/providers/microsoft.elasticsan/elasticsans", "2025-09-01"],
["mgmt-plane", "/subscriptions/{}/providers/microsoft.elasticsan/elasticsans", "2026-05-01-preview"],
["mgmt-plane", "/subscriptions/{}/resourcegroups/{}/providers/microsoft.elasticsan/elasticsans", "2026-05-01-preview"],
]
}

Expand Down Expand Up @@ -112,7 +112,7 @@ def url_parameters(self):
def query_parameters(self):
parameters = {
**self.serialize_query_param(
"api-version", "2025-09-01",
"api-version", "2026-05-01-preview",
required=True,
),
}
Expand Down Expand Up @@ -187,11 +187,9 @@ def _build_schema_on_200(cls):
)
properties.base_size_ti_b = AAZIntType(
serialized_name="baseSizeTiB",
flags={"required": True},
)
properties.extended_capacity_size_ti_b = AAZIntType(
serialized_name="extendedCapacitySizeTiB",
flags={"required": True},
)
properties.private_endpoint_connections = AAZListType(
serialized_name="privateEndpointConnections",
Expand All @@ -209,20 +207,30 @@ def _build_schema_on_200(cls):
)
properties.total_iops = AAZIntType(
serialized_name="totalIops",
flags={"read_only": True},
)
properties.total_m_bps = AAZIntType(
serialized_name="totalMBps",
)
properties.total_reserved_iops = AAZIntType(
serialized_name="totalReservedIops",
flags={"read_only": True},
)
properties.total_reserved_m_bps = AAZIntType(
serialized_name="totalReservedMBps",
flags={"read_only": True},
)
properties.total_size_ti_b = AAZIntType(
serialized_name="totalSizeTiB",
flags={"read_only": True},
)
properties.total_volume_size_gi_b = AAZIntType(
serialized_name="totalVolumeSizeGiB",
flags={"read_only": True},
)
properties.used_capacity_gi_b = AAZIntType(
serialized_name="usedCapacityGiB",
flags={"read_only": True},
)
properties.version = AAZStrType()
properties.volume_group_count = AAZIntType(
serialized_name="volumeGroupCount",
flags={"read_only": True},
Expand Down Expand Up @@ -358,7 +366,7 @@ def url_parameters(self):
def query_parameters(self):
parameters = {
**self.serialize_query_param(
"api-version", "2025-09-01",
"api-version", "2026-05-01-preview",
required=True,
),
}
Expand Down Expand Up @@ -433,11 +441,9 @@ def _build_schema_on_200(cls):
)
properties.base_size_ti_b = AAZIntType(
serialized_name="baseSizeTiB",
flags={"required": True},
)
properties.extended_capacity_size_ti_b = AAZIntType(
serialized_name="extendedCapacitySizeTiB",
flags={"required": True},
)
properties.private_endpoint_connections = AAZListType(
serialized_name="privateEndpointConnections",
Expand All @@ -455,20 +461,30 @@ def _build_schema_on_200(cls):
)
properties.total_iops = AAZIntType(
serialized_name="totalIops",
flags={"read_only": True},
)
properties.total_m_bps = AAZIntType(
serialized_name="totalMBps",
)
properties.total_reserved_iops = AAZIntType(
serialized_name="totalReservedIops",
flags={"read_only": True},
)
properties.total_reserved_m_bps = AAZIntType(
serialized_name="totalReservedMBps",
flags={"read_only": True},
)
properties.total_size_ti_b = AAZIntType(
serialized_name="totalSizeTiB",
flags={"read_only": True},
)
properties.total_volume_size_gi_b = AAZIntType(
serialized_name="totalVolumeSizeGiB",
flags={"read_only": True},
)
properties.used_capacity_gi_b = AAZIntType(
serialized_name="usedCapacityGiB",
flags={"read_only": True},
)
properties.version = AAZStrType()
properties.volume_group_count = AAZIntType(
serialized_name="volumeGroupCount",
flags={"read_only": True},
Expand Down
Loading
Loading