Authenticate to Azure resources from Go apps hosted on-premises

Apps hosted outside of Azure (for example on-premises or at a third-party data center) should use an application service principal to authenticate to Azure when accessing Azure resources. Application service principal objects are created using the app registration process in Azure. When an application service principal is created, a client ID and client secret will be generated for your app. The client ID, client secret, and your tenant ID are then stored in environment variables so they can be used by the Azure SDK for Go to authenticate your app to Azure at runtime.

A different app registration should be created for each environment the app is hosted in. This allows environment specific resource permissions to be configured for each service principal and ensures that an app deployed to one environment doesn't talk to Azure resources that are part of another environment.

1 - Register the application in Azure

An app can be registered with Azure using either the Azure portal or the Azure CLI.

az ad sp create-for-rbac --name <app-name>

The output of the command will be similar to the following. Make note of these values or keep this window open as you'll need these values in the next steps and won't be able to view the password (client secret) value again.

{
  "appId": "00001111-aaaa-2222-bbbb-3333cccc4444",
  "displayName": "msdocs-python-sdk-auth-prod",
  "password": "Ee5Ff~6Gg7.-Hh8Ii9Jj0Kk1Ll2Mm3_Nn4Oo5Pp6",
  "tenant": "aaaabbbb-0000-cccc-1111-dddd2222eeee"
}

2 - Assign roles to the application service principal

Next, you need to determine what roles (permissions) your app needs on what resources and assign those roles to your app. Roles can be assigned a role at a resource, resource group, or subscription scope. This example shows how to assign roles for the service principal at the resource group scope since most applications group all their Azure resources into a single resource group.

A service principal is assigned a role in Azure using the az role assignment create command.

az role assignment create --assignee {appId} \
    --scope /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName} \
    --role "{roleName}" 

To get the role names that a service principal can be assigned to, use the az role definition list command.

az role definition list \
    --query "sort_by([].{roleName:roleName, description:description}, &roleName)" \
    --output table

For example, to allow the service principal with the appId of 00001111-aaaa-2222-bbbb-3333cccc4444 read, write, and delete access to Azure Storage blob containers and data in all storage accounts in the msdocs-go-sdk-auth-example resource group in the subscription with ID aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e, you would assign the application service principal to the Storage Blob Data Contributor role using the following command.

az role assignment create --assignee 00001111-aaaa-2222-bbbb-3333cccc4444 \
    --scope /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-go-sdk-auth-example \
    --role "Storage Blob Data Contributor"

For information on assigning permissions at the resource or subscription level using the Azure CLI, see the article Assign Azure roles using the Azure CLI.

3 - Configure environment variables for application

You must set the AZURE_CLIENT_ID, AZURE_TENANT_ID, and AZURE_CLIENT_SECRET environment variables for the process that runs your Go app to make the application service principal credentials available to your app at runtime. The DefaultAzureCredential object looks for the service principal information in these environment variables.

Variable name Value
AZURE_CLIENT_ID Application ID of an Azure service principal
AZURE_TENANT_ID ID of the application's Microsoft Entra tenant
AZURE_CLIENT_SECRET Password of the Azure service principal
export AZURE_TENANT_ID="<active_directory_tenant_id>"
export AZURE_CLIENT_ID="<service_principal_appid>"
export AZURE_CLIENT_SECRET="<service_principal_password>"

4 - Implement DefaultAzureCredential in application

To authenticate Azure SDK client objects to Azure, your application should use the DefaultAzureCredential type from the azidentity package.

Start by adding the azidentity package to your application.

go get github.com/Azure/azure-sdk-for-go/sdk/azidentity

Next, for any Go code that instantiates an Azure SDK client in your app, you'll want to:

  1. Import the azidentity package.
  2. Create an instance of DefaultAzureCredential type.
  3. Pass the instance of DefaultAzureCredential type to the Azure SDK client constructor.

An example of this is shown in the following code segment.

import (
	"context"

	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
	"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
)

const (
	account       = "https://<replace_with_your_storage_account_name>.blob.core.windows.net/"
	containerName = "sample-container"
	blobName      = "sample-blob"
	sampleFile    = "path/to/sample/file"
)

func main() {
    // create a credential
    cred, err := azidentity.NewDefaultAzureCredential(nil)
    if err != nil {
      // TODO: handle error
    }
    
    // create a client for the specified storage account
    client, err := azblob.NewClient(account, cred, nil)
    if err != nil {
      // TODO: handle error
    }
    
    // TODO: perform some action with the azblob Client
    // _, err = client.DownloadFile(context.TODO(), <containerName>, <blobName>, <target_file>, <DownloadFileOptions>)
}

When the above code instantiates DefaultAzureCredential, then DefaultAzureCredential reads the environment variables AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET for the application service principal information to connect to Azure with.