Uruchamianie skryptu wdrożenia Bicep prywatnie za pośrednictwem prywatnego punktu końcowego

Korzystając z wersji interfejsu API zasobów Microsoft.Resources/deploymentScripts2023-08-01, można uruchamiać skrypty wdrażania w trybie prywatnym w usłudze Azure Container Instances (ACI).

Konfigurowanie środowiska

W tej konfiguracji usługa ACI utworzona przez skrypt wdrożenia jest uruchamiana w sieci wirtualnej i uzyskuje prywatny adres IP. Następnie nawiązuje połączenie z nowym lub istniejącym kontem magazynu danych za pośrednictwem prywatnego punktu końcowego. Właściwość containerSettings/subnetIds określa wartość ACI, która musi zostać wdrożona w podsieci sieci wirtualnej.

Zrzut ekranu przedstawiający architekturę wysokiego poziomu przedstawiającą sposób, w jaki infrastruktura jest połączona z prywatnym uruchamianiem skryptów wdrażania.

Aby uruchomić skrypty wdrażania prywatnie, potrzebna jest następująca infrastruktura, jak pokazano na diagramie architektury:

  • Utwórz sieć wirtualną z dwiema podsieciami:
    • Podsieć dla prywatnego punktu końcowego.
    • Podsieć dla usługi ACI— ta podsieć wymaga delegowania Microsoft.ContainerInstance/containerGroups .
  • Utwórz konto magazynu bez dostępu do sieci publicznej.
  • Utwórz prywatny punkt końcowy w sieci wirtualnej skonfigurowanej przy użyciu podzasobu file na koncie magazynu danych.
  • Utwórz prywatną strefę privatelink.file.core.windows.net DNS i zarejestruj prywatny adres IP punktu końcowego jako rekord A. Połącz prywatną strefę DNS z utworzoną siecią wirtualną.
  • Utwórz tożsamość zarządzaną przypisaną przez użytkownika z uprawnieniami Storage File Data Privileged Contributor do konta magazynowego i określ ją we właściwości identity w zasobie skryptu wdrożeniowego. Aby przypisać tożsamość, zobacz Tożsamość.
  • Zasób ACI jest tworzony automatycznie przez zasób skryptu wdrażania.

Ważna

Jeśli przypiszesz zarządzaną tożsamość z uprawnieniami przekraczającymi wymagania operacyjne skryptu, autor skryptu odpowiada za zapewnienie ochrony konta magazynowego przed niezamierzonym dostępem. Gdzie to możliwe, stosuj zasadę najmniejszych przywilejów.

Poniższy plik Bicep konfiguruje infrastrukturę wymaganą do prywatnego uruchamiania skryptu wdrażania:

@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@2025-01-31-preview' = {
  name: userAssignedIdentityName
  location: location
}

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

resource privateEndpoint 'Microsoft.Network/privateEndpoints@2025-01-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@2024-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@2025-01-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: '14.0'
    retentionInterval: 'P1D'
    scriptContent: 'Write-Host "Hello World!"'
  }
}

Usługa ACI pobiera obrazy kontenerów z usługi Microsoft Container Registry. Jeśli używasz zapory, dodaj adres URL mcr.microsoft.com do listy dozwolonych, aby pobrać obraz. Nieudane pobranie obrazu kontenera powoduje przejście usługi ACI w stan waiting, co ostatecznie prowadzi do błędu przekroczenia limitu czasu.

Następne kroki

W tym artykule przedstawiono sposób uruchamiania skryptów wdrażania za pośrednictwem prywatnego punktu końcowego. Dodatkowe informacje: