From 3073a74cd75675d7c7ba5ddbcc2450f2482de7c6 Mon Sep 17 00:00:00 2001 From: Anders Eide <anders.eide@outlook.com> Date: Tue, 1 Oct 2024 11:12:14 +0200 Subject: [PATCH] feat: Added cdn profile security policies support - `avm/res/cdn/profile` (#3025) ## Description Adds support for Security Policies on cdn/profile, making it possible to use the module to deploy Azure Front Door Premium with Web Application Firewall. Fixes #2376 ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.res.cdn.profile](https://github.com/anderseide/avm-bicep-registry-modules/actions/workflows/avm.res.cdn.profile.yml/badge.svg?branch=cdn-profile-security-policies)](https://github.com/anderseide/avm-bicep-registry-modules/actions/workflows/avm.res.cdn.profile.yml) | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] 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. - [x] 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`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings <!-- Please keep up to date with the contribution guide at https://aka.ms/avm/contribute/bicep --> --------- Co-authored-by: Guillaume Beaud <79973892+gbeaud@users.noreply.github.com> --- avm/res/cdn/profile/README.md | 322 +++++++++++++++++- avm/res/cdn/profile/afdEndpoint/main.json | 8 +- .../cdn/profile/afdEndpoint/route/main.json | 4 +- avm/res/cdn/profile/customdomain/main.json | 4 +- avm/res/cdn/profile/endpoint/main.json | 8 +- avm/res/cdn/profile/endpoint/origin/main.json | 4 +- avm/res/cdn/profile/main.bicep | 31 ++ avm/res/cdn/profile/main.json | 278 +++++++++++++-- avm/res/cdn/profile/origingroup/main.json | 8 +- .../cdn/profile/origingroup/origin/main.json | 4 +- avm/res/cdn/profile/ruleset/main.json | 8 +- avm/res/cdn/profile/ruleset/rule/main.json | 4 +- avm/res/cdn/profile/secret/main.json | 4 +- .../cdn/profile/securityPolicies/README.md | 101 ++++++ .../cdn/profile/securityPolicies/main.bicep | 54 +++ .../cdn/profile/securityPolicies/main.json | 128 +++++++ .../tests/e2e/afd.premium/main.test.bicep | 146 ++++++++ avm/res/cdn/profile/version.json | 4 +- 18 files changed, 1060 insertions(+), 60 deletions(-) create mode 100644 avm/res/cdn/profile/securityPolicies/README.md create mode 100644 avm/res/cdn/profile/securityPolicies/main.bicep create mode 100644 avm/res/cdn/profile/securityPolicies/main.json create mode 100644 avm/res/cdn/profile/tests/e2e/afd.premium/main.test.bicep diff --git a/avm/res/cdn/profile/README.md b/avm/res/cdn/profile/README.md index 369cadf61b..e2de8fbea8 100644 --- a/avm/res/cdn/profile/README.md +++ b/avm/res/cdn/profile/README.md @@ -27,6 +27,7 @@ This module deploys a CDN Profile. | `Microsoft.Cdn/profiles/ruleSets` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2023-05-01/profiles/ruleSets) | | `Microsoft.Cdn/profiles/ruleSets/rules` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2023-05-01/profiles/ruleSets/rules) | | `Microsoft.Cdn/profiles/secrets` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2023-05-01/profiles/secrets) | +| `Microsoft.Cdn/profiles/securityPolicies` | [2024-02-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/profiles/securityPolicies) | ## Usage examples @@ -36,12 +37,245 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/cdn/profile:<version>`. -- [As Azure Front Door](#example-1-as-azure-front-door) -- [Using only defaults](#example-2-using-only-defaults) -- [Using large parameter set](#example-3-using-large-parameter-set) -- [WAF-aligned](#example-4-waf-aligned) +- [As Azure Front Door Premium](#example-1-as-azure-front-door-premium) +- [As Azure Front Door](#example-2-as-azure-front-door) +- [Using only defaults](#example-3-using-only-defaults) +- [Using large parameter set](#example-4-using-large-parameter-set) +- [WAF-aligned](#example-5-waf-aligned) -### Example 1: _As Azure Front Door_ +### Example 1: _As Azure Front Door Premium_ + +This instance deploys the module as Azure Front Door Premium. + + +<details> + +<summary>via Bicep module</summary> + +```bicep +module profile 'br/public:avm/res/cdn/profile:<version>' = { + name: 'profileDeployment' + params: { + // Required parameters + name: 'dep-test-cdnpafdp' + sku: 'Premium_AzureFrontDoor' + // Non-required parameters + afdEndpoints: [ + { + name: 'dep-test-cdnpafdp-afd-endpoint' + routes: [ + { + customDomainNames: [ + 'dep-test-cdnpafdp-custom-domain' + ] + name: 'dep-test-cdnpafdp-afd-route' + originGroupName: 'dep-test-cdnpafdp-origin-group' + ruleSets: [ + { + name: 'deptestcdnpafdpruleset' + } + ] + } + ] + } + ] + customDomains: [ + { + certificateType: 'ManagedCertificate' + hostName: 'dep-test-cdnpafdp-custom-domain.azurewebsites.net' + name: 'dep-test-cdnpafdp-custom-domain' + } + ] + location: 'global' + originGroups: [ + { + loadBalancingSettings: { + additionalLatencyInMilliseconds: 50 + sampleSize: 4 + successfulSamplesRequired: 3 + } + name: 'dep-test-cdnpafdp-origin-group' + origins: [ + { + hostName: 'dep-test-cdnpafdp-origin.azurewebsites.net' + name: 'dep-test-cdnpafdp-origin' + } + ] + } + ] + originResponseTimeoutSeconds: 60 + ruleSets: [ + { + name: 'deptestcdnpafdpruleset' + rules: [ + { + actions: [ + { + name: 'UrlRedirect' + parameters: { + customHostname: 'dev-etradefd.trade.azure.defra.cloud' + customPath: '/test123' + destinationProtocol: 'Https' + redirectType: 'PermanentRedirect' + typeName: 'DeliveryRuleUrlRedirectActionParameters' + } + } + ] + name: 'deptestcdnpafdprule' + order: 1 + } + ] + } + ] + securityPolicies: [ + { + associations: [ + { + domains: [ + { + id: '<id>' + } + ] + patternsToMatch: [ + '/*' + ] + } + ] + name: 'deptestcdnpafdpsecpol' + wafPolicyResourceId: '<wafPolicyResourceId>' + } + ] + } +} +``` + +</details> +<p> + +<details> + +<summary>via JSON Parameter file</summary> + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "dep-test-cdnpafdp" + }, + "sku": { + "value": "Premium_AzureFrontDoor" + }, + // Non-required parameters + "afdEndpoints": { + "value": [ + { + "name": "dep-test-cdnpafdp-afd-endpoint", + "routes": [ + { + "customDomainNames": [ + "dep-test-cdnpafdp-custom-domain" + ], + "name": "dep-test-cdnpafdp-afd-route", + "originGroupName": "dep-test-cdnpafdp-origin-group", + "ruleSets": [ + { + "name": "deptestcdnpafdpruleset" + } + ] + } + ] + } + ] + }, + "customDomains": { + "value": [ + { + "certificateType": "ManagedCertificate", + "hostName": "dep-test-cdnpafdp-custom-domain.azurewebsites.net", + "name": "dep-test-cdnpafdp-custom-domain" + } + ] + }, + "location": { + "value": "global" + }, + "originGroups": { + "value": [ + { + "loadBalancingSettings": { + "additionalLatencyInMilliseconds": 50, + "sampleSize": 4, + "successfulSamplesRequired": 3 + }, + "name": "dep-test-cdnpafdp-origin-group", + "origins": [ + { + "hostName": "dep-test-cdnpafdp-origin.azurewebsites.net", + "name": "dep-test-cdnpafdp-origin" + } + ] + } + ] + }, + "originResponseTimeoutSeconds": { + "value": 60 + }, + "ruleSets": { + "value": [ + { + "name": "deptestcdnpafdpruleset", + "rules": [ + { + "actions": [ + { + "name": "UrlRedirect", + "parameters": { + "customHostname": "dev-etradefd.trade.azure.defra.cloud", + "customPath": "/test123", + "destinationProtocol": "Https", + "redirectType": "PermanentRedirect", + "typeName": "DeliveryRuleUrlRedirectActionParameters" + } + } + ], + "name": "deptestcdnpafdprule", + "order": 1 + } + ] + } + ] + }, + "securityPolicies": { + "value": [ + { + "associations": [ + { + "domains": [ + { + "id": "<id>" + } + ], + "patternsToMatch": [ + "/*" + ] + } + ], + "name": "deptestcdnpafdpsecpol", + "wafPolicyResourceId": "<wafPolicyResourceId>" + } + ] + } + } +} +``` + +</details> +<p> + +### Example 2: _As Azure Front Door_ This instance deploys the module as Azure Front Door. @@ -235,7 +469,7 @@ module profile 'br/public:avm/res/cdn/profile:<version>' = { </details> <p> -### Example 2: _Using only defaults_ +### Example 3: _Using only defaults_ This instance deploys the module with the minimum set of required parameters. @@ -287,7 +521,7 @@ module profile 'br/public:avm/res/cdn/profile:<version>' = { </details> <p> -### Example 3: _Using large parameter set_ +### Example 4: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -455,7 +689,7 @@ module profile 'br/public:avm/res/cdn/profile:<version>' = { </details> <p> -### Example 4: _WAF-aligned_ +### Example 5: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -603,6 +837,7 @@ module profile 'br/public:avm/res/cdn/profile:<version>' = { | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`ruleSets`](#parameter-rulesets) | array | Array of rule set objects. | | [`secrets`](#parameter-secrets) | array | Array of secret objects. | +| [`securityPolicies`](#parameter-securitypolicies) | array | Array of Security Policy objects (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies for details). | | [`tags`](#parameter-tags) | object | Endpoint tags. | ### Parameter: `name` @@ -857,6 +1092,77 @@ Array of secret objects. - Type: array - Default: `[]` +### Parameter: `securityPolicies` + +Array of Security Policy objects (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies for details). + +- Required: No +- Type: array +- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`associations`](#parameter-securitypoliciesassociations) | array | Domain names and URL patterns to math with this association. | +| [`name`](#parameter-securitypoliciesname) | string | Name of the security policy. | +| [`wafPolicyResourceId`](#parameter-securitypolicieswafpolicyresourceid) | string | Resource ID of WAF policy. | + +### Parameter: `securityPolicies.associations` + +Domain names and URL patterns to math with this association. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`domains`](#parameter-securitypoliciesassociationsdomains) | array | List of domain resource id to associate with this resource. | +| [`patternsToMatch`](#parameter-securitypoliciesassociationspatternstomatch) | array | List of patterns to match with this association. | + +### Parameter: `securityPolicies.associations.domains` + +List of domain resource id to associate with this resource. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`id`](#parameter-securitypoliciesassociationsdomainsid) | string | ResourceID to domain that will be associated. | + +### Parameter: `securityPolicies.associations.domains.id` + +ResourceID to domain that will be associated. + +- Required: Yes +- Type: string + +### Parameter: `securityPolicies.associations.patternsToMatch` + +List of patterns to match with this association. + +- Required: Yes +- Type: array + +### Parameter: `securityPolicies.name` + +Name of the security policy. + +- Required: Yes +- Type: string + +### Parameter: `securityPolicies.wafPolicyResourceId` + +Resource ID of WAF policy. + +- Required: Yes +- Type: string + ### Parameter: `tags` Endpoint tags. diff --git a/avm/res/cdn/profile/afdEndpoint/main.json b/avm/res/cdn/profile/afdEndpoint/main.json index 1f55d5cd6e..7cfef24e3f 100644 --- a/avm/res/cdn/profile/afdEndpoint/main.json +++ b/avm/res/cdn/profile/afdEndpoint/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3384292547879688658" + "version": "0.30.23.60470", + "templateHash": "792735746278824384" }, "name": "CDN Profiles AFD Endpoints", "description": "This module deploys a CDN Profile AFD Endpoint.", @@ -156,8 +156,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "18002678880456924020" + "version": "0.30.23.60470", + "templateHash": "1034122698174669197" }, "name": "CDN Profiles AFD Endpoint Route", "description": "This module deploys a CDN Profile AFD Endpoint route.", diff --git a/avm/res/cdn/profile/afdEndpoint/route/main.json b/avm/res/cdn/profile/afdEndpoint/route/main.json index 6a415af662..852e97f10c 100644 --- a/avm/res/cdn/profile/afdEndpoint/route/main.json +++ b/avm/res/cdn/profile/afdEndpoint/route/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "18002678880456924020" + "version": "0.30.23.60470", + "templateHash": "1034122698174669197" }, "name": "CDN Profiles AFD Endpoint Route", "description": "This module deploys a CDN Profile AFD Endpoint route.", diff --git a/avm/res/cdn/profile/customdomain/main.json b/avm/res/cdn/profile/customdomain/main.json index dd0a43d181..e45727e4ad 100644 --- a/avm/res/cdn/profile/customdomain/main.json +++ b/avm/res/cdn/profile/customdomain/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15211066835326278081" + "version": "0.30.23.60470", + "templateHash": "16955838730426729961" }, "name": "CDN Profiles Custom Domains", "description": "This module deploys a CDN Profile Custom Domains.", diff --git a/avm/res/cdn/profile/endpoint/main.json b/avm/res/cdn/profile/endpoint/main.json index 2fa89e8711..273dbe9fce 100644 --- a/avm/res/cdn/profile/endpoint/main.json +++ b/avm/res/cdn/profile/endpoint/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "6371656015674390162" + "version": "0.30.23.60470", + "templateHash": "3460565146034921053" }, "name": "CDN Profiles Endpoints", "description": "This module deploys a CDN Profile Endpoint.", @@ -125,8 +125,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11976988406992266750" + "version": "0.30.23.60470", + "templateHash": "4151069688274070352" }, "name": "CDN Profiles Endpoints Origins", "description": "This module deploys a CDN Profile Endpoint Origin.", diff --git a/avm/res/cdn/profile/endpoint/origin/main.json b/avm/res/cdn/profile/endpoint/origin/main.json index 13a2f8b35d..f4c079ff44 100644 --- a/avm/res/cdn/profile/endpoint/origin/main.json +++ b/avm/res/cdn/profile/endpoint/origin/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11976988406992266750" + "version": "0.30.23.60470", + "templateHash": "4151069688274070352" }, "name": "CDN Profiles Endpoints Origins", "description": "This module deploys a CDN Profile Endpoint Origin.", diff --git a/avm/res/cdn/profile/main.bicep b/avm/res/cdn/profile/main.bicep index c573133232..87323ba79f 100644 --- a/avm/res/cdn/profile/main.bicep +++ b/avm/res/cdn/profile/main.bicep @@ -49,6 +49,9 @@ param ruleSets array = [] @description('Optional. Array of AFD endpoint objects.') param afdEndpoints array = [] +@description('Optional. Array of Security Policy objects (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies for details).') +param securityPolicies securityPolicyType = [] + @description('Optional. Endpoint tags.') param tags object? @@ -251,6 +254,22 @@ module profile_afdEndpoints 'afdEndpoint/main.bicep' = [ } ] +module profile_securityPolicies 'securityPolicies/main.bicep' = [ + for (securityPolicy, index) in securityPolicies: { + name: '${uniqueString(deployment().name)}-Profile-SecurityPolicy-${index}' + dependsOn: [ + profile_afdEndpoints + profile_customDomains + ] + params: { + name: securityPolicy.name + profileName: profile.name + associations: securityPolicy.associations + wafPolicyResourceId: securityPolicy.wafPolicyResourceId + } + } +] + @description('The name of the CDN profile.') output name string = profile.name @@ -279,6 +298,18 @@ output uri string = !empty(endpointProperties) ? profile_endpoint.outputs.uri : // Definitions // // =============== // +import { associationsType } from 'securityPolicies/main.bicep' +type securityPolicyType = { + @description('Required. Name of the security policy.') + name: string + + @description('Required. Domain names and URL patterns to math with this association.') + associations: associationsType + + @description('Required. Resource ID of WAF policy.') + wafPolicyResourceId: string +}[] + type lockType = { @description('Optional. Specify the name of lock.') name: string? diff --git a/avm/res/cdn/profile/main.json b/avm/res/cdn/profile/main.json index 965bf9cb4c..169cee8564 100644 --- a/avm/res/cdn/profile/main.json +++ b/avm/res/cdn/profile/main.json @@ -5,14 +5,40 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "16789354290120442948" + "version": "0.30.23.60470", + "templateHash": "18013902785904421717" }, "name": "CDN Profiles", "description": "This module deploys a CDN Profile.", "owner": "Azure/module-maintainers" }, "definitions": { + "securityPolicyType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the security policy." + } + }, + "associations": { + "$ref": "#/definitions/associationsType", + "metadata": { + "description": "Required. Domain names and URL patterns to math with this association." + } + }, + "wafPolicyResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of WAF policy." + } + } + } + } + }, "lockType": { "type": "object", "properties": { @@ -110,6 +136,45 @@ } }, "nullable": true + }, + "associationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "domains": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. ResourceID to domain that will be associated." + } + } + } + }, + "metadata": { + "description": "Required. List of domain resource id to associate with this resource." + } + }, + "patternsToMatch": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. List of patterns to match with this association." + } + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "securityPolicies/main.bicep" + } + } } }, "parameters": { @@ -202,6 +267,13 @@ "description": "Optional. Array of AFD endpoint objects." } }, + "securityPolicies": { + "$ref": "#/definitions/securityPolicyType", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of Security Policy objects (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies for details)." + } + }, "tags": { "type": "object", "nullable": true, @@ -350,8 +422,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "6371656015674390162" + "version": "0.30.23.60470", + "templateHash": "3460565146034921053" }, "name": "CDN Profiles Endpoints", "description": "This module deploys a CDN Profile Endpoint.", @@ -470,8 +542,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11976988406992266750" + "version": "0.30.23.60470", + "templateHash": "4151069688274070352" }, "name": "CDN Profiles Endpoints Origins", "description": "This module deploys a CDN Profile Endpoint Origin.", @@ -723,8 +795,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "533126228291817357" + "version": "0.30.23.60470", + "templateHash": "7661706938502506866" }, "name": "CDN Profiles Secret", "description": "This module deploys a CDN Profile Secret.", @@ -872,8 +944,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15211066835326278081" + "version": "0.30.23.60470", + "templateHash": "16955838730426729961" }, "name": "CDN Profiles Custom Domains", "description": "This module deploys a CDN Profile Custom Domains.", @@ -1039,8 +1111,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1182415535491789973" + "version": "0.30.23.60470", + "templateHash": "16948516107556143812" }, "name": "CDN Profiles Origin Group", "description": "This module deploys a CDN Profile Origin Group.", @@ -1176,8 +1248,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "14493731512795008787" + "version": "0.30.23.60470", + "templateHash": "4669077701065465911" }, "name": "CDN Profiles Origin", "description": "This module deploys a CDN Profile Origin.", @@ -1402,8 +1474,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "9792708426765797662" + "version": "0.30.23.60470", + "templateHash": "11520922481694023973" }, "name": "CDN Profiles Rule Sets", "description": "This module deploys a CDN Profile rule set.", @@ -1488,8 +1560,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "12904222825428666192" + "version": "0.30.23.60470", + "templateHash": "8818585542646204223" }, "name": "CDN Profiles Rules", "description": "This module deploys a CDN Profile rule.", @@ -1676,8 +1748,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3384292547879688658" + "version": "0.30.23.60470", + "templateHash": "792735746278824384" }, "name": "CDN Profiles AFD Endpoints", "description": "This module deploys a CDN Profile AFD Endpoint.", @@ -1827,8 +1899,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "18002678880456924020" + "version": "0.30.23.60470", + "templateHash": "1034122698174669197" }, "name": "CDN Profiles AFD Endpoint Route", "description": "This module deploys a CDN Profile AFD Endpoint route.", @@ -2119,6 +2191,168 @@ "profile_originGroups", "profile_ruleSets" ] + }, + "profile_securityPolicies": { + "copy": { + "name": "profile_securityPolicies", + "count": "[length(parameters('securityPolicies'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Profile-SecurityPolicy-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('securityPolicies')[copyIndex()].name]" + }, + "profileName": { + "value": "[parameters('name')]" + }, + "associations": { + "value": "[parameters('securityPolicies')[copyIndex()].associations]" + }, + "wafPolicyResourceId": { + "value": "[parameters('securityPolicies')[copyIndex()].wafPolicyResourceId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.30.23.60470", + "templateHash": "11561080659040848436" + }, + "name": "CDN Profiles Security Policy", + "description": "This module deploys a CDN Profile Security Policy.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "associationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "domains": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. ResourceID to domain that will be associated." + } + } + } + }, + "metadata": { + "description": "Required. List of domain resource id to associate with this resource." + } + }, + "patternsToMatch": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. List of patterns to match with this association." + } + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The resource name." + } + }, + "profileName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent CDN profile. Required if the template is used in a standalone deployment." + } + }, + "wafPolicyResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of WAF Policy." + } + }, + "associations": { + "$ref": "#/definitions/associationsType", + "metadata": { + "description": "Required. Waf associations (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies?pivots=deployment-language-bicep#securitypolicywebapplicationfirewallassociation for details)." + } + } + }, + "resources": { + "profile": { + "existing": true, + "type": "Microsoft.Cdn/profiles", + "apiVersion": "2023-05-01", + "name": "[parameters('profileName')]" + }, + "securityPolicies": { + "type": "Microsoft.Cdn/profiles/securityPolicies", + "apiVersion": "2024-02-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('name'))]", + "properties": { + "parameters": { + "type": "WebApplicationFirewall", + "wafPolicy": { + "id": "[parameters('wafPolicyResourceId')]" + }, + "associations": "[parameters('associations')]" + } + }, + "dependsOn": [ + "profile" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the secrect." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the secrect." + }, + "value": "[resourceId('Microsoft.Cdn/profiles/securityPolicies', parameters('profileName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the secret was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "profile", + "profile_afdEndpoints", + "profile_customDomains" + ] } }, "outputs": { diff --git a/avm/res/cdn/profile/origingroup/main.json b/avm/res/cdn/profile/origingroup/main.json index 9a388bc48e..af9a692a27 100644 --- a/avm/res/cdn/profile/origingroup/main.json +++ b/avm/res/cdn/profile/origingroup/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1182415535491789973" + "version": "0.30.23.60470", + "templateHash": "16948516107556143812" }, "name": "CDN Profiles Origin Group", "description": "This module deploys a CDN Profile Origin Group.", @@ -142,8 +142,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "14493731512795008787" + "version": "0.30.23.60470", + "templateHash": "4669077701065465911" }, "name": "CDN Profiles Origin", "description": "This module deploys a CDN Profile Origin.", diff --git a/avm/res/cdn/profile/origingroup/origin/main.json b/avm/res/cdn/profile/origingroup/origin/main.json index 56306ce135..8ee5bf04df 100644 --- a/avm/res/cdn/profile/origingroup/origin/main.json +++ b/avm/res/cdn/profile/origingroup/origin/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "14493731512795008787" + "version": "0.30.23.60470", + "templateHash": "4669077701065465911" }, "name": "CDN Profiles Origin", "description": "This module deploys a CDN Profile Origin.", diff --git a/avm/res/cdn/profile/ruleset/main.json b/avm/res/cdn/profile/ruleset/main.json index 47ff335b1c..2d040690b5 100644 --- a/avm/res/cdn/profile/ruleset/main.json +++ b/avm/res/cdn/profile/ruleset/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "9792708426765797662" + "version": "0.30.23.60470", + "templateHash": "11520922481694023973" }, "name": "CDN Profiles Rule Sets", "description": "This module deploys a CDN Profile rule set.", @@ -91,8 +91,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "12904222825428666192" + "version": "0.30.23.60470", + "templateHash": "8818585542646204223" }, "name": "CDN Profiles Rules", "description": "This module deploys a CDN Profile rule.", diff --git a/avm/res/cdn/profile/ruleset/rule/main.json b/avm/res/cdn/profile/ruleset/rule/main.json index bb37297681..98e0f0fef8 100644 --- a/avm/res/cdn/profile/ruleset/rule/main.json +++ b/avm/res/cdn/profile/ruleset/rule/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "12904222825428666192" + "version": "0.30.23.60470", + "templateHash": "8818585542646204223" }, "name": "CDN Profiles Rules", "description": "This module deploys a CDN Profile rule.", diff --git a/avm/res/cdn/profile/secret/main.json b/avm/res/cdn/profile/secret/main.json index a23afb02a0..6c300587db 100644 --- a/avm/res/cdn/profile/secret/main.json +++ b/avm/res/cdn/profile/secret/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "533126228291817357" + "version": "0.30.23.60470", + "templateHash": "7661706938502506866" }, "name": "CDN Profiles Secret", "description": "This module deploys a CDN Profile Secret.", diff --git a/avm/res/cdn/profile/securityPolicies/README.md b/avm/res/cdn/profile/securityPolicies/README.md new file mode 100644 index 0000000000..c30d3c5dad --- /dev/null +++ b/avm/res/cdn/profile/securityPolicies/README.md @@ -0,0 +1,101 @@ +# CDN Profiles Security Policy `[Microsoft.Cdn/profiles/securityPolicies]` + +This module deploys a CDN Profile Security Policy. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Cdn/profiles/securityPolicies` | [2024-02-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/profiles/securityPolicies) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`associations`](#parameter-associations) | array | Waf associations (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies?pivots=deployment-language-bicep#securitypolicywebapplicationfirewallassociation for details). | +| [`name`](#parameter-name) | string | The resource name. | +| [`wafPolicyResourceId`](#parameter-wafpolicyresourceid) | string | Resource ID of WAF Policy. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`profileName`](#parameter-profilename) | string | The name of the parent CDN profile. Required if the template is used in a standalone deployment. | + +### Parameter: `associations` + +Waf associations (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies?pivots=deployment-language-bicep#securitypolicywebapplicationfirewallassociation for details). + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`domains`](#parameter-associationsdomains) | array | List of domain resource id to associate with this resource. | +| [`patternsToMatch`](#parameter-associationspatternstomatch) | array | List of patterns to match with this association. | + +### Parameter: `associations.domains` + +List of domain resource id to associate with this resource. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`id`](#parameter-associationsdomainsid) | string | ResourceID to domain that will be associated. | + +### Parameter: `associations.domains.id` + +ResourceID to domain that will be associated. + +- Required: Yes +- Type: string + +### Parameter: `associations.patternsToMatch` + +List of patterns to match with this association. + +- Required: Yes +- Type: array + +### Parameter: `name` + +The resource name. + +- Required: Yes +- Type: string + +### Parameter: `wafPolicyResourceId` + +Resource ID of WAF Policy. + +- Required: Yes +- Type: string + +### Parameter: `profileName` + +The name of the parent CDN profile. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the secrect. | +| `resourceGroupName` | string | The name of the resource group the secret was created in. | +| `resourceId` | string | The resource ID of the secrect. | diff --git a/avm/res/cdn/profile/securityPolicies/main.bicep b/avm/res/cdn/profile/securityPolicies/main.bicep new file mode 100644 index 0000000000..1d98e235d9 --- /dev/null +++ b/avm/res/cdn/profile/securityPolicies/main.bicep @@ -0,0 +1,54 @@ +metadata name = 'CDN Profiles Security Policy' +metadata description = 'This module deploys a CDN Profile Security Policy.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The resource name.') +param name string + +@description('Conditional. The name of the parent CDN profile. Required if the template is used in a standalone deployment.') +param profileName string + +@description('Required. Resource ID of WAF Policy.') +param wafPolicyResourceId string + +// param associations associationsType +@description('Required. Waf associations (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies?pivots=deployment-language-bicep#securitypolicywebapplicationfirewallassociation for details).') +param associations associationsType + +resource profile 'Microsoft.Cdn/profiles@2023-05-01' existing = { + name: profileName +} + +resource securityPolicies 'Microsoft.Cdn/profiles/securityPolicies@2024-02-01' = { + name: name + parent: profile + properties: { + parameters: { + type: 'WebApplicationFirewall' + wafPolicy: { + id: wafPolicyResourceId + } + associations: associations + } + } +} + +@export() +type associationsType = { + @description('Required. List of domain resource id to associate with this resource.') + domains: { + @description('Required. ResourceID to domain that will be associated.') + id: string + }[] + @description('Required. List of patterns to match with this association.') + patternsToMatch: string[] +}[] + +@description('The name of the secrect.') +output name string = securityPolicies.name + +@description('The resource ID of the secrect.') +output resourceId string = securityPolicies.id + +@description('The name of the resource group the secret was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/cdn/profile/securityPolicies/main.json b/avm/res/cdn/profile/securityPolicies/main.json new file mode 100644 index 0000000000..be06e14c99 --- /dev/null +++ b/avm/res/cdn/profile/securityPolicies/main.json @@ -0,0 +1,128 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.30.23.60470", + "templateHash": "11561080659040848436" + }, + "name": "CDN Profiles Security Policy", + "description": "This module deploys a CDN Profile Security Policy.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "associationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "domains": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. ResourceID to domain that will be associated." + } + } + } + }, + "metadata": { + "description": "Required. List of domain resource id to associate with this resource." + } + }, + "patternsToMatch": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. List of patterns to match with this association." + } + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The resource name." + } + }, + "profileName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent CDN profile. Required if the template is used in a standalone deployment." + } + }, + "wafPolicyResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of WAF Policy." + } + }, + "associations": { + "$ref": "#/definitions/associationsType", + "metadata": { + "description": "Required. Waf associations (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies?pivots=deployment-language-bicep#securitypolicywebapplicationfirewallassociation for details)." + } + } + }, + "resources": { + "profile": { + "existing": true, + "type": "Microsoft.Cdn/profiles", + "apiVersion": "2023-05-01", + "name": "[parameters('profileName')]" + }, + "securityPolicies": { + "type": "Microsoft.Cdn/profiles/securityPolicies", + "apiVersion": "2024-02-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('name'))]", + "properties": { + "parameters": { + "type": "WebApplicationFirewall", + "wafPolicy": { + "id": "[parameters('wafPolicyResourceId')]" + }, + "associations": "[parameters('associations')]" + } + }, + "dependsOn": [ + "profile" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the secrect." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the secrect." + }, + "value": "[resourceId('Microsoft.Cdn/profiles/securityPolicies', parameters('profileName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the secret was created in." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/cdn/profile/tests/e2e/afd.premium/main.test.bicep b/avm/res/cdn/profile/tests/e2e/afd.premium/main.test.bicep new file mode 100644 index 0000000000..e735b9c94b --- /dev/null +++ b/avm/res/cdn/profile/tests/e2e/afd.premium/main.test.bicep @@ -0,0 +1,146 @@ +targetScope = 'subscription' + +metadata name = 'As Azure Front Door Premium' +metadata description = 'This instance deploys the module as Azure Front Door Premium.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cdn.profiles-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'cdnpafdp' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module wafPolicy 'br/public:avm/res/network/front-door-web-application-firewall-policy:0.2.0' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-dep-waf-policy-${serviceShort}' + params: { + name: 'dep${namePrefix}${serviceShort}wafpolicy' + sku: 'Premium_AzureFrontDoor' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: 'dep-${namePrefix}-test-${serviceShort}' + location: 'global' + originResponseTimeoutSeconds: 60 + sku: 'Premium_AzureFrontDoor' + customDomains: [ + { + name: 'dep-${namePrefix}-test-${serviceShort}-custom-domain' + hostName: 'dep-${namePrefix}-test-${serviceShort}-custom-domain.azurewebsites.net' + certificateType: 'ManagedCertificate' + } + ] + originGroups: [ + { + name: 'dep-${namePrefix}-test-${serviceShort}-origin-group' + loadBalancingSettings: { + additionalLatencyInMilliseconds: 50 + sampleSize: 4 + successfulSamplesRequired: 3 + } + origins: [ + { + name: 'dep-${namePrefix}-test-${serviceShort}-origin' + hostName: 'dep-${namePrefix}-test-${serviceShort}-origin.azurewebsites.net' + } + ] + } + ] + ruleSets: [ + { + name: 'dep${namePrefix}test${serviceShort}ruleset' + rules: [ + { + name: 'dep${namePrefix}test${serviceShort}rule' + order: 1 + actions: [ + { + name: 'UrlRedirect' + parameters: { + typeName: 'DeliveryRuleUrlRedirectActionParameters' + redirectType: 'PermanentRedirect' + destinationProtocol: 'Https' + customPath: '/test123' + customHostname: 'dev-etradefd.trade.azure.defra.cloud' + } + } + ] + } + ] + } + ] + afdEndpoints: [ + { + name: 'dep-${namePrefix}-test-${serviceShort}-afd-endpoint' + routes: [ + { + name: 'dep-${namePrefix}-test-${serviceShort}-afd-route' + originGroupName: 'dep-${namePrefix}-test-${serviceShort}-origin-group' + customDomainNames: ['dep-${namePrefix}-test-${serviceShort}-custom-domain'] + ruleSets: [ + { + name: 'dep${namePrefix}test${serviceShort}ruleset' + } + ] + } + ] + } + ] + securityPolicies: [ + { + name: 'dep${namePrefix}test${serviceShort}secpol' + associations: [ + { + domains: [ + { + id: resourceId( + subscription().subscriptionId, + resourceGroup.name, + 'Microsoft.Cdn/profiles/afdEndpoints', + 'dep-${namePrefix}-test-${serviceShort}', + 'dep-${namePrefix}-test-${serviceShort}-afd-endpoint' + ) + } + ] + patternsToMatch: [ + '/*' + ] + } + ] + wafPolicyResourceId: wafPolicy.outputs.resourceId + } + ] + } + } +] diff --git a/avm/res/cdn/profile/version.json b/avm/res/cdn/profile/version.json index a8eda31021..9ed3662aba 100644 --- a/avm/res/cdn/profile/version.json +++ b/avm/res/cdn/profile/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +}