Skip to content
Merged
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
145 changes: 145 additions & 0 deletions docs/extensions/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ The following documents describes AutoRest specific vendor extensions for [OpenA
- [x-ms-azure-resource](#x-ms-azure-resource) - indicates that the [Definition Schema Object](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#schemaObject) is a resource as defined by the [Resource Manager API](https://msdn.microsoft.com/en-us/library/azure/dn790568.aspx)
- [x-ms-request-id](#x-ms-request-id) - allows to overwrite the request id header name
- [x-ms-client-request-id](#x-ms-client-request-id) - allows to overwrite the client request id header name
- [x-ms-arm-id-details](#x-ms-arm-id-details) - indicates the allowed resources that can be referred to by an `arm-id` formatted string field.

### Metadata extensions

Expand Down Expand Up @@ -1192,6 +1193,150 @@ When set, specifies the header parameter to be used instead of `x-ms-client-requ
}
```

## x-ms-arm-id-details

Can only be set on `"type": "string"` fields with `"format": "arm-id"`.

When set, specifies the set of resource types which can be referenced by this `arm-id`. If this extension isn't provided for a particular `arm-id`, the field can refer to any valid ARM ID.

**Parent element**: [Parameter Object](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#parameterObject), [Schema Object](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#schemaObject), or [Items Object](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#itemsObject)

### Schema

| Field Name | Type | Description |
| ---------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| allowedResources | `[AllowedResource]` | **Required** An array of allowed ARM resources. Each element represents a particular type of ARM resource which can be referred to by this `arm-id`. |

**AllowedResource schema**:

| Field Name | Type | Description |
| ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| scopes | `[string]` | An array of scopes. See [Allowed Scopes](#allowed-scopes). If not specified, the default scope is `["ResourceGroup"]`. |
| type | `string` | **Required** The type of resource that is being referred to. For example `Microsoft.Network/virtualNetworks` or `Microsoft.Network/virtualNetworks/subnets`. See [Example Types](#example-types) for more examples. |

#### Allowed Scopes
Copy link
Member

Choose a reason for hiding this comment

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

What are the thoughts on how scopes should be interpreted? Does this apply a restriction to the same tenant / subscription / resource group / management group as the referring resource, or something else?

I worry about making this too deployment-specific. It would be nice to apply this in general to encode actual 'locality' restrictions that could also be used both for smart tools to validate an input reference or for for smart tools to deploy the resource. One such restriction would be 'location', for resources that must be in the same region as the referring resource.

Copy link
Member Author

Choose a reason for hiding this comment

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

What are the thoughts on how scopes should be interpreted? Does this apply a restriction to the same tenant / subscription / resource group / management group as the referring resource, or something else?

I hadn't thought about "the referenced resource must be in the same region/tenant/subscription as the current resource". That's an interesting angle and we could definitely consider expanding this proposal to cover that case as well. Scope as proposed is about the ability to restrict what deployment locations are allowed for the given resource type.

So you would read {"scopes": ["ResourceGroup"], "type": "Microsoft.Network/virtualNetworks"} as "This property must refer to an ARM ID pointing to a virtual network rooted in a resource group". There's no requirement of what subscription that RG is in. If you were to validate this ARM ID with a regex it would look something like /subscriptions/[a-zA-Z0-0-]+/resourceGroups/[a-zA-Z0-0-]+/Microsoft.Network/virtualNetworks/[a-zA-Z0-0-]+. This reference explicitly disallows a Microsoft.Network/virtualNetworks deployed elsewhere, say in a management group (That's probably not allowed today but some resources are dual-homed I believe).

Services that don't care can use ``{"scopes": ["*"], "type": "Microsoft.Network/virtualNetworks"}. This is the same concept of scope` that is proposed/discussed in @majastrz's document as well. It's really just a shorthand for `/subscriptions/{}/resourceGroups/{}` (and the various other scope prefixes).

Copy link
Member

Choose a reason for hiding this comment

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

So, management groups typically sit 'above' subscriptions, so, unless I am wildly mistaken, you can only deploy a virtual network to a resource group. Management Groups are a good deployment location for extension resources, but don't apply to resources that are Tracked Resources or Subscription resources, or resource group resources.

I guess here, you want to be able to deploy the resource without having to know anything about the properties of the resource type, is that correct?

Copy link
Member Author

Choose a reason for hiding this comment

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

I guess here, you want to be able to deploy the resource without having to know anything about the properties of the resource type, is that correct?

Yes, exactly. Maybe I need to do some documentation improvements (or we need to pick a different name than Scope?). As discussed over teams the issue is that this is a reference to a say Microsoft.Network/virtualnetworks, and at the reference site there's no knowledge of what that thing looks like. The objective for this annotation would be that client tools could give better hints/warnings about ARM references, so ideally given this annotation we can look at an ARM ID and confirm if it's valid. Without a scope section though we couldn't confirm that:
/subscriptions/<id>/Microsoft.Network/virtualNetworks/myVnet was an invalid reference, because it's not at the right scope.

Copy link
Member

Choose a reason for hiding this comment

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

I would say, this is good for the first version, and perhaps we can augment this later with some restrictions around the resource (for example, that it has to be in the same region,


The following values are allowed for `scopes`. These values were derived from the [scope field in ARM templates](https://docs.microsoft.com/azure/azure-resource-manager/templates/scope-extension-resources?tabs=azure-cli).
| Scope | URL prefix | Meaning |
| ----------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Tenant` | `/` | The resource is deployed into a tenant |
| `Subscription` | `/subscriptions/{subscriptionId}/` | The resource is deployed into a subscription |
| `ResourceGroup` | `/subscriptions/{subscriptionId}/resourceGroups/{group}` | The resource is deployed into a resource group |
| `ManagementGroup` | `/providers/Microsoft.Management/managementGroups/{managementGroupName}/` | The resource is deployed into a management group |
| `Extension` | `{parentScope}/providers/{extensionNamespace}/{extensionType}/{extensionName}/` | The resource is an extension resource and may be deployed as a subresource of another resource. `parentScope` may be a resource in any of the above scopes. |
| `*` | Any of the above | The resource may be deployed into any of the above scopes. This is identical to `["Tenant", "Subscription", "ResourceGroup", "ManagementGroup", "Extension"`] |

#### Example Types

Below is a table showing an example entry for various different kinds of resource types

| Resource kind | Example |
| -------------------------------------- | ---------------------------------------------------------------------------- |
| Resource in a tenant | `{"scopes": ["Tenant"], "type": "Microsoft.Capacity/reservationOrders"}` |
| Resource in a subscription | `{"scopes": ["Subscription"], "type": "Microsoft.Resources/resourceGroups"}` |
| Resource in a resource group | `{"scopes": ["ResourceGroup"], "type": "Microsoft.Network/virtualNetworks"}` |
| Resource in a management group | `{"scopes": ["ManagementGroup"], "type": "Microsoft.Blueprint/blueprints"}` |
| Extension resource | `{"scopes": ["Extension"], "type": "Microsoft.Authorization/locks"}` |
| Any resource in resource group | `{"scopes": ["ResourceGroup"], "type": "*"}` |
| Any compute resource in resource group | `{"scopes": ["ResourceGroup"], "type": "Microsoft.Compute/*"}` |

Sub-resources are specified in the same manner as their parent resource but with additional paths on the end. For example to refer to a subnet: `Microsoft.Network/virtualNetworks/subnets`.

**Note** that we do not currently support limiting references to an extension resource by the kind of resource it is on. For example you can refer to _any_ resource lock (`Microsoft.Authorization/locks`) but not to a resource lock but only when it's on a CosmosDB.

#### Examples

**Example**: An `arm-id` field that can refer to any ARM resource ID.

```json5
"MyExampleType": {
"properties": {
"id": {
"type": "string",
"format": "arm-id"
}
}
}
```

**Example**: An `arm-id` field that must refer to a virtual network

```json5
"MyExampleType": {
"properties": {
"vnetId": {
"type": "string",
"format": "arm-id",
"x-ms-arm-id-details": {
"allowedResources": [
{
"type": "Microsoft.Network/virtualNetworks"
}
]
}
}
}
}
```

**Example (preferred)**: An `arm-id` field with no additional information about what kind of resource it must refer to, referring to the common type.

```json5
"MyExampleType": {
"properties": {
"id": {
"$ref": "../../../../../common-types/resource-management/v2/types.json#/definitions/ArmId",
Copy link
Contributor

Choose a reason for hiding this comment

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

what's the common type 'ArmId' for ?

Copy link
Member Author

Choose a reason for hiding this comment

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

@johanste mentioned that he thought it would be useful to have a shared type in the common defs (like we do for Resource and a few other types today).

}
}
}
```

**Example (preferred)**: An `arm-id` field that must refer to a virtual network, via a referenced definition

```json5
"MyExampleType": {
"properties": {
"vnetId": {
"$ref": "#/definitions/VNetId",
}
}
},
"VNetId": {
"type": "string",
"format": "arm-id",
"x-ms-arm-id-details": {
"allowedResources": [
{
"type": "Microsoft.Network/virtualNetworks"
}
]
}
}
```

**Example**: An array of `arm-id`'s that refer to a subnet

```json5
"MyExampleType": {
"properties": {
"vnets": {
"type": "array",
"items": {
"type": "string",
"format": "arm-id",
"x-ms-arm-id-details": {
"allowedResources": [
{
"type": "Microsoft.Network/virtualNetworks/subnets"
}
]
}
}
}
}
}
```

## x-nullable

Set `"x-nullable": true` on a schema to indicate that a `null` is a legal value. By default, a `null` value should be disallowed when forming a request and rejected during payload deserialization.
Expand Down