diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e2ab1172d0..40c8b77871 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -105,6 +105,7 @@ /avm/res/key-vault/vault/ @Azure/avm-res-keyvault-vault-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/kubernetes-configuration/extension/ @Azure/avm-res-kubernetesconfiguration-extension-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/kubernetes-configuration/flux-configuration/ @Azure/avm-res-kubernetesconfiguration-fluxconfiguration-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/res/kubernetes/connected-cluster/ @Azure/avm-res-kubernetes-connectedcluster-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/kusto/cluster/ @Azure/avm-res-kusto-cluster-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/load-test-service/load-test/ @Azure/avm-res-loadtestservice-loadtest-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/logic/workflow/ @Azure/avm-res-logic-workflow-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 871a9782a5..501510025f 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -140,6 +140,7 @@ body: - "avm/res/key-vault/vault" - "avm/res/kubernetes-configuration/extension" - "avm/res/kubernetes-configuration/flux-configuration" + - "avm/res/kubernetes/connected-cluster" - "avm/res/kusto/cluster" - "avm/res/load-test-service/load-test" - "avm/res/logic/workflow" diff --git a/.github/workflows/avm.res.kubernetes.connected-cluster.yml b/.github/workflows/avm.res.kubernetes.connected-cluster.yml new file mode 100644 index 0000000000..c0342a00ae --- /dev/null +++ b/.github/workflows/avm.res.kubernetes.connected-cluster.yml @@ -0,0 +1,88 @@ +name: "avm.res.kubernetes.connected-cluster" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.kubernetes.connected-cluster.yml" + - "avm/res/kubernetes/connected-cluster/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/kubernetes/connected-cluster" + workflowPath: ".github/workflows/avm.res.kubernetes.connected-cluster.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/kubernetes/connected-cluster/README.md b/avm/res/kubernetes/connected-cluster/README.md new file mode 100644 index 0000000000..e85c47df05 --- /dev/null +++ b/avm/res/kubernetes/connected-cluster/README.md @@ -0,0 +1,416 @@ +# Kubernetes Connected Cluster `[Microsoft.Kubernetes/connectedClusters]` + +This module deploys an Azure Arc connected cluster. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Kubernetes/connectedClusters` | [2024-07-15-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Kubernetes/2024-07-15-preview/connectedClusters) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/kubernetes/connected-cluster:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [WAF-aligned](#example-2-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +
+ +via Bicep module + +```bicep +module connectedCluster 'br/public:avm/res/kubernetes/connected-cluster:' = { + name: 'connectedClusterDeployment' + params: { + // Required parameters + name: 'kccmin001' + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

+ +via JSON parameters file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "kccmin001" + }, + // Non-required parameters + "location": { + "value": "" + } + } +} +``` + +
+

+ +

+ +via Bicep parameters file + +```bicep-params +using 'br/public:avm/res/kubernetes/connected-cluster:' + +// Required parameters +param name = 'kccmin001' +// Non-required parameters +param location = '' +``` + +
+

+ +### Example 2: _WAF-aligned_ + +This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. + + +

+ +via Bicep module + +```bicep +module connectedCluster 'br/public:avm/res/kubernetes/connected-cluster:' = { + name: 'connectedClusterDeployment' + params: { + // Required parameters + name: 'kccwaf001' + // Non-required parameters + location: '' + oidcIssuerEnabled: true + workloadIdentityEnabled: true + } +} +``` + +
+

+ +

+ +via JSON parameters file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "kccwaf001" + }, + // Non-required parameters + "location": { + "value": "" + }, + "oidcIssuerEnabled": { + "value": true + }, + "workloadIdentityEnabled": { + "value": true + } + } +} +``` + +
+

+ +

+ +via Bicep parameters file + +```bicep-params +using 'br/public:avm/res/kubernetes/connected-cluster:' + +// Required parameters +param name = 'kccwaf001' +// Non-required parameters +param location = '' +param oidcIssuerEnabled = true +param workloadIdentityEnabled = true +``` + +
+

+ +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the Azure Arc connected cluster. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`aadAdminGroupObjectIds`](#parameter-aadadmingroupobjectids) | array | The Azure AD admin group object IDs. | +| [`aadTenantId`](#parameter-aadtenantid) | string | Optional. The Azure AD tenant ID. | +| [`agentAutoUpgrade`](#parameter-agentautoupgrade) | string | Enable automatic agent upgrades. | +| [`enableAzureRBAC`](#parameter-enableazurerbac) | bool | Enable Azure RBAC. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`identityType`](#parameter-identitytype) | string | The identity type for the cluster. Allowed values: "SystemAssigned", "None". | +| [`location`](#parameter-location) | string | Location for all Resources. | +| [`oidcIssuerEnabled`](#parameter-oidcissuerenabled) | bool | Enable OIDC issuer. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`tags`](#parameter-tags) | object | Tags for the cluster resource. | +| [`workloadIdentityEnabled`](#parameter-workloadidentityenabled) | bool | Enable workload identity. | + +### Parameter: `name` + +The name of the Azure Arc connected cluster. + +- Required: Yes +- Type: string + +### Parameter: `aadAdminGroupObjectIds` + +The Azure AD admin group object IDs. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `aadTenantId` + +Optional. The Azure AD tenant ID. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `agentAutoUpgrade` + +Enable automatic agent upgrades. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `enableAzureRBAC` + +Enable Azure RBAC. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `identityType` + +The identity type for the cluster. Allowed values: "SystemAssigned", "None". + +- Required: No +- Type: string +- Default: `'SystemAssigned'` +- Allowed: + ```Bicep + [ + 'None' + 'SystemAssigned' + ] + ``` + +### Parameter: `location` + +Location for all Resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `oidcIssuerEnabled` + +Enable OIDC issuer. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'User Access Administrator'` + - `'Role Based Access Control Administrator (Preview)'` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". | +| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `tags` + +Tags for the cluster resource. + +- Required: No +- Type: object + +### Parameter: `workloadIdentityEnabled` + +Enable workload identity. + +- Required: No +- Type: bool +- Default: `False` + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location of the connected cluster. | +| `name` | string | The name of the connected cluster. | +| `resourceGroupName` | string | The resource group of the connected cluster. | +| `resourceId` | string | The resource ID of the connected cluster. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `br/public:avm/utl/types/avm-common-types:0.5.1` | Remote reference | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/kubernetes/connected-cluster/main.bicep b/avm/res/kubernetes/connected-cluster/main.bicep new file mode 100644 index 0000000000..7d067ca103 --- /dev/null +++ b/avm/res/kubernetes/connected-cluster/main.bicep @@ -0,0 +1,163 @@ +metadata name = 'Kubernetes Connected Cluster' +metadata description = 'This module deploys an Azure Arc connected cluster.' + +// ============== // +// Parameters // +// ============== // + +@description('Required. The name of the Azure Arc connected cluster.') +param name string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Optional. The identity type for the cluster. Allowed values: "SystemAssigned", "None".') +@allowed([ + 'SystemAssigned' + 'None' +]) +param identityType string = 'SystemAssigned' + +@description('Optional. Tags for the cluster resource.') +param tags object? + +@description('Optional. Optional. The Azure AD tenant ID.') +param aadTenantId string = '' + +@description('Optional. The Azure AD admin group object IDs.') +param aadAdminGroupObjectIds array = [] + +@description('Optional. Enable Azure RBAC.') +param enableAzureRBAC bool = false + +@description('Optional. Enable automatic agent upgrades.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param agentAutoUpgrade string = 'Enabled' + +@description('Optional. Enable OIDC issuer.') +param oidcIssuerEnabled bool = false + +@description('Optional. Enable workload identity.') +param workloadIdentityEnabled bool = false + +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType[]? + +var builtInRoleNames = { + // Add other relevant built-in roles here for your resource as per BCPNFR5 + 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') + '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' + ) +} + +var formattedRoleAssignments = [ + for (roleAssignment, index) in (roleAssignments ?? []): union(roleAssignment, { + roleDefinitionId: builtInRoleNames[?roleAssignment.roleDefinitionIdOrName] ?? (contains( + roleAssignment.roleDefinitionIdOrName, + '/providers/Microsoft.Authorization/roleDefinitions/' + ) + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)) + }) +] + +// ============= // +// Resources // +// ============= // + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { + name: '46d3xbcp.res.kubernetes-connectedcluster.${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 definition +resource connectedCluster 'Microsoft.Kubernetes/connectedClusters@2024-07-15-preview' = { + name: name + kind: 'ProvisionedCluster' + location: location + identity: { + type: identityType + } + tags: tags + properties: { + aadProfile: !empty(aadTenantId) + ? { + tenantID: aadTenantId + adminGroupObjectIDs: aadAdminGroupObjectIds + enableAzureRBAC: enableAzureRBAC + } + : null + agentPublicKeyCertificate: '' + arcAgentProfile: { + agentAutoUpgrade: agentAutoUpgrade + } + distribution: null + infrastructure: null + oidcIssuerProfile: { + enabled: oidcIssuerEnabled + } + securityProfile: { + workloadIdentity: { + enabled: workloadIdentityEnabled + } + } + azureHybridBenefit: null + } +} + +resource connectedCluster_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(connectedCluster.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) + properties: { + roleDefinitionId: roleAssignment.roleDefinitionId + 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: connectedCluster + } +] + +@description('The name of the connected cluster.') +output name string = connectedCluster.name + +@description('The resource ID of the connected cluster.') +output resourceId string = connectedCluster.id + +@description('The resource group of the connected cluster.') +output resourceGroupName string = resourceGroup().name + +@description('The location of the connected cluster.') +output location string = connectedCluster.location diff --git a/avm/res/kubernetes/connected-cluster/main.json b/avm/res/kubernetes/connected-cluster/main.json new file mode 100644 index 0000000000..9013f2052c --- /dev/null +++ b/avm/res/kubernetes/connected-cluster/main.json @@ -0,0 +1,306 @@ +{ + "$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.33.93.31351", + "templateHash": "7551101804629042655" + }, + "name": "Kubernetes Connected Cluster", + "description": "This module deploys an Azure Arc connected cluster." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Azure Arc connected cluster." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "identityType": { + "type": "string", + "defaultValue": "SystemAssigned", + "allowedValues": [ + "SystemAssigned", + "None" + ], + "metadata": { + "description": "Optional. The identity type for the cluster. Allowed values: \"SystemAssigned\", \"None\"." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags for the cluster resource." + } + }, + "aadTenantId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Optional. The Azure AD tenant ID." + } + }, + "aadAdminGroupObjectIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The Azure AD admin group object IDs." + } + }, + "enableAzureRBAC": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable Azure RBAC." + } + }, + "agentAutoUpgrade": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Enable automatic agent upgrades." + } + }, + "oidcIssuerEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable OIDC issuer." + } + }, + "workloadIdentityEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable workload identity." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "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')]", + "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')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.kubernetes-connectedcluster.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('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" + } + } + } + } + }, + "connectedCluster": { + "type": "Microsoft.Kubernetes/connectedClusters", + "apiVersion": "2024-07-15-preview", + "name": "[parameters('name')]", + "kind": "ProvisionedCluster", + "location": "[parameters('location')]", + "identity": { + "type": "[parameters('identityType')]" + }, + "tags": "[parameters('tags')]", + "properties": { + "aadProfile": "[if(not(empty(parameters('aadTenantId'))), createObject('tenantID', parameters('aadTenantId'), 'adminGroupObjectIDs', parameters('aadAdminGroupObjectIds'), 'enableAzureRBAC', parameters('enableAzureRBAC')), null())]", + "agentPublicKeyCertificate": "", + "arcAgentProfile": { + "agentAutoUpgrade": "[parameters('agentAutoUpgrade')]" + }, + "distribution": null, + "infrastructure": null, + "oidcIssuerProfile": { + "enabled": "[parameters('oidcIssuerEnabled')]" + }, + "securityProfile": { + "workloadIdentity": { + "enabled": "[parameters('workloadIdentityEnabled')]" + } + }, + "azureHybridBenefit": null + } + }, + "connectedCluster_roleAssignments": { + "copy": { + "name": "connectedCluster_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Kubernetes/connectedClusters/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Kubernetes/connectedClusters', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "connectedCluster" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the connected cluster." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the connected cluster." + }, + "value": "[resourceId('Microsoft.Kubernetes/connectedClusters', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the connected cluster." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location of the connected cluster." + }, + "value": "[reference('connectedCluster', '2024-07-15-preview', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/kubernetes/connected-cluster/tests/e2e/defaults/main.test.bicep b/avm/res/kubernetes/connected-cluster/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..d519e81970 --- /dev/null +++ b/avm/res/kubernetes/connected-cluster/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,31 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-kubernets-connectedcluster-${serviceShort}-rg' + +@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 = 'kccmin' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +#disable-next-line no-hardcoded-location // Due to quotas and capacity challenges, this region must be used in the AVM testing subscription +var enforcedLocation = 'southeastasia' + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: enforcedLocation +} + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + location: enforcedLocation + } +} diff --git a/avm/res/kubernetes/connected-cluster/tests/e2e/waf-aligned/main.test.bicep b/avm/res/kubernetes/connected-cluster/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..0f17d77ebe --- /dev/null +++ b/avm/res/kubernetes/connected-cluster/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,33 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-kubernets-connectedcluster-${serviceShort}-rg' + +@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 = 'kccwaf' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +#disable-next-line no-hardcoded-location // Due to quotas and capacity challenges, this region must be used in the AVM testing subscription +var enforcedLocation = 'southeastasia' + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: enforcedLocation +} + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + location: enforcedLocation + oidcIssuerEnabled: true + workloadIdentityEnabled: true + } +} diff --git a/avm/res/kubernetes/connected-cluster/version.json b/avm/res/kubernetes/connected-cluster/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/res/kubernetes/connected-cluster/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +}