
![aws sm](../pictures/eso-az-kv-azure-kv.png)

## Azure Key vault

External Secrets Operator integrates with [Azure Key vault](https://azure.microsoft.com/en-us/services/key-vault/) for secrets, certificates and Keys management.

### Authentication

We support authentication with Microsoft Entra identities that can be used as Workload Identity or [AAD Pod Identity](https://azure.github.io/aad-pod-identity/docs/) as well as with Service Principal credentials.

Since the [AAD Pod Identity](https://azure.github.io/aad-pod-identity/docs/) is deprecated, it is recommended to use the [Workload Identity](https://azure.github.io/azure-workload-identity) authentication.

We support connecting to different cloud flavours azure supports: `PublicCloud`, `USGovernmentCloud`, `ChinaCloud`, `GermanCloud` and `AzureStackCloud` (for Azure Stack Hub/Edge). You have to specify the `environmentType` and point to the correct cloud flavour. This defaults to `PublicCloud`.

For environments with non-standard endpoints (Azure Stack, Azure China with AKS Workload Identity, etc.), you can provide custom cloud configuration to override the default endpoints. See the [Custom Cloud Configuration](#custom-cloud-configuration) section below.

```yaml
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
  name: azure-backend
spec:
  provider:
    azurekv:
      # PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud
      environmentType: PublicCloud # default
```

Minimum required permissions are `Get` over secret and certificate permissions. This can be done by adding a Key Vault access policy:

```sh
KUBELET_IDENTITY_OBJECT_ID=$(az aks show --resource-group <AKS_CLUSTER_RG_NAME> --name <AKS_CLUSTER_NAME> --query 'identityProfile.kubeletidentity.objectId' -o tsv)
az keyvault set-policy --name kv-name-with-certs --object-id "$KUBELET_IDENTITY_OBJECT_ID" --certificate-permissions get --secret-permissions get
```

#### Service Principal key authentication

A service Principal client and Secret is created and the JSON keyfile is stored in a `Kind=Secret`. The `ClientID` and `ClientSecret` or `ClientCertificate` (in PEM format) should be configured for the secret. This service principal should have proper access rights to the keyvault to be managed by the operator.

#### Managed Identity authentication

A Managed Identity should be created in Azure, and that Identity should have proper rights to the keyvault to be managed by the operator.

Use [aad-pod-identity](https://azure.github.io/aad-pod-identity/docs/) to assign the identity to external-secrets operator. To add the selector to external-secrets operator, use `podLabels` in your values.yaml in case of Helm installation of external-secrets.

If there are multiple Managed Identities for different keyvaults, the operator should have been assigned all identities via [aad-pod-identity](https://azure.github.io/aad-pod-identity/docs/), then the SecretStore configuration should include the Id of the identity to be used via the `identityId` field.

```yaml
{% include 'azkv-secret-store-mi.yaml' %}
```

#### Workload Identity

In Microsoft Entra, Workload Identity can be Application, user-assigned Managed Identity and Service Principal.

You can use [Azure AD Workload Identity Federation](https://docs.microsoft.com/en-us/azure/active-directory/develop/workload-identity-federation) to access Azure managed services like Key Vault **without needing to manage secrets**. You need to configure a trust relationship between your Kubernetes Cluster and Azure AD. This can be done in various ways, for instance using `terraform`, the Azure Portal or the `az` cli. We found the [azwi](https://azure.github.io/azure-workload-identity/docs/installation/azwi.html) cli very helpful. The Azure [Workload Identity Quick Start Guide](https://azure.github.io/azure-workload-identity/docs/quick-start.html) is also good place to get started.

This is basically a two step process:

1. Create a Kubernetes Service Account ([guide](https://azure.github.io/azure-workload-identity/docs/quick-start.html#5-create-a-kubernetes-service-account))

```sh
azwi serviceaccount create phase sa \
  --aad-application-name "${APPLICATION_NAME}" \
  --service-account-namespace "${SERVICE_ACCOUNT_NAMESPACE}" \
  --service-account-name "${SERVICE_ACCOUNT_NAME}"
```
2. Configure the trust relationship between Azure AD and Kubernetes ([guide](https://azure.github.io/azure-workload-identity/docs/quick-start.html#6-establish-federated-identity-credential-between-the-aad-application-and-the-service-account-issuer--subject))

```sh
azwi serviceaccount create phase federated-identity \
  --aad-application-name "${APPLICATION_NAME}" \
  --service-account-namespace "${SERVICE_ACCOUNT_NAMESPACE}" \
  --service-account-name "${SERVICE_ACCOUNT_NAME}" \
  --service-account-issuer-url "${SERVICE_ACCOUNT_ISSUER}"
```

With these prerequisites met you can configure `ESO` to use that Service Account. You have two options:

##### Mounted Service Account
You run the controller and mount that particular service account into the pod by adding the label `azure.workload.identity/use: "true"`to the pod. That grants _everyone_ who is able to create a secret store or reference a correctly configured one the ability to read secrets. **This approach is usually not recommended**. But may make sense when you want to share an identity with multiple namespaces. Also see our [Multi-Tenancy Guide](../guides/multi-tenancy.md) for design considerations.

```yaml
{% include 'azkv-workload-identity-mounted.yaml' %}
```

##### Referenced Service Account
You run the controller without service account (effectively without azure permissions). Now you have to configure the SecretStore and set the `serviceAccountRef` and point to the service account you have just created. **This is usually the recommended approach**. It makes sense for everyone who wants to run the controller without Azure permissions and delegate authentication via service accounts in particular namespaces. Also see our [Multi-Tenancy Guide](../guides/multi-tenancy.md) for design considerations.

```yaml
{% include 'azkv-workload-identity.yaml' %}
```

In case you don't have the clientId when deploying the SecretStore, such as when deploying a Helm chart that includes instructions for creating a [Managed Identity](https://github.com/Azure/azure-service-operator/blob/main/v2/samples/managedidentity/v1api20181130/v1api20181130_userassignedidentity.yaml) using [Azure Service Operator](https://azure.github.io/azure-service-operator/) next to the SecretStore definition, you may encounter an interpolation problem. Helm lacks dependency management, which means it can create an issue when the clientId is only known after everything is deployed. Although the Service Account can inject `clientId` and `tenantId` into a pod, it doesn't support secretKeyRef/configMapKeyRef. Therefore, you can deliver the clientId and tenantId directly, bypassing the Service Account.

The following example demonstrates using the secretRef field to directly deliver the `clientId` and `tenantId` to the SecretStore while utilizing Workload Identity authentication.

```yaml
{% include 'azkv-workload-identity-secretref.yaml' %}
```

### Custom Cloud Configuration

External Secrets Operator supports custom cloud endpoints for Azure Stack Hub, Azure Stack Edge, and other scenarios where the default cloud endpoints don't match your environment. This feature requires using the new Azure SDK.

#### Azure China Workload Identity

Azure China's AKS uses a different OIDC issuer (`login.partner.microsoftonline.cn`) than the standard China Cloud endpoint (`login.chinacloudapi.cn`). When using Workload Identity with AKS in Azure China, you need to override the Active Directory endpoint:

```yaml
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
  name: azure-china-workload-identity
spec:
  provider:
    azurekv:
      vaultUrl: "https://my-vault.vault.azure.cn"
      environmentType: ChinaCloud
      authType: WorkloadIdentity
      # REQUIRED: Must be true to use custom cloud configuration
      useAzureSDK: true
      # Override the Active Directory endpoint to match AKS OIDC issuer
      customCloudConfig:
        activeDirectoryEndpoint: "https://login.partner.microsoftonline.cn/"
        keyVaultEndpoint: "https://vault.azure.cn/"
        resourceManagerEndpoint: "https://management.chinacloudapi.cn/"
      serviceAccountRef:
        name: my-service-account
        namespace: default
```

#### Azure Stack Configuration

For Azure Stack Hub or Azure Stack Edge environments:

```yaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: azure-stack-backend
spec:
  provider:
    azurekv:
      vaultUrl: "https://my-vault.vault.local.azurestack.external/"
      # REQUIRED: Must be set to AzureStackCloud for custom environments
      environmentType: AzureStackCloud
      # REQUIRED: Must be true for Azure Stack (legacy SDK doesn't support custom clouds)
      useAzureSDK: true
      # REQUIRED: Custom cloud endpoints for your Azure Stack deployment
      customCloudConfig:
        # Azure Active Directory endpoint for authentication
        activeDirectoryEndpoint: "https://login.microsoftonline.com/"
        # Optional: Key Vault endpoint if different from vaultUrl domain
        keyVaultEndpoint: "https://vault.local.azurestack.external/"
        # Optional: Resource Manager endpoint for resource operations
        resourceManagerEndpoint: "https://management.local.azurestack.external/"
      # ... rest of authentication configuration (Service Principal example)
      authType: ServicePrincipal
      tenantId: "your-tenant-id"
      authSecretRef:
        clientId:
          name: azure-secret
          key: client-id
        clientSecret:
          name: azure-secret
          key: client-secret
```

**Important Notes:**
- `useAzureSDK: true` is mandatory when using `customCloudConfig`
- `customCloudConfig` can be used with any `environmentType` (PublicCloud, ChinaCloud, etc.)
- For AzureStackCloud, `customCloudConfig` is required
- Contact your Azure Stack administrator for the correct endpoint URLs

### Update secret store
Be sure the `azurekv` provider is listed in the `Kind=SecretStore`

```yaml
{% include 'azkv-secret-store.yaml' %}
```
**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `clientId` and `clientSecret`  with the namespaces where the secrets reside.

Or in case of Managed Identity authentication:

```yaml
{% include 'azkv-secret-store-mi.yaml' %}
```

### Object Types

Azure Key Vault manages different [object types](https://docs.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#object-types), we support `keys`, `secrets` and `certificates`. Simply prefix the key with `key`, `secret` or `cert` to retrieve the desired type (defaults to secret).

| Object Type   | Return Value                                                                                                                                                                                                                      |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `secret`      | the raw secret value.                                                                                                                                                                                                             |
| `key`         | A JWK which contains the public key. Azure Key Vault does **not** export the private key. You may want to use [template functions](../guides/templating.md) to transform this JWK into PEM encoded PKIX ASN.1 DER format. |
| `certificate` | The raw CER contents of the x509 certificate. You may want to use [template functions](../guides/templating.md) to transform this into your desired encoding                                                             |

### Creating external secret

To create a Kubernetes secret from the Azure Key vault secret a `Kind=ExternalSecret` is needed.

You can manage keys/secrets/certificates saved inside the keyvault , by setting a "/" prefixed type in the secret name, the default type is a `secret`. Other supported values are `cert` and `key`.

```yaml
{% include 'azkv-external-secret.yaml' %}
```

The operator will fetch the Azure Key vault secret and inject it as a `Kind=Secret`. Then the Kubernetes secret can be fetched by issuing:

```sh
kubectl get secret secret-to-be-created -n <namespace> -o jsonpath='{.data.dev-secret-test}' | base64 -d
```

To select all secrets inside the key vault or all tags inside a secret, you can use the `dataFrom` directive:

```yaml
{% include 'azkv-datafrom-external-secret.yaml' %}
```

To get a PKCS#12 certificate from Azure Key Vault and inject it as a `Kind=Secret` of type `kubernetes.io/tls`:

```yaml
{% include 'azkv-pkcs12-cert-external-secret.yaml' %}
```

### Creating a PushSecret
You can push secrets to Azure Key Vault into the different `secret`, `key` and `certificate` APIs.

#### Pushing to a Secret
Pushing to a Secret requires no previous setup. with the secret available in Kubernetes, you can simply refer it to a PushSecret object to have it created on Azure Key Vault:
```yaml
{% include 'azkv-pushsecret-secret.yaml' %}
```
!!! note
      In order to create a PushSecret targeting keys, `CreateSecret` and `DeleteSecret` actions must be granted to the Service Principal/Identity configured on the SecretStore.

#### Pushing to a Key
The first step is to generate a valid Private Key. Supported Formats include `PRIVATE KEY`, `RSA PRIVATE KEY` AND `EC PRIVATE KEY` (EC/PKCS1/PKCS8 types). After uploading your key to a Kubernetes Secret, the next step is to create a PushSecret manifest with the following configuration:

```yaml
{% include 'azkv-pushsecret-key.yaml' %}
```

!!! note
      In order to create a PushSecret targeting keys, `ImportKey` and `DeleteKey` actions must be granted to the Service Principal/Identity configured on the SecretStore.
#### Pushing to a Certificate
The first step is to generate a valid P12 certificate. Currently, only PKCS1/PKCS8 types are supported. Currently only password-less P12 certificates are supported.

After uploading your P12 certificate to a Kubernetes Secret, the next step is to create a PushSecret manifest with the following configuration
```yaml
{% include 'azkv-pushsecret-certificate.yaml' %}
```
!!! note
       In order to create a PushSecret targeting keys, `ImportCertificate` and `DeleteCertificate` actions must be granted to the Service Principal/Identity configured on the SecretStore.
