_191# Fetch IPs from external source; in this case, an API endpoint that returns the list of IPs and descriptions in JSON format
_191Write-Host "Fetching IPs from some external source..."
_191$response = Invoke-RestMethod -Uri ${env:IP_LIST_ENDPOINT} -Method GET -Headers @{
_191 "Content-Type" = "application/json"
_191if (-not $response) {
_191 throw "Failed to fetch IPs from source."
_191Write-Host "Successfully fetched IPs"
_191# Parse the response into a list of IPs and descriptions
_191$allowedIPList = foreach ($item in $response.items.ip_addresses) {
_191 Description = $item.description
_191# Print the list of IPs to the console.
_191$allowedIPList | Format-Table -AutoSize
_191if ($allowedIPList.Count -eq 0) {
_191 Write-Host "No IPs to update."
_191# TODO: make this dynamic
_191$resourceGroupName = "mc-xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx"
_191$appName = "mc-xxxxxx-xxxx-xxxx-xxxx-xxxx-cm"
_191# Fetch the existing IP restrictions
_191# Note that Azure CLI has read limits per hour per subscription
_191$existingRestrictions = az webapp config access-restriction show `
_191 --resource-group $resourceGroupName `
_191 --query "ipSecurityRestrictions" | ConvertFrom-Json
_191# Sample output (when not using --query "ipSecurityRestrictions" ):
_191# "ipSecurityRestrictions": [
_191# "additional_properties": "",
_191# "description": null,
_191# "ip_address": "AzureDevOps",
_191# "name": "AzureDevOps",
_191# "subnet_mask": null,
_191# "subnet_traffic_tag": null,
_191# "tag": "ServiceTag",
_191# "vnet_subnet_resource_id": null,
_191# "vnet_traffic_tag": null
_191# "additional_properties": "",
_191# "description": null,
_191# "ip_address": "AzureCloud",
_191# "name": "AzureCloud",
_191# "subnet_mask": null,
_191# "subnet_traffic_tag": null,
_191# "tag": "ServiceTag",
_191# "vnet_subnet_resource_id": null,
_191# "vnet_traffic_tag": null
_191# "additional_properties": "",
_191# "description": "Allow all access",
_191# "ip_address": "Any",
_191# "name": "Allow all",
_191# "priority": 2147483647,
_191# "subnet_mask": null,
_191# "subnet_traffic_tag": null,
_191# "vnet_subnet_resource_id": null,
_191# "vnet_traffic_tag": null
_191# "ipSecurityRestrictionsDefaultAction": "Allow",
_191# "scmIpSecurityRestrictions": [
_191# "additional_properties": "",
_191# "description": "Allow all access",
_191# "ip_address": "Any",
_191# "name": "Allow all",
_191# "priority": 2147483647,
_191# "subnet_mask": null,
_191# "subnet_traffic_tag": null,
_191# "vnet_subnet_resource_id": null,
_191# "vnet_traffic_tag": null
_191# "scmIpSecurityRestrictionsDefaultAction": "Allow",
_191# "scmIpSecurityRestrictionsUseMain": true
_191Write-Host "Existing App Service IP restrictions (JSON):"
_191$existingRestrictions | ConvertTo-Json | Write-Host
_191# Priority min value is 0 and max is 2147483647 -- multiple rules can have the same priority
_191# Adding the AzureDevOps and AzureCloud service tags to the App Service to ensure that both Azure and Azure DevOps can still access the App Service
_191$allowedServiceTags = @("AzureDevOps", "AzureCloud")
_191foreach ($tag in $allowedServiceTags) {
_191 $existingTag = $existingRestrictions | Where-Object { $_.tag -eq "ServiceTag" -and $_.name -eq $tag }
_191 if ($null -ne $existingTag) {
_191 Write-Output "Service Tag: $tag already exists. Skipping..."
_191 Write-Output "Attempting to add Service Tag: $tag"
_191 # Note that there is an Azure CLI limit of 1,200 write operations per hour per subscription
_191 az webapp config access-restriction add `
_191 --resource-group $resourceGroupName `
_191 --priority $priority `
_191 --service-tag $tag | Out-Null
_191 throw "Failed to add Service Tag. Error: $_"
_191Write-Host "Calculating which IPs need to be added..."
_191$allowedIPsToAdd = $allowedIPList | Where-Object {
_191 if ([string]::IsNullOrWhiteSpace($ip)) {
_191 Write-Host "Skipping empty IP address"
_191 -not ($existingRestrictions | Where-Object {
_191 ($_.ip_address -eq $ip -or $_.ip_address -eq "$ip/32") -and $_.tag -ne "ServiceTag"
_191if ($allowedIPsToAdd.Count -eq 0) {
_191 Write-Host "All allowed IPs are already in the Azure App Service"
_191Write-Host "$($allowedIPsToAdd.Count) IPs will be added to the Azure App Service"
_191Write-Host "Allowed IPs to add (JSON):"
_191$allowedIPsToAdd | ConvertTo-Json | Write-Host
_191foreach ($allowedIP in $allowedIPsToAdd) {
_191 $allowedIPAddress = $allowedIP.IP
_191 $allowedIPDescription = $allowedIP.Description
_191 $ruleName = $allowedIPDescription
_191 # Use IP address as rule name if no description is provided
_191 if ([string]::IsNullOrWhiteSpace($ruleName)) {
_191 $ruleName = $allowedIPAddress
_191 # Note that the rule name is required, and it must not be longer than 32 characters
_191 $ruleName = $ruleName.Substring(0, [Math]::Min(32, $ruleName.Length))
_191 Write-Output "Adding IP: $allowedIPAddress with rule name: $ruleName"
_191 # Note that there is an Azure CLI limit of 1,200 write operations per hour per subscription
_191 az webapp config access-restriction add `
_191 --resource-group $resourceGroupName `
_191 --rule-name $ruleName `
_191 --description $allowedIPDescription `
_191 --priority $priority `
_191 --ip-address $allowedIPAddress | Out-Null
_191 throw "Failed to add IP. Error: $_"