How to use Microsoft Azure with Juju


Add cloud

Juju already has knowledge of the Azure cloud, which means adding your Azure account to Juju is quick and easy.

You can see more specific information on Juju’s Azure support (e.g. the supported regions) by running:

juju show-cloud azure

To ensure that Juju’s information is up to date (e.g. new region support), you can update Juju’s public cloud data by running:

juju update-public-clouds

Add credentials

The Credentials page offers a full treatment of credential management.

Several steps are required to add Azure credentials to Juju:

  • Install the CLI tool
  • Log in to Azure
  • Import the credentials

Azure account credentials have been reported to expire. If a previously working setup suddenly behaves as if incorrect credentials are being used then you may need to update the credentials on the controller. See Updating remote credentials for guidance.

Alternately, you can use your credentials with Juju as a Service, where charms can be deployed within a graphical environment that comes equipped with a ready-made controller.

A note on permissions in locked-down environments

As of this writing, the Azure account needs, at minimum, the following permissions, in order to enable fully automated provisioning of Virtual Machines with attached storage with Juju. (In most public cloud environments, you’ll have these permissions by default, and can skip this section.)

Microsoft.Compute/skus (read) 
Microsoft.Resources/subscriptions/resourceGroups (read, write, delete) 
Microsoft.Resources/deployments/ (write/read/delete/cancel/validate)
Microsoft.Network/networkSecurityGroups (write, read, delete, other - join)
Microsoft.Network/virtualNetworks/ (write, read, delete) 
Microsoft.Compute/virtualMachineScaleSets/ (write, read, delete, other -  start action, other - deallocate action, other - restart action, other powerOff action) 
Microsoft.Network/virtualNetworks/subnets/ (read, write, delete, other - join)
Microsoft.Compute/availabilitySets (write, read, delete)
Microsoft.Network/publicIPAddresses (write, read, delete, other - join - optional for public services)
Microsoft.Network/networkInterfaces (write, read, delete, other - join)
Microsoft.Compute/virtualMachines (write, read, delete, other - start, power off, restart, deallocate) 
Microsoft.Compute/disks (write, read, delete)

Install the CLI tool

You will need to import your Azure credentials into Juju using the Azure CLI 2.0 tool from Microsoft.

Ubuntu/Linux users can install Azure CLI 2.0 with the following command:

curl -sL | sudo bash

For instructions that cover installing Azure CLI on Microsoft Windows and Apple macOS, see Microsoft’s Install Azure CLI 2.0 documentation.

If the installer encounters any difficulties it will let you know. Examples include the inability to find your system’s Python interpreter or missing software dependencies. Run any commands that the installer suggests in order to rectify these deficiencies. You may need to prepend sudo to some commands (only do this if needed). After each command, rerun the above curl command. Accept the suggested default answers to any questions it may ask (by just pressing ‘Enter’).

For Ubuntu 20.04 (focal) and later, no additional setup steps are needed. For Ubuntu 18.04 (bionic) the following commands are needed prior to the curl command: sudo ln -s /usr/bin/python3 /usr/bin/python sudo apt install python3-distutils gcc python3-dev

At the end you will be asked to run exec -l $SHELL to restart your shell.

Verify that the tool is properly installed by running az --version.

Log in to Azure

Log in to your Azure account in order to display the credentials that you will, in turn, provide to Juju:

az login

This will output a URL and a code, for example:

To sign in, use a web browser to open the page and enter the code D6FRLOY6U to authenticate.

After entering the code the web site should show ‘Microsoft Azure Cross-platform Command Line Interface’. Press the ‘Continue’ button. The resulting page will ask you to “Pick an account”. This is just the email address that you’ve associated with your Azure account. Click on it. You are now logged in to your Azure account.

Import the credentials

Back on the command line, the output from az login should now display your Azure account information:

    "cloudName": "AzureCloud",
    "id": "f717c8c1-8e5e-4d38-be7f-ed1e1c879e18",
    "isDefault": true,
    "name": "Pay-As-You-Go",
    "state": "Enabled",
    "tenantId": "0fb95fd9-f42f-4c78-94c9-e3d01c2bc5af",
    "user": {
      "name": "",
      "type": "user"

Now initiate the process to import your Azure credentials into Juju:

juju add-credential azure

You will first be asked for an arbitrary credential name (e.g. ‘ubuntu’). Secondly, you will be asked to select an ‘Auth Type’ from among the following two methods:

  • interactive
  • service-principal-secret

The default choice is interactive and it is the recommended method. It is far quicker and easier than the manual service-principal-secret method. Here, we’ll assume that ‘interactive’ has been chosen.

For guidance on the manual method, see Manually adding Azure credentials. Use this method if the interactive option fails, or if you want to automate the configuration process.

You then will be asked for your subscription id. In the example above, it is ‘f717c8c1-8e5e-4d38-be7f-ed1e1c879e18’. The recommended way is to simply press ‘Enter’ and let the tool automatically retrieve what’s needed. If you do so, after a few seconds you will see the following (assuming ‘ubuntu’ is the credential name):

Credential "ubuntu" added locally for cloud "azure".

You may once again be asked to visit the site and enter a code. In this case it is important to verify that the credential process is complete before closing the authentication window.

You can also verify that the credentials were successfully added by running juju credentials.

Create a controller

You are now ready to create a Juju controller for cloud ‘azure’:

juju bootstrap azure azure-controller

Above, the name given to the new controller is ‘azure-controller’. Azure will provision an instance to run the controller on.

This will result in the controller environment being visible in the Azure portal.

For a detailed explanation and examples of the bootstrap command see the Creating a controller page.

Spaces support

The Azure provider supports network spaces.

juju subnets will show the subnets Juju knows about for a given model.

To add a space in one of the available subnets:

juju add-space foo

and now add a machine in that space:

juju add-machine --constraints="space=foo"

This will create a machine and use “subnet1” for the network configuration of the primary NIC machine-X-primary. If no space is specified, Juju will default to using a subnet called juju-internal-network in the configured vnet.

You can create another space:

juju add-space bar

and create a machine in both spaces:

juju add-machine --constraints="spaces=foo,bar"

A machine gets created with 2 NICs, machine-X-primary and machine-X-interface-1. Each NIC is bound to the subnet of each space.

If you do this instead (ie a space with multiple subnets):

juju add-space foobar
juju add-machine --constraints="spaces=foobar"

Then the machine only gets one NIC and one of the subnets is chosen at random.

Azure-specific features

Azure accounts are initially limited to 10 cores (trial accounts can be even lower). You will need to file a support ticket with Azure to raise your quota limit.

Juju supports a number of Azure specific features:

  • availability set support
  • bring your own resource group
  • bring your own virtual network
  • full disk encryption on instance volumes

Availability sets

As long as at least two units are deployed, Azure guarantees 99.95% availability of the application overall. Exposed ports are automatically load-balanced across all units within the application. Using availability sets disables manual placement and the add-machine command.

Availability sets work differently from zones, but serve the same basic purpose. With zones, Juju directly ensures each unit of a distribution group is placed into a different zone. With sets, Juju places each unit of a distribution group into the same set, and Azure will then try to ensure that not all units in the set will become unavailable at the same time.

Availability sets are not enforced when unit placement (i.e. the --to option for the deploy or add-unit commands) is used.

When creating a new machine, the algorithm used for choosing the availability set is:

  • if the machine is a controller, use the availability set name juju-controller
  • if the machine has units assigned, create an availability set with a name based on the value of the tags.JujuUnitsDeployed tag in vmTags, if it exists
  • otherwise, do not assign the machine to an availability set

Custom resource groups

By default, Juju will create a new resource group for each model, to hold the artifacts for that model. Sometimes, permissions will be such that the user does not have rights to create new resource groups, but they can use existing groups. Juju models can be configured to use such pre-existing resource groups using the resource-group-name model config, eg

juju add-model test --config resource-group-name=myresgroup

It’s also possible to bootstrap using an existing resource group for the controller model, eg

juju bootstrap azure --config resource-group-name=myresgroup --no-default-model

Note that the --no-default-model option is required to ensure Juju does not create an additional “default” model in a new resource group as would normally occur.

Note: When destroying a controller containing models with BYO resource groups, those resource groups will need to be cleaned up manually since there’s a limitation in how Juju tears down a controller, preventing those groups from being cleaned up up automatically.

Custom virtual network

It’s possible to use an existing virtual network when provisioning instances. Say there’s an existing resource group containing a virtual network

|- test-rg
  |- test-vn
    |- juju-controller-subnet
    |- subnet2

The network model config attribute can be used to specify this network be used instead of Juju creating a new one:

juju add-model --config network=test-rg/test-vn

Depending on spaces setup (see below), the machine can be provisioned with NICs on each subnet.

If the specified virtual network has a security group already, Juju will use that, otherwise it will create a new one called juju-internal-nsg.

Placement also works, eg to choose one of the subnets in the vnet:

juju deploy mysql --to subnet=subnet2

Note: For bootstrap, if space constraints are not used to specify a particular subnet, Juju will prefer a subnet called juju-controller-subnet to use for the controller’s primary NIC. If a so named subnet does not exist, Juju will choose one at random, or else error. A simple option for specifying a particular non-default subnet to use (if spaces are not set up) is to use placement: juju bootstrap azure --config network=test-rg/test-vn --to subnet=subnet2 You must also ensure that the subnet has a security group which is configured to allow incoming traffic to the following ports: - 22 (for bootstrap to use ssh to provision the instance) - 17070 (to allow Juju clients and agents to connect to the controller API server)

Full disk encryption

When provisioning an Azure instance, it is possible to ask for root disk encryption using a disk encryption set and a key vault.

You can either use your own disk encryption set or have Juju create a key vault and disk encryption set. Right now, the key size and algorithm is not user configurable. It’s RSA and 4096 bits.

The provisioning is configured using a Juju storage pool. The configurable parameters are:

- encrypted (must be true for Juju to act on it)
- disk-encryption-set-name (defaults to vault name prefix)
- vault-name-prefix
- vault-key-name (defaults to "disk-secret")
- vault-user-id

If just the disk encryption set name is configured, Juju will use that existing disk encryption set and not create any additional resources. It can reside in a different resource group by specifying resgroup_name/DES_name

If a vault name prefix is also configured, Juju will create a vault called vault_name_prefix-resource_group_name. Vault names need to be unique across a subscription.

If vault-user-id is configured, that user will be given full access to manage the vault and keys created by Juju.

To add an instance with disk encryption, first create a storage pool and then use the root-disk-source constraint when adding the Juju machine, eg

juju create-storage-pool byo azure encrypted=true disk-encryption-set-name="mydisksecrets"
juju add-machine --constraints="root-disk-source=byo"


juju create-storage-pool des azure encrypted=true vault-name-prefix=mysecrets vault-user-id=27f86c31-bca0-4a37-b233-0e5030107696
juju add-machine --constraints="root-disk-source=des"


If you want the controller to also use disk encryption, the storage pool will need to be created at bootstrap time, eg

juju bootstrap azure \
  --no-default-model \
  --storage-pool name=foo \
  --storage-pool type=azure \
  --storage-pool encrypted=true \
  --storage-pool vault-name-prefix=secret \

The same arg handling as is used for --config is used here; either a YAML file or multiple cmd args can be used with --storage-pool.

Next steps

A controller is created with two models - the ‘controller’ model, which should be reserved for Juju’s internal operations, and a model named ‘default’, which can be used for deploying user workloads.

See these pages for ideas on what to do next:

@timClicks I added a note here about the add-credential operation re-requesting authentication, which seems to happen sometimes. The rest of the instructions seem good as of today :slight_smile:

Thanks for taking the time to make the change :slight_smile:

Suggest removing the email address in the cloud credentials initialisation config example.