From 77907bb0dfbcb970b365f6ee2117a21e5731ae9e Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Mon, 20 Jan 2025 22:02:44 +0100 Subject: [PATCH] feat: Add possibility to specify CORS rules for file services & queue. (#4117) ## Description - Added support for CORSRules to File & Queue Services - Added test cases - Added UDT for CORSRules - Updated current implementation of CORSRules in Storage Accout BlobServices to avoid overwriting current settings by default (tested) > Re-Based from PR : #3862 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.storage.storage-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg?branch=users%2Falsehr%2Fheadcr4sh_feature%2F3861_rebase&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --------- Co-authored-by: Benjamin P. Jung Co-authored-by: Benjamin P. Jung --- avm/res/storage/storage-account/README.md | 276 ++++++++++++++++ .../storage-account/blob-service/README.md | 75 ++++- .../storage-account/blob-service/main.bicep | 35 +- .../storage-account/blob-service/main.json | 73 ++++- .../storage-account/file-service/README.md | 68 ++++ .../storage-account/file-service/main.bicep | 31 ++ .../storage-account/file-service/main.json | 73 ++++- avm/res/storage/storage-account/main.bicep | 3 + avm/res/storage/storage-account/main.json | 309 +++++++++++++++++- .../storage-account/queue-service/README.md | 68 ++++ .../storage-account/queue-service/main.bicep | 34 +- .../storage-account/queue-service/main.json | 76 ++++- .../storage-account/table-service/README.md | 68 ++++ .../storage-account/table-service/main.bicep | 34 +- .../storage-account/table-service/main.json | 76 ++++- .../tests/e2e/max/main.test.bicep | 96 +++++- 16 files changed, 1358 insertions(+), 37 deletions(-) diff --git a/avm/res/storage/storage-account/README.md b/avm/res/storage/storage-account/README.md index a89d996c3d..045fedd330 100644 --- a/avm/res/storage/storage-account/README.md +++ b/avm/res/storage/storage-account/README.md @@ -507,6 +507,29 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { publicAccess: 'None' } ] + corsRules: [ + { + allowedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + allowedMethods: [ + 'GET' + 'PUT' + ] + allowedOrigins: [ + 'http://*.contoso.com' + 'http://www.fabrikam.com' + ] + exposedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + maxAgeInSeconds: 200 + } + ] deleteRetentionPolicyDays: 9 deleteRetentionPolicyEnabled: true diagnosticSettings: [ @@ -543,6 +566,29 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { enableNfsV3: true enableSftp: true fileServices: { + corsRules: [ + { + allowedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + allowedMethods: [ + 'GET' + 'PUT' + ] + allowedOrigins: [ + 'http://*.contoso.com' + 'http://www.fabrikam.com' + ] + exposedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + maxAgeInSeconds: 200 + } + ] diagnosticSettings: [ { eventHubAuthorizationRuleResourceId: '' @@ -757,6 +803,29 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { } ] queueServices: { + corsRules: [ + { + allowedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + allowedMethods: [ + 'GET' + 'PUT' + ] + allowedOrigins: [ + 'http://*.contoso.com' + 'http://www.fabrikam.com' + ] + exposedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + maxAgeInSeconds: 200 + } + ] diagnosticSettings: [ { eventHubAuthorizationRuleResourceId: '' @@ -825,6 +894,29 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { sasExpirationPeriod: '180.00:00:00' skuName: 'Standard_LRS' tableServices: { + corsRules: [ + { + allowedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + allowedMethods: [ + 'GET' + 'PUT' + ] + allowedOrigins: [ + 'http://*.contoso.com' + 'http://www.fabrikam.com' + ] + exposedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + maxAgeInSeconds: 200 + } + ] diagnosticSettings: [ { eventHubAuthorizationRuleResourceId: '' @@ -949,6 +1041,29 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { "publicAccess": "None" } ], + "corsRules": [ + { + "allowedHeaders": [ + "x-ms-meta-data", + "x-ms-meta-source-path", + "x-ms-meta-target-path" + ], + "allowedMethods": [ + "GET", + "PUT" + ], + "allowedOrigins": [ + "http://*.contoso.com", + "http://www.fabrikam.com" + ], + "exposedHeaders": [ + "x-ms-meta-data", + "x-ms-meta-source-path", + "x-ms-meta-target-path" + ], + "maxAgeInSeconds": 200 + } + ], "deleteRetentionPolicyDays": 9, "deleteRetentionPolicyEnabled": true, "diagnosticSettings": [ @@ -995,6 +1110,29 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { }, "fileServices": { "value": { + "corsRules": [ + { + "allowedHeaders": [ + "x-ms-meta-data", + "x-ms-meta-source-path", + "x-ms-meta-target-path" + ], + "allowedMethods": [ + "GET", + "PUT" + ], + "allowedOrigins": [ + "http://*.contoso.com", + "http://www.fabrikam.com" + ], + "exposedHeaders": [ + "x-ms-meta-data", + "x-ms-meta-source-path", + "x-ms-meta-target-path" + ], + "maxAgeInSeconds": 200 + } + ], "diagnosticSettings": [ { "eventHubAuthorizationRuleResourceId": "", @@ -1227,6 +1365,29 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { }, "queueServices": { "value": { + "corsRules": [ + { + "allowedHeaders": [ + "x-ms-meta-data", + "x-ms-meta-source-path", + "x-ms-meta-target-path" + ], + "allowedMethods": [ + "GET", + "PUT" + ], + "allowedOrigins": [ + "http://*.contoso.com", + "http://www.fabrikam.com" + ], + "exposedHeaders": [ + "x-ms-meta-data", + "x-ms-meta-source-path", + "x-ms-meta-target-path" + ], + "maxAgeInSeconds": 200 + } + ], "diagnosticSettings": [ { "eventHubAuthorizationRuleResourceId": "", @@ -1305,6 +1466,29 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { }, "tableServices": { "value": { + "corsRules": [ + { + "allowedHeaders": [ + "x-ms-meta-data", + "x-ms-meta-source-path", + "x-ms-meta-target-path" + ], + "allowedMethods": [ + "GET", + "PUT" + ], + "allowedOrigins": [ + "http://*.contoso.com", + "http://www.fabrikam.com" + ], + "exposedHeaders": [ + "x-ms-meta-data", + "x-ms-meta-source-path", + "x-ms-meta-target-path" + ], + "maxAgeInSeconds": 200 + } + ], "diagnosticSettings": [ { "eventHubAuthorizationRuleResourceId": "", @@ -1425,6 +1609,29 @@ param blobServices = { publicAccess: 'None' } ] + corsRules: [ + { + allowedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + allowedMethods: [ + 'GET' + 'PUT' + ] + allowedOrigins: [ + 'http://*.contoso.com' + 'http://www.fabrikam.com' + ] + exposedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + maxAgeInSeconds: 200 + } + ] deleteRetentionPolicyDays: 9 deleteRetentionPolicyEnabled: true diagnosticSettings: [ @@ -1461,6 +1668,29 @@ param enableHierarchicalNamespace = true enableNfsV3: true param enableSftp = true param fileServices = { + corsRules: [ + { + allowedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + allowedMethods: [ + 'GET' + 'PUT' + ] + allowedOrigins: [ + 'http://*.contoso.com' + 'http://www.fabrikam.com' + ] + exposedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + maxAgeInSeconds: 200 + } + ] diagnosticSettings: [ { eventHubAuthorizationRuleResourceId: '' @@ -1675,6 +1905,29 @@ param privateEndpoints = [ } ] param queueServices = { + corsRules: [ + { + allowedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + allowedMethods: [ + 'GET' + 'PUT' + ] + allowedOrigins: [ + 'http://*.contoso.com' + 'http://www.fabrikam.com' + ] + exposedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + maxAgeInSeconds: 200 + } + ] diagnosticSettings: [ { eventHubAuthorizationRuleResourceId: '' @@ -1743,6 +1996,29 @@ param roleAssignments = [ param sasExpirationPeriod = '180.00:00:00' param skuName = 'Standard_LRS' param tableServices = { + corsRules: [ + { + allowedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + allowedMethods: [ + 'GET' + 'PUT' + ] + allowedOrigins: [ + 'http://*.contoso.com' + 'http://www.fabrikam.com' + ] + exposedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-source-path' + 'x-ms-meta-target-path' + ] + maxAgeInSeconds: 200 + } + ] diagnosticSettings: [ { eventHubAuthorizationRuleResourceId: '' diff --git a/avm/res/storage/storage-account/blob-service/README.md b/avm/res/storage/storage-account/blob-service/README.md index 81fe6cad98..f2504c36fb 100644 --- a/avm/res/storage/storage-account/blob-service/README.md +++ b/avm/res/storage/storage-account/blob-service/README.md @@ -38,7 +38,7 @@ This module deploys a Storage Account Blob Service. | [`containerDeleteRetentionPolicyDays`](#parameter-containerdeleteretentionpolicydays) | int | Indicates the number of days that the deleted item should be retained. | | [`containerDeleteRetentionPolicyEnabled`](#parameter-containerdeleteretentionpolicyenabled) | bool | The blob service properties for container soft delete. Indicates whether DeleteRetentionPolicy is enabled. | | [`containers`](#parameter-containers) | array | Blob containers to create. | -| [`corsRules`](#parameter-corsrules) | array | Specifies CORS rules for the Blob service. You can include up to five CorsRule elements in the request. If no CorsRule elements are included in the request body, all CORS rules will be deleted, and CORS will be disabled for the Blob service. | +| [`corsRules`](#parameter-corsrules) | array | The List of CORS rules. You can include up to five CorsRule elements in the request. | | [`defaultServiceVersion`](#parameter-defaultserviceversion) | string | Indicates the default version to use for requests to the Blob service if an incoming request's version is not specified. Possible values include version 2008-10-27 and all more recent versions. | | [`deleteRetentionPolicyAllowPermanentDelete`](#parameter-deleteretentionpolicyallowpermanentdelete) | bool | This property when set to true allows deletion of the soft deleted blob versions and snapshots. This property cannot be used with blob restore policy. This property only applies to blob service and does not apply to containers or file share. | | [`deleteRetentionPolicyDays`](#parameter-deleteretentionpolicydays) | int | Indicates the number of days that the deleted blob should be retained. | @@ -121,11 +121,80 @@ Blob containers to create. ### Parameter: `corsRules` -Specifies CORS rules for the Blob service. You can include up to five CorsRule elements in the request. If no CorsRule elements are included in the request body, all CORS rules will be deleted, and CORS will be disabled for the Blob service. +The List of CORS rules. You can include up to five CorsRule elements in the request. - Required: No - Type: array -- Default: `[]` +- MinValue: 1 +- MaxValue: 365 + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowedHeaders`](#parameter-corsrulesallowedheaders) | array | A list of headers allowed to be part of the cross-origin request. | +| [`allowedMethods`](#parameter-corsrulesallowedmethods) | array | A list of HTTP methods that are allowed to be executed by the origin. | +| [`allowedOrigins`](#parameter-corsrulesallowedorigins) | array | A list of origin domains that will be allowed via CORS, or "*" to allow all domains. | +| [`exposedHeaders`](#parameter-corsrulesexposedheaders) | array | A list of response headers to expose to CORS clients. | +| [`maxAgeInSeconds`](#parameter-corsrulesmaxageinseconds) | int | The number of seconds that the client/browser should cache a preflight response. | + +### Parameter: `corsRules.allowedHeaders` + +A list of headers allowed to be part of the cross-origin request. + +- Required: Yes +- Type: array +- MinValue: 1 +- MaxValue: 365 + +### Parameter: `corsRules.allowedMethods` + +A list of HTTP methods that are allowed to be executed by the origin. + +- Required: Yes +- Type: array +- Allowed: + ```Bicep + [ + 'CONNECT' + 'DELETE' + 'GET' + 'HEAD' + 'MERGE' + 'OPTIONS' + 'PATCH' + 'POST' + 'PUT' + 'TRACE' + ] + ``` +- MinValue: 1 +- MaxValue: 365 + +### Parameter: `corsRules.allowedOrigins` + +A list of origin domains that will be allowed via CORS, or "*" to allow all domains. + +- Required: Yes +- Type: array +- MinValue: 1 +- MaxValue: 365 + +### Parameter: `corsRules.exposedHeaders` + +A list of response headers to expose to CORS clients. + +- Required: Yes +- Type: array +- MinValue: 1 +- MaxValue: 365 + +### Parameter: `corsRules.maxAgeInSeconds` + +The number of seconds that the client/browser should cache a preflight response. + +- Required: Yes +- Type: int - MinValue: 1 - MaxValue: 365 diff --git a/avm/res/storage/storage-account/blob-service/main.bicep b/avm/res/storage/storage-account/blob-service/main.bicep index ac40903859..33c95333c8 100644 --- a/avm/res/storage/storage-account/blob-service/main.bicep +++ b/avm/res/storage/storage-account/blob-service/main.bicep @@ -28,8 +28,8 @@ param containerDeleteRetentionPolicyDays int? @description('Optional. This property when set to true allows deletion of the soft deleted blob versions and snapshots. This property cannot be used with blob restore policy. This property only applies to blob service and does not apply to containers or file share.') param containerDeleteRetentionPolicyAllowPermanentDelete bool = false -@description('Optional. Specifies CORS rules for the Blob service. You can include up to five CorsRule elements in the request. If no CorsRule elements are included in the request body, all CORS rules will be deleted, and CORS will be disabled for the Blob service.') -param corsRules array = [] +@description('Optional. The List of CORS rules. You can include up to five CorsRule elements in the request.') +param corsRules corsRuleType[]? @description('Optional. Indicates the default version to use for requests to the Blob service if an incoming request\'s version is not specified. Possible values include version 2008-10-27 and all more recent versions.') param defaultServiceVersion string = '' @@ -90,9 +90,11 @@ resource blobServices 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01 ? containerDeleteRetentionPolicyAllowPermanentDelete : null } - cors: { - corsRules: corsRules - } + cors: corsRules != null + ? { + corsRules: corsRules + } + : null defaultServiceVersion: !empty(defaultServiceVersion) ? defaultServiceVersion : null deleteRetentionPolicy: { enabled: deleteRetentionPolicyEnabled @@ -173,3 +175,26 @@ output resourceId string = blobServices.id @description('The name of the deployed blob service.') output resourceGroupName string = resourceGroup().name + +// =============== // +// Definitions // +// =============== // + +@export() +@description('The type for a cors rule.') +type corsRuleType = { + @description('Required. A list of headers allowed to be part of the cross-origin request.') + allowedHeaders: string[] + + @description('Required. A list of HTTP methods that are allowed to be executed by the origin.') + allowedMethods: ('CONNECT' | 'DELETE' | 'GET' | 'HEAD' | 'MERGE' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' | 'TRACE')[] + + @description('Required. A list of origin domains that will be allowed via CORS, or "*" to allow all domains.') + allowedOrigins: string[] + + @description('Required. A list of response headers to expose to CORS clients.') + exposedHeaders: string[] + + @description('Required. The number of seconds that the client/browser should cache a preflight response.') + maxAgeInSeconds: int +} diff --git a/avm/res/storage/storage-account/blob-service/main.json b/avm/res/storage/storage-account/blob-service/main.json index 71c57a51a4..dc14de34f9 100644 --- a/avm/res/storage/storage-account/blob-service/main.json +++ b/avm/res/storage/storage-account/blob-service/main.json @@ -6,13 +6,73 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "17622492193190468017" + "templateHash": "16945288904009561261" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service.", "owner": "Azure/module-maintainers" }, "definitions": { + "corsRuleType": { + "type": "object", + "properties": { + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of headers allowed to be part of the cross-origin request." + } + }, + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], + "metadata": { + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." + } + }, + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." + } + }, + "exposedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of response headers to expose to CORS clients." + } + }, + "maxAgeInSeconds": { + "type": "int", + "metadata": { + "description": "Required. The number of seconds that the client/browser should cache a preflight response." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cors rule." + } + }, "diagnosticSettingFullType": { "type": "object", "properties": { @@ -192,9 +252,12 @@ }, "corsRules": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/corsRuleType" + }, + "nullable": true, "metadata": { - "description": "Optional. Specifies CORS rules for the Blob service. You can include up to five CorsRule elements in the request. If no CorsRule elements are included in the request body, all CORS rules will be deleted, and CORS will be disabled for the Blob service." + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." } }, "defaultServiceVersion": { @@ -296,9 +359,7 @@ "days": "[parameters('containerDeleteRetentionPolicyDays')]", "allowPermanentDelete": "[if(equals(parameters('containerDeleteRetentionPolicyEnabled'), true()), parameters('containerDeleteRetentionPolicyAllowPermanentDelete'), null())]" }, - "cors": { - "corsRules": "[parameters('corsRules')]" - }, + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]", "defaultServiceVersion": "[if(not(empty(parameters('defaultServiceVersion'))), parameters('defaultServiceVersion'), null())]", "deleteRetentionPolicy": { "enabled": "[parameters('deleteRetentionPolicyEnabled')]", diff --git a/avm/res/storage/storage-account/file-service/README.md b/avm/res/storage/storage-account/file-service/README.md index c200d9b22d..33e0f1f519 100644 --- a/avm/res/storage/storage-account/file-service/README.md +++ b/avm/res/storage/storage-account/file-service/README.md @@ -30,6 +30,7 @@ This module deploys a Storage Account File Share Service. | Parameter | Type | Description | | :-- | :-- | :-- | +| [`corsRules`](#parameter-corsrules) | array | The List of CORS rules. You can include up to five CorsRule elements in the request. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`name`](#parameter-name) | string | The name of the file service. | | [`protocolSettings`](#parameter-protocolsettings) | object | Protocol settings for file service. | @@ -43,6 +44,73 @@ The name of the parent Storage Account. Required if the template is used in a st - Required: Yes - Type: string +### Parameter: `corsRules` + +The List of CORS rules. You can include up to five CorsRule elements in the request. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowedHeaders`](#parameter-corsrulesallowedheaders) | array | A list of headers allowed to be part of the cross-origin request. | +| [`allowedMethods`](#parameter-corsrulesallowedmethods) | array | A list of HTTP methods that are allowed to be executed by the origin. | +| [`allowedOrigins`](#parameter-corsrulesallowedorigins) | array | A list of origin domains that will be allowed via CORS, or "*" to allow all domains. | +| [`exposedHeaders`](#parameter-corsrulesexposedheaders) | array | A list of response headers to expose to CORS clients. | +| [`maxAgeInSeconds`](#parameter-corsrulesmaxageinseconds) | int | The number of seconds that the client/browser should cache a preflight response. | + +### Parameter: `corsRules.allowedHeaders` + +A list of headers allowed to be part of the cross-origin request. + +- Required: Yes +- Type: array + +### Parameter: `corsRules.allowedMethods` + +A list of HTTP methods that are allowed to be executed by the origin. + +- Required: Yes +- Type: array +- Allowed: + ```Bicep + [ + 'CONNECT' + 'DELETE' + 'GET' + 'HEAD' + 'MERGE' + 'OPTIONS' + 'PATCH' + 'POST' + 'PUT' + 'TRACE' + ] + ``` + +### Parameter: `corsRules.allowedOrigins` + +A list of origin domains that will be allowed via CORS, or "*" to allow all domains. + +- Required: Yes +- Type: array + +### Parameter: `corsRules.exposedHeaders` + +A list of response headers to expose to CORS clients. + +- Required: Yes +- Type: array + +### Parameter: `corsRules.maxAgeInSeconds` + +The number of seconds that the client/browser should cache a preflight response. + +- Required: Yes +- Type: int + ### Parameter: `diagnosticSettings` The diagnostic settings of the service. diff --git a/avm/res/storage/storage-account/file-service/main.bicep b/avm/res/storage/storage-account/file-service/main.bicep index e768491045..7efcb7b8da 100644 --- a/avm/res/storage/storage-account/file-service/main.bicep +++ b/avm/res/storage/storage-account/file-service/main.bicep @@ -18,6 +18,9 @@ param shareDeleteRetentionPolicy object = { days: 7 } +@description('Optional. The List of CORS rules. You can include up to five CorsRule elements in the request.') +param corsRules corsRuleType[]? + import { diagnosticSettingFullType } from 'br/public:avm/utl/types/avm-common-types:0.4.0' @description('Optional. The diagnostic settings of the service.') param diagnosticSettings diagnosticSettingFullType[]? @@ -35,6 +38,11 @@ resource fileServices 'Microsoft.Storage/storageAccounts/fileServices@2023-04-01 name: name parent: storageAccount properties: { + cors: corsRules != null + ? { + corsRules: corsRules + } + : null protocolSettings: protocolSettings shareDeleteRetentionPolicy: shareDeleteRetentionPolicy } @@ -93,3 +101,26 @@ output resourceId string = fileServices.id @description('The resource group of the deployed file share service.') output resourceGroupName string = resourceGroup().name + +// =============== // +// Definitions // +// =============== // + +@export() +@description('The type for a cors rule.') +type corsRuleType = { + @description('Required. A list of headers allowed to be part of the cross-origin request.') + allowedHeaders: string[] + + @description('Required. A list of HTTP methods that are allowed to be executed by the origin.') + allowedMethods: ('CONNECT' | 'DELETE' | 'GET' | 'HEAD' | 'MERGE' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' | 'TRACE')[] + + @description('Required. A list of origin domains that will be allowed via CORS, or "*" to allow all domains.') + allowedOrigins: string[] + + @description('Required. A list of response headers to expose to CORS clients.') + exposedHeaders: string[] + + @description('Required. The number of seconds that the client/browser should cache a preflight response.') + maxAgeInSeconds: int +} diff --git a/avm/res/storage/storage-account/file-service/main.json b/avm/res/storage/storage-account/file-service/main.json index e719cb4c8d..0ee8c35c63 100644 --- a/avm/res/storage/storage-account/file-service/main.json +++ b/avm/res/storage/storage-account/file-service/main.json @@ -6,13 +6,73 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "5821640068498605661" + "templateHash": "4806721167991273113" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service.", "owner": "Azure/module-maintainers" }, "definitions": { + "corsRuleType": { + "type": "object", + "properties": { + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of headers allowed to be part of the cross-origin request." + } + }, + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], + "metadata": { + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." + } + }, + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." + } + }, + "exposedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of response headers to expose to CORS clients." + } + }, + "maxAgeInSeconds": { + "type": "int", + "metadata": { + "description": "Required. The number of seconds that the client/browser should cache a preflight response." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cors rule." + } + }, "diagnosticSettingFullType": { "type": "object", "properties": { @@ -168,6 +228,16 @@ "description": "Optional. The service properties for soft delete." } }, + "corsRules": { + "type": "array", + "items": { + "$ref": "#/definitions/corsRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." + } + }, "diagnosticSettings": { "type": "array", "items": { @@ -198,6 +268,7 @@ "apiVersion": "2023-04-01", "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]", "properties": { + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]", "protocolSettings": "[parameters('protocolSettings')]", "shareDeleteRetentionPolicy": "[parameters('shareDeleteRetentionPolicy')]" } diff --git a/avm/res/storage/storage-account/main.bicep b/avm/res/storage/storage-account/main.bicep index 5aaa1c54b2..c0bc5da506 100644 --- a/avm/res/storage/storage-account/main.bicep +++ b/avm/res/storage/storage-account/main.bicep @@ -615,6 +615,7 @@ module storageAccount_fileServices 'file-service/main.bicep' = if (!empty(fileSe protocolSettings: fileServices.?protocolSettings shareDeleteRetentionPolicy: fileServices.?shareDeleteRetentionPolicy shares: fileServices.?shares + corsRules: queueServices.?corsRules } } @@ -625,6 +626,7 @@ module storageAccount_queueServices 'queue-service/main.bicep' = if (!empty(queu storageAccountName: storageAccount.name diagnosticSettings: queueServices.?diagnosticSettings queues: queueServices.?queues + corsRules: queueServices.?corsRules } } @@ -635,6 +637,7 @@ module storageAccount_tableServices 'table-service/main.bicep' = if (!empty(tabl storageAccountName: storageAccount.name diagnosticSettings: tableServices.?diagnosticSettings tables: tableServices.?tables + corsRules: tableServices.?corsRules } } diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index 6e57aa5e4d..d270f041df 100644 --- a/avm/res/storage/storage-account/main.json +++ b/avm/res/storage/storage-account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "1475636558824888919" + "templateHash": "5764713369417236781" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account.", @@ -2623,13 +2623,73 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "17622492193190468017" + "templateHash": "16945288904009561261" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service.", "owner": "Azure/module-maintainers" }, "definitions": { + "corsRuleType": { + "type": "object", + "properties": { + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of headers allowed to be part of the cross-origin request." + } + }, + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], + "metadata": { + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." + } + }, + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." + } + }, + "exposedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of response headers to expose to CORS clients." + } + }, + "maxAgeInSeconds": { + "type": "int", + "metadata": { + "description": "Required. The number of seconds that the client/browser should cache a preflight response." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cors rule." + } + }, "diagnosticSettingFullType": { "type": "object", "properties": { @@ -2809,9 +2869,12 @@ }, "corsRules": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/corsRuleType" + }, + "nullable": true, "metadata": { - "description": "Optional. Specifies CORS rules for the Blob service. You can include up to five CorsRule elements in the request. If no CorsRule elements are included in the request body, all CORS rules will be deleted, and CORS will be disabled for the Blob service." + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." } }, "defaultServiceVersion": { @@ -2913,9 +2976,7 @@ "days": "[parameters('containerDeleteRetentionPolicyDays')]", "allowPermanentDelete": "[if(equals(parameters('containerDeleteRetentionPolicyEnabled'), true()), parameters('containerDeleteRetentionPolicyAllowPermanentDelete'), null())]" }, - "cors": { - "corsRules": "[parameters('corsRules')]" - }, + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]", "defaultServiceVersion": "[if(not(empty(parameters('defaultServiceVersion'))), parameters('defaultServiceVersion'), null())]", "deleteRetentionPolicy": { "enabled": "[parameters('deleteRetentionPolicyEnabled')]", @@ -3488,6 +3549,9 @@ }, "shares": { "value": "[tryGet(parameters('fileServices'), 'shares')]" + }, + "corsRules": { + "value": "[tryGet(parameters('queueServices'), 'corsRules')]" } }, "template": { @@ -3498,13 +3562,73 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "5821640068498605661" + "templateHash": "4806721167991273113" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service.", "owner": "Azure/module-maintainers" }, "definitions": { + "corsRuleType": { + "type": "object", + "properties": { + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of headers allowed to be part of the cross-origin request." + } + }, + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], + "metadata": { + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." + } + }, + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." + } + }, + "exposedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of response headers to expose to CORS clients." + } + }, + "maxAgeInSeconds": { + "type": "int", + "metadata": { + "description": "Required. The number of seconds that the client/browser should cache a preflight response." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cors rule." + } + }, "diagnosticSettingFullType": { "type": "object", "properties": { @@ -3660,6 +3784,16 @@ "description": "Optional. The service properties for soft delete." } }, + "corsRules": { + "type": "array", + "items": { + "$ref": "#/definitions/corsRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." + } + }, "diagnosticSettings": { "type": "array", "items": { @@ -3690,6 +3824,7 @@ "apiVersion": "2023-04-01", "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]", "properties": { + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]", "protocolSettings": "[parameters('protocolSettings')]", "shareDeleteRetentionPolicy": "[parameters('shareDeleteRetentionPolicy')]" } @@ -4205,6 +4340,9 @@ }, "queues": { "value": "[tryGet(parameters('queueServices'), 'queues')]" + }, + "corsRules": { + "value": "[tryGet(parameters('queueServices'), 'corsRules')]" } }, "template": { @@ -4215,13 +4353,73 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "15558678445347429038" + "templateHash": "3469519329529657459" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service.", "owner": "Azure/module-maintainers" }, "definitions": { + "corsRuleType": { + "type": "object", + "properties": { + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of headers allowed to be part of the cross-origin request." + } + }, + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], + "metadata": { + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." + } + }, + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." + } + }, + "exposedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of response headers to expose to CORS clients." + } + }, + "maxAgeInSeconds": { + "type": "int", + "metadata": { + "description": "Required. The number of seconds that the client/browser should cache a preflight response." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cors rule." + } + }, "diagnosticSettingFullType": { "type": "object", "properties": { @@ -4360,6 +4558,16 @@ "description": "Optional. Queues to create." } }, + "corsRules": { + "type": "array", + "items": { + "$ref": "#/definitions/corsRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." + } + }, "diagnosticSettings": { "type": "array", "items": { @@ -4385,7 +4593,9 @@ "type": "Microsoft.Storage/storageAccounts/queueServices", "apiVersion": "2023-04-01", "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", - "properties": {} + "properties": { + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]" + } }, "queueServices_diagnosticSettings": { "copy": { @@ -4721,6 +4931,9 @@ }, "tables": { "value": "[tryGet(parameters('tableServices'), 'tables')]" + }, + "corsRules": { + "value": "[tryGet(parameters('tableServices'), 'corsRules')]" } }, "template": { @@ -4731,13 +4944,73 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "3329223749131374550" + "templateHash": "6416471281168447584" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service.", "owner": "Azure/module-maintainers" }, "definitions": { + "corsRuleType": { + "type": "object", + "properties": { + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of headers allowed to be part of the cross-origin request." + } + }, + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], + "metadata": { + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." + } + }, + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." + } + }, + "exposedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of response headers to expose to CORS clients." + } + }, + "maxAgeInSeconds": { + "type": "int", + "metadata": { + "description": "Required. The number of seconds that the client/browser should cache a preflight response." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cors rule." + } + }, "diagnosticSettingFullType": { "type": "object", "properties": { @@ -4876,6 +5149,16 @@ "description": "Optional. tables to create." } }, + "corsRules": { + "type": "array", + "items": { + "$ref": "#/definitions/corsRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." + } + }, "diagnosticSettings": { "type": "array", "items": { @@ -4901,7 +5184,9 @@ "type": "Microsoft.Storage/storageAccounts/tableServices", "apiVersion": "2023-04-01", "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", - "properties": {} + "properties": { + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]" + } }, "tableServices_diagnosticSettings": { "copy": { diff --git a/avm/res/storage/storage-account/queue-service/README.md b/avm/res/storage/storage-account/queue-service/README.md index 5c8d2c0fbb..da1162e755 100644 --- a/avm/res/storage/storage-account/queue-service/README.md +++ b/avm/res/storage/storage-account/queue-service/README.md @@ -30,6 +30,7 @@ This module deploys a Storage Account Queue Service. | Parameter | Type | Description | | :-- | :-- | :-- | +| [`corsRules`](#parameter-corsrules) | array | The List of CORS rules. You can include up to five CorsRule elements in the request. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`queues`](#parameter-queues) | array | Queues to create. | @@ -40,6 +41,73 @@ The name of the parent Storage Account. Required if the template is used in a st - Required: Yes - Type: string +### Parameter: `corsRules` + +The List of CORS rules. You can include up to five CorsRule elements in the request. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowedHeaders`](#parameter-corsrulesallowedheaders) | array | A list of headers allowed to be part of the cross-origin request. | +| [`allowedMethods`](#parameter-corsrulesallowedmethods) | array | A list of HTTP methods that are allowed to be executed by the origin. | +| [`allowedOrigins`](#parameter-corsrulesallowedorigins) | array | A list of origin domains that will be allowed via CORS, or "*" to allow all domains. | +| [`exposedHeaders`](#parameter-corsrulesexposedheaders) | array | A list of response headers to expose to CORS clients. | +| [`maxAgeInSeconds`](#parameter-corsrulesmaxageinseconds) | int | The number of seconds that the client/browser should cache a preflight response. | + +### Parameter: `corsRules.allowedHeaders` + +A list of headers allowed to be part of the cross-origin request. + +- Required: Yes +- Type: array + +### Parameter: `corsRules.allowedMethods` + +A list of HTTP methods that are allowed to be executed by the origin. + +- Required: Yes +- Type: array +- Allowed: + ```Bicep + [ + 'CONNECT' + 'DELETE' + 'GET' + 'HEAD' + 'MERGE' + 'OPTIONS' + 'PATCH' + 'POST' + 'PUT' + 'TRACE' + ] + ``` + +### Parameter: `corsRules.allowedOrigins` + +A list of origin domains that will be allowed via CORS, or "*" to allow all domains. + +- Required: Yes +- Type: array + +### Parameter: `corsRules.exposedHeaders` + +A list of response headers to expose to CORS clients. + +- Required: Yes +- Type: array + +### Parameter: `corsRules.maxAgeInSeconds` + +The number of seconds that the client/browser should cache a preflight response. + +- Required: Yes +- Type: int + ### Parameter: `diagnosticSettings` The diagnostic settings of the service. diff --git a/avm/res/storage/storage-account/queue-service/main.bicep b/avm/res/storage/storage-account/queue-service/main.bicep index d50c991cb2..a6451a3fbb 100644 --- a/avm/res/storage/storage-account/queue-service/main.bicep +++ b/avm/res/storage/storage-account/queue-service/main.bicep @@ -9,6 +9,9 @@ param storageAccountName string @description('Optional. Queues to create.') param queues array? +@description('Optional. The List of CORS rules. You can include up to five CorsRule elements in the request.') +param corsRules corsRuleType[]? + import { diagnosticSettingFullType } from 'br/public:avm/utl/types/avm-common-types:0.2.1' @description('Optional. The diagnostic settings of the service.') param diagnosticSettings diagnosticSettingFullType[]? @@ -23,7 +26,13 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' existing resource queueServices 'Microsoft.Storage/storageAccounts/queueServices@2023-04-01' = { name: name parent: storageAccount - properties: {} + properties: { + cors: corsRules != null + ? { + corsRules: corsRules + } + : null + } } resource queueServices_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ @@ -75,3 +84,26 @@ output resourceId string = queueServices.id @description('The resource group of the deployed file share service.') output resourceGroupName string = resourceGroup().name + +// =============== // +// Definitions // +// =============== // + +@export() +@description('The type for a cors rule.') +type corsRuleType = { + @description('Required. A list of headers allowed to be part of the cross-origin request.') + allowedHeaders: string[] + + @description('Required. A list of HTTP methods that are allowed to be executed by the origin.') + allowedMethods: ('CONNECT' | 'DELETE' | 'GET' | 'HEAD' | 'MERGE' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' | 'TRACE')[] + + @description('Required. A list of origin domains that will be allowed via CORS, or "*" to allow all domains.') + allowedOrigins: string[] + + @description('Required. A list of response headers to expose to CORS clients.') + exposedHeaders: string[] + + @description('Required. The number of seconds that the client/browser should cache a preflight response.') + maxAgeInSeconds: int +} diff --git a/avm/res/storage/storage-account/queue-service/main.json b/avm/res/storage/storage-account/queue-service/main.json index 84f2e69daf..038d83a48f 100644 --- a/avm/res/storage/storage-account/queue-service/main.json +++ b/avm/res/storage/storage-account/queue-service/main.json @@ -6,13 +6,73 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "15558678445347429038" + "templateHash": "3469519329529657459" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service.", "owner": "Azure/module-maintainers" }, "definitions": { + "corsRuleType": { + "type": "object", + "properties": { + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of headers allowed to be part of the cross-origin request." + } + }, + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], + "metadata": { + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." + } + }, + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." + } + }, + "exposedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of response headers to expose to CORS clients." + } + }, + "maxAgeInSeconds": { + "type": "int", + "metadata": { + "description": "Required. The number of seconds that the client/browser should cache a preflight response." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cors rule." + } + }, "diagnosticSettingFullType": { "type": "object", "properties": { @@ -151,6 +211,16 @@ "description": "Optional. Queues to create." } }, + "corsRules": { + "type": "array", + "items": { + "$ref": "#/definitions/corsRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." + } + }, "diagnosticSettings": { "type": "array", "items": { @@ -176,7 +246,9 @@ "type": "Microsoft.Storage/storageAccounts/queueServices", "apiVersion": "2023-04-01", "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", - "properties": {} + "properties": { + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]" + } }, "queueServices_diagnosticSettings": { "copy": { diff --git a/avm/res/storage/storage-account/table-service/README.md b/avm/res/storage/storage-account/table-service/README.md index ba3f15b61d..92f5e9b535 100644 --- a/avm/res/storage/storage-account/table-service/README.md +++ b/avm/res/storage/storage-account/table-service/README.md @@ -30,6 +30,7 @@ This module deploys a Storage Account Table Service. | Parameter | Type | Description | | :-- | :-- | :-- | +| [`corsRules`](#parameter-corsrules) | array | The List of CORS rules. You can include up to five CorsRule elements in the request. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`tables`](#parameter-tables) | array | tables to create. | @@ -40,6 +41,73 @@ The name of the parent Storage Account. Required if the template is used in a st - Required: Yes - Type: string +### Parameter: `corsRules` + +The List of CORS rules. You can include up to five CorsRule elements in the request. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowedHeaders`](#parameter-corsrulesallowedheaders) | array | A list of headers allowed to be part of the cross-origin request. | +| [`allowedMethods`](#parameter-corsrulesallowedmethods) | array | A list of HTTP methods that are allowed to be executed by the origin. | +| [`allowedOrigins`](#parameter-corsrulesallowedorigins) | array | A list of origin domains that will be allowed via CORS, or "*" to allow all domains. | +| [`exposedHeaders`](#parameter-corsrulesexposedheaders) | array | A list of response headers to expose to CORS clients. | +| [`maxAgeInSeconds`](#parameter-corsrulesmaxageinseconds) | int | The number of seconds that the client/browser should cache a preflight response. | + +### Parameter: `corsRules.allowedHeaders` + +A list of headers allowed to be part of the cross-origin request. + +- Required: Yes +- Type: array + +### Parameter: `corsRules.allowedMethods` + +A list of HTTP methods that are allowed to be executed by the origin. + +- Required: Yes +- Type: array +- Allowed: + ```Bicep + [ + 'CONNECT' + 'DELETE' + 'GET' + 'HEAD' + 'MERGE' + 'OPTIONS' + 'PATCH' + 'POST' + 'PUT' + 'TRACE' + ] + ``` + +### Parameter: `corsRules.allowedOrigins` + +A list of origin domains that will be allowed via CORS, or "*" to allow all domains. + +- Required: Yes +- Type: array + +### Parameter: `corsRules.exposedHeaders` + +A list of response headers to expose to CORS clients. + +- Required: Yes +- Type: array + +### Parameter: `corsRules.maxAgeInSeconds` + +The number of seconds that the client/browser should cache a preflight response. + +- Required: Yes +- Type: int + ### Parameter: `diagnosticSettings` The diagnostic settings of the service. diff --git a/avm/res/storage/storage-account/table-service/main.bicep b/avm/res/storage/storage-account/table-service/main.bicep index 8f7c9ab9a1..9eb0ceac6e 100644 --- a/avm/res/storage/storage-account/table-service/main.bicep +++ b/avm/res/storage/storage-account/table-service/main.bicep @@ -9,6 +9,9 @@ param storageAccountName string @description('Optional. tables to create.') param tables array = [] +@description('Optional. The List of CORS rules. You can include up to five CorsRule elements in the request.') +param corsRules corsRuleType[]? + import { diagnosticSettingFullType } from 'br/public:avm/utl/types/avm-common-types:0.2.1' @description('Optional. The diagnostic settings of the service.') param diagnosticSettings diagnosticSettingFullType[]? @@ -23,7 +26,13 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' existing resource tableServices 'Microsoft.Storage/storageAccounts/tableServices@2023-04-01' = { name: name parent: storageAccount - properties: {} + properties: { + cors: corsRules != null + ? { + corsRules: corsRules + } + : null + } } resource tableServices_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ @@ -74,3 +83,26 @@ output resourceId string = tableServices.id @description('The resource group of the deployed table service.') output resourceGroupName string = resourceGroup().name + +// =============== // +// Definitions // +// =============== // + +@export() +@description('The type for a cors rule.') +type corsRuleType = { + @description('Required. A list of headers allowed to be part of the cross-origin request.') + allowedHeaders: string[] + + @description('Required. A list of HTTP methods that are allowed to be executed by the origin.') + allowedMethods: ('CONNECT' | 'DELETE' | 'GET' | 'HEAD' | 'MERGE' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' | 'TRACE')[] + + @description('Required. A list of origin domains that will be allowed via CORS, or "*" to allow all domains.') + allowedOrigins: string[] + + @description('Required. A list of response headers to expose to CORS clients.') + exposedHeaders: string[] + + @description('Required. The number of seconds that the client/browser should cache a preflight response.') + maxAgeInSeconds: int +} diff --git a/avm/res/storage/storage-account/table-service/main.json b/avm/res/storage/storage-account/table-service/main.json index 5acaed4231..d760d677ea 100644 --- a/avm/res/storage/storage-account/table-service/main.json +++ b/avm/res/storage/storage-account/table-service/main.json @@ -6,13 +6,73 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "3329223749131374550" + "templateHash": "6416471281168447584" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service.", "owner": "Azure/module-maintainers" }, "definitions": { + "corsRuleType": { + "type": "object", + "properties": { + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of headers allowed to be part of the cross-origin request." + } + }, + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], + "metadata": { + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." + } + }, + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." + } + }, + "exposedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of response headers to expose to CORS clients." + } + }, + "maxAgeInSeconds": { + "type": "int", + "metadata": { + "description": "Required. The number of seconds that the client/browser should cache a preflight response." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cors rule." + } + }, "diagnosticSettingFullType": { "type": "object", "properties": { @@ -151,6 +211,16 @@ "description": "Optional. tables to create." } }, + "corsRules": { + "type": "array", + "items": { + "$ref": "#/definitions/corsRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." + } + }, "diagnosticSettings": { "type": "array", "items": { @@ -176,7 +246,9 @@ "type": "Microsoft.Storage/storageAccounts/tableServices", "apiVersion": "2023-04-01", "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", - "properties": {} + "properties": { + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]" + } }, "tableServices_diagnosticSettings": { "copy": { diff --git a/avm/res/storage/storage-account/tests/e2e/max/main.test.bicep b/avm/res/storage/storage-account/tests/e2e/max/main.test.bicep index 337cab61ff..6215b89072 100644 --- a/avm/res/storage/storage-account/tests/e2e/max/main.test.bicep +++ b/avm/res/storage/storage-account/tests/e2e/max/main.test.bicep @@ -257,6 +257,29 @@ module testDeployment '../../../main.bicep' = [ containerDeleteRetentionPolicyDays: 10 deleteRetentionPolicyEnabled: true deleteRetentionPolicyDays: 9 + corsRules: [ + { + allowedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-target-path' + 'x-ms-meta-source-path' + ] + exposedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-target-path' + 'x-ms-meta-source-path' + ] + allowedOrigins: [ + 'http://*.contoso.com' + 'http://www.fabrikam.com' + ] + allowedMethods: [ + 'GET' + 'PUT' + ] + maxAgeInSeconds: 200 + } + ] } fileServices: { diagnosticSettings: [ @@ -306,6 +329,29 @@ module testDeployment '../../../main.bicep' = [ shareQuota: 102400 } ] + corsRules: [ + { + allowedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-target-path' + 'x-ms-meta-source-path' + ] + exposedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-target-path' + 'x-ms-meta-source-path' + ] + allowedOrigins: [ + 'http://*.contoso.com' + 'http://www.fabrikam.com' + ] + allowedMethods: [ + 'GET' + 'PUT' + ] + maxAgeInSeconds: 200 + } + ] } tableServices: { diagnosticSettings: [ @@ -370,6 +416,29 @@ module testDeployment '../../../main.bicep' = [ ] } ] + corsRules: [ + { + allowedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-target-path' + 'x-ms-meta-source-path' + ] + exposedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-target-path' + 'x-ms-meta-source-path' + ] + allowedOrigins: [ + 'http://*.contoso.com' + 'http://www.fabrikam.com' + ] + allowedMethods: [ + 'GET' + 'PUT' + ] + maxAgeInSeconds: 200 + } + ] } queueServices: { diagnosticSettings: [ @@ -419,6 +488,29 @@ module testDeployment '../../../main.bicep' = [ metadata: {} } ] + corsRules: [ + { + allowedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-target-path' + 'x-ms-meta-source-path' + ] + exposedHeaders: [ + 'x-ms-meta-data' + 'x-ms-meta-target-path' + 'x-ms-meta-source-path' + ] + allowedOrigins: [ + 'http://*.contoso.com' + 'http://www.fabrikam.com' + ] + allowedMethods: [ + 'GET' + 'PUT' + ] + maxAgeInSeconds: 200 + } + ] } sasExpirationPeriod: '180.00:00:00' managedIdentities: { @@ -503,9 +595,5 @@ module testDeployment '../../../main.bicep' = [ Role: 'DeploymentValidation' } } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] } ]