Deploy Azure Bastion centrally in Hub & Spoke

Azure Bastion Host is used for remote access of virtual machines without need exposing thoose virtual machines with public IPs. When launched Azure Bastion Host came with a serious drawback, as it did not support VNET peering, hence, we needed to deploy per Virtual Network, making the solution expensive as you needed one per spoke.

Luckily, Azure Bastion Host now has support for VNET peering. Hence the requirement to have one Azure Bastion host per virtual network is not required anymore to get it to work. The next logical placement of the Azure Bastion Host is now to put it more centrally, to keep one Azure Bastion host per region deployment, thus saving cost. A common place can be the connection hub vnet, since it is a well connected with the spokes. If that is the case, you do not want to give more access then needed, since there is other important resources in that subscription. All needed permissions for the Azure Bastion Host can be read at the FAQ for BastionHost for VNET Peering.

Implementation
First part is to establish a role defination as there is no role for Bastion Host as for this date. Hence we need to create a custom role defination. Here is deployment template for BastionReader.
Important to note, in case you do not have access to the root management group, you will need to modify it to a scope where you have the permissions to add Role definitions.

Second part is to deploy the actual Azure Bastion host resource and allocate an group to that role defination. Template for that can be found here.

The input parameters are

  • Basename, prefix for the resources
  • VirtualnetworkID, this is the ID to the virtual network with the AzureBastionSubnet
  • Role defination ID, the ID of the Bastion Reader
  • AAD group, the object ID of the group that will have access to the Bastion resource

After the template is deployed, we are now ready to use the Azure Bastion Host. When using it, make sure that subscription filter is not filtering out the subscription where you are Azure Bastion Host is located, since it utilize this for lookup.

Conclussion
From an architectual this is not an optimal solution, since you do not want to give permissions in the your hub subscription to “everyone” if not needed. Hopefully there is some clever solutions from Microsoft coming to change this.

Convert Azure Firewall to Firewall Manager – part 1

Firewall Manager and Firewall policies has been the new kid on the block for some time now (General avaialable in June) and with the new Azure Firewall Premium Firewall only being supported with Firewall Policy (link), it is logical to start migrating existing Azure Firewall to utilize Firewall Policy to be able to consume all new services.

The first part is to get the existing ruleset setup in your existing firewall to a new Azure Firewall policy. Microsoft have published a article with an script for it here . There were a few missing things such as IP groups support and some assumption, so a slightly modified script can be found here at my git repo link .

The script will do the following

  • Create an Firewall policy with specified name (and resource group if not already created)
  • Poll all info from the specified Firewall
  • Set threat detection setting into the Firewall policy
  • Loop thru Application Rule set, Network Rule set and NAT rule set from the source and apply that to the Azure Firewall policy created

To run the script, open up either Cloudshell or your local Powershell prompt and select the subscription where Azure Firewall is located. Fill in your current firewall resource group and firewall name and then the names of the Firewall Policy and resource group.

.\Export-RulesToFirewallPolicy.ps1 -FirewallResourceGroup "firewall-rg" -FirewallName "firewall" -FirewallPolicyResourceGroup "firewallpolicy-rg" -FirewallPolicyName "firewallpolicy" -FirewallPolicyLocation westeurope

Now the firewall policy are ready to either be used in to convert existing firewall or a new Azure Firewall.

Avoid Source NAT for none RFC1918 ranges in Azure Firewall

In certain scenarios, some companies are using public IPs for internal purposes. This more common in education or larger old enterprises as they got assigned a sizeable public IP range. This creates some unique challanges for Azure Firewall in combination with ExpressRoute or VPN.
By default, Azure firewall will source NAT communication with IP adresses not defined in the RFC 1918 space (10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16).

If the none RFC1918 space is coming from ExpressRoute or VPN, it will source NAT to one of the Azure Firewall interfaces. For example if you got 192.168.0.0/26 defined as your AzureFirewallSubnet, it can be 192.168.0.6 for example. This is choosen “random”, since AzureFirewall consist of at least 2 instances behind the scene. Hence, if a virtual machine (Virtual Machine Windows) in Azure with the source IP of 172.0.0.10, sitting “behind” the firewall, communicating with an on-premise virtual machine (Virtual Machine Linux) with the IP of 30.30.30.10, the target machine, will see one of the Azure Firewall IPs as source IP, for example 192.168.0.5.

For certain applications, this can brake functionality and therefor not a desired behaviour. Lucikly Microsoft released a new feature, where we can defined our own ranges, that should be excluded from source NAT. From Azure Portal, navigate to the Firewall and press Private IP range.

Here, already defined is IANA Private ranges (RFC1918), here we can add our 30.30.30.0/16 range, to make it excluded from Source NAT. After change is applied. Virtual Machine Linux will see 172.16.0.10 of Virtual Machine Windows as the source IP, instead of the Azure Firewall Internal IP.

Via ARM template
If you want to add this via ARM templates instead, add the following snippet under the properties configuration

"additionalProperties": {
                    "Network.SNAT.PrivateRanges": "IANAPrivateRanges , 30.30.30.0/24"
                },

Powershell OneLiners #1

Sometimes, the need for a certificate to verify something quickly. Here is an example how to add certificate that expire after 50 years on two domain names from your Powershell prompt.

New-SelfSignedCertificate -DnsName "app.domain.local", "test-app.domain.local" -CertStoreLocation "cert:\LocalMachine\My" -NotAfter (Get-date).AddYears(50) -KeyExportPolicy Exportable

Azure API Management setup with networking via VNET

API Management makes it possible to publish APIs for exteranl and/or internal purposes in a secure manner. The Developer or Premium tier enables virtual network support, giving the possibility to access resources in your virtual network, or on-premise if you have VPN/ExpressRoute setup.

There is two different kind of network integrations for API management, external and internal. The External are external publishing and the gateway are able to access internal resources.

While an Internal only offer it API services internally

For our example, we will go with the External.

Before proceeding, a dedicated subnet need to be setup just for API management (a /29 works fine) and if there is an advertised default route, there is a need to add a User defined route to Internet as a default one.

First off, we need the ResourceID for the subnet, copy paste the resourceID

$VNET = Get-AzureRmVirtualNetwork  -Name "API-VNET" -ResourceGroup "Common-RG"
Get-AzureRmVirtualNetworkSubnetConfig -Name "API-VNET-SUBNET" -VirtualNetwork $VNET

After this, defined the API Management Network and save it to the variable $apivnet

$Location = "WestEurope"
$apivnet = New-AzureRmApiManagementVirtualNetwork -Location $Location -SubnetResourceId "/subscriptions/b6c5a626-dfd6-40ca-9408-525e7345e8de/resourceGroups/Common-RG/providers/Microsoft.Network/virtualNetworks/API-VNET3/subnets/API-VNET-SUBNET"

Now, create the API Management, specify a Name, an Organization, AdminEmail and the SKU (need to be Developer or Premium) and finally the VpnType, that is the networking type.

$APIRG = New-AzureRMResourceGroup -Name "API-RG" -Location $Location
New-AzureRmApiManagement -ResourceGroupName $($APIRG.ResourceGroupName)  -Name "api-dev" -Location $Location -Organization "SystembolagetDev" -AdminEmail "foo@bar.org" -Sku Developer -VirtualNetwork $apivnet -VpnType External

The command will take a while, since the VNET integration takes up to 15 minutes to complete. After that, we are set!

Force a reboot with DSC in ARM-template

When deploying an ARM-tempalate, the Azure Virtual Network got updated with the new DNS server. The Virtual machines deployed before this update, still got the old DNS client settings, before the VM gets rebooted. This becomes a bit of a challange, when trying to domain join the computer with Desired State Configuration (DSC), since it will not find the domain controller (if not the old DNS settings where already pointed to a current domain controller).

To force a reboot thru DSC can be achieved by the module xPendingReboot and DSC Script. xPendingReboot checks if there is a pending reboot of the machine and works in conjunction with the Local Configuration Manager (LCM). If LCM is set to RebootNodeIfNeeded=$true, it will reboot. However, a new machine got no pending reboot. To achieve this we will need to set DSCMachineStatus to 1 and write a small registry item. This will indicate there is a PendingReboot.

First we need to load the module (including the domainJoin as well)

Import-DscResource -ModuleName xPendingReboot, xDSCDomainjoin

Set the LCM RebootNodeIfNeeded to true

LocalConfigurationManager 
{
   RebootNodeIfNeeded = $true
}

Create a xPendingReboot resource

xPendingReboot Reboot
{
   Name = "Reboot"
}

Add our “fake” reboot.

Script Reboot
{
    TestScript = {
    return (Test-Path HKLM:\SOFTWARE\MyMainKey\RebootKey)
    }
    SetScript = {
			New-Item -Path HKLM:\SOFTWARE\MyMainKey\RebootKey -Force
			$global:DSCMachineStatus = 1 
        }
    GetScript = { return @{result = 'result'}}
}

and finally the domainjoin with a DependsOn so it will happend before domainjoin.

xDSCDomainjoin JoinDomain
{
  Domain = $DomainName
  Credential = $DomainCreds
  DependsOn = "[Script]Reboot"
}

Pass null conditions to JSON-template with Powershell – splat!

With Azure ARM templates we have the possibility to add conditions. Meaning that we can for example decide if we want to deploy a certain resource or not that is defined in the JSON-template. This gives us great flexibility when authoring ARM templates.

An example is that you might decide to have an option to create a VPN-Gateway for your Network template and want to option to say Yes or No as input. If in the deployment you choose No, you do not really want to enter all the other parameters required for VPN, even if they are defined in your ARM Template. The parameter file you can choose as input (and is created by default if you work with Visual Studio) handles this null values. So far so good.

However, sometimes, we want to solve this from Powershell and inject parameters directly, without needing a parameter file. To accomplish this, we will need to create a hash table with the relevant parameters. For example we have two parameters. VPN, that is yes or no. If we choose no, we do not really want enter a VPNSharedKey, since it will be not needed.

First we enter the input parameters to the PowerShell script. The script gives the operator the possibility to choose two answers on VPN, yes or No.

[Cmdletbinding()]
Param
(
    [Parameter(Mandatory=$true)]
    [ValidateSet('yes','no')]
    [string[]]$VPN,
    [Parameter(Mandatory=$false)]
    [String]$VPNSharedKey
)

Then we pass this variables into our hash table

$Parameters @{
VPN ="$VPN"
VPNSharedKey ="$VPNSharedKey"
}

Hence we can launch the Powershell script (Azuredeploy.json being our template file)

.\Deploy-ToAzure.ps1 -VPN no -TemplateFile C:\Azuredeploy.json

For further reading, see Microsoft documentation about splatting about_Splatting