diff --git a/docs/wiki/Getting started - Scenario 2 Onboard module library and CI environment.md b/docs/wiki/Getting started - Scenario 2 Onboard module library and CI environment.md index dd941c2347..6af7faf822 100644 --- a/docs/wiki/Getting started - Scenario 2 Onboard module library and CI environment.md +++ b/docs/wiki/Getting started - Scenario 2 Onboard module library and CI environment.md @@ -385,9 +385,15 @@ Finally, the elements described above must further be configured in the followin | File | Parameter | Notes | | - | - | - | -| `modules\Microsoft.Web\sites\.test\common\deploy.bicep` | `appSettingsKeyValuePairs.EASYAUTH_SECRET` | Key Vault secret URI without version (e.g., 'https://Test-KeyVault.vault.azure.net/secrets/aBcDeFghIjK69Ln') | -| `modules\Microsoft.Web\sites\.test\common\deploy.bicep` | `authSettingV2Configuration.identityProviders.azureActiveDirectory.registration.clientId` | App ID from the Azure Active Directory App (e.g., '11111111-1111-1111-1111-11111111111') | -| `modules\Microsoft.Web\sites\.test\common\deploy.bicep` | `authSettingV2Configuration.identityProviders.azureActiveDirectory.validation.allowedAudiences` | API endpoint from the Azure Active Directory app (e.g., 'api://11111111-1111-1111-1111-11111111111') | +| `modules/Microsoft.Web/sites/.test/common/deploy.bicep` | `appSettingsKeyValuePairs.EASYAUTH_SECRET` | Key Vault secret URI without version (e.g., 'https://Test-KeyVault.vault.azure.net/secrets/aBcDeFghIjK69Ln') | +| `modules/Microsoft.Web/sites/.test/common/deploy.bicep` | `authSettingV2Configuration.identityProviders.azureActiveDirectory.registration.clientId` | App ID from the Azure Active Directory App (e.g., '11111111-1111-1111-1111-11111111111') | +| `modules/Microsoft.Web/sites/.test/common/deploy.bicep` | `authSettingV2Configuration.identityProviders.azureActiveDirectory.validation.allowedAudiences` | API endpoint from the Azure Active Directory app (e.g., 'api://11111111-1111-1111-1111-11111111111') | + +### Microsoft.ContainerInstance/containerGroup + +To successfully run the Customer-Managed-Keys encryption test `encr/deploy.test.bicep` of the Container Instance module, you first need to register a Service Principal instance of the `Azure Container Instance Service` Azure application in your test Tenant. This can be achieved, for example, by running the command `New-AzADServicePrincipal -ApplicationId '6bb8e274-af5d-4df2-98a3-4fd78b4cafd9'`. For further information, please refer to the official [docs](https://learn.microsoft.com/en-us/azure/container-instances/container-instances-encrypt-data#create-service-principal-for-aci). + +Once the Service Principal is created, please update the `properties/principalId` of the `keyPermissions` deployment in the dependencies file `modules/Microsoft.ContainerInstance/containerGroups/.test/encr/dependencies.bicep` with its object ID. You can fetch the object ID using the command `(Get-AzADServicePrincipal -DisplayName 'Azure Container Instance Service').Id`. # 5. (Optional) Convert library to ARM diff --git a/modules/Microsoft.ContainerInstance/containerGroups/.test/encr/dependencies.bicep b/modules/Microsoft.ContainerInstance/containerGroups/.test/encr/dependencies.bicep new file mode 100644 index 0000000000..6556bed151 --- /dev/null +++ b/modules/Microsoft.ContainerInstance/containerGroups/.test/encr/dependencies.bicep @@ -0,0 +1,61 @@ +@description('Required. The name of the managed identity to create.') +param managedIdentityName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Key Vault to create.') +@minLength(3) +@maxLength(24) +param keyVaultName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: true // Required by batch account + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2022-07-01' = { + name: 'keyEncryptionKey' + properties: { + kty: 'RSA' + } + } +} + +// Ref: https://learn.microsoft.com/en-us/azure/container-instances/container-instances-encrypt-data#create-service-principal-for-aci +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${keyVault::key.id}-${location}-Azure Container Instance Service-Key Vault Crypto User') + scope: keyVault + properties: { + principalId: '8b659b68-1eb9-4ea5-ab00-a6a182520436' // Replace with your tenant 'Azure Container Instance Service' Service Principal Object Id + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6') // Key Vault Crypto Service Encryption User. Allows Keys: get, list, wrap key, unwrap key + principalType: 'ServicePrincipal' + } +} + +@description('The resource ID of the created managed identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The name of the Key Vault Encryption Key.') +output keyVaultEncryptionKeyName string = keyVault::key.name diff --git a/modules/Microsoft.ContainerInstance/containerGroups/.test/encr/deploy.test.bicep b/modules/Microsoft.ContainerInstance/containerGroups/.test/encr/deploy.test.bicep new file mode 100644 index 0000000000..78d1415326 --- /dev/null +++ b/modules/Microsoft.ContainerInstance/containerGroups/.test/encr/deploy.test.bicep @@ -0,0 +1,117 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.containerinstance.containergroups-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'cicgecr' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module resourceGroupResources 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + managedIdentityName: 'dep-<>-msi-${serviceShort}' + // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) + keyVaultName: 'dep-<>-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + lock: 'CanNotDelete' + containers: [ + { + name: '<>-az-aci-x-001' + properties: { + command: [] + environmentVariables: [] + image: 'mcr.microsoft.com/azuredocs/aci-helloworld' + ports: [ + { + port: '80' + protocol: 'Tcp' + } + { + port: '443' + protocol: 'Tcp' + } + ] + resources: { + requests: { + cpu: 2 + memoryInGB: 2 + } + } + } + } + { + name: '<>-az-aci-x-002' + properties: { + command: [] + environmentVariables: [] + image: 'mcr.microsoft.com/azuredocs/aci-helloworld' + ports: [ + { + port: '8080' + protocol: 'Tcp' + } + ] + resources: { + requests: { + cpu: 2 + memoryInGB: 2 + } + } + } + } + ] + ipAddressPorts: [ + { + protocol: 'Tcp' + port: 80 + } + { + protocol: 'Tcp' + port: 443 + } + ] + systemAssignedIdentity: true + userAssignedIdentities: { + '${resourceGroupResources.outputs.managedIdentityResourceId}': {} + } + cMKKeyName: resourceGroupResources.outputs.keyVaultEncryptionKeyName + cMKKeyVaultResourceId: resourceGroupResources.outputs.keyVaultResourceId + } +} diff --git a/modules/Microsoft.ContainerInstance/containerGroups/deploy.bicep b/modules/Microsoft.ContainerInstance/containerGroups/deploy.bicep index a5daf54f9b..102f82d098 100644 --- a/modules/Microsoft.ContainerInstance/containerGroups/deploy.bicep +++ b/modules/Microsoft.ContainerInstance/containerGroups/deploy.bicep @@ -44,22 +44,6 @@ param autoGeneratedDomainNameLabelScope string = 'TenantReuse' @description('Optional. The Dns name label for the resource.') param dnsNameLabel string = '' -@allowed([ - 'Standard' - 'Dedicated' -]) -@description('Optional. Specify the Sku.') -param sku string = 'Standard' - -@description('Optional. If Non-Microsoft-managed encryption should be used, specify the key vaults base URL.') -param encryptionVaultBaseUrl string = '' - -@description('Optional. If Non-Microsoft-managed encryption should be used, specify the key name.') -param encrytionKeyName string = '' - -@description('Optional. If Non-Microsoft-managed encryption should be used, specify the key version.') -param encryptionKeyVersion string = '' - @description('Optional. List of dns servers used by the containers for lookups.') param dnsNameServers array = [] @@ -95,6 +79,22 @@ param tags object = {} @description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') param enableDefaultTelemetry bool = true +@description('Optional. The container group SKU.') +@allowed([ + 'Dedicated' + 'Standard' +]) +param sku string = 'Standard' + +@description('Optional. The resource ID of a key vault to reference a customer managed key for encryption from.') +param cMKKeyVaultResourceId string = '' + +@description('Optional. The name of the customer managed key to use for encryption.') +param cMKKeyName string = '' + +@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') +param cMKKeyVersion string = '' + var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') var identity = identityType != 'None' ? { @@ -102,46 +102,6 @@ var identity = identityType != 'None' ? { userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null } : null -var dnsConfig = !empty(dnsNameServers) ? { - nameServers: dnsNameServers - searchDomains: dnsSearchDomains -} : null - -var encryptionProperties = !empty(encryptionVaultBaseUrl) ? { - vaultBaseUrl: encryptionVaultBaseUrl - keyName: encrytionKeyName - keyVersion: encryptionKeyVersion -} : null - -var subnetIds = !empty(subnetId) ? [ - { - id: subnetId - } -] : null - -var generatedDomainNameLabelScope = !empty(dnsNameServers) ? autoGeneratedDomainNameLabelScope : null - -var basicContainerProperties = { - containers: containers - dnsConfig: dnsConfig - encryptionProperties: encryptionProperties - imageRegistryCredentials: imageRegistryCredentials - initContainers: initContainers - restartPolicy: restartPolicy - osType: osType - ipAddress: { - type: ipAddressType - autoGeneratedDomainNameLabelScope: generatedDomainNameLabelScope - dnsNameLabel: dnsNameLabel - ports: ipAddressPorts - } - sku: sku - subnetIds: subnetIds - volumes: volumes -} - -var containerProperties = !empty(dnsNameServers) ? union(basicContainerProperties, { dnsConfig: dnsConfig }) : basicContainerProperties - resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' properties: { @@ -154,12 +114,51 @@ resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (ena } } +resource cmkKeyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' existing = if (!empty(cMKKeyVaultResourceId)) { + name: last(split(cMKKeyVaultResourceId, '/')) + scope: resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) +} + +resource cMKKeyVaultKey 'Microsoft.KeyVault/vaults/keys@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId) && !empty(cMKKeyName)) { + name: '${last(split(cMKKeyVaultResourceId, '/'))}/${cMKKeyName}' + scope: resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) +} + resource containergroup 'Microsoft.ContainerInstance/containerGroups@2021-10-01' = { name: name location: location identity: identity tags: tags - properties: containerProperties + properties: union({ + containers: containers + encryptionProperties: !empty(cMKKeyName) ? { + keyName: cMKKeyName + keyVersion: !empty(cMKKeyVersion) ? cMKKeyVersion : last(split(cMKKeyVaultKey.properties.keyUriWithVersion, '/')) + vaultBaseUrl: cmkKeyVault.properties.vaultUri + } : null + imageRegistryCredentials: imageRegistryCredentials + initContainers: initContainers + restartPolicy: restartPolicy + osType: osType + ipAddress: { + type: ipAddressType + autoGeneratedDomainNameLabelScope: !empty(dnsNameServers) ? autoGeneratedDomainNameLabelScope : null + dnsNameLabel: dnsNameLabel + ports: ipAddressPorts + } + sku: sku + subnetIds: !empty(subnetId) ? [ + { + id: subnetId + } + ] : null + volumes: volumes + }, !empty(dnsNameServers) ? { + dnsConfig: { + nameServers: dnsNameServers + searchDomains: dnsSearchDomains + } + } : {}) } resource containergroup_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { diff --git a/modules/Microsoft.ContainerInstance/containerGroups/readme.md b/modules/Microsoft.ContainerInstance/containerGroups/readme.md index a2025bb808..418bef8451 100644 --- a/modules/Microsoft.ContainerInstance/containerGroups/readme.md +++ b/modules/Microsoft.ContainerInstance/containerGroups/readme.md @@ -39,13 +39,13 @@ The top-level resource in Azure Container Instances is the container group. A co | Parameter Name | Type | Default Value | Allowed Values | Description | | :-- | :-- | :-- | :-- | :-- | | `autoGeneratedDomainNameLabelScope` | string | `'TenantReuse'` | `[Noreuse, ResourceGroupReuse, SubscriptionReuse, TenantReuse, Unsecure]` | Specify level of protection of the domain name label. | +| `cMKKeyName` | string | `''` | | The name of the customer managed key to use for encryption. | +| `cMKKeyVaultResourceId` | string | `''` | | The resource ID of a key vault to reference a customer managed key for encryption from. | +| `cMKKeyVersion` | string | `''` | | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | | `dnsNameLabel` | string | `''` | | The Dns name label for the resource. | | `dnsNameServers` | array | `[]` | | List of dns servers used by the containers for lookups. | | `dnsSearchDomains` | string | `''` | | DNS search domain which will be appended to each DNS lookup. | | `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | -| `encryptionKeyVersion` | string | `''` | | If Non-Microsoft-managed encryption should be used, specify the key version. | -| `encryptionVaultBaseUrl` | string | `''` | | If Non-Microsoft-managed encryption should be used, specify the key vaults base URL. | -| `encrytionKeyName` | string | `''` | | If Non-Microsoft-managed encryption should be used, specify the key name. | | `imageRegistryCredentials` | array | `[]` | | The image registry credentials by which the container group is created from. | | `initContainers` | array | `[]` | | A list of container definitions which will be executed before the application container starts. | | `ipAddressType` | string | `'Public'` | `[Private, Public]` | Specifies if the IP is exposed to the public internet or private VNET. - Public or Private. | @@ -53,7 +53,7 @@ The top-level resource in Azure Container Instances is the container group. A co | `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | | `osType` | string | `'Linux'` | | The operating system type required by the containers in the container group. - Windows or Linux. | | `restartPolicy` | string | `'Always'` | `[Always, Never, OnFailure]` | Restart policy for all containers within the container group. - Always: Always restart. OnFailure: Restart on failure. Never: Never restart. - Always, OnFailure, Never. | -| `sku` | string | `'Standard'` | `[Dedicated, Standard]` | Specify the Sku. | +| `sku` | string | `'Standard'` | `[Dedicated, Standard]` | The container group SKU. | | `subnetId` | string | `''` | | Resource ID of the subnet. Only specify when ipAddressType is Private. | | `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | | `tags` | object | `{object}` | | Tags of the resource. | @@ -475,7 +475,192 @@ module containerGroups './Microsoft.ContainerInstance/containerGroups/deploy.bic

-

Example 2: Min

+

Example 2: Encr

+ +
+ +via Bicep module + +```bicep +module containerGroups './Microsoft.ContainerInstance/containerGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-cicgecr' + params: { + // Required parameters + containers: [ + { + name: '<>-az-aci-x-001' + properties: { + command: [] + environmentVariables: [] + image: 'mcr.microsoft.com/azuredocs/aci-helloworld' + ports: [ + { + port: '80' + protocol: 'Tcp' + } + { + port: '443' + protocol: 'Tcp' + } + ] + resources: { + requests: { + cpu: 2 + memoryInGB: 2 + } + } + } + } + { + name: '<>-az-aci-x-002' + properties: { + command: [] + environmentVariables: [] + image: 'mcr.microsoft.com/azuredocs/aci-helloworld' + ports: [ + { + port: '8080' + protocol: 'Tcp' + } + ] + resources: { + requests: { + cpu: 2 + memoryInGB: 2 + } + } + } + } + ] + name: '<>cicgecr001' + // Non-required parameters + cMKKeyName: '' + cMKKeyVaultResourceId: '' + enableDefaultTelemetry: '' + ipAddressPorts: [ + { + port: 80 + protocol: 'Tcp' + } + { + port: 443 + protocol: 'Tcp' + } + ] + lock: 'CanNotDelete' + systemAssignedIdentity: true + userAssignedIdentities: { + '': {} + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "containers": { + "value": [ + { + "name": "<>-az-aci-x-001", + "properties": { + "command": [], + "environmentVariables": [], + "image": "mcr.microsoft.com/azuredocs/aci-helloworld", + "ports": [ + { + "port": "80", + "protocol": "Tcp" + }, + { + "port": "443", + "protocol": "Tcp" + } + ], + "resources": { + "requests": { + "cpu": 2, + "memoryInGB": 2 + } + } + } + }, + { + "name": "<>-az-aci-x-002", + "properties": { + "command": [], + "environmentVariables": [], + "image": "mcr.microsoft.com/azuredocs/aci-helloworld", + "ports": [ + { + "port": "8080", + "protocol": "Tcp" + } + ], + "resources": { + "requests": { + "cpu": 2, + "memoryInGB": 2 + } + } + } + } + ] + }, + "name": { + "value": "<>cicgecr001" + }, + // Non-required parameters + "cMKKeyName": { + "value": "" + }, + "cMKKeyVaultResourceId": { + "value": "" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "ipAddressPorts": { + "value": [ + { + "port": 80, + "protocol": "Tcp" + }, + { + "port": 443, + "protocol": "Tcp" + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "systemAssignedIdentity": { + "value": true + }, + "userAssignedIdentities": { + "value": { + "": {} + } + } + } +} +``` + +
+

+ +

Example 3: Min

@@ -576,7 +761,7 @@ module containerGroups './Microsoft.ContainerInstance/containerGroups/deploy.bic

-

Example 3: Private

+

Example 4: Private