diff --git a/avm/ptn/deployment-script/import-image-to-acr/README.md b/avm/ptn/deployment-script/import-image-to-acr/README.md index b079fe65c1..42038de2c6 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/README.md +++ b/avm/ptn/deployment-script/import-image-to-acr/README.md @@ -281,6 +281,8 @@ module importImageToAcr 'br/public:avm/ptn/deployment-script/import-image-to-acr | [`overwriteExistingImage`](#parameter-overwriteexistingimage) | bool | The image will be overwritten if it already exists in the ACR with the same tag. Default is false. | | [`retryMax`](#parameter-retrymax) | int | The maximum number of retries for the script import operation. Default is 3. | | [`runOnce`](#parameter-runonce) | bool | How the deployment script should be forced to execute. Default is to force the script to deploy the image to run every time. | +| [`sourceRegistryPassword`](#parameter-sourceregistrypassword) | securestring | The password for the source registry. Required if the source registry is private, or to logon to the public docker registry. | +| [`sourceRegistryUsername`](#parameter-sourceregistryusername) | string | The username for the source registry. Required if the source registry is private, or to logon to the public docker registry. | | [`storageAccountResourceId`](#parameter-storageaccountresourceid) | string | The resource id of the storage account to use for the deployment script. An existing storage account is needed, if PrivateLink is going to be used for the deployment script. | | [`subnetResourceIds`](#parameter-subnetresourceids) | array | The subnet ids to use for the deployment script. An existing subnet is needed, if PrivateLink is going to be used for the deployment script. | | [`tags`](#parameter-tags) | object | Tags of the resource. | @@ -298,7 +300,12 @@ A fully qualified image name to import. - Required: Yes - Type: string -- Example: `mcr.microsoft.com/k8se/quickstart-jobs:latest` +- Example: + ```Bicep + mcr.microsoft.com/k8se/quickstart-jobs:latest + docker.io/library/image:latest + docker.io/hello-world:latest + ``` ### Parameter: `name` @@ -415,6 +422,22 @@ How the deployment script should be forced to execute. Default is to force the s - Type: bool - Default: `False` +### Parameter: `sourceRegistryPassword` + +The password for the source registry. Required if the source registry is private, or to logon to the public docker registry. + +- Required: No +- Type: securestring +- Default: `''` + +### Parameter: `sourceRegistryUsername` + +The username for the source registry. Required if the source registry is private, or to logon to the public docker registry. + +- Required: No +- Type: string +- Default: `''` + ### Parameter: `storageAccountResourceId` The resource id of the storage account to use for the deployment script. An existing storage account is needed, if PrivateLink is going to be used for the deployment script. @@ -458,13 +481,17 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/resources/deployment-script:0.2.3` | Remote reference | +| `br/public:avm/res/resources/deployment-script:0.4.0` | Remote reference | ## Notes The deployment script service will need and provision a Storage Account as well as a Container Instance to execute the provided script. _The deployment script resource is available only in the regions where Azure Container Instances is available._ -> The service cleans up these resources after the deployment script finishes. You incur charges for these resources until they're removed. +> The service cleans up these resources after the deployment script finishes. You incur charges for these resources until they are removed. + +### Authentication to source Container Registry + +Authentication is possible by setting the ```sourceRegistryUsername``` and ```sourceRegistryPassword``` parameters. An example that uses Key Vault is in the max sample. It is commented out, as for the shared environments no user exists, that could be used to access e.g. docker hub images. ### Private network access diff --git a/avm/ptn/deployment-script/import-image-to-acr/main.bicep b/avm/ptn/deployment-script/import-image-to-acr/main.bicep index eb578a27a1..64be560c36 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/main.bicep +++ b/avm/ptn/deployment-script/import-image-to-acr/main.bicep @@ -28,10 +28,21 @@ param managedIdentityName string? @description('Required. A fully qualified image name to import.') @metadata({ - example: 'mcr.microsoft.com/k8se/quickstart-jobs:latest' + example: [ + 'mcr.microsoft.com/k8se/quickstart-jobs:latest' + 'docker.io/library/image:latest' + 'docker.io/hello-world:latest' + ] }) param image string +@description('Optional. The username for the source registry. Required if the source registry is private, or to logon to the public docker registry.') +param sourceRegistryUsername string = '' + +@description('Optional. The password for the source registry. Required if the source registry is private, or to logon to the public docker registry.') +@secure() +param sourceRegistryPassword string = '' + @description('Optional. The new image name in the ACR. You can use this to import a publically available image with a custom name for later updating from e.g., your build pipeline.') @metadata({ example: 'your-image-name:tag' @@ -147,7 +158,7 @@ resource acrRoleAssignmentNewManagedIdentity 'Microsoft.Authorization/roleAssign } } -module imageImport 'br/public:avm/res/resources/deployment-script:0.2.3' = { +module imageImport 'br/public:avm/res/resources/deployment-script:0.4.0' = { name: name ?? 'ACR-Import-${last(split(replace(image,':','-'),'/'))}' scope: resourceGroup() params: { @@ -159,41 +170,20 @@ module imageImport 'br/public:avm/res/resources/deployment-script:0.2.3' = { : { userAssignedResourcesIds: [newManagedIdentity.id] } kind: 'AzureCLI' runOnce: runOnce - azCliVersion: '2.61.0' // available tags are listed here: https://mcr.microsoft.com/v2/azure-cli/tags/list + azCliVersion: '2.63.0' // available tags are listed here: https://mcr.microsoft.com/v2/azure-cli/tags/list timeout: 'PT30M' // set timeout to 30m retentionInterval: 'PT1H' // cleanup after 1h - environmentVariables: { - secureList: [ - { - name: 'acrName' - value: acrName - } - { - name: 'imageName' - value: image - } - { - name: 'newImageName' - value: newImageName - } - { - name: 'overwriteExistingImage' - value: toLower(string(overwriteExistingImage)) - } - { - name: 'initialDelay' - value: '${string(initialScriptDelay)}s' - } - { - name: 'retryMax' - value: string(retryMax) - } - { - name: 'retrySleep' - value: '5s' - } - ] - } + environmentVariables: [ + { name: 'acrName', value: acrName } + { name: 'imageName', value: image } + { name: 'newImageName', value: newImageName } + { name: 'overwriteExistingImage', value: toLower(string(overwriteExistingImage)) } + { name: 'initialDelay', value: '${string(initialScriptDelay)}s' } + { name: 'retryMax', value: string(retryMax) } + { name: 'retrySleep', value: '5s' } + { name: 'sourceRegistryUsername', value: sourceRegistryUsername } + { name: 'sourceRegistryPassword', secureValue: sourceRegistryPassword } + ] cleanupPreference: cleanupPreference storageAccountResourceId: storageAccountResourceId containerGroupName: '${resourceGroup().name}-infrastructure' @@ -210,9 +200,17 @@ module imageImport 'br/public:avm/res/resources/deployment-script:0.2.3' = { do echo "Importing Image ($retryLoopCount): $imageName into ACR: $acrName\n" if [ $overwriteExistingImage = 'true' ]; then - az acr import -n $acrName --source $imageName --image $newImageName --force + if [ -n "$sourceRegistryUsername" ] && [ -n "$sourceRegistryPassword" ]; then + az acr import -n $acrName --source $imageName --image $newImageName --force --username $sourceRegistryUsername --password $sourceRegistryPassword + else + az acr import -n $acrName --source $imageName --image $newImageName --force + fi else - az acr import -n $acrName --source $imageName --image $newImageName + if [ -n "$sourceRegistryUsername" ] && [ -n "$sourceRegistryPassword" ]; then + az acr import -n $acrName --source $imageName --image $newImageName --username $sourceRegistryUsername --password $sourceRegistryPassword + else + az acr import -n $acrName --source $imageName --image $newImageName + fi fi sleep $retrySleep diff --git a/avm/ptn/deployment-script/import-image-to-acr/main.json b/avm/ptn/deployment-script/import-image-to-acr/main.json index ffc946ccc3..51751e27f8 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/main.json +++ b/avm/ptn/deployment-script/import-image-to-acr/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18410876545978102921" + "version": "0.29.47.4906", + "templateHash": "15179702678978782456" }, "name": "import-image-to-acr", "description": "This modules deployes an image to an Azure Container Registry.", @@ -103,10 +103,28 @@ "image": { "type": "string", "metadata": { - "example": "mcr.microsoft.com/k8se/quickstart-jobs:latest", + "example": [ + "mcr.microsoft.com/k8se/quickstart-jobs:latest", + "docker.io/library/image:latest", + "docker.io/hello-world:latest" + ], "description": "Required. A fully qualified image name to import." } }, + "sourceRegistryUsername": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The username for the source registry. Required if the source registry is private, or to logon to the public docker registry." + } + }, + "sourceRegistryPassword": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. The password for the source registry. Required if the source registry is private, or to logon to the public docker registry." + } + }, "newImageName": { "type": "string", "defaultValue": "[last(split(parameters('image'), '/'))]", @@ -289,7 +307,7 @@ "value": "[parameters('runOnce')]" }, "azCliVersion": { - "value": "2.61.0" + "value": "2.63.0" }, "timeout": { "value": "PT30M" @@ -298,38 +316,44 @@ "value": "PT1H" }, "environmentVariables": { - "value": { - "secureList": [ - { - "name": "acrName", - "value": "[parameters('acrName')]" - }, - { - "name": "imageName", - "value": "[parameters('image')]" - }, - { - "name": "newImageName", - "value": "[parameters('newImageName')]" - }, - { - "name": "overwriteExistingImage", - "value": "[toLower(string(parameters('overwriteExistingImage')))]" - }, - { - "name": "initialDelay", - "value": "[format('{0}s', string(parameters('initialScriptDelay')))]" - }, - { - "name": "retryMax", - "value": "[string(parameters('retryMax'))]" - }, - { - "name": "retrySleep", - "value": "5s" - } - ] - } + "value": [ + { + "name": "acrName", + "value": "[parameters('acrName')]" + }, + { + "name": "imageName", + "value": "[parameters('image')]" + }, + { + "name": "newImageName", + "value": "[parameters('newImageName')]" + }, + { + "name": "overwriteExistingImage", + "value": "[toLower(string(parameters('overwriteExistingImage')))]" + }, + { + "name": "initialDelay", + "value": "[format('{0}s', string(parameters('initialScriptDelay')))]" + }, + { + "name": "retryMax", + "value": "[string(parameters('retryMax'))]" + }, + { + "name": "retrySleep", + "value": "5s" + }, + { + "name": "sourceRegistryUsername", + "value": "[parameters('sourceRegistryUsername')]" + }, + { + "name": "sourceRegistryPassword", + "secureValue": "[parameters('sourceRegistryPassword')]" + } + ] }, "cleanupPreference": { "value": "[parameters('cleanupPreference')]" @@ -344,7 +368,7 @@ "value": "[parameters('subnetResourceIds')]" }, "scriptContent": { - "value": "#!/bin/bash\n set -e\n\n echo \"Waiting on RBAC replication ($initialDelay)\\n\"\n sleep $initialDelay\n\n # retry loop to catch errors (usually RBAC delays, but 'Error copying blobs' is also not unheard of)\n retryLoopCount=0\n until [ $retryLoopCount -ge $retryMax ]\n do\n echo \"Importing Image ($retryLoopCount): $imageName into ACR: $acrName\\n\"\n if [ $overwriteExistingImage = 'true' ]; then\n az acr import -n $acrName --source $imageName --image $newImageName --force\n else\n az acr import -n $acrName --source $imageName --image $newImageName\n fi\n\n sleep $retrySleep\n retryLoopCount=$((retryLoopCount+1))\n done\n\n echo \"done\\n\"" + "value": "#!/bin/bash\n set -e\n\n echo \"Waiting on RBAC replication ($initialDelay)\\n\"\n sleep $initialDelay\n\n # retry loop to catch errors (usually RBAC delays, but 'Error copying blobs' is also not unheard of)\n retryLoopCount=0\n until [ $retryLoopCount -ge $retryMax ]\n do\n echo \"Importing Image ($retryLoopCount): $imageName into ACR: $acrName\\n\"\n if [ $overwriteExistingImage = 'true' ]; then\n if [ -n \"$sourceRegistryUsername\" ] && [ -n \"$sourceRegistryPassword\" ]; then\n az acr import -n $acrName --source $imageName --image $newImageName --force --username $sourceRegistryUsername --password $sourceRegistryPassword\n else\n az acr import -n $acrName --source $imageName --image $newImageName --force\n fi\n else\n if [ -n \"$sourceRegistryUsername\" ] && [ -n \"$sourceRegistryPassword\" ]; then\n az acr import -n $acrName --source $imageName --image $newImageName --username $sourceRegistryUsername --password $sourceRegistryPassword\n else\n az acr import -n $acrName --source $imageName --image $newImageName\n fi\n fi\n\n sleep $retrySleep\n retryLoopCount=$((retryLoopCount+1))\n done\n\n echo \"done\\n\"" } }, "template": { @@ -354,8 +378,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "148060868388125113" + "version": "0.29.47.4906", + "templateHash": "5978422939896103340" }, "name": "Deployment Scripts", "description": "This module deploys Deployment Scripts.", @@ -407,6 +431,13 @@ "items": { "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": { @@ -469,32 +500,29 @@ "nullable": true }, "environmentVariableType": { - "type": "secureObject", + "type": "object", "properties": { - "secureList": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "secureValue": { - "type": "string", - "nullable": true - }, - "value": { - "type": "string", - "nullable": true - } - } - }, + "name": { + "type": "string", "metadata": { - "description": "Optional. The list of environment variables to pass over to the deployment script." + "description": "Required. The name of the environment variable." + } + }, + "secureValue": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Required. The value of the secure environment variable." + } + }, + "value": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. The value of the environment variable." } } - }, - "nullable": true + } } }, "parameters": { @@ -564,9 +592,13 @@ } }, "environmentVariables": { - "$ref": "#/definitions/environmentVariableType", + "type": "array", + "items": { + "$ref": "#/definitions/environmentVariableType" + }, + "nullable": true, "metadata": { - "description": "Optional. The environment variables to pass over to the script. The list is passed as an object with a key name \"secureList\" and the value is the list of environment variables (array). The list must have a 'name' and a 'value' or a 'secretValue' property for each object." + "description": "Optional. The environment variables to pass over to the script." } }, "supportingScriptUris": { @@ -595,7 +627,7 @@ }, "retentionInterval": { "type": "string", - "nullable": true, + "defaultValue": "P1D", "metadata": { "description": "Optional. Interval for which the service retains the script resource after it reaches a terminal state. Resource will be deleted when this duration expires. Duration is based on ISO 8601 pattern (for example P7D means one week)." } @@ -669,6 +701,11 @@ }, "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)))))]" + }, { "name": "subnetIds", "count": "[length(coalesce(parameters('subnetResourceIds'), createArray()))]", @@ -681,7 +718,7 @@ "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')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" }, "containerSettings": { @@ -705,7 +742,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.resources-deploymentscript.{0}.{1}', replace('0.2.3', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.resources-deploymentscript.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -735,7 +772,7 @@ "containerSettings": "[if(not(empty(variables('containerSettings'))), variables('containerSettings'), null())]", "storageAccountSettings": "[if(not(empty(parameters('storageAccountResourceId'))), if(not(empty(parameters('storageAccountResourceId'))), createObject('storageAccountKey', if(empty(parameters('subnetResourceIds')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '//'), '/')[2], split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), 'dummyAccount'), '/'))), '2023-01-01').keys[0].value, null()), 'storageAccountName', last(split(parameters('storageAccountResourceId'), '/'))), null()), null())]", "arguments": "[parameters('arguments')]", - "environmentVariables": "[if(not(equals(parameters('environmentVariables'), null())), parameters('environmentVariables').secureList, createArray())]", + "environmentVariables": "[parameters('environmentVariables')]", "scriptContent": "[if(not(empty(parameters('scriptContent'))), parameters('scriptContent'), null())]", "primaryScriptUri": "[if(not(empty(parameters('primaryScriptUri'))), parameters('primaryScriptUri'), null())]", "supportingScriptUris": "[if(not(empty(parameters('supportingScriptUris'))), parameters('supportingScriptUris'), null())]", @@ -765,20 +802,20 @@ "deploymentScript_roleAssignments": { "copy": { "name": "deploymentScript_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Resources/deploymentScripts/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Resources/deploymentScripts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Resources/deploymentScripts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "deploymentScript" @@ -828,7 +865,7 @@ "metadata": { "description": "The output of the deployment script." }, - "value": "[if(contains(reference('deploymentScript'), 'outputs'), reference('deploymentScript').outputs, createObject())]" + "value": "[coalesce(tryGet(reference('deploymentScript'), 'outputs'), createObject())]" }, "deploymentScriptLogs": { "type": "array", diff --git a/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/dependencies.bicep b/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/dependencies.bicep index c66f2f19c2..f68b4451d5 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/dependencies.bicep +++ b/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/dependencies.bicep @@ -13,6 +13,9 @@ param acrName string @description('Required. The name of the Storage Account to create.') param storageAccountName string +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + var ipRange = '10.0.0.0' module identity 'br/public:avm/res/managed-identity/user-assigned-identity:0.2.1' = { @@ -105,6 +108,52 @@ module storage 'br/public:avm/res/storage/storage-account:0.9.0' = { } } +// KeyVault stores the password to login to the source container registry +resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: null + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + dependsOn: [identity] + + resource containerRegistrySecret 'secrets@2023-07-01' = { + name: 'ContainerRegistryPassword' + properties: { + // put the password of the source container registry here + value: '' + } + } + + resource rbac 'accessPolicies@2023-07-01' = { + name: 'add' + properties: { + accessPolicies: [ + { + tenantId: tenant().tenantId + objectId: identity.outputs.principalId + permissions: { + keys: [] + secrets: ['get', 'list', 'set'] + certificates: [] + storage: [] + } + } + ] + } + } +} + // the container registry to upload the image into module acr 'br/public:avm/res/container-registry/registry:0.2.0' = { name: '${uniqueString(resourceGroup().name, location)}-acr' @@ -145,3 +194,9 @@ output storageAccountResourceId string = storage.outputs.resourceId @description('The resource ID of the created subnet designated for the Deployment Script.') output deploymentScriptSubnetResourceId string = vnet::subnet_deploymentscript.id + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The name of the created Key Vault secret.') +output keyVaultSecretName string = keyVault::containerRegistrySecret.name diff --git a/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep b/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep index a00d32d62c..3c7e75c684 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep +++ b/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep @@ -31,6 +31,7 @@ module dependencies 'dependencies.bicep' = { virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' acrName: 'dep${namePrefix}acr${serviceShort}' storageAccountName: 'dep${namePrefix}sa${serviceShort}' + keyVaultName: 'dep${namePrefix}kv${serviceShort}' managedIdentityName: 'dep-${namePrefix}-mi-${serviceShort}' } } @@ -46,6 +47,11 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { // Test Execution // // ============== // +resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = { + name: last(split(dependencies.outputs.keyVaultResourceId, '/')) + scope: resourceGroup +} + @batchSize(1) module testDeployment '../../../main.bicep' = [ for iteration in ['init', 'idem']: { @@ -59,7 +65,10 @@ module testDeployment '../../../main.bicep' = [ } acrName: dependencies.outputs.acrName location: resourceLocation - image: 'mcr.microsoft.com/k8se/quickstart-jobs:latest' + image: 'mcr.microsoft.com/k8se/quickstart-jobs:latest' // e.g. for docker images, that will be authenticated with the below properties 'docker.io/hello-world:latest' + // commented out, as the user is not available in the test environment + // sourceRegistryUsername: 'username' + // sourceRegistryPassword: keyVault.getSecret(dependencies.outputs.keyVaultSecretName) newImageName: 'your-image-name:tag' cleanupPreference: 'OnExpiration' assignRbacRole: true diff --git a/avm/ptn/deployment-script/import-image-to-acr/version.json b/avm/ptn/deployment-script/import-image-to-acr/version.json index daf1a794d9..17dd49a0b9 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/version.json +++ b/avm/ptn/deployment-script/import-image-to-acr/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ]