Thursday, 13 November 2025

Whitelist Microsoft Hosted Azure Pipline Agent IPs in Required Azure Resources and Remove Whitelisted IPs Dynamically with Azure Pipelines

  If you are deploying Azure infrastructure using Micorosft hosted Azure pipeline agents you may have to whitelist Microsoft hosted agent IP adress in resources such as storage account where you keep your terraform state, or the key vaults, if you add update secrets to the key vault via terraform and if such resouces are network protected within a vNet in Azure. If the IP is not whitelisted there will be access issue and piplines would fail to make required updates. Let's look at two steps we can implement to add agent IP to a state storage and key vault, and then remove the IP once tarraform plan or apply is done.

Expectation is to execute two tasks as shown below in the pipeline job.


Below is the task in Azure pipeline using poweshell core, which can be used with Azure CLI task, to add IP of the hosted build agent to KV and  a storage account. Here we use url https://api.ipify.org/ to get the IP of the Azure pipeline agent. Then we apply to storage and the key vault. The task will verify if the keyvault is available before adding IP, so it will not fail in the intial run where the key vault is not available.

parameters:
  - name: serviceconnection
    type: string

steps:
  - task: AzureCLI@2
    displayName: 'Apply agent IP to Azure resources'
    inputs:
      azureSubscription: '${{ parameters.serviceconnection }}'
      scriptType: pscore
      scriptLocation: inlineScript
      inlineScript: |
        $rgName = "$(resource_name_prefix)-rg";
        $kvName = "$(resource_name_prefix)-kv";
        $StateRgName = "$(prefix)-shared-state-rg";
        $stateStorageAccountName = "$(prefix)$(environment)tfstatesa01";
        $subscriptionId = "$(subscription_id)";
        $managementSubscriptionId = "$(management_subscription_id)";
        $publicIp = (Invoke-WebRequest -uri "https://api.ipify.org/").Content.Trim();

        echo "Applying agent public IP $publicIp to state storage account $stateStorageAccountName in RG $StateRgName ...";
        az storage account network-rule add --account-name $stateStorageAccountName --resource-group $StateRgName --ip-address "$publicIp" --subscription $managementSubscriptionId

        $groups = az group list --subscription $subscriptionId | ConvertFrom-Json

        if (($groups.name -ne $null) -and ($groups.name.Contains($rgName)))
        {
            echo "RG $rgName found. Searching for KV $kvName ...";
            $kvs = az keyvault list -g $rgName --subscription $subscriptionId | ConvertFrom-Json
        
            if (($kvs.name -ne $null) -and ($kvs.name.Contains($kvName)))
            {
                echo "KV $kvName found. Applying agent public IP $publicIp to KV ...";
                az keyvault network-rule add --name $kvName --resource-group $rgName --ip-address "$publicIp/32" --subscription $subscriptionId
            }
            else
            {
                echo "KV $kvName not found.";
            }
        }
        else
        {
            echo "RG $rgName not found.";
        }

        Start-Sleep -Seconds 30


Then we can have a task that is running always, even if terraform steps fail  it wil ensure to remove the added IP of the pipeline agent from the resources. Below task remove only the agent IP if it exist in the storage or in the key vault.

parameters:
  - name: serviceconnection
    type: string
  
steps:
  - task: AzureCLI@2
    condition: always()
    displayName: 'Remove agent IP from Azure resources'
    inputs:
      azureSubscription: '${{ parameters.serviceconnection }}'
      scriptType: pscore
      scriptLocation: inlineScript
      inlineScript: |
        $rgName = "$(resource_name_prefix)-rg";
        $kvName = "$(resource_name_prefix)-kv";
        $StateRgName = "$(prefix)-shared-state-rg";
        $stateStorageAccountName = "$(prefix)$(environment)tfstatesa01";
        $subscriptionId = "$(subscription_id)";
        $managementSubscriptionId = "$(management_subscription_id)";
        $publicIp = (Invoke-WebRequest -uri "https://api.ipify.org/").Content.Trim();

        $storageRules = az storage account network-rule list --account-name $stateStorageAccountName --resource-group $StateRgName --subscription $managementSubscriptionId | ConvertFrom-Json

        if(($storageRules.ipRules -ne $null) -and ($storageRules.ipRules.ipAddressOrRange -ne $null) -and ($storageRules.ipRules.ipAddressOrRange.Contains("$publicIp")))
        {
            echo "Agent public IP $publicIp is available in state storage account $stateStorageAccountName in RG $StateRgName. Deleting Agent public IP $publicIp ...";
            az storage account network-rule remove --account-name $stateStorageAccountName --resource-group $StateRgName --ip-address "$publicIp" --subscription $managementSubscriptionId
        }
        else
        {
            echo "Agent public IP $publicIp is not available in state storage account $stateStorageAccountName in RG $StateRgName."
        }

        $groups = az group list --subscription $subscriptionId | ConvertFrom-Json
        
        if (($groups.name -ne $null) -and ($groups.name.Contains($rgName)))
        {
            echo "RG $rgName found. Searching for KV $kvName ...";
            $kvs = az keyvault list -g $rgName --subscription $subscriptionId | ConvertFrom-Json

            if (($kvs.name -ne $null) -and ($kvs.name.Contains($kvName)))
            {
                echo "KV $kvName found. Checking if agent public IP $publicIp is available in KV ...";
                $rules = az keyvault network-rule list --name $kvName --resource-group $rgName --subscription $subscriptionId | ConvertFrom-Json

                if(($rules.ipRules -ne $null) -and ($rules.ipRules.value -ne $null) -and ($rules.ipRules.value.Contains("$publicIp/32")))
                {
                    echo "Agent public IP $publicIp is available in KV $kvName. Deleting Agent public IP $publicIp ...";
                    az keyvault network-rule remove --name $kvName --resource-group $rgName --ip-address "$publicIp/32" --subscription $subscriptionId
                }
                else
                {
                    echo "Agent public IP $publicIp is not available in KV $kvName."
                }
            }
            else
            {
                echo "KV $kvName not found.";
            }
        }
        else
        {
            echo "RG $rgName not found.";
        }



No comments:

Popular Posts