How to use Azure API from Go SDK

In this post we will go through some basic example on how to use Azure SDK in Go. The example program we will go through is pretty simple. First, it gets a list of all resource groups in an Azure subscription, then it iterates over all VMs within every resource group. And guess what.. it does all of that using Go’s awesome concurrency (go go goroutines). Sounds pretty straightforward, but in fact the operations we do in the example should give you a good overview on how to use Azure’s SDKs and API in general.

The program is located in the github repository, you are free to clone it, fork it and work on it. You only need to have a working go and dep installations.

 

Authenticating

Let’s start ! However, before we proceed, we first need to somehow authenticate agains Azure API. For Azure this means creating a service principal account that our program will use to authenticate and assume a role with permissions needed to execute API actions.

To create a service principal, let’s use Azure CLI, as shown below. The command will output an authentication file with information such as client id, client secrets and bunch of information needed to connect to Azure. Remember to keep it secure !

az ad sp create-for-rbac —sdk-auth > my.auth

Now that we have the service principal created, clone the repo of the example

git clone git@github.com:nordcloud/azure-go-example.git

The program is super simple and consists of one file — main.go. Before we proceed, we however need to run dep ensure to have the golang SDK dependencies vendored in our program directory.

 

dep ensure

The code 

Let’s open the main.go file and see the main() method.

What you can spot is the general flow of what example program does. First, it authorises using service provider identity we created in previous steps, then gets a list of all resource groups in a subscription, and finally for every resource group, it lists all the VMs.

Ok, let’s see what happens in the code. Let’s look at the newSessionFromFile method. What is the interesting part is the line where we get the authorizer with SDK’s method NewAuthorizerFromFile.

 

func newSessionFromFile() (*AzureSession, error) {
authorizer, err := auth.NewAuthorizerFromFile(azure.PublicCloud.ResourceManagerEndpoint)
if err != nil {
    return nil, errors.Wrap(err, "Can't initialize authorizer")
}
authInfo, err := readJSON(os.Getenv("AZURE_AUTH_LOCATION"))
if err != nil {
    return nil, errors.Wrap(err, "Can't get authinfo")
}
sess := AzureSession{
    SubscriptionID: (*authInfo)["subscriptionId"].(string),
    Authorizer:     authorizer,
}
return &sess, nil
}

This method assumes that AZURE_AUTH_LOCATION env variable contains the path to the service principal auth file we created before. It reads the file and returns an authorizer that is later passed to resource API clients. We pack the authorizer into AzureSession struct, along with the subscription id. We read the id of our subscription from the same auth file file with the readJSON() method.

Let’s go back to the main() method. Now, that we have a working session, we need to get a list of resource groups. For that I let’s see thegetGroups method. It takes a session as an argument and creates a new client for the groups API. The clients is passed an authorizer we created with in the previous step.

 

grClient := resources.NewGroupsClient(sess.SubscriptionID)

 

The pattern, where you create a client and execute its methods (typically List, Get, CreateOrUpdate) will be the same for all resources in the SDK. Once you get the knack of it, you will use Azure API in Go without looking into doc.

To get the resource groups list we iterate over the list of all resource groups returned by the ListComplete method of the resource group client and add them to a list.

Azure SDKs are auto generated and more or less very RESTful. Each resource type will have the same methods, such as CreateOrUpdate, List, and so on. You can see the API description here, the methods described there will map to SDK methods and return types to Go structs.

for list, err := grClient.ListComplete(context.Background(), "", nil); list.NotDone(); err = list.Next() {
    if err != nil {
        return nil, errors.Wrap(err, "error traversing RG list")
    }
    rgName := *list.Value().Name
    tab = append(tab, rgName)
}

Ok what we have done so far is we authorized us and got a list of resource groups. Now, for every group we will list all VMs that are in the group. Moreover, we will do this concurrently using the go routines ! For loop in the main() method iterates over resource groups returned by the getGroups methods and for every rg returned, it runs a concurrent goroutine using the go keyword.

 

go getVM(sess, group, &wg)

The goroutine is implemented in the getVM method. The method does similar stuff to getGroups. It creates a virtual machines client (NewVirtualMachinesClient) and iterates over all VMs printing them.

 

for vm, err := vmClient.ListComplete(context.Background(), rg); vm.NotDone(); err = vm.Next() {
    if err != nil {
        log.Print("got error while traverising RG list: ", err)
    }
    i := vm.Value()
    fmt.Printf("(%s) VM %s\n", rg, *i.Name)
}

The main thread is waiting for the termination of all concurrent goroutines using the WaitGroup primitive and it’s Wait() method, which is used here to implement a barrier that waits for all go routines to finish. You can read more about WaitGroups and synchronization here.

 

Running the example

Before we run the program, we first need to export the AZURE_AUTH_LOCATION variable with the path to the my.auth file with service principal information.

 

export AZURE_AUTH_LOCATION=/path/to/my.auth
go run main.go

 

You should see something like this as an output:

(rg1) VM ubuntu
(rg1) VM ubuntutest
(rg2) VM ubuntu2
(rg2) VM ubuntu3
(rg2) VM ubuntu5
(rg2) VM ubuntu44
(rg2) VM ubuntu333
(rg2) VM ubuntu3
(myGroup) VM windows
(myGroup) VM Windas
(testGroup233) VM vm-1-west
(testGroup233) VM vm-2-west
(testGroup233) VM vm-3-west
(testGroup233) VM vm-4-west
...

 

In the next post we will show you how to use GCP SDK from Go 🙂