Microsoft Azure
The Flux APIs integrate with the following Microsoft Azure services:
- The source-controller integrates the OCIRepository API with Azure Container Registry (ACR) for pulling OCI artifacts into the cluster.
- The image-reflector-controller integrates the ImageRepository and ImagePolicy APIs with ACR for scanning tags and digests of OCI artifacts and reflecting them into the cluster.
- The source-controller integrates the GitRepository API with Azure DevOps (ADO) for pulling manifests from Git repositories and packaging them as artifacts inside the cluster.
- The image-automation-controller integrates the ImageUpdateAutomation API with ADO for automating image updates in Git repositories.
- The notification-controller integrates the Provider API with ADO for updating commit statuses in Git repositories upon events related to Flux resources.
- The source-controller integrates the Bucket API with Azure Blob Storage (ABS) for pulling manifests from containers and packaging them as artifacts inside the cluster.
- The kustomize-controller integrates the Kustomization API with Azure Key Vault (AKV) for decrypting SOPS-encrypted secrets when applying manifests in the cluster.
- The notification-controller integrates the Provider API with Azure Event Hubs (AEH) for sending notifications about Flux resources outside the cluster.
The next sections briefly describe Microsoft Entra ID, formerly known as Azure Active Directory (AAD), and Azure RBAC, Azure’s identity and access management systems, and how they can be used to grant Flux access to resources offered by the services above. Bear in mind that Microsoft Entra ID and Azure RBAC have more features than the ones described here. We describe only the features that are relevant for the Flux integrations.
Note: Flux also supports Microsoft Teams webhooks, but Microsoft Teams webhooks do not support Microsoft Entra ID. For more information about the notification-controller integration with Microsoft Teams, see the Provider API docs.
Identity
For all the integrations with Azure, Microsoft Entra ID will authenticate two types of identities for Flux:
For both types a Service Principal can be enabled, with each identity having its own Service Principal. The Service Principal is the entity of an identity that is used to integrate with other Azure systems, such as Azure RBAC and ADO.
Also, each identity type has its own methods of authentication. In particular, Managed Identities support secret-less authentication, while Applications support secret-based authentication. The former is more secure.
For further understanding how to configure authentication for both types of identities, refer to the Authentication section.
Access Management
Azure Role-Based Access Control (RBAC) is the system that Azure employs to manage access for most of its resources. Some services, such as ADO, do not use Azure RBAC and instead have their own access control system.
Identities in Azure need permissions to access resources from Azure services.
Permissions cannot be granted directly to identities. Instead, Azure RBAC uses
roles
to group permissions, and only roles can be granted to identities. Each role has a
display name and a globally unique ID. For example, the role Storage Blob Data Reader
,
whose ID is /providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1
,
has the following definition:
{
"assignableScopes": [
"/"
],
"description": "Allows for read access to Azure Storage blob containers and data",
"id": "/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1",
"name": "2a2b9908-6ea1-4ae2-8e65-a410df84e7d1",
"permissions": [
{
"actions": [
"Microsoft.Storage/storageAccounts/blobServices/containers/read",
"Microsoft.Storage/storageAccounts/blobServices/generateUserDelegationKey/action"
],
"notActions": [],
"dataActions": [
"Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read"
],
"notDataActions": []
}
],
"roleName": "Storage Blob Data Reader",
"roleType": "BuiltInRole",
"type": "Microsoft.Authorization/roleDefinitions"
}
The structure of permissions is usually Microsoft.<service>/<resource type>/<action>
and they are grouped into two categories:
- Data actions: These actions are related to data operations, such as reading or writing data.
- Non-data actions: These actions are related to management operations, such as creating or deleting resources.
Azure has a large number of predefined roles, but users can also create custom roles inside their Azure subscriptions.
For an identity to be allowed to perform an action on a resource, the identity needs to be granted, on a scope covering that resource, a role that contains the permission required for that action. The role needs to be granted directly on the resource scope, or on the scope of a parent resource in the resource hierarchy:
- Resource
- Resource Group (set of resources)
- Subscription (set of resource groups)
- Management Group (set of subscriptions)
If the role is granted on a parent resource, the relationship will be inherited by all the descendants. For example, if a role is granted on a subscription, all the resource groups and resources inside that subscription will inherit the grant.
Reminder: The grant relationship has three involved entities:
- The identity that will use the permissions to perform actions.
- The scope covering the resource that will be the target of the actions.
- The role containing the permissions required for the actions.
Granting permissions
Azure RBAC grants roles to Microsoft Entra identities through the Service Principal of the identity. The principal ID of an identity can be looked up through the client ID (also known as application ID) of the identity.
To find a principal ID using Terraform/OpenTofu, use the client ID in the following data source
(look for the object_id
attribute):
To find a principal ID using the az
CLI, use the client ID in the following command:
Via Azure Service Operator custom resources
To use Azure Service Operator (ASO) for granting roles you can use the following custom resource:
Note: ASO is the most GitOps-friendly way of managing Azure resources. With it you can even use Flux itself to deploy and continuously reconcile your Azure custom resources (the Kubernetes ones), and ASO itself will continuously reconcile the actual Azure resources.
Via Terraform/OpenTofu resources
To use Terraform/OpenTofu for granting roles you can use the following resource:
Note: Terraform/OpenTofu is a great declarative way of managing Azure resources, but it’s not as GitOps-friendly as ASO, as these tools do not prescribe continuous reconciliation of the resources. There are alternatives to achieve that, but they are not a core part of the tools.
Via the az
CLI
To use the az
CLI for granting roles you can use the following command:
Note: The
az
CLI is a great tool for experimenting with Azure resources and their integrations with Flux, but it’s not a GitOps-friendly way of managing Azure resources. If you need continuous reconciliation of the resources, prefer using ASO or Terraform/OpenTofu.
Authorization
In this section we describe the recommended roles and permissions for enabling the Flux integrations with Azure services.
For Azure Container Registry
The OCIRepository
, ImageRepository
and ImagePolicy
Flux APIs are integrated with
ACR. The OCIRepository
API can be used to pull OCI artifacts from ACR repositories
into the cluster, while the ImageRepository
and ImagePolicy
APIs
can be used to reflect tags and digests of such artifacts also inside the cluster.
The recommended role containing the required permissions for the OCIRepository
,
ImageRepository
and ImagePolicy
APIs is:
The scopes on which the AcrPull
role can be granted are:
- Resource:
/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME/providers/Microsoft.ContainerRegistry/registries/REGISTRY_NAME
- Resource Group:
/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME
- Subscription:
/subscriptions/SUBSCRIPTION_ID
- Management Group:
/providers/Microsoft.Management/managementGroups/MANAGEMENT_GROUP_NAME
For Azure DevOps
The GitRepository
, ImageUpdateAutomation
and Provider
Flux APIs are integrated with
ADO. The GitRepository
API can be used to pull manifests from ADO Git repositories
and package them as artifacts inside the cluster, the ImageUpdateAutomation
API
can be used to automate image updates in ADO Git repositories, and the Provider
API
can be used to update commit statuses in ADO Git repositories upon events related to
Flux resources.
While ADO integrates with Microsoft Entra ID Service Principals, it does not integrate with Azure RBAC. As mentioned before, ADO has its own access control system.
The Service Principal must be onboarded into the ADO organization and
added to a group. For the GitRepository
API, the Readers
group is sufficient,
while for the ImageUpdateAutomation
and Provider
APIs the Contributors
group
is required for pushing commits to the repository and updating commit statuses,
respectively.
ASO does not support managing ADO resources (see issue).
Via Terraform/OpenTofu resources
To grant access to Service Principals using Terraform/OpenTofu, first you need to configure the ADO provider with the ADO organization and project:
Then you can use the following resources:
azuredevops_service_principal_entitlement
: This resource onboards a Service Principal into an ADO organization.azuredevops_group_membership
: This resource adds the Service Principal to an ADO group.
Use the descriptor
attribute of the azuredevops_service_principal_entitlement
in the members
field of the azuredevops_group_membership
resource.
Use the
azuredevops_group
data source to find the ID of the desired group, and use it in the
group
field of the azuredevops_group_membership
resource.
Via the az
CLI
To grant access to Service Principals using the az
CLI, you can use the following commands:
- Login to ADO:
az devops login
- Configure the ADO organization and project:
az devops configure
- Onboard the Service Principal into the ADO organization. This doesn’t have a dedicated command yet,
so use
az rest
to call the REST API forAdding Service Principal Entitlements
. - Find the member ID of the Service Principal in the organization using
az rest
with the REST API forQuerying Subjects
, thequery
field of the request body should be set to the principal ID of the Service Principal. The response should be a list ofGraphSubject
, the member ID will be thedescriptor
field. - List the groups in the organization to find the ID of the desired group:
az devops security group list
- Add the Service Principal to the group:
az devops security group membership add
For Azure Blob Storage
The Bucket
Flux API is integrated with ABS. The Bucket
API can be used
to pull manifests from ABS containers and package them as artifacts inside the cluster.
The recommended role containing the required permissions for the Bucket
API is:
The scopes on which the Storage Blob Data Reader
role can be granted are:
- Resource:
/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME/providers/Microsoft.Storage/storageAccounts/STORAGE_ACCOUNT_NAME/blobServices/default/containers/CONTAINER_NAME
- Storage Account:
/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME/providers/Microsoft.Storage/storageAccounts/STORAGE_ACCOUNT_NAME
- Resource Group:
/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME
- Subscription:
/subscriptions/SUBSCRIPTION_ID
- Management Group:
/providers/Microsoft.Management/managementGroups/MANAGEMENT_GROUP_NAME
For Azure Key Vault
The Kustomization
Flux API is integrated with AKV.
The Kustomization
API is used to apply manifests in the cluster, and it can use AKV to
decrypt SOPS-encrypted secrets before applying them.
The recommended role containing the required permissions for the Kustomization
API is:
The scopes on which the Key Vault Crypto User
role can be granted are:
- Resource:
/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME/providers/Microsoft.KeyVault/vaults/VAULT_NAME
- Resource Group:
/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME
- Subscription:
/subscriptions/SUBSCRIPTION_ID
- Management Group:
/providers/Microsoft.Management/managementGroups/MANAGEMENT_GROUP_NAME
For Azure Event Hubs
The Provider
Flux API is integrated with AEH. The Provider
API can be used
to send notifications about Flux resources to Event Hubs.
The recommended role containing the required permissions for the Provider
API is:
The scopes on which the Azure Event Hubs Data Sender
role can be granted are:
- Resource:
/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME/providers/Microsoft.EventHub/namespaces/NAMESPACE_NAME/eventhubs/EVENTHUB_NAME
- Namespace:
/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME/providers/Microsoft.EventHub/namespaces/NAMESPACE_NAME
- Resource Group:
/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME
- Subscription:
/subscriptions/SUBSCRIPTION_ID
- Management Group:
/providers/Microsoft.Management/managementGroups/MANAGEMENT_GROUP_NAME
Authentication
As mentioned in the Identity section, Azure supports two types of identities for Flux: Managed Identities and Applications. This section describes how to authenticate each type of identity. Managed Identities are the recommended way of authenticating to Azure services, as they support secret-less authentication. Applications are not recommended, as they require secret-based authentication, which is less secure.
Recommendation: Always prefer secret-less over secret-based authentication if the alternative is available. Secrets can be stolen to abuse the permissions granted to the identities they represent, and for public clouds like Azure this can be done by simply having Internet access. This requires secrets to be regularly rotated and more security controls to be put in place, like audit logs, secret management tools, etc. Secret-less authentication does not have this problem, as the identity is authenticated using a token that is not stored anywhere and is only valid for a short period of time, usually one hour. It’s much harder to steal an identity this way.
With Workload Identity Federation
Workload Identity Federation is an Azure feature that allows external identities to authenticate with Azure services without the need for a per-identity static credential. This is done by exchanging a short-lived token issued by the external identity provider (in this case, Kubernetes) for a short-lived Azure Access Token. This access token is then used to authenticate with Azure services. This process is more broadly known as OIDC federation and is supported only for Managed Identities.
Supported clusters
Azure supports Workload Identity Federation for both AKS and non-AKS clusters.
In AKS clusters you need to enable the options OIDC Issuer and Workload Identity.
For both AKS and non-AKS clusters you need to know the Issuer URL of the cluster, which is an input required for creating Federated Credentials (see below).
Supported identity types
As mentioned before, the only identity type supported by Workload Identity Federation is Managed Identities.
Flux acquires the roles granted to a Managed Identity by using a Kubernetes Service Account to impersonate this Managed Identity. To configure a Kubernetes Service Account to impersonate a Managed Identity, two steps are required:
- Allow the Kubernetes Service Account to impersonate the Managed Identity. This is done by creating a Federated Credential that ties together the Kubernetes Service Account and the Managed Identity.
The inputs identifying the Managed Identity for creating the Federated Credential are:
- The Resource Group.
- The Name of the Managed Identity inside the Resource Group.
The inputs identifying the Kubernetes Service Account for creating the Federated Credential are:
- The Issuer URL of the cluster.
- The Subject string that Kubernetes uses to identify the Service Account, which is
system:serviceaccount:NAMESPACE:KSA_NAME
.
Additionally, the Federated Credential needs:
- A name for itself.
- The Audience to be set to
api://AzureADTokenExchange
.
To create a Federated Credential using ASO, you can use the following custom resource:
To create a Federated Credential using Terraform/OpenTofu, you can use the following resource:
to create a Federated Credential using the az
CLI, you can use the following command:
- Annotate the Kubernetes Service Account with the Managed Identity Client ID and Tenant ID.
This is done by adding the annotations
azure.workload.identity/client-id: CLIENT_ID
andazure.workload.identity/tenant-id: TENANT_ID
to the Kubernetes Service Account:
apiVersion: v1
kind: ServiceAccount
metadata:
name: KSA_NAME
namespace: NAMESPACE
annotations:
azure.workload.identity/client-id: CLIENT_ID
azure.workload.identity/tenant-id: TENANT_ID
Configuring Flux to use a Kubernetes Service Account to authenticate with Azure can be done either at the object level or at the controller level.
With Application Certificates
All Azure integrations except for ACR support configuring authentication through an Application Certificate.
Applications support static certificates that can be used to generate temporary access tokens for authenticating with Azure services.
ASO does not support creating Application Certificates (see issue).
To create an Application Certificate using Terraform/OpenTofu, you can use the following resource:
To create or reset an Application Certificate using the az
CLI, you can use the following command:
Configuring Flux to use an Application Certificate can be done either at the object level or at the controller level.
At the object level
All the Flux APIs support configuring authentication at the object level. This allows users to adhere to the Least Privilege Principle, as each Flux resource can be configured with its own identity and therefore its own permissions. This is useful for multi-tenancy scenarios, where different teams can use the same cluster but need to be isolated from each other inside their own namespaces.
For Workload Identity Federation
Before following the steps below, make sure to complete the cluster setup described here, and to configure the Kubernetes Service Account as described here.
⚠️ Attention: Only for AKS clusters targeting Azure resources object-level workload identity is currently unstable. In its current state the feature may stop working without notice depending on actions taken by Azure to meet AKS customers’ demands. For continued support the feature may need to evolve in a way that additional configuration steps will be required for it to continue working in future Flux releases. See this issue for more information.
For configuring authentication through a Kubernetes Service Account at the object level the following steps are required:
- Enable the feature gate
ObjectLevelWorkloadIdentity
in the target Flux controller Deployment during bootstrap:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
patches:
- target:
kind: Deployment
name: (some-controller)
patch: |
- op: add
path: /spec/template/spec/containers/0/args/-
value: --feature-gates=ObjectLevelWorkloadIdentity=true
- Set the
.spec.provider
field toazure
in the Flux resource. For theKustomization
API, this field is not required/does not exist, SOPS detects the provider from the metadata in the SOPS-encrypted Secret. - Use the
.spec.serviceAccountName
field to specify the name of the Kubernetes Service Account in the same namespace as the Flux resource. For theKustomization
API, the field is.spec.decryption.serviceAccountName
.
Note: The
azure.workload.identity/client-id
andazure.workload.identity/tenant-id
annotations are defined by AKS, but Flux also uses them to identify the Managed Identity to impersonate in non-AKS clusters. This is for providing users with a seamless experience.
At the moment, the ADO integrations with the GitRepository
, ImageUpdateAutomation
and
Provider
APIs and the ABS integration with the Bucket
API do not support configuring
authentication through Workload Identity Federation at the object level.
Support for these integrations will be introduced in Flux v2.7.
For Application Certificates
Only the ABS and AKV integrations support configuring authentication through an Application Certificate.
For configuring authentication through an Application Certificate for
the Bucket
API:
- Set the
.spec.provider
field toazure
. - Set the
.spec.secretRef.name
field to the name of the Kubernetes Secret in the same namespace as theBucket
resource.
The Secret must looks like this:
apiVersion: v1
kind: Secret
metadata:
name: SECRET_NAME
namespace: NAMESPACE
type: Opaque
stringData:
tenantId: <tenant ID>
clientId: <client ID>
clientCertificate: <certificate and private key>
# Plus optionally
clientCertificatePassword: <password if the private key is encrypted>
clientCertificateSendChain: "true" # this boolean value must be quoted
For the Kustomization
API:
- Set the
.spec.decryption.secretRef.name
field to the name of the Kubernetes Secret in the same namespace as theKustomization
resource.
The Secret must look like this:
apiVersion: v1
kind: Secret
metadata:
name: SECRET_NAME
namespace: NAMESPACE
type: Opaque
stringData:
sops.azure-kv: |
tenantId: <tenant ID>
clientId: <client ID>
clientCertificate: <certificate and private key>
# Plus optionally
clientCertificatePassword: <password if the private key is encrypted>
clientCertificateSendChain: true # this boolean value must NOT be quoted
At the controller level
All the Flux APIs support configuring authentication at the controller level. This is more appropriate for single-tenant scenarios, where all the Flux resources inside the cluster belong to the same team and hence can share the same identity and permissions.
For Workload Identity Federation
Before following the steps below, make sure to complete the cluster setup described here, and to configure the Kubernetes Service Account as described here.
If the cluster is AKS, the controller Kubernetes Service Account and Deployment must be patched during bootstrap:
- The Kubernetes Service Account of the controller must be configured
to impersonate a Managed Identity. This is done by adding the
azure.workload.identity/client-id
andazure.workload.identity/tenant-id
annotations. - The label
azure.workload.identity/use: "true"
must be added to the controller Deployment template metadata. This label is used by the AKS mutating webhook to automatically configure the controller pod during admission to use Workload Identity.
The controller patch should look like this:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
patches:
- target:
kind: ServiceAccount
name: "(some-controller)"
patch: |
apiVersion: v1
kind: ServiceAccount
metadata:
name: controller
annotations:
azure.workload.identity/client-id: CLIENT_ID
azure.workload.identity/tenant-id: TENANT_ID
- target:
kind: Deployment
name: "(some-controller)"
patch: |
apiVersion: apps/v1
kind: Deployment
metadata:
name: (some-controller)
spec:
template:
metadata:
labels:
azure.workload.identity/use: "true"
If the configuration above is done after bootstrap, restart (delete) the controller for the binding to take effect.
If the cluster is not AKS, the controller Deployment must be patched during bootstrap:
- A projected volume must be mounted in the controller Deployment with a Kubernetes
Service Account token whose audience is set to
api://AzureADTokenExchange
. - The environment variables shown below must be set in the controller Deployment.
The controller patch should look like this:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
patches:
- target:
kind: Deployment
name: "(some-controller)"
patch: |
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: AZURE_TENANT_ID
value: TENANT_ID
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: AZURE_CLIENT_ID
value: CLIENT_ID
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: AZURE_AUTHORITY_HOST
value: https://login.microsoftonline.com/
# or https://login.microsoftonline.us/ for US Gov
# or https://login.chinacloudapi.cn/ for China
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: AZURE_FEDERATED_TOKEN_FILE
value: /var/run/service-account/azure-token
- op: add
path: /spec/template/spec/volumes/-
value:
name: azure-token
projected:
sources:
- serviceAccountToken:
audience: api://AzureADTokenExchange
expirationSeconds: 3600
path: azure-token
- op: add
path: /spec/template/spec/containers/0/volumeMounts/-
value:
name: azure-token
mountPath: /var/run/service-account
readOnly: true
For Application Certificates
Only the ABS and AKV integrations support configuring authentication through an Application Certificate.
Mount the Kubernetes Secret containing the certificate and private key in the controller Deployment and set the environment variables shown below during bootstrap:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
patches:
- target:
kind: Deployment
name: "(some-controller)"
patch: |
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: AZURE_TENANT_ID
value: TENANT_ID
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: AZURE_CLIENT_ID
value: CLIENT_ID
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: AZURE_CLIENT_CERTIFICATE_PATH
value: /var/run/app-cert/cert-and-key
- op: add
path: /spec/template/spec/volumes/-
value:
name: app-cert
secret:
secretName: SECRET_NAME
- op: add
path: /spec/template/spec/containers/0/volumeMounts/-
value:
name: app-cert
mountPath: /var/run/app-cert/cert-and-key
subPath: cert-and-key
readOnly: true
# ---------------
# Plus optionally
# ---------------
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: AZURE_CLIENT_CERTIFICATE_PASSWORD
valueFrom:
secretKeyRef:
name: SECRET_NAME
key: key-password
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: AZURE_CLIENT_SEND_CERTIFICATE_CHAIN
value: "true" # this boolean value must be quoted
The Secret should look like this:
apiVersion: v1
kind: Secret
metadata:
name: SECRET_NAME
namespace: flux-system
type: Opaque
stringData:
cert-and-key: <certificate and private key>
# Plus optionally
key-password: <password if the private key is encrypted>
At the node level
Only for the ACR integrations Flux supports authentication at the node level for AKS. This is because users often already have to configure authentication at the node level for AKS to be able to pull container images from ACR in order to start pods. By supporting this authentication method Flux allows users to configure ACR authentication in a single way. See docs.
⚠️ Node level authentication may work for other integrations as well, but Flux only has continuous integration tests for the ACR integration in order to support the specific use case described above.