Saturday, January 3, 2015

Scripting a basic network in Azure

Azure has a strong PowerShell API that allows for scripting of all components within their infrastructure as a service offering.  In this blog post, I am going to use parts of this API to create a basic network that I can use to further build on later.

Here is a diagram of what I am trying to build.



In the diagram above, the Azure virtual network is the base.  While configuring the virtual network, you will need to know some basic information such as the address space you intend to use, the subnets you would like to configure, DNS servers that will be used for the environment, and S2S or P2S connectivity details.  For my purposes, I am going to use the following configuration:

Address Space:  10.0.0.0/8
Server Subnet: 10.1.0.0/24
DMZ Subnet: 10.2.0.0/24
Secure Subnet: 10.3.0.0/24
Client Subnet: 10.4.0.0/24

DC1 - 10.1.0.4
DC2 - 10.1.0.5

There are two commands that the Azure PowerShell API has to interact with vnet configuration.  The Get-AzureVnetConfig allows you to export the existing configuration to a file.  The Set-AzureVnetConfig takes an XML configuration file and apply that config to the subscription.  One interesting quirk is that these commands are used to control all virtual network, dns, and local network configurations.  If you, say, used the get command to get the XML, deleted a virtual network config, and then used the set command to apply it, the corresponding virtual network would be deleted (or attempted to be deleted).

This quirk throws a slight complication in my script.  In reality, I want to add a new virtual network, and if it already exists, skip to further steps down the line.  It turns out that powershell makes interacting with XML quite easy, and creating the corresponding add scripts are actually quite simple to do.

Here is the code I use to add my virtual network configuration to an existing configuration file.


$networkConfigurationPath = [System.IO.Path]::GetTempFileName()

Write-Host $networkConfigurationPath

Get-AzureVNetConfig -ExportToFile $networkConfigurationPath

AppendDNSTo -filePath $networkConfigurationPath -dnsServerName "PIA-DC1" -dnsServerIP "10.1.0.4"
AppendDNSTo -filePath $networkConfigurationPath -dnsServername "PIA-DC2" -dnsServerIP "10.1.0.5"

$subnets = @{
    "Server" = "10.1.0.0/24";
    "DMZ" = "10.2.0.0/24";
    "Secure" = "10.3.0.0/24";
    "Client" = "10.4.0.0/24";
}

$vnetName = "PIA"

AppendVNetTo -filePath $networkConfigurationPath    -dnsServerRefs @("PIA-DC1","PIA-DC2") `
                                                    -vnetName $vnetName `
                                                    -vnetLocation $location `
                                                    -addressSpace "10.0.0.0/8" `
                                                    -subnets $subnets


Set-AzureVNetConfig -ConfigurationPath $networkConfigurationPath

Remove-Item -Path $networkConfigurationPath


The code above basically goes through the two steps.  Firstly, add the DNS servers that you wish to reference in the vnet.  Second, add the VNET.  Here is a reference for the AppendDNSTo function.


function AppendDNSTo{
    <#
    .SYNOPSIS
    Helper method to add DNS to an existing Azure network config file
    .DESCRIPTION
    Appends a DNS entry to an existing config.  If it already exists, will continue without error
    .PARAMETER configPath
    The path to an already existing configuration
    .PARAMETER dnsServerName
    The Name of the new DNS Server
    .PARAMETER dnsServerIP
    The IP of the new DNS Server
    .PARAMETER failIfExists
    Instructs program to throw error if DNS already exists
    #>

    param(
    [Parameter(Mandatory=$true)]
    [string]$filePath,
    [Parameter(Mandatory=$true)]
    [string]$dnsServerName,
    [Parameter(Mandatory=$true)]
    [string]$dnsServerIP,
    [bool]$failIfExists = $false
    )

    TestGivenPath -filePath $filePath

    $xml = [xml] (Get-Content $filePath)

    $dnsServers = $xml.NetworkConfiguration.VirtualNetworkConfiguration.Dns.DnsServers

    if ($dnsServers.ChildNodes | ? {$_.name -eq $dnsServerName}){
        Write-Debug "DNS Server already exists"
        if ($failIfExists){
            throw "DNS Server Name provided already exists in config file"
        }
    }
    else{
        $dnsServerToAdd = $xml.CreateElement("DnsServer",$xml.DocumentElement.NamespaceURI)
        $dnsServerToAdd.SetAttribute("name",$dnsServerName)
        $dnsServerToAdd.SetAttribute("IPAddress",$dnsServerIP)
        $dnsServers.AppendChild($dnsServerToAdd)
        $xml.Save($filePath)
    }

}

Now that the basic vnet has been created, we need to create the "firewall" between each zone as depicted in the diagram.  With the recent addition of multiple nics, I could see a few different ways to do this, potentially using 3rd party solutions.  However, Azure also provides the concept of Network Security Groups which can be used.  These are basically stateful firewalls that can be applied to either a VM or a subnet.  What is also interesting is that they come with a default ruleset that is easy to work with.  After installing mine, here is what the rules came out to.



While not complete for my purposes, it is a good base that allows you to get started with network security groups quickly.

Here is a function I wrote to help with adding NSG to the subnets.  It is pretty simple.  Create one, and associate with the subnet.


function AddNetworkSecurityGroupAndAssociate{
    param(
    [Parameter(Mandatory=$true)]
    [string]$nsgName,
    [Parameter(Mandatory=$true)]
    [string]$nsgLabel,
    [Parameter(Mandatory=$true)]
    [string]$subnetName,
    [Parameter(Mandatory=$true)]
    [string]$vnetName,
    [Parameter(Mandatory=$true)]
    [string]$location
    )

    New-AzureNetworkSecurityGroup -Name $nsgName -Location $location -Label $nsgLabel
    Get-AzureNetworkSecurityGroup -Name $nsgName | Set-AzureNetworkSecurityGroupToSubnet -VirtualNetworkName $vnetName -SubnetName $subnetName
}

While I don't have anything in this virtual network to test with, I was able to quite simply confirm the configuration.  I now have a script to build out a basic network in any subscription.  Cool!