go-junos & Windows Azure IPs

In a previous blog post, I gave an example of how I use the go-junos library to help automate creating and modifying address objects and variables in Junos Space. Here's another good, time saving example of creating multiple address groups to be used within a policy.

Background: Windows Azure

We have multiple development teams here that need to publish their work/apps to the Azure cloud. Without totally circumventing the firewall and allowing full access out, it was a bit of a challenge to allow them to publish to a cloud-based service as there are so many different types of hosts and IP addresses that are used.

We do use some DNS objects, but this doesn't completely solve our issue, despite allowing the Azure cloud domains in our proxy servers as well.

I was able to find a list (XML file) on Microsoft's website of all of their Azure data center IP addresses. This is a lengthy list, and there's no way that I'm manually creating 500+ objects by hand. Sure I could import them into Space using a CSV file, but I'd still have to search and replace a bit to get the information in the right format.

Enter go-junos to the rescue!
All The Things

Creating The Azure IP Addresses

Here's a sample of what the XML file looks like that I got from Microsoft:

<?xml version="1.0" encoding="utf-8"?>  
<AzurePublicIpAddresses xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  
  <Region Name="europewest">
    <IpRange Subnet="65.52.128.0/19" />
    <IpRange Subnet="94.245.97.0/24" />
    <IpRange Subnet="137.116.192.0/19" />
    <IpRange Subnet="157.55.8.64/26" />
    <IpRange Subnet="157.55.8.128/27" />
    <IpRange Subnet="157.55.8.160/28" />
    <IpRange Subnet="168.63.0.0/19" />
    <IpRange Subnet="168.63.96.0/19" />
    <IpRange Subnet="193.149.80.0/22" />
    <IpRange Subnet="213.199.128.0/21" />
    <!-- lots more -->
  </Region>
</AzurePublicIpAddresses>  

There are multiple Region sections, each with at least 20 subnets. I basically want to create an address group within Junos Space that reflects this - each region gets it's own group. Frorm there, it's easy to pick and choose what groups I want to include in my policy.

Here's the Go code I initially wrote to just print what I described above to the console.

I typically always do this (print the output first) just to make sure I'm getting the desired result.

package main

import (  
    "encoding/xml"
    "fmt"
    "io/ioutil"
    "log"
)

type AzurePublicIPAddresses struct {  
    Region []Region `xml:"Region"`
}

type Region struct {  
    Name    string   `xml:"Name,attr"`
    Subnets []Subnet `xml:"IpRange"`
}

type Subnet struct {  
    Subnet string `xml:"Subnet,attr"`
}

func main() {  
    flag.Parse()
    var azure AzurePublicIPAddresses
    x, err := ioutil.ReadFile(os.Args[1])
    if err != nil {
        log.Fatal(err)
    }

    if err := xml.Unmarshal(x, &azure); err != nil {
        log.Fatal(err)
    }

    for _, r := range azure.Region {
        fmt.Printf("%+v\n", r.Name)
        for _, s := range r.Subnets {
            fmt.Printf("--> %+v\n", s.Subnet)
        }
        fmt.Print("\n")
    }
}

Gist file on Github

Once I run this, I get output like so:

uscentral  
--> 23.100.80.0/21
--> 23.101.112.0/20
--> 168.61.128.0/17
--> 193.149.72.0/21
--> 23.100.240.0/20
--> 23.99.128.0/19
--> 23.99.160.0/22
--> 23.99.164.0/23
--> 23.99.166.0/24
--> 23.99.167.0/28
--> 23.99.168.0/24
--> 23.99.192.0/18
--> 104.43.128.0/17
--> 104.208.0.0/27

asiaeast  
--> 23.98.32.0/21
--> 23.98.40.0/22
--> 23.100.88.0/21
--> 23.101.0.0/20
--> 65.52.160.0/19
--> 111.221.64.0/22
--> 111.221.69.0/25
-- lots more --

So now that I'm confident I can loop over each region and list the subnets, I can create the objects in Junos Space. I use the following functions to do that:

Here's the full code of the program:

package main

import (  
    "encoding/xml"
    "flag"
    "fmt"
    "github.com/scottdware/go-junos"
    "io/ioutil"
    "log"
    "os"
    "time"
)

type AzurePublicIPAddresses struct {  
    Region []Region `xml:"Region"`
}

type Region struct {  
    Name    string   `xml:"Name,attr"`
    Subnets []Subnet `xml:"IpRange"`
}

type Subnet struct {  
    Subnet string `xml:"Subnet,attr"`
}

var (  
    server   = flag.String("server", "", "Junos Space server")
    user     = flag.String("user", "", "Username")
    password = flag.String("password", "", "Password")
    source   = flag.String("source", "", "XML file with Azure subnets")
)

func main() {  
    flag.Parse()
    var azure AzurePublicIPAddresses
    space := junos.NewServer(*server, *user, *password)
    x, err := ioutil.ReadFile(*source)
    if err != nil {
        log.Fatal(err)
    }

    if err := xml.Unmarshal(x, &azure); err != nil {
        log.Fatal(err)
    }

    for _, r := range azure.Region {
        gName := fmt.Sprintf("Azure_%s", r.Name)
        desc := fmt.Sprintf("Azure IPs - %s", r.Name)
        space.AddGroup(true, gName, desc)
        for _, s := range r.Subnets {
            addrName := fmt.Sprintf("AzureSubnet_%s", s.Subnet)
            space.AddAddress(addrName, s.Subnet, "")
            time.Sleep(time.Millisecond * 250)
            space.ModifyObject(true, "add", gName, addrName)
        }
    }
}

Gist on Github

I hope you enjoyed another good example of using go-junos to easily manipulate Junos Space.