diff --git a/.github/workflows/avm.platform.manage-workflow-issue.yml b/.github/workflows/avm.platform.manage-workflow-issue.yml index 8d37f57d3d..9c80793d18 100644 --- a/.github/workflows/avm.platform.manage-workflow-issue.yml +++ b/.github/workflows/avm.platform.manage-workflow-issue.yml @@ -3,6 +3,7 @@ name: "avm.platform.manage-workflow-issue" on: schedule: - cron: "30 5 * * *" # Every day at 5:30 am + workflow_dispatch: jobs: manage-issues: @@ -14,16 +15,22 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - - env: - GH_TOKEN: ${{ github.token }} - name: Manage issues + - uses: tibdex/github-app-token@v2 + id: generate-token + with: + app_id: ${{ secrets.APP_ID }} + private_key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Manage issues shell: pwsh + env: + GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} run: | # Load used functions . (Join-Path $env:GITHUB_WORKSPACE 'avm' 'utilities' 'pipelines' 'platform' 'Set-AvmGithubIssueForWorkflow.ps1') $functionInput = @{ Repo = "${{ github.repository_owner }}/${{ github.event.repository.name }}" + RepoRoot = $env:GITHUB_WORKSPACE LimitNumberOfRuns = 500 LimitInDays = 2 IgnoreWorkflows = @() diff --git a/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml b/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml index 775d83c94d..48cc265747 100644 --- a/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml +++ b/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml @@ -16,16 +16,22 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - - env: - GH_TOKEN: ${{ github.token }} - name: "Run scripts" + - uses: tibdex/github-app-token@v2 + id: generate-token + with: + app_id: ${{ secrets.TEAM_LINTER_APP_ID }} + private_key: ${{ secrets.TEAM_LINTER_PRIVATE_KEY }} + - name: "Run scripts" shell: pwsh + env: + GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} run: | # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'avm' 'utilities' 'pipelines' 'platform' 'Set-AvmGitHubIssueOwnerConfig.ps1') + . (Join-Path $env:GITHUB_WORKSPACE 'avm' 'utilities' 'pipelines' 'platform' 'Set-AvmGitHubIssueOwnerConfig.ps1') $functionInput = @{ - Repo = "${{ github.repository_owner }}/${{ github.event.repository.name }}" + Repo = "${{ github.repository_owner }}/${{ github.event.repository.name }}" + RepoRoot = $env:GITHUB_WORKSPACE IssueUrl = "${{ github.event.issue.url }}" } diff --git a/avm/res/aad/domain-service/README.md b/avm/res/aad/domain-service/README.md index 87f33b1d5f..fed0bb3998 100644 --- a/avm/res/aad/domain-service/README.md +++ b/avm/res/aad/domain-service/README.md @@ -1,6 +1,6 @@ # Azure Active Directory Domain Services `[Microsoft.AAD/domainServices]` -This module deploys an Azure Active Directory Domain Services (AADDS). +This module deploys an Azure Active Directory Domain Services (AADDS) instance. ## Navigation diff --git a/avm/res/aad/domain-service/main.bicep b/avm/res/aad/domain-service/main.bicep index cd0e56bfcd..ee8122ec51 100644 --- a/avm/res/aad/domain-service/main.bicep +++ b/avm/res/aad/domain-service/main.bicep @@ -1,5 +1,5 @@ metadata name = 'Azure Active Directory Domain Services' -metadata description = 'This module deploys an Azure Active Directory Domain Services (AADDS).' +metadata description = 'This module deploys an Azure Active Directory Domain Services (AADDS) instance.' metadata owner = 'Azure/module-maintainers' @minLength(1) diff --git a/avm/res/aad/domain-service/main.json b/avm/res/aad/domain-service/main.json index c210fd41ff..b82ba31545 100644 --- a/avm/res/aad/domain-service/main.json +++ b/avm/res/aad/domain-service/main.json @@ -6,10 +6,10 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "10683253750371964167" + "templateHash": "7265620724598107360" }, "name": "Azure Active Directory Domain Services", - "description": "This module deploys an Azure Active Directory Domain Services (AADDS).", + "description": "This module deploys an Azure Active Directory Domain Services (AADDS) instance.", "owner": "Azure/module-maintainers" }, "definitions": { diff --git a/avm/res/app-configuration/configuration-store/tests/e2e/encr/dependencies.bicep b/avm/res/app-configuration/configuration-store/tests/e2e/encr/dependencies.bicep index bd17946f56..8e5006c291 100644 --- a/avm/res/app-configuration/configuration-store/tests/e2e/encr/dependencies.bicep +++ b/avm/res/app-configuration/configuration-store/tests/e2e/encr/dependencies.bicep @@ -21,8 +21,8 @@ resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true - softDeleteRetentionInDays: 90 + enablePurgeProtection: true // Required for encryption to work + softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true enabledForDeployment: true diff --git a/avm/res/app/managed-environment/README.md b/avm/res/app/managed-environment/README.md index b91247a73e..feac29db4e 100644 --- a/avm/res/app/managed-environment/README.md +++ b/avm/res/app/managed-environment/README.md @@ -48,7 +48,21 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' logAnalyticsWorkspaceResourceId: '' name: 'amemin001' // Non-required parameters + dockerBridgeCidr: '172.16.0.1/28' + infrastructureResourceGroupName: '' + infrastructureSubnetId: '' + internal: true location: '' + platformReservedCidr: '172.17.17.0/24' + platformReservedDnsIP: '172.17.17.17' + workloadProfiles: [ + { + maximumCount: 3 + minimumCount: 0 + name: 'CAW01' + workloadProfileType: 'D4' + } + ] } } ``` @@ -73,8 +87,36 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' "value": "amemin001" }, // Non-required parameters + "dockerBridgeCidr": { + "value": "172.16.0.1/28" + }, + "infrastructureResourceGroupName": { + "value": "" + }, + "infrastructureSubnetId": { + "value": "" + }, + "internal": { + "value": true + }, "location": { "value": "" + }, + "platformReservedCidr": { + "value": "172.17.17.0/24" + }, + "platformReservedDnsIP": { + "value": "172.17.17.17" + }, + "workloadProfiles": { + "value": [ + { + "maximumCount": 3, + "minimumCount": 0, + "name": "CAW01", + "workloadProfileType": "D4" + } + ] } } } @@ -397,7 +439,13 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' | Parameter | Type | Description | | :-- | :-- | :-- | -| [`infrastructureSubnetId`](#parameter-infrastructuresubnetid) | string | Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if "internal" is set to true. | +| [`dockerBridgeCidr`](#parameter-dockerbridgecidr) | string | CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant. | +| [`infrastructureResourceGroupName`](#parameter-infrastructureresourcegroupname) | string | Name of the infrastructure resource group. If not provided, it will be set with a default value. Required if zoneRedundant is set to true to make the resource WAF compliant. | +| [`infrastructureSubnetId`](#parameter-infrastructuresubnetid) | string | Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if "internal" is set to true. Required if zoneRedundant is set to true to make the resource WAF compliant. | +| [`internal`](#parameter-internal) | bool | Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then "infrastructureSubnetId" must be provided. Required if zoneRedundant is set to true to make the resource WAF compliant. | +| [`platformReservedCidr`](#parameter-platformreservedcidr) | string | IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant. | +| [`platformReservedDnsIP`](#parameter-platformreserveddnsip) | string | An IP address from the IP range defined by "platformReservedCidr" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant. | +| [`workloadProfiles`](#parameter-workloadprofiles) | array | Workload profiles configured for the Managed Environment. Required if zoneRedundant is set to true to make the resource WAF compliant. | **Optional parameters** @@ -408,18 +456,12 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' | [`daprAIConnectionString`](#parameter-dapraiconnectionstring) | securestring | Application Insights connection string used by Dapr to export Service to Service communication telemetry. | | [`daprAIInstrumentationKey`](#parameter-dapraiinstrumentationkey) | securestring | Azure Monitor instrumentation key used by Dapr to export Service to Service communication telemetry. | | [`dnsSuffix`](#parameter-dnssuffix) | string | DNS suffix for the environment domain. | -| [`dockerBridgeCidr`](#parameter-dockerbridgecidr) | string | CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | -| [`infrastructureResourceGroupName`](#parameter-infrastructureresourcegroupname) | string | Name of the infrastructure resource group. If not provided, it will be set with a default value. | -| [`internal`](#parameter-internal) | bool | Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then "infrastructureSubnetId" must be provided. | | [`location`](#parameter-location) | string | Location for all Resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`logsDestination`](#parameter-logsdestination) | string | Logs destination. | -| [`platformReservedCidr`](#parameter-platformreservedcidr) | string | IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. | -| [`platformReservedDnsIP`](#parameter-platformreserveddnsip) | string | An IP address from the IP range defined by "platformReservedCidr" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`tags`](#parameter-tags) | object | Tags of the resource. | -| [`workloadProfiles`](#parameter-workloadprofiles) | array | Workload profiles configured for the Managed Environment. | | [`zoneRedundant`](#parameter-zoneredundant) | bool | Whether or not this Managed Environment is zone-redundant. | ### Parameter: `logAnalyticsWorkspaceResourceId` @@ -436,14 +478,62 @@ Name of the Container Apps Managed Environment. - Required: Yes - Type: string +### Parameter: `dockerBridgeCidr` + +CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `infrastructureResourceGroupName` + +Name of the infrastructure resource group. If not provided, it will be set with a default value. Required if zoneRedundant is set to true to make the resource WAF compliant. + +- Required: No +- Type: string +- Default: `[take(format('ME_{0}', parameters('name')), 63)]` + ### Parameter: `infrastructureSubnetId` -Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if "internal" is set to true. +Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if "internal" is set to true. Required if zoneRedundant is set to true to make the resource WAF compliant. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `internal` + +Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then "infrastructureSubnetId" must be provided. Required if zoneRedundant is set to true to make the resource WAF compliant. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `platformReservedCidr` + +IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `platformReservedDnsIP` + +An IP address from the IP range defined by "platformReservedCidr" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant. - Required: No - Type: string - Default: `''` +### Parameter: `workloadProfiles` + +Workload profiles configured for the Managed Environment. Required if zoneRedundant is set to true to make the resource WAF compliant. + +- Required: No +- Type: array +- Default: `[]` + ### Parameter: `certificatePassword` Password of the certificate used by the custom domain. @@ -484,14 +574,6 @@ DNS suffix for the environment domain. - Type: string - Default: `''` -### Parameter: `dockerBridgeCidr` - -CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. - -- Required: No -- Type: string -- Default: `''` - ### Parameter: `enableTelemetry` Enable/Disable usage telemetry for module. @@ -500,22 +582,6 @@ Enable/Disable usage telemetry for module. - Type: bool - Default: `True` -### Parameter: `infrastructureResourceGroupName` - -Name of the infrastructure resource group. If not provided, it will be set with a default value. - -- Required: No -- Type: string -- Default: `[take(format('ME_{0}', parameters('name')), 63)]` - -### Parameter: `internal` - -Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then "infrastructureSubnetId" must be provided. - -- Required: No -- Type: bool -- Default: `False` - ### Parameter: `location` Location for all Resources. @@ -568,22 +634,6 @@ Logs destination. - Type: string - Default: `'log-analytics'` -### Parameter: `platformReservedCidr` - -IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. - -- Required: No -- Type: string -- Default: `''` - -### Parameter: `platformReservedDnsIP` - -An IP address from the IP range defined by "platformReservedCidr" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. - -- Required: No -- Type: string -- Default: `''` - ### Parameter: `roleAssignments` Array of role assignments to create. @@ -680,21 +730,13 @@ Tags of the resource. - Required: No - Type: object -### Parameter: `workloadProfiles` - -Workload profiles configured for the Managed Environment. - -- Required: No -- Type: array -- Default: `[]` - ### Parameter: `zoneRedundant` Whether or not this Managed Environment is zone-redundant. - Required: No - Type: bool -- Default: `False` +- Default: `True` ## Outputs diff --git a/avm/res/app/managed-environment/main.bicep b/avm/res/app/managed-environment/main.bicep index 9810f65087..6ed8dc58fd 100644 --- a/avm/res/app/managed-environment/main.bicep +++ b/avm/res/app/managed-environment/main.bicep @@ -31,23 +31,23 @@ param daprAIConnectionString string = '' @secure() param daprAIInstrumentationKey string = '' -@description('Optional. CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform.') +@description('Conditional. CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant.') param dockerBridgeCidr string = '' -@description('Conditional. Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if "internal" is set to true.') +@description('Conditional. Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if "internal" is set to true. Required if zoneRedundant is set to true to make the resource WAF compliant.') param infrastructureSubnetId string = '' -@description('Optional. Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then "infrastructureSubnetId" must be provided.') +@description('Conditional. Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then "infrastructureSubnetId" must be provided. Required if zoneRedundant is set to true to make the resource WAF compliant.') param internal bool = false -@description('Optional. IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform.') +@description('Conditional. IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant.') param platformReservedCidr string = '' -@description('Optional. An IP address from the IP range defined by "platformReservedCidr" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform.') +@description('Conditional. An IP address from the IP range defined by "platformReservedCidr" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant.') param platformReservedDnsIP string = '' @description('Optional. Whether or not this Managed Environment is zone-redundant.') -param zoneRedundant bool = false +param zoneRedundant bool = true @description('Optional. Password of the certificate used by the custom domain.') @secure() @@ -63,42 +63,50 @@ param dnsSuffix string = '' @description('Optional. The lock settings of the service.') param lock lockType -@description('Optional. Workload profiles configured for the Managed Environment.') +@description('Conditional. Workload profiles configured for the Managed Environment. Required if zoneRedundant is set to true to make the resource WAF compliant.') param workloadProfiles array = [] -@description('Optional. Name of the infrastructure resource group. If not provided, it will be set with a default value.') +@description('Conditional. Name of the infrastructure resource group. If not provided, it will be set with a default value. Required if zoneRedundant is set to true to make the resource WAF compliant.') param infrastructureResourceGroupName string = take('ME_${name}', 63) var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') - 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) } -resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { - name: '46d3xbcp.res.app-managedenvironment.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' - properties: { - mode: 'Incremental' - template: { - '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' - contentVersion: '1.0.0.0' - resources: [] - outputs: { - telemetry: { - type: 'String' - value: 'For more information, see https://aka.ms/avm/TelemetryInfo' +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = + if (enableTelemetry) { + name: '46d3xbcp.res.app-managedenvironment.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } } } } } -} -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = if (!empty(logAnalyticsWorkspaceResourceId)) { - name: last(split(logAnalyticsWorkspaceResourceId, '/'))! - scope: resourceGroup(split(logAnalyticsWorkspaceResourceId, '/')[2], split(logAnalyticsWorkspaceResourceId, '/')[4]) -} +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = + if (!empty(logAnalyticsWorkspaceResourceId)) { + name: last(split(logAnalyticsWorkspaceResourceId, '/'))! + scope: resourceGroup(split(logAnalyticsWorkspaceResourceId, '/')[2], split(logAnalyticsWorkspaceResourceId, '/')[4]) + } resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = { name: name @@ -132,28 +140,37 @@ resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = { } } -resource managedEnvironment_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(managedEnvironment.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) - properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') ? roleAssignment.roleDefinitionIdOrName : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) - principalId: roleAssignment.principalId - description: roleAssignment.?description - principalType: roleAssignment.?principalType - condition: roleAssignment.?condition - conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set - delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId +resource managedEnvironment_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (roleAssignments ?? []): { + name: guid(managedEnvironment.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) + ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] + : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: managedEnvironment } - scope: managedEnvironment -}] - -resource managedEnvironment_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { - name: lock.?name ?? 'lock-${name}' - properties: { - level: lock.?kind ?? '' - notes: lock.?kind == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot delete or modify the resource or child resources.' +] + +resource managedEnvironment_lock 'Microsoft.Authorization/locks@2020-05-01' = + if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' + ? 'Cannot delete resource or child resources.' + : 'Cannot delete or modify the resource or child resources.' + } + scope: managedEnvironment } - scope: managedEnvironment -} @description('The name of the resource group the Managed Environment was deployed into.') output resourceGroupName string = resourceGroup().name diff --git a/avm/res/app/managed-environment/main.json b/avm/res/app/managed-environment/main.json index 5fd1fd0e1a..005a9294d1 100644 --- a/avm/res/app/managed-environment/main.json +++ b/avm/res/app/managed-environment/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12287523603498112228" + "version": "0.26.54.24096", + "templateHash": "1208664329573960589" }, "name": "App ManagedEnvironments", "description": "This module deploys an App Managed Environment (also known as a Container App Environment).", @@ -170,40 +170,40 @@ "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform." + "description": "Conditional. CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant." } }, "infrastructureSubnetId": { "type": "string", "defaultValue": "", "metadata": { - "description": "Conditional. Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if \"internal\" is set to true." + "description": "Conditional. Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if \"internal\" is set to true. Required if zoneRedundant is set to true to make the resource WAF compliant." } }, "internal": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then \"infrastructureSubnetId\" must be provided." + "description": "Conditional. Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then \"infrastructureSubnetId\" must be provided. Required if zoneRedundant is set to true to make the resource WAF compliant." } }, "platformReservedCidr": { "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform." + "description": "Conditional. IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant." } }, "platformReservedDnsIP": { "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. An IP address from the IP range defined by \"platformReservedCidr\" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform." + "description": "Conditional. An IP address from the IP range defined by \"platformReservedCidr\" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant." } }, "zoneRedundant": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { "description": "Optional. Whether or not this Managed Environment is zone-redundant." } @@ -239,14 +239,14 @@ "type": "array", "defaultValue": [], "metadata": { - "description": "Optional. Workload profiles configured for the Managed Environment." + "description": "Conditional. Workload profiles configured for the Managed Environment. Required if zoneRedundant is set to true to make the resource WAF compliant." } }, "infrastructureResourceGroupName": { "type": "string", "defaultValue": "[take(format('ME_{0}', parameters('name')), 63)]", "metadata": { - "description": "Optional. Name of the infrastructure resource group. If not provided, it will be set with a default value." + "description": "Conditional. Name of the infrastructure resource group. If not provided, it will be set with a default value. Required if zoneRedundant is set to true to make the resource WAF compliant." } } }, diff --git a/avm/res/app/managed-environment/tests/e2e/defaults/dependencies.bicep b/avm/res/app/managed-environment/tests/e2e/defaults/dependencies.bicep index 737827c1fd..c4ea8327c4 100644 --- a/avm/res/app/managed-environment/tests/e2e/defaults/dependencies.bicep +++ b/avm/res/app/managed-environment/tests/e2e/defaults/dependencies.bicep @@ -4,6 +4,11 @@ param location string = resourceGroup().location @description('Required. The name of the Log Analytics Workspace to create.') param logAnalyticsWorkspaceName string +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +var addressPrefix = '10.0.0.0/16' + resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { name: logAnalyticsWorkspaceName location: location @@ -18,5 +23,37 @@ resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10 }) } +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + delegations: [ + { + name: 'Microsoft.App.environments' + properties: { + serviceName: 'Microsoft.App/environments' + } + } + ] + } + } + ] + } + +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + @description('The resource ID of the created Log Analytics Workspace.') output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id diff --git a/avm/res/app/managed-environment/tests/e2e/defaults/main.test.bicep b/avm/res/app/managed-environment/tests/e2e/defaults/main.test.bicep index 53f5ae658f..2e2152ec1a 100644 --- a/avm/res/app/managed-environment/tests/e2e/defaults/main.test.bicep +++ b/avm/res/app/managed-environment/tests/e2e/defaults/main.test.bicep @@ -35,6 +35,7 @@ module nestedDependencies 'dependencies.bicep' = { name: '${uniqueString(deployment().name, resourceLocation)}-paramNested' params: { location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' } } @@ -49,8 +50,22 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}001' - location: resourceLocation logAnalyticsWorkspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + location: resourceLocation + workloadProfiles: [ + { + workloadProfileType: 'D4' + name: 'CAW01' + minimumCount: 0 + maximumCount: 3 + } + ] + internal: true + dockerBridgeCidr: '172.16.0.1/28' + platformReservedCidr: '172.17.17.0/24' + platformReservedDnsIP: '172.17.17.17' + infrastructureSubnetId: nestedDependencies.outputs.subnetResourceId + infrastructureResourceGroupName: 'me-${resourceGroupName}' } dependsOn: [ nestedDependencies diff --git a/avm/res/automation/automation-account/tests/e2e/encr/dependencies.bicep b/avm/res/automation/automation-account/tests/e2e/encr/dependencies.bicep index 49d0dfa3aa..859e78741b 100644 --- a/avm/res/automation/automation-account/tests/e2e/encr/dependencies.bicep +++ b/avm/res/automation/automation-account/tests/e2e/encr/dependencies.bicep @@ -21,8 +21,8 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 - enablePurgeProtection: true enabledForTemplateDeployment: true enabledForDiskEncryption: true enabledForDeployment: true diff --git a/avm/res/batch/batch-account/tests/e2e/encr/dependencies.bicep b/avm/res/batch/batch-account/tests/e2e/encr/dependencies.bicep index 7519eccd2b..7d610a83e7 100644 --- a/avm/res/batch/batch-account/tests/e2e/encr/dependencies.bicep +++ b/avm/res/batch/batch-account/tests/e2e/encr/dependencies.bicep @@ -74,7 +74,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/cache/redis/README.md b/avm/res/cache/redis/README.md index a8e005dfd5..35061323b5 100644 --- a/avm/res/cache/redis/README.md +++ b/avm/res/cache/redis/README.md @@ -398,6 +398,8 @@ module redis 'br/public:avm/res/cache/redis:' = { } ] redisVersion: '6' + replicasPerMaster: 3 + replicasPerPrimary: 3 shardCount: 1 skuName: 'Premium' tags: { @@ -408,6 +410,7 @@ module redis 'br/public:avm/res/cache/redis:' = { zones: [ 1 2 + 3 ] } } @@ -487,6 +490,12 @@ module redis 'br/public:avm/res/cache/redis:' = { "redisVersion": { "value": "6" }, + "replicasPerMaster": { + "value": 3 + }, + "replicasPerPrimary": { + "value": 3 + }, "shardCount": { "value": 1 }, @@ -505,7 +514,8 @@ module redis 'br/public:avm/res/cache/redis:' = { "zones": { "value": [ 1, - 2 + 2, + 3 ] } } @@ -541,7 +551,7 @@ module redis 'br/public:avm/res/cache/redis:' = { | [`redisConfiguration`](#parameter-redisconfiguration) | object | All Redis Settings. Few possible keys: rdb-backup-enabled,rdb-storage-connection-string,rdb-backup-frequency,maxmemory-delta,maxmemory-policy,notify-keyspace-events,maxmemory-samples,slowlog-log-slower-than,slowlog-max-len,list-max-ziplist-entries,list-max-ziplist-value,hash-max-ziplist-entries,hash-max-ziplist-value,set-max-intset-entries,zset-max-ziplist-entries,zset-max-ziplist-value etc. | | [`redisVersion`](#parameter-redisversion) | string | Redis version. Only major version will be used in PUT/PATCH request with current valid values: (4, 6). | | [`replicasPerMaster`](#parameter-replicaspermaster) | int | The number of replicas to be created per primary. | -| [`replicasPerPrimary`](#parameter-replicasperprimary) | int | The number of replicas to be created per primary. | +| [`replicasPerPrimary`](#parameter-replicasperprimary) | int | The number of replicas to be created per primary. Needs to be the same as replicasPerMaster for a Premium Cluster Cache. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`shardCount`](#parameter-shardcount) | int | The number of shards to be created on a Premium Cluster Cache. | | [`skuName`](#parameter-skuname) | string | The type of Redis cache to deploy. | @@ -1217,15 +1227,15 @@ The number of replicas to be created per primary. - Required: No - Type: int -- Default: `1` +- Default: `3` ### Parameter: `replicasPerPrimary` -The number of replicas to be created per primary. +The number of replicas to be created per primary. Needs to be the same as replicasPerMaster for a Premium Cluster Cache. - Required: No - Type: int -- Default: `1` +- Default: `3` ### Parameter: `roleAssignments` @@ -1330,7 +1340,7 @@ The type of Redis cache to deploy. - Required: No - Type: string -- Default: `'Basic'` +- Default: `'Premium'` - Allowed: ```Bicep [ @@ -1385,7 +1395,14 @@ If the zoneRedundant parameter is true, replicas will be provisioned in the avai - Required: No - Type: array -- Default: `[]` +- Default: + ```Bicep + [ + 1 + 2 + 3 + ] + ``` ## Outputs diff --git a/avm/res/cache/redis/main.bicep b/avm/res/cache/redis/main.bicep index a7975ce724..7cd77eda1c 100644 --- a/avm/res/cache/redis/main.bicep +++ b/avm/res/cache/redis/main.bicep @@ -51,11 +51,11 @@ param redisVersion string = '6' @minValue(1) @description('Optional. The number of replicas to be created per primary.') -param replicasPerMaster int = 1 +param replicasPerMaster int = 3 @minValue(1) -@description('Optional. The number of replicas to be created per primary.') -param replicasPerPrimary int = 1 +@description('Optional. The number of replicas to be created per primary. Needs to be the same as replicasPerMaster for a Premium Cluster Cache.') +param replicasPerPrimary int = 3 @minValue(1) @description('Optional. The number of shards to be created on a Premium Cluster Cache.') @@ -79,7 +79,7 @@ param capacity int = 1 'Standard' ]) @description('Optional. The type of Redis cache to deploy.') -param skuName string = 'Basic' +param skuName string = 'Premium' @description('Optional. Static IP address. Optionally, may be specified when deploying a Redis cache inside an existing Azure Virtual Network; auto assigned by default.') param staticIP string = '' @@ -94,7 +94,7 @@ param tenantSettings object = {} param zoneRedundant bool = true @description('Optional. If the zoneRedundant parameter is true, replicas will be provisioned in the availability zones specified here. Otherwise, the service will choose where replicas are deployed.') -param zones array = [] +param zones int[] = [1, 2, 3] @description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') param privateEndpoints privateEndpointType diff --git a/avm/res/cache/redis/main.json b/avm/res/cache/redis/main.json index 0dcf9d3c88..b7da02c047 100644 --- a/avm/res/cache/redis/main.json +++ b/avm/res/cache/redis/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "7874819819895628744" + "templateHash": "15170120544539480286" }, "name": "Redis Cache", "description": "This module deploys a Redis Cache.", @@ -535,7 +535,7 @@ }, "replicasPerMaster": { "type": "int", - "defaultValue": 1, + "defaultValue": 3, "minValue": 1, "metadata": { "description": "Optional. The number of replicas to be created per primary." @@ -543,10 +543,10 @@ }, "replicasPerPrimary": { "type": "int", - "defaultValue": 1, + "defaultValue": 3, "minValue": 1, "metadata": { - "description": "Optional. The number of replicas to be created per primary." + "description": "Optional. The number of replicas to be created per primary. Needs to be the same as replicasPerMaster for a Premium Cluster Cache." } }, "shardCount": { @@ -575,7 +575,7 @@ }, "skuName": { "type": "string", - "defaultValue": "Basic", + "defaultValue": "Premium", "allowedValues": [ "Basic", "Premium", @@ -615,7 +615,14 @@ }, "zones": { "type": "array", - "defaultValue": [], + "items": { + "type": "int" + }, + "defaultValue": [ + 1, + 2, + 3 + ], "metadata": { "description": "Optional. If the zoneRedundant parameter is true, replicas will be provisioned in the availability zones specified here. Otherwise, the service will choose where replicas are deployed." } diff --git a/avm/res/cache/redis/tests/e2e/waf-aligned/main.test.bicep b/avm/res/cache/redis/tests/e2e/waf-aligned/main.test.bicep index bd46cbeaa6..6e86ccb362 100644 --- a/avm/res/cache/redis/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/cache/redis/tests/e2e/waf-aligned/main.test.bicep @@ -88,7 +88,7 @@ module testDeployment '../../../main.bicep' = [ } minimumTlsVersion: '1.2' zoneRedundant: true - zones: [1, 2] + zones: [1, 2, 3] privateEndpoints: [ { privateDnsZoneResourceIds: [ @@ -103,6 +103,8 @@ module testDeployment '../../../main.bicep' = [ } ] redisVersion: '6' + replicasPerMaster: 3 + replicasPerPrimary: 3 shardCount: 1 skuName: 'Premium' managedIdentities: { diff --git a/avm/res/cognitive-services/account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep b/avm/res/cognitive-services/account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep index 64f4ad94b8..a9da625760 100644 --- a/avm/res/cognitive-services/account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep +++ b/avm/res/cognitive-services/account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep @@ -31,7 +31,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/cognitive-services/account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep b/avm/res/cognitive-services/account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep index 1ad042356a..b12458a9b8 100644 --- a/avm/res/cognitive-services/account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep +++ b/avm/res/cognitive-services/account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep @@ -21,7 +21,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/disk-encryption-set/tests/e2e/accessPolicies/dependencies.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/accessPolicies/dependencies.bicep index 4bc293926f..810b6e459f 100644 --- a/avm/res/compute/disk-encryption-set/tests/e2e/accessPolicies/dependencies.bicep +++ b/avm/res/compute/disk-encryption-set/tests/e2e/accessPolicies/dependencies.bicep @@ -21,7 +21,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by disk encryption set + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/disk-encryption-set/tests/e2e/defaults/dependencies.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/defaults/dependencies.bicep index a79f18bab3..19eadf3cfc 100644 --- a/avm/res/compute/disk-encryption-set/tests/e2e/defaults/dependencies.bicep +++ b/avm/res/compute/disk-encryption-set/tests/e2e/defaults/dependencies.bicep @@ -13,7 +13,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by disk encryption set + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/disk-encryption-set/tests/e2e/max/dependencies.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/max/dependencies.bicep index e6e79856d7..bf2bbaace8 100644 --- a/avm/res/compute/disk-encryption-set/tests/e2e/max/dependencies.bicep +++ b/avm/res/compute/disk-encryption-set/tests/e2e/max/dependencies.bicep @@ -21,7 +21,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by disk encryption set + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/disk-encryption-set/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/waf-aligned/dependencies.bicep index 336e29b226..eb651cd09b 100644 --- a/avm/res/compute/disk-encryption-set/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/compute/disk-encryption-set/tests/e2e/waf-aligned/dependencies.bicep @@ -21,7 +21,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by disk encryption set + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/image/tests/e2e/max/dependencies.bicep b/avm/res/compute/image/tests/e2e/max/dependencies.bicep index 6a26edc6cf..229b7d2262 100644 --- a/avm/res/compute/image/tests/e2e/max/dependencies.bicep +++ b/avm/res/compute/image/tests/e2e/max/dependencies.bicep @@ -155,7 +155,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required for encrption to work + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/image/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/compute/image/tests/e2e/waf-aligned/dependencies.bicep index 6a26edc6cf..229b7d2262 100644 --- a/avm/res/compute/image/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/compute/image/tests/e2e/waf-aligned/dependencies.bicep @@ -155,7 +155,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required for encrption to work + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/virtual-machine-scale-set/tests/e2e/linux.ssecmk/dependencies.bicep b/avm/res/compute/virtual-machine-scale-set/tests/e2e/linux.ssecmk/dependencies.bicep index 324e66e29f..4d041e8ada 100644 --- a/avm/res/compute/virtual-machine-scale-set/tests/e2e/linux.ssecmk/dependencies.bicep +++ b/avm/res/compute/virtual-machine-scale-set/tests/e2e/linux.ssecmk/dependencies.bicep @@ -50,7 +50,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by disk encryption set + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/dependencies.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/dependencies.bicep index 3ae96a4216..bb849994dd 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/dependencies.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/dependencies.bicep @@ -41,7 +41,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by disk encryption set + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/container-instance/container-group/tests/e2e/encr/dependencies.bicep b/avm/res/container-instance/container-group/tests/e2e/encr/dependencies.bicep index b908867ee2..1a2b461308 100644 --- a/avm/res/container-instance/container-group/tests/e2e/encr/dependencies.bicep +++ b/avm/res/container-instance/container-group/tests/e2e/encr/dependencies.bicep @@ -23,7 +23,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/container-registry/registry/tests/e2e/encr/dependencies.bicep b/avm/res/container-registry/registry/tests/e2e/encr/dependencies.bicep index 3d3446c847..ba949b79ea 100644 --- a/avm/res/container-registry/registry/tests/e2e/encr/dependencies.bicep +++ b/avm/res/container-registry/registry/tests/e2e/encr/dependencies.bicep @@ -46,7 +46,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/container-service/managed-cluster/tests/e2e/azure/dependencies.bicep b/avm/res/container-service/managed-cluster/tests/e2e/azure/dependencies.bicep index e2274a7fbb..86b3297af0 100644 --- a/avm/res/container-service/managed-cluster/tests/e2e/azure/dependencies.bicep +++ b/avm/res/container-service/managed-cluster/tests/e2e/azure/dependencies.bicep @@ -67,7 +67,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-11-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by nodepool vmss + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/databricks/workspace/tests/e2e/max/dependencies.bicep b/avm/res/databricks/workspace/tests/e2e/max/dependencies.bicep index 5bf7118978..ea6c34b09e 100644 --- a/avm/res/databricks/workspace/tests/e2e/max/dependencies.bicep +++ b/avm/res/databricks/workspace/tests/e2e/max/dependencies.bicep @@ -47,7 +47,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true @@ -73,7 +73,7 @@ resource keyVaultDisk 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/databricks/workspace/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/databricks/workspace/tests/e2e/waf-aligned/dependencies.bicep index f8aa5aab74..689a482cfd 100644 --- a/avm/res/databricks/workspace/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/databricks/workspace/tests/e2e/waf-aligned/dependencies.bicep @@ -47,7 +47,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true @@ -73,7 +73,7 @@ resource keyVaultDisk 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/dependencies2.bicep b/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/dependencies2.bicep index 0077f627ce..2966ac2358 100644 --- a/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/dependencies2.bicep +++ b/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/dependencies2.bicep @@ -30,8 +30,8 @@ resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true - softDeleteRetentionInDays: 90 + enablePurgeProtection: true // Required for encryption to work + softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true enabledForDeployment: true @@ -74,8 +74,8 @@ resource geoBackupKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true - softDeleteRetentionInDays: 90 + enablePurgeProtection: true // Required for encryption to work + softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true enabledForDeployment: true diff --git a/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/encr/dependencies.bicep b/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/encr/dependencies.bicep index 52be6351dc..367f400a42 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/encr/dependencies.bicep +++ b/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/encr/dependencies.bicep @@ -8,44 +8,47 @@ param keyVaultName string param managedIdentityName string resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { - name: managedIdentityName - location: location + name: managedIdentityName + location: location } resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = { - name: keyVaultName - location: location - properties: { - sku: { - family: 'A' - name: 'standard' - } - tenantId: tenant().tenantId - enablePurgeProtection: true - softDeleteRetentionInDays: 7 - enabledForTemplateDeployment: true - enabledForDiskEncryption: true - enabledForDeployment: true - enableRbacAuthorization: true - accessPolicies: [] + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' } - - resource key 'keys@2023-07-01' = { - name: 'keyEncryptionKey' - properties: { - kty: 'RSA' - } + tenantId: tenant().tenantId + enablePurgeProtection: true // Required for encryption to work + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2023-07-01' = { + name: 'keyEncryptionKey' + properties: { + kty: 'RSA' } + } } resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-Key-Reader-RoleAssignment') - scope: keyVault::key - properties: { - principalId: managedIdentity.properties.principalId - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') // Key Vault Crypto User - principalType: 'ServicePrincipal' - } + name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-Key-Reader-RoleAssignment') + scope: keyVault::key + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '12338af0-0e69-4776-bea7-57ae8d297424' + ) // Key Vault Crypto User + principalType: 'ServicePrincipal' + } } @description('The resource ID of the created Managed Identity.') diff --git a/avm/res/dev-test-lab/lab/tests/e2e/max/dependencies.bicep b/avm/res/dev-test-lab/lab/tests/e2e/max/dependencies.bicep index 376758c41c..5e1c0b1d25 100644 --- a/avm/res/dev-test-lab/lab/tests/e2e/max/dependencies.bicep +++ b/avm/res/dev-test-lab/lab/tests/e2e/max/dependencies.bicep @@ -32,7 +32,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required for encrption to work + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/event-hub/namespace/tests/e2e/encr/dependencies.bicep b/avm/res/event-hub/namespace/tests/e2e/encr/dependencies.bicep index 6c4085b1e5..c8d5689b90 100644 --- a/avm/res/event-hub/namespace/tests/e2e/encr/dependencies.bicep +++ b/avm/res/event-hub/namespace/tests/e2e/encr/dependencies.bicep @@ -46,7 +46,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by event hub namespace + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/machine-learning-services/workspace/tests/e2e/encr/dependencies.bicep b/avm/res/machine-learning-services/workspace/tests/e2e/encr/dependencies.bicep index 4e41990054..99e9ed9814 100644 --- a/avm/res/machine-learning-services/workspace/tests/e2e/encr/dependencies.bicep +++ b/avm/res/machine-learning-services/workspace/tests/e2e/encr/dependencies.bicep @@ -24,7 +24,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required for CMK + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/network/connection/tests/e2e/defaults/dependencies.bicep b/avm/res/network/connection/tests/e2e/defaults/dependencies.bicep index a8398dc99e..3cb8518745 100644 --- a/avm/res/network/connection/tests/e2e/defaults/dependencies.bicep +++ b/avm/res/network/connection/tests/e2e/defaults/dependencies.bicep @@ -42,6 +42,12 @@ resource primaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = resource primaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { name: primaryPublicIPName location: location + properties: { + publicIPAllocationMethod: 'Static' + } + sku: { + name: 'Standard' + } } resource primaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = { @@ -95,6 +101,12 @@ resource secondaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' resource secondaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { name: secondaryPublicIPName location: location + properties: { + publicIPAllocationMethod: 'Static' + } + sku: { + name: 'Standard' + } } resource secondaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = { diff --git a/avm/res/network/connection/tests/e2e/max/dependencies.bicep b/avm/res/network/connection/tests/e2e/max/dependencies.bicep index a8398dc99e..f180cae86e 100644 --- a/avm/res/network/connection/tests/e2e/max/dependencies.bicep +++ b/avm/res/network/connection/tests/e2e/max/dependencies.bicep @@ -42,6 +42,12 @@ resource primaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = resource primaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { name: primaryPublicIPName location: location + properties: { + publicIPAllocationMethod: 'Static' + } + sku: { + name: 'Standard' + } } resource primaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = { @@ -95,6 +101,12 @@ resource secondaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' resource secondaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { name: secondaryPublicIPName location: location + properties: { + publicIPAllocationMethod: 'Static' + } + sku: { + name: 'Standard' + } } resource secondaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = { diff --git a/avm/res/network/connection/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/network/connection/tests/e2e/waf-aligned/dependencies.bicep index a8398dc99e..f180cae86e 100644 --- a/avm/res/network/connection/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/network/connection/tests/e2e/waf-aligned/dependencies.bicep @@ -42,6 +42,12 @@ resource primaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = resource primaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { name: primaryPublicIPName location: location + properties: { + publicIPAllocationMethod: 'Static' + } + sku: { + name: 'Standard' + } } resource primaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = { @@ -95,6 +101,12 @@ resource secondaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' resource secondaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { name: secondaryPublicIPName location: location + properties: { + publicIPAllocationMethod: 'Static' + } + sku: { + name: 'Standard' + } } resource secondaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = { diff --git a/avm/res/service-bus/namespace/tests/e2e/encr/dependencies.bicep b/avm/res/service-bus/namespace/tests/e2e/encr/dependencies.bicep index 5afdae26cd..c8d5689b90 100644 --- a/avm/res/service-bus/namespace/tests/e2e/encr/dependencies.bicep +++ b/avm/res/service-bus/namespace/tests/e2e/encr/dependencies.bicep @@ -46,7 +46,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by service bus namespace + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/storage/storage-account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep b/avm/res/storage/storage-account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep index 287edca40f..26d25098d8 100644 --- a/avm/res/storage/storage-account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep +++ b/avm/res/storage/storage-account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep @@ -38,7 +38,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true @@ -60,7 +60,10 @@ resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { scope: keyVault::key properties: { principalId: storageAccount.identity.principalId - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') // Key Vault Crypto User + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '12338af0-0e69-4776-bea7-57ae8d297424' + ) // Key Vault Crypto User principalType: 'ServicePrincipal' } } diff --git a/avm/res/storage/storage-account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep b/avm/res/storage/storage-account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep index f01760e1ff..a2f946fa9c 100644 --- a/avm/res/storage/storage-account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep +++ b/avm/res/storage/storage-account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep @@ -21,7 +21,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true @@ -89,7 +89,10 @@ resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { scope: keyVault::key properties: { principalId: managedIdentity.properties.principalId - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') // Key Vault Crypto User + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '12338af0-0e69-4776-bea7-57ae8d297424' + ) // Key Vault Crypto User principalType: 'ServicePrincipal' } } diff --git a/avm/res/synapse/workspace/tests/e2e/encrwsai/dependencies.bicep b/avm/res/synapse/workspace/tests/e2e/encrwsai/dependencies.bicep index 6d7a736f40..d74453b7c8 100644 --- a/avm/res/synapse/workspace/tests/e2e/encrwsai/dependencies.bicep +++ b/avm/res/synapse/workspace/tests/e2e/encrwsai/dependencies.bicep @@ -16,7 +16,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/synapse/workspace/tests/e2e/encrwuai/dependencies.bicep b/avm/res/synapse/workspace/tests/e2e/encrwuai/dependencies.bicep index 04f775602e..487efa3cbe 100644 --- a/avm/res/synapse/workspace/tests/e2e/encrwuai/dependencies.bicep +++ b/avm/res/synapse/workspace/tests/e2e/encrwuai/dependencies.bicep @@ -24,7 +24,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 b/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 index 7cce7865d1..42bbfda099 100644 --- a/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 +++ b/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 @@ -1,63 +1,3 @@ -<# -.SYNOPSIS -Parses AVM module CSV file - -.DESCRIPTION -Depending on the parameter, the correct CSV file will be parsed and returned a an object - -.PARAMETER ModuleIndex -Type of CSV file, that should be parsed ('Bicep-Resource', 'Bicep-Pattern') - -.EXAMPLE -Next line will parse the AVM Bicep modules -Get-AvmCsvData -ModuleIndex 'Bicep-Resource' - -#> -Function Get-AvmCsvData { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [ValidateSet('Bicep-Resource', 'Bicep-Pattern')] - [string] $ModuleIndex - ) - - # CSV file URLs - $BicepResourceUrl = 'https://aka.ms/avm/index/bicep/res/csv' - $BicepPatternUrl = 'https://aka.ms/avm/index/bicep/ptn/csv' - - # Retrieve the CSV file - switch ($ModuleIndex) { - 'Bicep-Resource' { - try { - $unfilteredCSV = Invoke-WebRequest -Uri $BicepResourceUrl - } catch { - Write-Error 'Unable to retrieve CSV file - Check network connection.' - } - } - 'Bicep-Pattern' { - try { - $unfilteredCSV = Invoke-WebRequest -Uri $BicepPatternUrl - } catch { - Write-Error 'Unable to retrieve CSV file - Check network connection.' - } - } - } - - # Convert the CSV content to a PowerShell object - $formattedBicepFullCsv = ConvertFrom-Csv $unfilteredCSV.Content - - # Loop through each item in the filtered data - foreach ($item in $formattedBicepFullCsv) { - # Remove '@Azure/' from the ModuleOwnersGHTeam property - $item.ModuleOwnersGHTeam = $item.ModuleOwnersGHTeam -replace '@Azure/', '' - # Remove '@Azure/' from the ModuleContributorsGHTeam property - $item.ModuleContributorsGHTeam = $item.ModuleContributorsGHTeam -replace '@Azure/', '' - } - - # Return the modified data - return $formattedBicepFullCsv -} - <# .SYNOPSIS Assigns issues to module owners and adds comments and labels @@ -66,10 +6,13 @@ Assigns issues to module owners and adds comments and labels For the given issue, the module owner (according to the AVM CSV file) will be notified in a comment and assigned to the issue .PARAMETER Repo -Repository name according to GitHub (owner/name) +Mandatory. The name of the respository to scan. Needs to have the structure "/", like 'Azure/bicep-registry-modules/' + +.PARAMETER RepoRoot +Optional. Path to the root of the repository. .PARAMETER IssueUrl -The full GitHub URL to the issue +Mandatory. The URL of the GitHub issue, like 'https://github.com/Azure/bicep-registry-modules/issues/757' .EXAMPLE Set-AvmGitHubIssueOwnerConfig -Repo 'Azure/bicep-registry-modules' -IssueUrl 'https://github.com/Azure/bicep-registry-modules/issues/757' @@ -84,9 +27,15 @@ function Set-AvmGitHubIssueOwnerConfig { [string] $Repo, [Parameter(Mandatory = $true)] - [string] $IssueUrl + [string] $IssueUrl, + + [Parameter(Mandatory = $false)] + [string] $RepoRoot = (Get-Item -Path $PSScriptRoot).parent.parent.parent.parent.FullName ) + # Loading helper functions + . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Get-AvmCsvData.ps1') + $issue = gh issue view $IssueUrl.Replace('api.', '').Replace('repos/', '') --json 'author,title,url,body,comments' --repo $Repo | ConvertFrom-Json -Depth 100 if ($issue.title.StartsWith('[AVM Module Issue]')) { @@ -97,23 +46,41 @@ function Set-AvmGitHubIssueOwnerConfig { } $moduleIndex = $moduleName.StartsWith('avm/res') ? 'Bicep-Resource' : 'Bicep-Pattern' + # get CSV data $module = Get-AvmCsvData -ModuleIndex $moduleIndex | Where-Object ModuleName -EQ $moduleName - if ([string]::IsNullOrEmpty($module)) { - throw "Module $moduleName was not found in $moduleIndex CSV file." - } + # new/unknown module + if ($null -eq $module) { + $reply = @" +**@$($issue.author.login), thanks for submitting this issue for the ``$moduleName`` module!** - $reply = @" -@$($issue.author.login), thanks for submitting this issue for the ``$moduleName`` module! +> [!IMPORTANT] +> The module does not exist yet, we look into it. Please file a new module proposal under [AVM Module proposal](https://aka.ms/avm/moduleproposal). +"@ + } + # orphaned module + elseif ($module.ModuleStatus -eq 'Module Orphaned :eyes:') { + $reply = @" +**@$($issue.author.login), thanks for submitting this issue for the ``$moduleName`` module!** -A member of the @azure/$($module.ModuleOwnersGHTeam) or @azure/$($module.ModuleContributorsGHTeam) team will review it soon! +> [!IMPORTANT] +> Please note, that this module is currently orphaned. The @Azure/avm-core-team-technical-bicep, will attempt to find an owner for it. In the meantime, the core team may assist with this issue. Thank you for your patience! "@ + } + # existing module + else { + $reply = @" +**@$($issue.author.login), thanks for submitting this issue for the ``$moduleName`` module!** - if ($PSCmdlet.ShouldProcess("attention label to issue [$($issue.title)]", 'Add')) { - # add labels - gh issue edit $issue.url --add-label 'Needs: Attention :wave:' --repo $Repo +> [!IMPORTANT] +> A member of the @azure/$($module.ModuleOwnersGHTeam) or @azure/$($module.ModuleContributorsGHTeam) team will review it soon! +"@ } + # add issue to project + $ProjectNumber = 566 # AVM - Module Issues + Add-GithubIssueToProject -Repo $Repo -ProjectNumber $ProjectNumber -IssueUrl $IssueUrl + if ($PSCmdlet.ShouldProcess("class label to issue [$($issue.title)]", 'Add')) { gh issue edit $issue.url --add-label ($moduleIndex -eq 'Bicep-Resource' ? 'Class: Resource Module :package:' : 'Class: Pattern Module :package:') --repo $Repo } @@ -123,15 +90,21 @@ A member of the @azure/$($module.ModuleOwnersGHTeam) or @azure/$($module.ModuleC gh issue comment $issue.url --body $reply --repo $Repo } - if ($PSCmdlet.ShouldProcess(("owner [{0}] to issue [$($issue.title)]" -f $module.PrimaryModuleOwnerGHHandle), 'Assign')) { - # assign owner - $assign = gh issue edit $issue.url --add-assignee $module.PrimaryModuleOwnerGHHandle --repo $Repo - } + if (($module.ModuleStatus -ne 'Module Orphaned :eyes:') -and (-not ([string]::IsNullOrEmpty($module.PrimaryModuleOwnerGHHandle)))) { + if ($PSCmdlet.ShouldProcess(("owner [{0}] to issue [$($issue.title)]" -f $module.PrimaryModuleOwnerGHHandle), 'Assign')) { + # assign owner + $assign = gh issue edit $issue.url --add-assignee $module.PrimaryModuleOwnerGHHandle --repo $Repo + } + + if ([String]::IsNullOrEmpty($assign)) { + if ($PSCmdlet.ShouldProcess("missing user comment to issue [$($issue.title)]", 'Add')) { + $reply = @" +> [!WARNING] +> This issue couldn't be assigend due to an internal error. @$($module.PrimaryModuleOwnerGHHandle), please make sure this issue is assigned to you and please provide an initial response as soon as possible, in accordance with the [AVM Support statement](https://aka.ms/AVM/Support). +"@ - if ([String]::IsNullOrEmpty($assign)) { - if ($PSCmdlet.ShouldProcess("missing user comment to issue [$($issue.title)]", 'Add')) { - $reply = "This issue couldn't be assigend due to an internal error. @$($module.PrimaryModuleOwnerGHHandle), please make sure this issue is assigned to you and please provide an initial response as soon as possible, in accordance with the [AVM Support statement](https://aka.ms/AVM/Support)." - gh issue comment $issue.url --body $reply --repo $Repo + gh issue comment $issue.url --body $reply --repo $Repo + } } } } diff --git a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 index 7a30bfa96d..a2c1a5f98d 100644 --- a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 +++ b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 @@ -6,7 +6,10 @@ Check for failing pipelines and create issues for those, that are failing. If a pipeline fails, a new issue will be created, with a link to the failed pipeline. If the issue is already existing, a comment will be added, if a new run failed (with the link for the new failed run). If a pipeline run succeeds and an issue is open for the failed run, it will be closed (and a link to the successful run is added to the issue). .PARAMETER Repo -Mandatory. The name of the respository to scan. Needs to have the structure "/" +Mandatory. The name of the respository to scan. Needs to have the structure "/", like 'Azure/bicep-registry-modules/' + +.PARAMETER RepoRoot +Optional. Path to the root of the repository. .PARAMETER LimitNumberOfRuns Optional. Number of recent runs to scan for failed runs. Default is 100. @@ -23,7 +26,7 @@ Set-AvmGithubIssueForWorkflow -Repo 'owner/repo01' -LimitNumberOfRuns 100 -Limit Check the last 100 workflow runs in the repository 'owner/repo01' that happened in the last 2 days. If the workflow name is 'Pipeline 01', then ignore the workflow run. .NOTES -The function requires GitHub CLI to be installed. +Will be triggered by the workflow avm.platform.manage-workflow-issue.yml #> function Set-AvmGithubIssueForWorkflow { [CmdletBinding(SupportsShouldProcess)] @@ -31,6 +34,9 @@ function Set-AvmGithubIssueForWorkflow { [Parameter(Mandatory = $true)] [string] $Repo, + [Parameter(Mandatory = $false)] + [string] $RepoRoot = (Get-Item -Path $PSScriptRoot).parent.parent.parent.parent.FullName, + [Parameter(Mandatory = $false)] [int] $LimitNumberOfRuns = 100, @@ -41,6 +47,10 @@ function Set-AvmGithubIssueForWorkflow { [String[]] $IgnoreWorkflows = @() ) + # Loading helper functions + . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Get-AvmCsvData.ps1') + . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Add-GithubIssueToProject.ps1') + $issues = gh issue list --state open --limit 500 --label 'Type: AVM :a: :v: :m:,Type: Bug :bug:' --json 'title,url,body,comments,labels' --repo $Repo | ConvertFrom-Json -Depth 100 $runs = gh run list --json 'url,workflowName,headBranch,startedAt' --limit $LimitNumberOfRuns --repo $Repo | ConvertFrom-Json -Depth 100 $workflowRuns = @{} @@ -81,7 +91,45 @@ function Set-AvmGithubIssueForWorkflow { if ($issues.title -notcontains $issueName) { if ($PSCmdlet.ShouldProcess("Issue [$issueName]", 'Create')) { - gh issue create --title "$issueName" --body "$failedrun" --label 'Type: AVM :a: :v: :m:,Type: Bug :bug:' --repo $Repo + $issueUrl = gh issue create --title "$issueName" --body "$failedrun" --label 'Type: AVM :a: :v: :m:,Type: Bug :bug:' --repo $Repo + $ProjectNumber = 538 # AVM - Issue Triage + $comment = @" +> [!IMPORTANT] +> @Azure/avm-core-team-technical-bicep, the workflow for the ``$moduleName`` module has failed. Please investigate the failed workflow run. +"@ + + if ($workflowRun.workflowName -match 'avm.(?:res|ptn)') { + $moduleName = $workflowRun.workflowName.Replace('.', '/') + $moduleIndex = $moduleName.StartsWith('avm/res') ? 'Bicep-Resource' : 'Bicep-Pattern' + # get CSV data + $module = Get-AvmCsvData -ModuleIndex $moduleIndex | Where-Object ModuleName -EQ $moduleName + + if (($module.ModuleStatus -ne 'Module Orphaned :eyes:') -and (-not ([string]::IsNullOrEmpty($module.PrimaryModuleOwnerGHHandle)))) { + $ProjectNumber = 566 # AVM - Module Issues + $comment = @" +> [!IMPORTANT] +> @$($module.ModuleOwnersGHTeam), the workflow for the ``$moduleName`` module has failed. Please investigate the failed workflow run. If you are not able to do so, please inform the AVM core team to take over. +"@ + # assign owner + $assign = gh issue edit $issue.url --add-assignee $module.PrimaryModuleOwnerGHHandle --repo $Repo + + if ([String]::IsNullOrEmpty($assign)) { + if ($PSCmdlet.ShouldProcess("missing user comment to issue [$($issue.title)]", 'Add')) { + $comment = @" +> [!WARNING] +> This issue couldn't be assigend due to an internal error. @$($module.PrimaryModuleOwnerGHHandle), please make sure this issue is assigned to you and please provide an initial response as soon as possible, in accordance with the [AVM Support statement](https://aka.ms/AVM/Support). +"@ + + gh issue comment $issue.url --body $reply --repo $Repo + } + } + } + } + + # add issue to project + Add-GithubIssueToProject -Repo $Repo -ProjectNumber $ProjectNumber -IssueUrl $issueUrl + # add comment + gh issue comment $issueUrl --body $comment --repo $Repo } $issuesCreated++ diff --git a/avm/utilities/pipelines/platform/helper/Add-GithubIssueToProject.ps1 b/avm/utilities/pipelines/platform/helper/Add-GithubIssueToProject.ps1 new file mode 100644 index 0000000000..cd9d0222e4 --- /dev/null +++ b/avm/utilities/pipelines/platform/helper/Add-GithubIssueToProject.ps1 @@ -0,0 +1,57 @@ +<# +.SYNOPSIS +Adds an existing GitHub issue to an existing GitHub project (the new type, not the classic ones) + +.DESCRIPTION +Adds an existing GitHub issue to an existing GitHub project (the new type, not the classic ones) + +.PARAMETER Repo +Mandatory. The name of the respository to scan. Needs to have the structure "/", like 'Azure/bicep-registry-modules/' + +.PARAMETER ProjectNumber +Mandatory. The GitHub project number (see last part of project URL, for example 538 for https://github.com/orgs/Azure/projects/538) + +.PARAMETER IssueUrl +Mandatory. The URL of the GitHub issue, like 'https://github.com/Azure/bicep-registry-modules/issues/757' + +.EXAMPLE +Add-GithubIssueToProject -Repo 'Azure/bicep-registry-modules' -ProjectNumber 538 -IssueUrl 'https://github.com/Azure/bicep-registry-modules/issues/757' + +.NOTES +Needs to run under a context with the permissions to read/write organization projects +#> +function Add-GithubIssueToProject { + param ( + [Parameter(Mandatory = $true)] + [string] $Repo, + + [Parameter(Mandatory = $true)] + [int] $ProjectNumber, + + [Parameter(Mandatory = $true)] + [string] $IssueUrl + ) + + $Organization = $Repo.Split('/')[0] + + $Project = gh api graphql -f query=' + query($organization: String! $number: Int!){ + organization(login: $organization){ + projectV2(number: $number) { + id + } + } + }' -f organization=$Organization -F number=$ProjectNumber | ConvertFrom-Json -Depth 10 + + $ProjectId = $Project.data.organization.projectV2.id + $IssueId = (gh issue view $IssueUrl --repo $Repo --json 'id' | ConvertFrom-Json -Depth 100).id + + gh api graphql -f query=' + mutation($project:ID!, $issue:ID!) { + addProjectV2ItemById(input: {projectId: $project, contentId: $issue}) { + item { + id + } + } + }' -f project=$ProjectId -f issue=$IssueId +} diff --git a/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 b/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 new file mode 100644 index 0000000000..821f476bb7 --- /dev/null +++ b/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 @@ -0,0 +1,59 @@ +<# +.SYNOPSIS +Parses AVM module CSV file + +.DESCRIPTION +Depending on the parameter, the correct CSV file will be parsed and returned a an object + +.PARAMETER ModuleIndex +Mandatory. Type of CSV file, that should be parsed ('Bicep-Resource', 'Bicep-Pattern') + +.EXAMPLE +Get-AvmCsvData -ModuleIndex 'Bicep-Resource' + +Parse the AVM Bicep modules +#> +Function Get-AvmCsvData { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [ValidateSet('Bicep-Resource', 'Bicep-Pattern')] + [string] $ModuleIndex + ) + + # CSV file URLs + $BicepResourceUrl = 'https://aka.ms/avm/index/bicep/res/csv' + $BicepPatternUrl = 'https://aka.ms/avm/index/bicep/ptn/csv' + + # Retrieve the CSV file + switch ($ModuleIndex) { + 'Bicep-Resource' { + try { + $unfilteredCSV = Invoke-WebRequest -Uri $BicepResourceUrl + } catch { + throw 'Unable to retrieve CSV file - Check network connection.' + } + } + 'Bicep-Pattern' { + try { + $unfilteredCSV = Invoke-WebRequest -Uri $BicepPatternUrl + } catch { + throw 'Unable to retrieve CSV file - Check network connection.' + } + } + } + + # Convert the CSV content to a PowerShell object + $formattedBicepFullCsv = ConvertFrom-Csv $unfilteredCSV.Content + + # Loop through each item in the filtered data + foreach ($item in $formattedBicepFullCsv) { + # Remove '@Azure/' from the ModuleOwnersGHTeam property + $item.ModuleOwnersGHTeam = $item.ModuleOwnersGHTeam -replace '@Azure\/', '' + # Remove '@Azure/' from the ModuleContributorsGHTeam property + $item.ModuleContributorsGHTeam = $item.ModuleContributorsGHTeam -replace '@Azure\/', '' + } + + # Return the modified data + return $formattedBicepFullCsv +} diff --git a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 index 72bbe86ff0..938ec4556d 100644 --- a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 +++ b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 @@ -833,7 +833,7 @@ Describe 'Module tests' -Tag 'Module' { $templateResources = $templateFileContent.resources.Keys | ForEach-Object { $templateFileContent.resources[$_] } } - $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' } # The AVM telemetry prefix + $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '*46d3xbcp*' } # The AVM telemetry prefix $telemetryDeployment | Should -Not -BeNullOrEmpty -Because 'A telemetry resource with name prefix [46d3xbcp] should be present in the template' } @@ -850,7 +850,7 @@ Describe 'Module tests' -Tag 'Module' { $templateResources = $templateFileContent.resources.Keys | ForEach-Object { $templateFileContent.resources[$_] } } - $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' } # The AVM telemetry prefix + $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '*46d3xbcp*' } # The AVM telemetry prefix if (-not $telemetryDeployment) { Set-ItResult -Skipped -Because 'Skipping this test as telemetry was not implemented in template' @@ -873,7 +873,7 @@ Describe 'Module tests' -Tag 'Module' { $templateResources = $templateFileContent.resources.Keys | ForEach-Object { $templateFileContent.resources[$_] } } - $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' } # The AVM telemetry prefix + $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '*46d3xbcp*' } # The AVM telemetry prefix if (-not $telemetryDeployment) { Set-ItResult -Skipped -Because 'Skipping this test as telemetry was not implemented in template' @@ -927,7 +927,7 @@ Describe 'Module tests' -Tag 'Module' { } else { $templateResources = $templateFileContent.resources.Keys | ForEach-Object { $templateFileContent.resources[$_] } } - $telemetryDeploymentName = ($templateResources | Where-Object { $_.condition -like '*telemetry*' }).name # The AVM telemetry prefix + $telemetryDeploymentName = ($templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '*46d3xbcp*' }).name # The AVM telemetry prefix $telemetryDeploymentName | Should -Match "$expectedTelemetryIdentifier" } }