79619682

Date: 2025-05-13 12:50:18
Score: 0.5
Natty:
Report link

When you lock your ACR behind a private endpoint, the one piece that breaks is your build‐and‐push job: a Microsoft-hosted agent (or your local laptop) simply can’t ever reach your registry’s private IP. You have two ways to get around that:

1) Build & push to a private ACR

A. Use ACR Tasks (server-side build)

ACR Tasks run inside Azure, so they don’t need your agent to talk to the registry—but they do need permission through your ACR’s firewall/private endpoint.

  1. In the Azure Portal, go to your Container Registry → Networking

  2. Under Private endpoints, click your ACR private link.

  3. Under Firewall, toggle on “Allow trusted services” (this lets ACR Tasks in).

  4. From your pipeline use the exact same snippet you have:

    
    - task: AzureCLI@2
      displayName: 'Build & Push with ACR Tasks'
      inputs:
        azureSubscription: '$(azureSubscription)'
        scriptType: bash
        scriptLocation: inlineScript
        inlineScript: |
          az acr build \
            --registry $(acrName) \
            --image func-images:$(Build.BuildId) \
            --image func-images:latest \
            --file $(functionAppPath)/Dockerfile \
            $(functionAppPath)
    
  5. Confirm in the Portal’s Tasks blade that the build jobs are succeeding.

B. Or run docker build & docker push on a self-hosted agent in your VNET

If you’d rather build locally in your pipeline, that agent needs network access to your private ACR.

  1. Spin up an Azure VM (or Container Instance) in the same VNet/subnet (so it can resolve your private DNS zone)

  2. Install the Azure DevOps agent on that VM and add it to a self-hosted pool (e.g. MyVNetAgents)

  3. In your YAML switch pools and do a classic Docker build/push:

    pool:
      name: MyVNetAgents
    
    steps:
    - task: AzureCLI@2
      displayName: 'Login to ACR & Build/Push'
      inputs:
        azureSubscription: '$(azureSubscription)'
        scriptType: bash
        scriptLocation: inlineScript
        inlineScript: |
          az acr login --name $(acrName)
          docker build \
            -f $(functionAppPath)/Dockerfile \
            -t $(acrName).azurecr.io/func-images:$(Build.BuildId) \
            $(functionAppPath)
          docker push $(acrName).azurecr.io/func-images:$(Build.BuildId)
    
    

    2) Let your Container App actually pull that image

    Your Function-in-a-Container App has exactly the same “private registry” problem when it starts up. You have two choices here too:

    A. Feed it credentials

    When you first created the Container App (or its Environment) you can supply --registry-server, --registry-username and --registry-password. The CLI then stores those for every update/pull.

    az containerapp env create \
      --name my-env \
      --resource-group $(resourceGroup) \
      --location westus \
      --registry-server $(acrName).azurecr.io \
      --registry-username <YOUR-ACR-SPN-APPID> \
      --registry-password <YOUR-ACR-SPN-SECRET>
    
    

    Then your existing update:

    - task: AzureCLI@2
      displayName: 'Deploy to Container App'
      inputs:
        azureSubscription: '$(azureSubscription)'
        scriptType: bash
        scriptLocation: inlineScript
        inlineScript: |
          az containerapp update \
            --name $(containerAppName) \
            --resource-group $(resourceGroup) \
            --image $(acrName).azurecr.io/func-images:$(Build.BuildId)
    
    

    B. Use a Managed Identity (recommended)

    1. Turn on system-assigned identity on your Container App:

      az containerapp identity assign \
        --name $(containerAppName) \
        --resource-group $(resourceGroup)
      
      
    2. Grant that identity the AcrPull role on your registry:

      az role assignment create \
        --assignee <the-principal-id-you-got-above> \
        --role AcrPull \
        --scope /subscriptions/.../resourceGroups/.../providers/Microsoft.ContainerRegistry/registries/$(acrName)
      
      
    3. Update your Container App exactly as before—the identity will automatically be used for pulls:

      az containerapp update \
        --name $(containerAppName) \
        --resource-group $(resourceGroup) \
        --image $(acrName).azurecr.io/func-images:$(Build.BuildId)
      
      

    3) DNS & network checklist

    • DNS: your build VM (or ACR Tasks) must resolve
      mycontainerregistry-ehbcbtcwhpeyf9c2.azurecr.io → <private-endpoint-IP>
      via your Azure Private DNS zone (e.g. privatelink.azurecr.io).

    • VNet integration: both your build host and your Container App Environment must be on subnets that have that DNS zone linked.

    • Firewall rules: if you ever switch to public endpoints, you can open “Allow Azure services” or explicitly allow the Azure DevOps service tag—but private endpoint + firewall = host must be inside the VNet.

    TLDR (Summary):

    1. Decide where your build lives:

      • hosted ACR Tasks (enable “trusted services”), or

      • self-hosted agent in your VNet.

    2. Build & Push your Docker image to ACR.

    3. Configure your Container App to pull—either supply creds or use MSI + AcrPull.

    4. Wire up your YAML exactly as above.

    Once your build agent can actually talk to the registry IP, and your App can pull it, everything will flow end-to-end again.

Reasons:
  • Contains signature (1):
  • Long answer (-1):
  • Has code block (-0.5):
  • Starts with a question (0.5): When you
  • Low reputation (0.5):
Posted by: Ash