Condividi tramite


Eseguire lo script di distribuzione di Bicep privatamente in un endpoint privato

Con la Microsoft.Resources/deploymentScriptsversione dell'API della risorsa2023-08-01, è possibile eseguire script di distribuzione privatamente all'interno di un'Istanza di Azure Container (ACI).

Configurare l'ambiente

In questa configurazione, l’Istanza di Azure Container creata dallo script di distribuzione viene eseguita all’interno di una rete virtuale e ottiene un indirizzo IP privato. Stabilisce quindi una connessione con un account di archiviazione nuovo o esistente tramite un endpoint privato. La proprietà containerSettings/subnetIds specifica l'Istanza di Azure Container che deve essere distribuita in una subnet della rete virtuale.

Screenshot dell'architettura di alto livello che mostra come l'infrastruttura è connessa per eseguire gli script di distribuzione privatamente.

Per eseguire gli script di distribuzione privatamente, è necessaria l'infrastruttura seguente, come illustrato nel diagramma dell'architettura:

  • Creare una rete virtuale con due subnet:
    • Una subnet per l’endpoint privato.
    • Una subnet per l'Istanza di Azure Container, che richiede una delega Microsoft.ContainerInstance/containerGroups.
  • Creare un account di archiviazione senza accesso alla rete pubblica.
  • Creare un endpoint privato all'interno della rete virtuale configurata con la sotto-risorsa file nell'account di archiviazione.
  • Creare una zona DNS privata privatelink.file.core.windows.net e registrare l'indirizzo IP dell'endpoint privato come record A. Collegare la zona DNS privata alla rete virtuale creata.
  • Creare un'identità gestita assegnata dall'utente con autorizzazioni Storage File Data Privileged Contributor per l'account di archiviazione e specificarla nella proprietà identity nella risorsa script di distribuzione. Per assegnare l'identità, vedere Identità.
  • La risorsa Istanza di Azure Container viene creata automaticamente dalla risorsa script di distribuzione.

Il file Bicep seguente configura l'infrastruttura necessaria per l'esecuzione di uno script di distribuzione privatamente:

@maxLength(10) // Required maximum length, because the storage account has a maximum of 26 characters
param namePrefix string
param location string = resourceGroup().location
param userAssignedIdentityName string = '${namePrefix}Identity'
param storageAccountName string = '${namePrefix}stg${uniqueString(resourceGroup().id)}'
param vnetName string = '${namePrefix}Vnet'
param deploymentScriptName string = '${namePrefix}ds'

var roleNameStorageFileDataPrivilegedContributor = '69566ab7-960f-475b-8e7c-b3118f30c6bd'
var vnetAddressPrefix = '192.168.4.0/23'
var subnetEndpointAddressPrefix = '192.168.4.0/24'
var subnetACIAddressPrefix = '192.168.5.0/24'

resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
  name: userAssignedIdentityName
  location: location
}

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' = {
  name: storageAccountName
  kind: 'StorageV2'
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  properties: {
    publicNetworkAccess: 'Disabled'
    networkAcls: {
      defaultAction: 'Deny'
      bypass: 'AzureServices'
    }
  }
}

resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-11-01' = {
   name: storageAccount.name
   location: location
   properties: {
    privateLinkServiceConnections: [
      {
        name: storageAccount.name
        properties: {
          privateLinkServiceId: storageAccount.id
          groupIds: [
            'file'
          ]
        }
      }
    ]
    customNetworkInterfaceName: '${storageAccount.name}-nic'
    subnet: {
      id: virtualNetwork::privateEndpointSubnet.id
    }
   }
}

resource storageFileDataPrivilegedContributorReference 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
  name: roleNameStorageFileDataPrivilegedContributor
  scope: tenant()
}

resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(storageFileDataPrivilegedContributorReference.id, managedIdentity.id, storageAccount.id)
  scope: storageAccount
  properties: {
    principalId: managedIdentity.properties.principalId
    roleDefinitionId: storageFileDataPrivilegedContributorReference.id
    principalType: 'ServicePrincipal'
  }
}

resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
  name: 'privatelink.file.core.windows.net'
  location: 'global'

  resource virtualNetworkLink 'virtualNetworkLinks' = {
    name: uniqueString(virtualNetwork.name)
    location: 'global'
    properties: {
      registrationEnabled: false
      virtualNetwork: {
        id: virtualNetwork.id
      }
    }
  }

  resource resRecord 'A' = {
    name: storageAccount.name
    properties: {
      ttl: 10
      aRecords: [
        {
          ipv4Address: first(first(privateEndpoint.properties.customDnsConfigs)!.ipAddresses)
        }
      ]
    }
  }
}

resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-11-01' = {
  name: vnetName
  location: location
  properties:{
    addressSpace: {
      addressPrefixes: [
        vnetAddressPrefix
      ]
    }
  }

  resource privateEndpointSubnet 'subnets' = {
    name: 'PrivateEndpointSubnet'
    properties: {
      addressPrefixes: [
        subnetEndpointAddressPrefix
      ]
    }
  }

  resource containerInstanceSubnet 'subnets' = {
    name: 'ContainerInstanceSubnet'
    properties: {
      addressPrefix: subnetACIAddressPrefix
      delegations: [
        {
          name: 'containerDelegation'
          properties: {
            serviceName: 'Microsoft.ContainerInstance/containerGroups'
          }
        }
      ]
    }
  }
}

resource privateDeploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
  name: deploymentScriptName
  dependsOn: [
    privateEndpoint
    privateDnsZone::virtualNetworkLink
  ]
  location: location
  kind: 'AzurePowerShell'
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: {
      '${managedIdentity.id}' : {}
    }
  }
  properties: {
    storageAccountSettings: {
      storageAccountName: storageAccount.name
    }
    containerSettings: {
      subnetIds: [
        {
          id: virtualNetwork::containerInstanceSubnet.id
        }
      ]
    }
    azPowerShellVersion: '9.0'
    retentionInterval: 'P1D'
    scriptContent: 'Write-Host "Hello World!"'
  }
}

L’Istanza di Azure Container scarica le immagini del contenitore dal Registro Container Microsoft. Se si usa un firewall, consentire all'URL mcr.microsoft.com, aggiunto all’elenco di elementi consentiti, di scaricare l'immagine. Il mancato download dell'immagine del contenitore comporta l'immissione di uno stato di waiting da parte dell’Istanza di Azure Container, causando infine un errore di timeout.

Passaggi successivi

In questo articolo si è appreso come usare gli script di distribuzione in un endpoint privato. Per altre informazioni, vedere: