Problem
I have Azure VMs that require being accessed only from 3 IP addresses. Two of these IPs are static, the other is dynamic. Every time the dynamic IP changes, I must manually go to Azure portal and update NSG rules. Because this happens every few days, it makes me work 996. To solve this issue, I created Azure Function to automatically update the NSG rule with a simple HTTP request. I no longer need to manually go into Azure portal. Let's see how to do it.
Create Azure Function App
PowerShell Function
Create a new Azure Function and select PowerShell as runtime. After creation is completed, go to Identity blade, and enable system assigned managed identity.
Add Az modules
Go to App files - requirements.psd1 and add PowerShell modules:
@{
'Az.Accounts' = '2.*'
'Az.Network' = '5.*'
}
Allow Function Access for NSG
Go to Access control (IAM) of the NSG, add a new role assignment
Select "Contributor" in "Privileged administrator roles"
Select "Managed identity" and add the function app.
Write Function Logic
HTTP Trigger
Create a new Function with HTTP Trigger
Logic code
Copy the code and replace variables to your own values. In my case, cnBridgeIp
and corpIp
are two static IP addresses. The dynamic IP will come from HTTP request query string or body. You can modify the code to your need.
using namespace System.Net
param($Request, $TriggerMetadata)
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."
$ip = $Request.Query.ip
if (-not $ip) {
$ip = $Request.Body.Ip
}
$subscriptionId = "<id>"
$tenantId = "<id>"
$rsgName = "<group>"
$nsgName = "<nsg>"
$ruleName = "<rule>"
$cnBridgeIp = "<ip>"
$corpIp = "<ip>"
$body = "251"
if ($ip) {
# Set-AzNetworkSecurityRuleConfig
# https://learn.microsoft.com/en-us/powershell/module/az.network/set-aznetworksecurityruleconfig?view=azps-9.5.0
Select-AzSubscription -SubscriptionID $subscriptionId -TenantID $tenantId
$nsg = Get-AzNetworkSecurityGroup -Name $nsgName -ResourceGroupName $rsgName
# $httpsRule = $nsg | Get-AzNetworkSecurityRuleConfig -Name $ruleName
($nsg.SecurityRules | Where-Object { $_.Name -eq $ruleName }).SourceAddressPrefix = ([System.String[]] @($cnBridgeIp, $corpIp, $ip))
$nsg | Set-AzNetworkSecurityGroup | Get-AzNetworkSecurityRuleConfig -Name $ruleName
$body = "Updated NSG $nsgName for IP: $ip."
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::OK
Body = $body
})
}
else {
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::NotFound
})
}
Test
Click Test/Run, enter an IP address in HTTP request body.
Then go to your NSG to verify if the function logic is successful.
If everything is fine, click "Get function URL", then you can integrate this API endpoint to other part of your system to update NSG rule fully automatically for a dynamic IP address.
You may get your dynamic IP address by Azure Function or third-party APIs. For example, in a C# Function, you can do this:
// #r "System.Text.Json"
using System.Net;
// using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
// log.LogInformation("C# HTTP trigger function processed a request.");
var clientIp = req.HttpContext.Connection.RemoteIpAddress.ToString();
return new OkObjectResult(clientIp);
}
Soren
Fantastic tutorial and idea. I was looking for ways to let my script authenticate to my Azure account to edit an NSG, because I use GitHub Codespaces, which uses dynamic IPs, but then I found your article.
I never used Azure Functions before, but this seemed so fun that I had to try, and I did. Thanks a lot!