The Microsoft Azure cloud and Juju

List of supported clouds > Microsoft Azure

This document describes details specific to using your existing Microsoft Azure cloud with Juju.

See more: Microsoft Azure

When using the Microsoft Azure cloud with Juju, it is important to keep in mind that it is a (1) machine cloud and (2) not some other cloud.

See more: Cloud differences in Juju

As the differences related to (1) are already documented generically in our Tutorial, How-to guides, and Reference docs, here we record just those that follow from (2).

Requirements

If you’re in a locked-down environment:
Permissions:

- 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)

Notes on juju add-cloud

Type in Juju: azure.

Name in Juju: azure.

Notes on juju add-credential

If your credential stops working:

Credentials for the azure cloud have been reported to occasionally stop working over time. If this happens, try juju update-credential (passing as an argument the same credential) or juju add-credential (passing as an argument a new credential) + juju default-credential.

For some example workflows:

See Appendix: Example authentication workflows.

Authentication types

managed-identity (preferred)

Requirements:

  • Juju 3.6+.
  • A managed identity. See more: Appendix: How to create a managed identity.
  • The managed identity and the Juju resources must be created on the same subscription.
  • The add-credential steps must be run from either the Azure Cloud Shell^ or a jump host running in Azure in order to allow the cloud metadata endpoint to be reached.

This is the recommended way to authenticate with Azure as this way you are never touching your cloud credentials directly.

See more: Appendix: Example authentication workflows > Workflow 1

interactive = “service-principal-secret-via-browser”

This is the recommended way to authenticate with Azure if you want to use a service principal secret.

When you add the credential in this way and provide the subscription ID, Juju will open up a browser and you’ll be prompted to log in to Azure.

:information_source: If you are using the unconfined juju snap /snap/juju/current/bin/juju add-credential azure and have the azure CLI and you are logged in and you want to use the currently logged in user: You may leave the subscription ID empty – Juju will fill it in for you.

:warning: If you decide to fill in the optional fields as well: Make sure to set them to unique values (i.e., the application-name and role-definition-name fields cannot be the same).

:white_check_mark: Starting with Juju 3.6, you can also combine this authentication type with a managed identity by bootstrapping with the instance-role constraint.

See more: Appendix: Example authentication workflows > Workflows 2-3

service-principal-secret (dispreferred)

Starting with Juju 3.6, you can also combine this with a managed identity by bootstrapping with the instance-role constraint.

See more: Appendix: Example authentication workflows > Workflows 2-3

Notes on juju bootstrap

If during juju add-credential you chose interactive (= “service-principal-secret-via-browser”) or service-principal-secret: You can still combine this with a managed identity by running juju bootstrap with --constraints instance-role=....

See more: Appendix: Example authentication workflows > Workflow 2, Supported constraints

Cloud-specific model configuration keys

load-balancer-sku-name

Mirrors the LoadBalancerSkuName type in the Azure SDK.

type string
default value “Standard”
immutable false
mandatory true

resource-group-name

If set, use the specified resource group for all model artefacts instead of creating one based on the model UUID.

type string
default value schema.omit{}
immutable true
mandatory false

network

If set, use the specified virtual network for all model machines instead of creating one.

type string
default value schema.omit{}
immutable true
mandatory false

Supported constraints

CONSTRAINT
conflicting: [instance-type] vs [arch, cores, mem]
supported?
- allocate-public-ip :white_check_mark:
- arch :white_check_mark:
Valid values: amd64.
- container :white_check_mark:
- cores :white_check_mark:
- cpu-power :x:
- image-id :x:
- instance-role Starting with Juju 3.6: :white_check_mark:

Valid values: auto (Juju creates a managed identity for you) or a managed identity^ name in one of the following formats:

- If the managed identity is created in a resource group on the same subscription:
<resource group>/<identity name>

- If the managed identity is created in a resource group on a different subscription:
<subscription>/<resource group>/<identity name>

- If the managed identity is created in a resource group and that resource group is used to host the controller model:
<identity name>
e.g., juju bootstrap azure --config resource-group-name=<resource group> --constraints instance-role=<identity name>

Note: If you want your controller to be in the same resource group as the one used for the managed identity, during bootstrap also specify --config resource-group-name=<theresourcegroup>.

> See more: Appendix: Supported authentication types: Example workflows.

- instance-type :white_check_mark:
Valid values: See cloud provider.
- mem :white_check_mark:
- root-disk :white_check_mark:
- root-disk-source :white_check_mark:
Represents the juju storage pool for the root disk. By specifying a storage pool, the root disk can be configured to use encryption.
- spaces :x:
- tags :x:
- virt-type :x:
- zones :white_check_mark:

Supported placement directives

Appendix: Example authentication workflows

Worflow 1 – Managed identity only (recommended)

Requirements:

  • Juju 3.6+.
  • A managed identity. See more: Appendix: How to create a managed identity.
  • The managed identity and the Juju resources must be created on the same subscription.
  • The add-credential steps must be run from either the Azure Cloud Shell^ or a jump host running in Azure in order to allow the cloud metadata endpoint to be reached.
  1. Create a managed identity. See more: Appendix: How to create a managed identity.
  2. Run juju add-credential azure; choose managed-identity; supply the requested information (the“managed-identity-path” must be of the form <resourcegroup>/<identityname>).
  3. Bootstrap as usual.

Did you know? With this workflow where you provide the managed identity during add-credential you avoid the need for either your Juju client or your Juju controller to store your credential secrets. Relatedly, the user running add-credential / bootstrap doesn’t need to have any credential secrets supplied to them.

Workflow 2 – Service principal secret + managed identity

Requirements:

  • Juju 3.6+.
  • A managed identity. See more: Appendix: How to create a managed identity.
  1. Create a managed identity.
  2. Add a service-principal-secret:
    • interactive = “service-principal-via-browser” (recommended):
      • If you have the azure CLI and you are logged in and you want to use the currently logged in user: Run /snap/juju/current/bin/juju add-credential azure; choose interactive, then leave the subscription ID field empty – Juju will fill this in for you.
      • Otherwise: Run juju add-credential azure, choose interactive, then provide the subscription ID – Juju will open up a browser and you’ll be prompted to log in to Azure.
    • service-principal-secret: Run juju add-credential azure, then choose service-principal-secret and supply all the requested information.
  3. During bootstrap, provide the managed identity to the controller by using the instance-role constraint.

Did you know? With this workflow where you provide the managed identity during bootstrap you avoid the need for your Juju controller to store your credential secrets. Relatedly, the user running / bootstrap doesn’t need to have any credential secrets supplied to them.

Workflow 3 – Service principal secret only (dispreferred)

  1. Add a service-principal-secret:
    • interactive = “service-principal-via-browser” (recommended):
      • If you have the azure CLI and you are logged in and you want to use the currently logged in user: Run /snap/juju/current/bin/juju add-credential azure; choose interactive, then leave the subscription ID field empty – Juju will fill this in for you.
      • Otherwise: Run juju add-credential azure, choose interactive, then provide the subscription ID – Juju will open up a browser and you’ll be prompted to log in to Azure.
    • service-principal-secret: Run juju add-credential azure, then choose service-principal-secret and supply all the requested information.
  2. Bootstrap as usual.

Appendix: How to create a managed identity

This is just an example. For more information please see the upstream cloud documentation. See more: Microsoft Azure | Managed identities.

To create a managed identity for Juju to use, you will need to use the Azure CLI and be logged in to your account. This is a set up step that can be done ahead of time by an administrator.

The 4 values below need to be filled in according to your requirements.

$ export group=someresourcegroup
$ export location=someregion
$ export role=myrolename
$ export identityname=myidentity
$ export subscription=mysubscription_id

The role definition and role assignment can be scoped to either the subscription or a particular resource group. If scoped to a resource group, this group needs to be provided to Juju when bootstrapping so that the controller resources are also created in that group.

For a subscription scoped managed identity:

$ az group create --name "${group}" --location "${location}"
$ az identity create --resource-group "${group}" --name "${identityname}"
$ mid=$(az identity show --resource-group "${group}" --name "${identityname}" --query principalId --output tsv)
$ az role definition create --role-definition "{
  	\"Name\": \"${role}\",
  	\"Description\": \"Role definition for a Juju controller\",
  	\"Actions\": [
            	\"Microsoft.Compute/*\",
            	\"Microsoft.KeyVault/*\",
            	\"Microsoft.Network/*\",
            	\"Microsoft.Resources/*\",
            	\"Microsoft.Storage/*\",
            	\"Microsoft.ManagedIdentity/userAssignedIdentities/*\"
  	],
  	\"AssignableScopes\": [
        	\"/subscriptions/${subscription}\"
  	]
  }"
$ az role assignment create --assignee-object-id "${mid}" --assignee-principal-type "ServicePrincipal" --role "${role}" --scope "/subscriptions/${subscription}"

A resource scoped managed identity is similar except:

  • the role definition assignable scopes becomes
      \"AssignableScopes\": [
            \"/subscriptions/${subscription}/resourcegroups/${group}\"
      ]
  • the role assignment scope becomes

--scope "/subscriptions/${subscription}/resourcegroups/${group}"


Contributors: @kylerhornor , @taurus , @tmihoc, @wallyworld

@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.

- allocate-public-id

is a typo. Should be ip

@kylerhornor Fixed, thanks! (PS Added you to the Contributors list on the bottom of the doc.)

1 Like

Few comments on this doc:

notes on bootstrap: (1) (recommended) (1a) Create the managed identity.

We are saying that recommended way is the one that currently exists only in juju 3.6-beta2. Should we recommend something that exists in stable juju as most of the users would use a stable version? Also, there are no notes on bootstrap of the ‘service-principal-secret’ way at all, which is the main option for stable juju now. Would be good to add those.

notes on bootstrap:

(1) (recommended) (1a) Create the managed identity. …

(2) (2a) Create the managed identity yourself. …

(3) (3a) Create a credential type service-principal-secret …

This section is the same is in the other doc by wallyfworld, however sections are rephrased and options 2 and 3 are switched places comparing to wallyfworld’s doc. This creates confusion. I suggest having the same names for options and having them in the same order.

1 Like

I’ll add more information on the service-principal-secret.

About the recommendation to use an option that is only available in beta: Fair point. I’ll keep the recommendation but caveat that it requires at least 3.6 beta.

About the other post: It was not intended as a doc (i.e., permanent content) – just an announcement. But I can see how having two sources can be confusing. I’ll work with you to update this doc and then make the announcement merely point to the doc (instead of trying to give the instructions again).

Thanks!

Hi @tmihoc ,

Thank you for describing Azure here. I am trying to follow it using interactive/recommended mode:

... Run /snap/juju/current/bin/juju add-credential azure; 
choose interactive,
then leave the subscription ID field empty – Juju will fill this in for you.

but I cannot leave subscription field empty, Juju enforce me to fill subscription-id:

/snap/juju/current/bin/juju add-credential azure
...
Auth Types
  interactive
  service-principal-secret
  managed-identity

Select auth type [interactive]: interactive

Enter subscription-id: 

Enter subscription-id: 

Enter subscription-id: 

Enter subscription-id: 

The juju version is latest from edge:

Name             Version          Rev    Tracking
juju             3.6-rc1-079cfe6  28862  3.6/edge

Any hints? Thank you!

@taurus Are all the conditions met:

If you have the azure CLI and you are logged in and you want to use the currently logged in user:

?

OK, I am logged in now and I see it optional Enter subscription-id (optional). Nice!

After trying all the possible options and combination I cannot go through the following error right after the successful auth on Azure WEB devicelogin page:

To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code DELETED to authenticate.
ERROR finalizing credential: creating role definition: failed to create role definition: PUT https://management.azure.com/subscriptions/DELETED/providers/Microsoft.Authorization/roleDefinitions/DELETED
--------------------------------------------------------------------------------
RESPONSE 409: 409 Conflict
ERROR CODE: RoleDefinitionWithSameNameExists
--------------------------------------------------------------------------------
{
  "error": {
    "code": "RoleDefinitionWithSameNameExists",
    "message": "A custom role with the same name already exists in this directory. Use a different name."
  }
}
--------------------------------------------------------------------------------

Any hints here? Thank you!

By default, Juju creates a role definition called Juju Application Role Definition. I assume you are not entering something different during add-credential workflow.

It has code which queries existing role definitions and if it finds one with this name, it will not try and create a new one.

We’ve not seen any issues with this workflow in our testing. However, it seems there’s a problem that needs to be diagnosed.

Can you see if the role definition exists?

az role definition list --name "Juju Application Role Definition"

And if it does, try deleting it? You’ll need to delete any role assignments first.

az role assignment delete --role "/subscriptions/xxxxx/providers/Microsoft.Authorization/roleDefinitions/yyy"
az role definition delete --name "Juju Application Role Definition" --scope "/subscriptions/xxx"

where the --role arg is the role id from the role definition.

If this works,we need to figure out why the query to get an existing role is failing.

roleDefinitionClient := clientFactory.NewRoleDefinitionsClient()
pager := roleDefinitionClient.NewListPager(roleScope, &armauthorization.RoleDefinitionsClientListOptions{
	Filter: to.Ptr(fmt.Sprintf("roleName eq '%s'", roleName)),
})
...
1 Like

TL;DR: successfully boostrapped Juju 3.6-rc1 on Azure, steps-to-reproduce: https://charmhub.io/postgresql/docs/h-deploy-azure

The key problem was in (optional) fields. Hint: always fill them with unique values. :slight_smile:

1 Like

Updated the doc to reflect this (and also added you to the list of contributors on the bottom of the doc – thanks again!).

To follow up, the fact that a manually entered role name was needed is a bug

A fix has already been committed. The issue was the query to look for existing roles was not working as expected.

We expect this fix will make it to 3.6.1. When adding an Azure credential, you should be able to accept all the defaults for app name, role etc unless you have specific requirements.