How to use Azure SDK for Go by example for terraform contributor Part1

I'm trying to contribute for terraform related Azure implementation. In this case, the most important thing is to understand Azure SDK for Go. I'd like to share my learning.

On this blog, I'd like to explain using Log Analytics API as an example.

Import

You need to import not only Azure Go SDK its self, but also, Azure Go Autorest.  The autorest  is the OpenAPI specification code generator. Azure Go SDK is created top on it.  adal means Active Directory Authentication Library.  You can't import whole Azure Go SDK. You need to pick some packages under  /arm .

 

 import(
   :
    "github.com/Azure/azure-sdk-for-go/arm/operationalinsights"
    "github.com/Azure/go-autorest/autorest"
    "github.com/Azure/go-autorest/autorest/adal"
    "github.com/Azure/go-autorest/autorest/azure"
)

Service Principal Token

If you want to access REST API of Azure, you need to request an access token. If you try to contribute terraform, all you need it to understand the way to get an access token by a Service Principal. If you don't have a service principal, please read this article.

Once you've got a service principal, you can write code like this.  Using ADAL, you can get the Service Principal Token.  However, in this point, the service principal has not retrieved yet.

 

 func main() {
    spt, err := NewServicePrincipalTokenFromCredentials(c, azure.PublicCloud.ResourceManagerEndpoint)
    if err != nil {
        log.Fatalf("Err: %v", err)
        return
    }
 :
}
func NewServicePrincipalTokenFromCredentials(c map[string]string, scope string) (*adal.ServicePrincipalToken, error) {
    oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, c["AZURE_TENANT_ID"])
    if err != nil {
       panic(err)
    }
    return adal.NewServicePrincipalToken(*oauthConfig, c["AZURE_CLIENT_ID"], c["AZURE_CLIENT_SECRET"], scope)
}

 

Configure the Azure SDK Client

Then you need configure an client for Azure SDK for go. You can find a lot of clients under the /arm packages. In this example, I use LogAnalytics workspace client. Just add an Service Principal Token to the client.

     client := operationalinsights.NewWorkspacesClient(c["AZURE_SUBSCRIPTION_ID"])
    client.Authorizer = autorest.NewBearerAuthorizer(spt)

Why we can add the BearerAuthorizer? Let's see the definition. You can see that the autorest.Client is included. That is why we can use the functions on a client.

https://github.com/Azure/azure-sdk-for-go/blob/master/arm/operationalinsights/workspaces.go

 type ManagementClient struct {
 autorest.Client
 BaseURI string
 SubscriptionID string
}

https://github.com/Azure/azure-sdk-for-go/blob/master/arm/operationalinsights/client.go

 type WorkspacesClient struct {
 ManagementClient
}

 

Operate Azure

Now you are ready to operate Azure using Azure SDK for go.  For example, I  tried the CreateOrUpdate function for Workspace.

 

     parameter := operationalinsights.Workspace{
        Location: ToAddress("eastus"),
    }

    cancel := make(chan struct{})
    receive, error := client.CreateOrUpdate(resourceGroup, name, parameter, cancel)
     workspace := <-receive
    err = <-error
    if err != nil {
        log.Fatalf("Error: %v", err)
    }

 

The ToAddress method is like this. We need to avoid nil panic.

 func ToAddress(str string) *string {
    return &str
}

The struct of the Workspace is like this. All the struct attribute is the pointer. However, you can't use &"hello world" expression in go.

 type Workspace struct {
 autorest.Response `json:"-"`
 ID *string `json:"id,omitempty"`
 Name *string `json:"name,omitempty"`
 Type *string `json:"type,omitempty"`
 Location *string `json:"location,omitempty"`
 Tags *map[string]*string `json:"tags,omitempty"`
 *WorkspaceProperties `json:"properties,omitempty"`
 ETag *string `json:"eTag,omitempty"`
}

Getting Result

You can see the all result.  Unfortunately, The Azure SDK for go doesn't explain which value can we use for it. What does it mean? Then you can refer the REST API documentation.

NOTE:  You might notice some has no "*" in case of dereferences. e.g. workspace.Sku.Name . If you try *workspace.Sku.Name, you will get Invalid Indirect.

 log.Printf("ID: %s", *workspace.ID)
 log.Printf("Name: %s", *workspace.Name)
 log.Printf("Type: %s", *workspace.Type)
 log.Printf("Location: %s", *workspace.Location)
 if workspace.Tags != nil {
   for key, value := range *workspace.Tags {
     log.Print("[Tags]: key: ", key, "value: ", value)
   }
 }

switch workspace.ProvisioningState {
 case operationalinsights.Canceled:
 log.Printf("ProvisioningState: Canceled")
 case operationalinsights.Creating:
 log.Printf("ProvisioningState: Creating")
 case operationalinsights.Deleting:
 log.Printf("ProvisioningState: Deleting")
 case operationalinsights.Failed:
 log.Printf("ProvisioningState: Failed")
 case operationalinsights.ProvisioningAccount:
 log.Printf("ProvisioningState: ProvisioningAccount")
 case operationalinsights.Succeeded:
 log.Printf("ProvisioningState: Succeeded")
 }
 log.Printf("Source: %s", *workspace.Source)
 log.Printf("CustomerID: %s", *workspace.CustomerID) // a.k.a. Workspace ID
 log.Printf("PortalURL: %s", *workspace.PortalURL)
 log.Printf("Sku: %s", workspace.Sku.Name)
 log.Printf("RetentionInDays %d", *workspace.RetentionInDays)

 

Also you can get the Shared Key for the work space.

 sharedKeys, err := client.GetSharedKeys(resourceGroup, name)
 if err != nil {
 log.Fatalf("Error: %v", err)
 }

log.Printf("SharedKey: Primary Shared Key: ", *sharedKeys.PrimarySharedKey)
log.Printf("SharedKey: Shared Secondary Key: ", *sharedKeys.SecondarySharedKey)

 

This is the result.

 $ go run cmd/client/main.go 
2017/09/06 08:43:49 ID: /subscriptions/{YOUR_SUBSCRIPTION}/resourcegroups/spikeoms/providers/microsoft.operationalinsights/workspaces/spikesaoms¥n
2017/09/06 08:43:49 Name: spikesaoms¥n
2017/09/06 08:43:49 Type: Microsoft.OperationalInsights/workspaces¥n
2017/09/06 08:43:49 Location: eastus¥n
2017/09/06 08:43:49 ProvisioningState: Succeeded
2017/09/06 08:43:49 Source: Azure¥n
2017/09/06 08:43:49 CustomerID: {YOUR_WORKSPACE_ID}¥n
2017/09/06 08:43:49 PortalURL: https://eus.mms.microsoft.com/Account?tenant={YOUR_TENANT_ID}&resource=%2fsubscriptions%2f{YOUR_SUBSCRIPTION_ID}%2fresourcegroups%2fspikeoms%2fproviders%2fmicrosoft.operationalinsights%2fworkspaces%2fspikesaoms¥n
2017/09/06 08:43:49 Sku: free¥n
2017/09/06 08:43:49 RetentionInDays 0¥n
2017/09/06 08:43:49 SharedKey: Primary Shared Key: 9RSV......QUOFRi7w==
2017/09/06 08:43:49 SharedKey: Shared Secondary Key: Bqd9Ozk.....0zbomeyQ==

 

Now you are ready for contribute the terraform!

Resource

You can find whole source code on the gist.

GitHubGist: TsuyoshiUshio/main.go