Update firewall rules in Microsoft Azure with PowerShell
This article deals with the topic of how to administer your cloud infrastructure using the Microsoft Graph API.
As we at e.GO Mobile use Microsoft as our cloud provider, I will show you with a small example how to add and delete firewall rules for a PostgreSQL server hosted on Azure.
Background
A few years ago, Microsoft began to replace its terminal CMD.EXE with a more powerful alternative. Microsoft decided to develop a shell that is closely connected to the .NET Framework.
Initially only available for Windows, they started an open-source version of PowerShell, which is now platform-independent and can run on almost all common systems, including MacOS and Linux.
With this, you can develop scripts, even though it may be unfamiliar for Unix/Linux administrators, that can access the .NET Framework without any restrictions, whether as pure automation scripts or as GUI applications with Windows Forms.
Requirements
Apart from an Azure account, you need for this example:
- at least one PostgreSQL server in connection with Flexible Azure Database incl. firewall feature
- an Azure Active Directory (Microsoft Entra ID)
- the Azure CLI
Using the Azure CLI, you can log in by using the command az login
and execution of
az ad sp create-for-rbac --role Contributor --scopes /subscriptions/<SUBSCRIPTION-ID>/resourceGroups/<RESOURCE-GROUP-NAME>
will create server principals to access Azure resources via the Graph API.
Replace <SUBSCRIPTION-ID>
and <RESOURCE-GROUP-NAME>
with the correct values, you find in your Azure Portal.
Explaining the code
At GitHub there is a demo project as usual.
I will now go through the code and explain the most important parts of it:
Get public IP
For all REST API calls, I use the Invoke-RestMethod cmdlet.
To get the current public IP, I am using the service https://api.ipify.org … but it is also possible to use other services, as long as the function Get-MyIP is adjusted accordingly:
function Get-MyIP {
$url = "https://api.ipify.org"
$response = Invoke-RestMethod -Uri $url -Method GET
return $response.ToString()
}
Get API access token
This step works the same way as with other use cases of the Microsoft Graph API: A POST request is sent to the address https://login.microsoftonline.com/<TENANT-ID>/oauth2/token
with the following form data:
Name | Description |
---|---|
client_id |
appId value from previous Azure CLI call |
client_secret |
password value from previous Azure CLI call |
grant_type |
always client_credentials |
resource |
always https://management.azure.com/ |
A successful answer will return a JSON object with an access_token
property that we later will need for all upcoming REST API calls:
{
// ...
"access_token": "..."
}
In PowerShell an API call could look like this:
# collect data for request
# from environment variables
$clientId = $Env:TGF_AZURE_AD_CLIENT_ID
$clientSecret = $Env:TGF_AZURE_AD_CLIENT_SECRET
$tenantId = $Env:TGF_AZURE_AD_TENANT_ID
# request URL
$url = "https://login.microsoftonline.com/" `
+ [System.Web.HttpUtility]::UrlEncode($tenantId) `
+ "/oauth2/token"
# HTTP request headers
$headers = @{
"Content-Type" = "application/x-www-form-urlencoded"
}
# build body
$formData = "grant_type=" + [System.Web.HttpUtility]::UrlEncode("client_credentials") `
+ "&client_id=" + [System.Web.HttpUtility]::UrlEncode($clientId) `
+ "&client_secret=" + [System.Web.HttpUtility]::UrlEncode($clientSecret) `
+ "&resource=" + [System.Web.HttpUtility]::UrlEncode("https://management.azure.com/")
# do the request ...
$response = Invoke-RestMethod -Uri $url -Method POST -Headers $headers -Body $formData
# as a common OAuth2 response, the access token
# is in `access_token` of $response
$accessToken = $response.access_token
Use of System.Web.HttpUtility.UrlEncode() method demonstrates how easy it is to interact with .NET Framework via the PowerShell.
Add firewall rule
The AllowIPForPostgresServer.ps1 script demonstrates how to add a firewall rule for a PostgreSQL server.
To read the settings for the API call from the command line parameters, a Get-PostgresSettings function is located in Utils.ps1:
# collect PostgreSQL settings
# and pass-through command line arguments
$postgresSettings = Get-PostgresSettings $args
# public IP, you can also replace with a static IP
# or with data from outside of course
$myIP = Get-MyIP
# get access token
$accessToken = Get-AzureAccessToken
# build URI
$url = "https://management.azure.com/subscriptions/" + [System.Web.HttpUtility]::UrlEncode($postgresSettings.Subscription) `
+ "/resourceGroups/" + [System.Web.HttpUtility]::UrlEncode($postgresSettings.ResourceGroup) `
+ "/providers/Microsoft.DBforPostgreSQL/flexibleServers/" + [System.Web.HttpUtility]::UrlEncode($postgresSettings.Server) `
+ "/firewallRules/" + [System.Web.HttpUtility]::UrlEncode($postgresSettings.Rule) + "?api-version=2022-12-01"
# setup headers
$headers = @{
"Authorization" = "Bearer $($accessToken)"
"Content-Type" = "application/json"
}
# create JSON body
$json = @{
"properties" = @{
"endIpAddress" = $myIP
"startIpAddress" = $myIP
}
} | ConvertTo-Json
# s. https://learn.microsoft.com/en-us/rest/api/postgresql/flexibleserver/firewall-rules/create-or-update?view=rest-postgresql-flexibleserver-2022-12-01&tabs=HTTP
$response = Invoke-RestMethod -Uri $url -Method PUT -Headers $headers -Body $json
# output response
$response
Remove firewall rule
Removing a rule is very similar (s. RemoveIPForPostgresServer.ps1):
$postgresSettings = Get-PostgresSettings $args
$myIP = Get-MyIP
$accessToken = Get-AzureAccessToken
$url = "https://management.azure.com/subscriptions/" + [System.Web.HttpUtility]::UrlEncode($postgresSettings.Subscription) `
+ "/resourceGroups/" + [System.Web.HttpUtility]::UrlEncode($postgresSettings.ResourceGroup) `
+ "/providers/Microsoft.DBforPostgreSQL/flexibleServers/" + [System.Web.HttpUtility]::UrlEncode($postgresSettings.Server) `
+ "/firewallRules/" + [System.Web.HttpUtility]::UrlEncode($postgresSettings.Rule) + "?api-version=2022-12-01"
$headers = @{
"Authorization" = "Bearer $($accessToken)"
}
# s. https://learn.microsoft.com/en-us/rest/api/postgresql/flexibleserver/firewall-rules/delete?view=rest-postgresql-flexibleserver-2022-12-01&tabs=HTTP
$response = Invoke-RestMethod -Uri $url -Method DELETE -Headers $headers
$response
Conclusion
I have successfully tested the scripts on MacOS and they should also be executable on Windows and Linux, since PowerShell is platform-independent and I have only used common techniques.
Have fun while trying it out! 🎉