Proposed Pull Request Change

title description ms.topic ms.subservice ms.service ms.custom ms.date author ms.author ai-usage
Create and Manage Persistent Volumes with Azure Files in Azure Kubernetes Service (AKS) Learn how to create and manage persistent volumes using Azure Files with the Container Storage Interface (CSI) driver in Azure Kubernetes Service (AKS) to provide scalable and reliable storage for your containerized applications. how-to aks-storage azure-kubernetes-service aeo-round-2 08/25/2026 schaffererin schaffererin ai-assisted
📄 Document Links
GitHub View on GitHub Microsoft Learn View on Microsoft Learn
⚠ Content Truncation Detected
The generated rewrite appears to be incomplete.
Original lines: -
Output lines: -
Ratio: -
Raw New Markdown
Generating updated version of doc...
Rendered New Markdown
Generating updated version of doc...
+0 -0
+0 -0
--- title: Create and Manage Persistent Volumes with Azure Files in Azure Kubernetes Service (AKS) description: Learn how to create and manage persistent volumes using Azure Files with the Container Storage Interface (CSI) driver in Azure Kubernetes Service (AKS) to provide scalable and reliable storage for your containerized applications. ms.topic: how-to ms.subservice: aks-storage ms.service: azure-kubernetes-service ms.custom: aeo-round-2 ms.date: 08/25/2026 author: schaffererin ms.author: schaffererin ai-usage: ai-assisted # Customer intent: "As a Kubernetes administrator, I want to learn how to create and manage persistent volumes using Azure Files CSI drivers in Azure Kubernetes Service (AKS) so that I can provide scalable and reliable storage solutions for my containerized applications." --- # Create and manage persistent volumes (PVs) with Azure Files in Azure Kubernetes Service (AKS) Azure Files enables multiple pods to share persistent storage in AKS using SMB or NFS protocols, surviving pod restarts, node failures, and cluster scaling events. This article shows you how to dynamically and statically create an Azure file share for use by multiple pods in an Azure Kubernetes Service (AKS) cluster. > [!NOTE] > The Azure Files CSI driver only permits the mounting of SMB file shares using key-based (NTLM v2) authentication, and therefore doesn't support the maximum security profile of Azure File share settings. Mounting NFS file shares doesn't require key-based authentication. > [!NOTE] > We recommend FIO when running benchmarking tests. For more information, see [benchmarking tools and tests](/azure/storage/files/nfs-performance#benchmarking-tools-and-tests). ## Quick-start checklist 1. **Verify prerequisites**: Ensure Azure CLI 2.0.59+, Azure Files CSI driver enabled, and storage account available 1. **Choose or create a storage class**: Use built-in classes (`azurefile-csi`, `azurefile-csi-premium`) or create a custom one 1. **Create a PersistentVolumeClaim (PVC)**: Define storage size and access mode (typically ReadWriteMany) 1. **Create a pod**: Reference the PVC in your pod's volume configuration 1. **Verify the mount**: Confirm the volume is mounted correctly using `kubectl describe pod` ## Prerequisites - Azure CLI version 2.0.59 or later installed and configured. Find the version using the `az --version` command. To install or upgrade, see [Install Azure CLI][install-azure-cli]. - The [Azure Files CSI driver](./csi-storage-drivers.md) enabled on your AKS cluster. - An Azure [storage account][azure-storage-account]. - When choosing between SSD (Premium) and HDD (Standard) file shares, it's important you understand the provisioning model and requirements of the expected usage pattern you plan to run on Azure Files. Azure Files has three billing models: [provisioned v2](/azure/storage/files/understanding-billing#provisioned-v2-model) (recommended), [pay-as-you-go](/azure/storage/files/understanding-billing#pay-as-you-go-model), and the legacy [provisioned v1](/azure/storage/files/understanding-billing#provisioned-v1-model). For more information, see [Choosing an Azure Files performance tier based on usage patterns][azure-files-usage]. ## Create a complete dynamically provisioned volume The following example creates a general-purpose Azure Files storage class, a PVC that dynamically provisions a file share, and a pod that mounts the file share. The example uses the `Standard_LRS` SKU and SMB protocol. For production workloads, choose the [Azure Files performance tier and redundancy option][azure-files-usage] that meets your performance and availability requirements. 1. Create a file named `azure-file-dynamic.yaml` and paste in the following manifest: ```yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: azurefile-csi-standard provisioner: file.csi.azure.com allowVolumeExpansion: true reclaimPolicy: Delete volumeBindingMode: Immediate parameters: skuName: Standard_LRS protocol: smb --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: azurefile-pvc spec: accessModes: - ReadWriteMany storageClassName: azurefile-csi-standard resources: requests: storage: 5Gi --- apiVersion: v1 kind: Pod metadata: name: azurefile-app spec: containers: - name: nginx image: mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine resources: requests: cpu: 100m memory: 128Mi limits: cpu: 250m memory: 256Mi volumeMounts: - name: azurefile-volume mountPath: /mnt/azure volumes: - name: azurefile-volume persistentVolumeClaim: claimName: azurefile-pvc ``` 1. Apply the manifest using the [`kubectl apply`][kubectl-apply] command: ```bash kubectl apply -f azure-file-dynamic.yaml ``` 1. Confirm that the PVC is in the `Bound` state and the pod is in the `Running` state: ```bash kubectl get storageclass azurefile-csi-standard kubectl get pvc azurefile-pvc kubectl get pod azurefile-app ``` 1. Verify that the pod can write to and read from the dynamically provisioned file share: ```bash kubectl exec azurefile-app -- sh -c "echo 'Azure Files is mounted' > /mnt/azure/test.txt" kubectl exec azurefile-app -- cat /mnt/azure/test.txt ``` Your output should resemble the following example output: ```output Azure Files is mounted ``` 1. When you no longer need the example resources, delete them: ```bash kubectl delete -f azure-file-dynamic.yaml ``` Because the storage class uses the `Delete` reclaim policy, deleting the PVC also deletes the dynamically provisioned Azure file share. ## Use built-in storage classes to create dynamic PVs with Azure Files Storage classes define how a unit of storage is dynamically created with a persistent volume. A storage account is automatically created in the [node resource group][node-resource-group] for use with the storage class to hold the Azure Files file share. When you use CSI drivers on AKS, there are two extra built-in `StorageClasses` that use the Azure Files CSI storage drivers (the other CSI storage classes are created with the cluster alongside the in-tree default storage classes): - `azurefile-csi`: Creates an Azure file share on HDD storage. - `azurefile-csi-premium`: Creates an Azure file share on SSD storage. The reclaim policy on both storage classes ensures that the underlying Azure file share is deleted when the respective PV is deleted. The storage classes also configure the file shares to be expandable, you just need to edit the [persistent volume claim][persistent-volume-claim-overview] (PVC) with the new size. You can select one of the following [Azure storage redundancy SKUs][storage-skus] for the `skuname` parameter in the storage class definition: - **PremiumV2_LRS** (recommended): SSD provisioned v2, locally redundant storage - **PremiumV2_ZRS** (recommended): SSD provisioned v2, zone-redundant storage - **Premium_LRS**: SSD provisioned v1 (legacy), locally redundant storage - **Premium_ZRS**: SSD provisioned v1 (legacy), zone-redundant storage - **StandardV2_LRS**: HDD provisioned v2, locally redundant storage - **StandardV2_ZRS**: HDD provisioned v2, zone-redundant storage - **StandardV2_GRS**: HDD provisioned v2, geo-redundant storage - **StandardV2_GZRS**: HDD provisioned v2, geo-zone-redundant storage - **Standard_LRS**: HDD pay-as-you-go, locally redundant storage - **Standard_GRS**: HDD pay-as-you-go, geo-redundant storage - **Standard_ZRS**: HDD pay-as-you-go, zone-redundant storage > [!IMPORTANT] > To use the provisioned v2 billing model for Azure Files, you must use the Azure Files CSI driver [version 1.35.0](https://github.com/kubernetes-sigs/azurefile-csi-driver/releases/tag/v1.35.0) or later. > [!NOTE] > For new deployments, we recommend SSD provisioned v2 (`PremiumV2_LRS` or `PremiumV2_ZRS`) for most workloads. SSD file shares offer higher performance and low-latency disk support for I/O-intensive workloads. The minimum file share capacity for Premium accounts is 100 GiB. ## Create custom storage classes for dynamic PVs with Azure Files The default storage classes are suitable for most scenarios. In some cases, you might want to have your own storage class customized with your own parameters. For example, you might want to configure the `mountOptions` of the file share. 1. Create a file named `azure-file-sc.yaml` and paste in the following example manifest: ```yaml kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: my-azurefile provisioner: file.csi.azure.com reclaimPolicy: Delete volumeBindingMode: Immediate allowVolumeExpansion: true mountOptions: # Canonical permissions: 0755/uid=1000/gid=1000 for least privilege. # Use 0777/uid=0/gid=0 only if app requires root or broad write access. - dir_mode=0755 - file_mode=0755 - uid=1000 - gid=1000 - mfsymlinks - cache=strict - actimeo=30 - nosharesock parameters: skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS (SSD v1), StandardV2_LRS (HDD v2), Standard_LRS (HDD pay-as-you-go) ``` 1. Create the storage class using the [`kubectl apply`][kubectl-apply] command: ```bash kubectl apply -f azure-file-sc.yaml ``` Your output should resemble the following example output: ```output storageclass.storage.k8s.io/my-azurefile created ``` ## Storage class parameters for dynamic PVs with Azure Files The following table includes parameters you can use to define a custom storage class for your persistent volume claims (PVCs) with Azure Files: | Name | Meaning | Available values | Required | Default value | | ---- | ------- | ---------------- | -------- | ------------- | | `accountAccessTier` | [Access tier for storage account][access-tiers-overview] | Standard account can choose `Hot` or `Cool`, and Premium account can only choose `Premium`. | No | Empty. Use default setting for different storage account types. | | `accountQuota` | Limits the quota for an account. You can specify a maximum quota in GB (102400 GB by default). If the account exceeds the specified quota, the driver skips selecting the account. | | No | `102400` | | `allowBlobPublicAccess` | Allow or disallow public access to all blobs or containers for storage account created by driver. | `true` or `false` | No | `false` | | `createFolderIfNotExist` | Specify whether to create the folder if it doesn't exist in the Azure file share. Supported from Azure Files CSI driver v1.34.0. | `true` or `false` | No | `false` | | `disableDeleteRetentionPolicy` | Specify whether disable DeleteRetentionPolicy for storage account created by driver. | `true` or `false` | No | `false` | | `enableLargeFileShares` | Indicate whether the storage account should have large file shares enabled. Use this parameter only on a Standard account, because Premium accounts already support large file shares by default. | `true` or `false` | No | `false` | | `folderName` | Specify folder name in Azure file share. Supports the `${pvc.metadata.name}`, `${pvc.metadata.namespace}`, and `${pv.metadata.name}` placeholders. | Existing folder name in Azure file share. | No | If folder name doesn't exist in file share, the mount fails. | | `getLatestAccount` | Determines whether to get the latest account key based on the creation time. This driver gets the first key by default. | `true` or `false` | No | `false` | | `location` | Specify the Azure region of the Azure storage account. | For example, `eastus`. | No | If empty, driver uses the same location name as current AKS cluster. | | `matchTags` | Match tags when driver tries to find a suitable storage account. | `true` or `false` | No | `false` | | `networkEndpointType` | Specify network endpoint type for the storage account created by driver. If `privateEndpoint` is specified, a private endpoint is created for the storage account. For other cases, a service endpoint is created by default. | "",`privateEndpoint` | No | "" | | `protocol` | Specify file share protocol. | `smb`, `nfs` | No | `smb` | | `provisionedBandwidth` | Provisioned throughput (MiB/s) for the [Azure Files provisioned v2 model](/azure/storage/files/understanding-billing#provisioned-v2-model). Only applicable to `PremiumV2` and `StandardV2` SKUs. Supported from driver v1.33.4. | Integer string, for example `"200"`. | No | | | `provisionedIOPS` | Provisioned IOPS for the [Azure Files provisioned v2 model](/azure/storage/files/understanding-billing#provisioned-v2-model). Only applicable to `PremiumV2` and `StandardV2` SKUs. Supported from driver v1.33.4. | Integer string, for example `"5000"`. | No | | | `requireInfraEncryption` | Specify whether or not the service applies a secondary layer of encryption with platform managed keys for data at rest for storage account created by driver. | `true` or `false` | No | `false` | | `resourceGroup` | Specify the resource group for the Azure Files storage account. | Existing resource group name | No | If empty, driver uses the same resource group name as current AKS cluster. | | `selectRandomMatchingAccount` | Determines whether to randomly select a matching account. By default, the driver always selects the first matching account in alphabetical order (Note: This driver uses account search cache, which results in uneven distribution of file creation across multiple accounts). | `true` or `false` | No | `false` | | `server` | Specify Azure storage account server address. | Existing server address, for example `accountname.privatelink.file.core.windows.net`. | No | If empty, driver uses default `accountname.file.core.windows.net` or other sovereign cloud account address. | | `shareAccessTier` | [Access tier for file share][storage-tiers] | General purpose v2 account can choose between `TransactionOptimized` (default), `Hot`, and `Cool`. Premium storage account type for file shares only. | No | Empty. Use default setting for different storage account types. | | `shareName` | Specify Azure file share name. | Existing or new Azure file share name. | No | If empty, driver generates an Azure file share name. | | `shareNamePrefix` | Specify Azure file share name prefix created by driver. | Share name can only contain lowercase letters, numbers, hyphens, and length should be fewer than 21 characters. | No | | | `skuName` | Azure Files storage account type (alias: `storageAccountType`) | `Standard_LRS`, `Standard_ZRS`, `Standard_GRS`, `Standard_RAGRS`, `Standard_RAGZRS`,`Premium_LRS`, `Premium_ZRS`, `StandardV2_LRS`, `StandardV2_ZRS`, `StandardV2_GRS`, `StandardV2_GZRS`, `PremiumV2_LRS`, `PremiumV2_ZRS` | No | `Standard_LRS` <br> Minimum file share size for Premium account type is 100 GB. <br> ZRS account type is supported in limited regions. <br> NFS file share only supports Premium account type. <br> Standard V2 SKU names are for [Azure Files provisioned v2 model](/azure/storage/files/understanding-billing#provisioned-v2-model). | | `storageAccount` | Specify an Azure storage account name. | storageAccountName | No | When a specific storage account name is not provided, the driver will look for a suitable storage account that matches the account settings within the same resource group. If it fails to find a matching storage account, it will create a new one. However, if a storage account name is specified, the storage account must already exist. | | `storageEndpointSuffix` | Specify Azure storage endpoint suffix. | `core.windows.net`, `core.chinacloudapi.cn`, etc. | No | If empty, driver uses default storage endpoint suffix according to cloud environment. For example, `core.windows.net`. | | `subscriptionID` | Specify Azure subscription ID where Azure file share is created. | Azure subscription ID | No | If not empty, `resourceGroup` must be provided. | | `tags` | [Tags][tag-resources] are created in new storage account. | Tag format: 'foo=aaa,bar=bbb' | No | "" | | --- | **The following parameters are only for SMB protocol** | --- | --- | --- | | `clientID` | Specify the Azure client ID used to create the Azure file share. If empty, the kubelet managed identity is used when mounting without an account key. | Azure client ID | No | | | `enableMultichannel` | Specify whether to enable [SMB multichannel](/azure/storage/files/files-smb-protocol?tabs=azure-portal#smb-multichannel) for a Premium storage account. Used with the `max_channels=4` (or `2`, `3`) mount option. | `true` or `false` | No | `false` | | `storeAccountKey` | Specify whether to store account key to Kubernetes secret. | `true` or `false` <br> `false` means driver uses kubelet identity to get account key. | No | `true` | | `secretName` | Specify secret name to store account key. | | No | | | `secretNamespace` | Specify the namespace of secret to store account key. <br><br> **Note**: <br> If `secretNamespace` isn't specified, the secret is created in the same namespace as the pod. | `default`,`kube-system`, etc. | No | PVC namespace, for example `csi.storage.k8s.io/pvc/namespace` | | `useDataPlaneAPI` | Specify whether to use [data plane API][data-plane-api] for file share create/delete/resize, which could solve the SRP API throttling issue because the data plane API has almost no limit, while it would fail when there's firewall or Vnet settings on storage account. The `oauth` value (supported from driver v1.33.0) uses an OAuth token for data plane API authentication. | `true`, `false`, or `oauth` | No | `false` | | --- | **The following parameters are only for NFS protocol** | --- | --- | --- | | `allowSharedKeyAccess` | Allow or disallow shared key access for the storage account created by the driver. | `true` or `false` | No | `true` | | `encryptInTransit` | Support [encryption in transit (EiT) for NFS shares](/azure/storage/files/encryption-in-transit-for-nfs-shares). | `true` or `false` | No | `false` | | `mountPermissions` | Mounted folder permissions. The default is `0777`. If set to `0`, driver doesn't perform `chmod` after mount | `0777` | No | | | `rootSquashType` | Specify root squashing behavior on the share. The default is `NoRootSquash` | `AllSquash`, `NoRootSquash`, `RootSquash` | No | | | --- | **The following parameters are only for virtual network setting (for example: NFS, private endpoint)** | --- | --- | --- | | `fsGroupChangePolicy` | Indicates how the driver changes volume's ownership. Pod `securityContext.fsGroupChangePolicy` is ignored. | `OnRootMismatch` (default), `Always`, `None` | No | `OnRootMismatch` | | `publicNetworkAccess` | The `PublicNetworkAccess` property of the storage account created by the driver. | `Enabled`, `Disabled`, `SecuredByPerimeter` | No | | | `subnetName` | Subnet name | Existing subnet name of the agent node. | No | If empty, driver uses the `subnetName` value in Azure cloud config file. | | `vnetLinkName` | Virtual network link name associated with the private DNS zone. | Existing vnet link name. | No | If empty, driver uses `<vnetName>-vnetlink`. | | `vnetName` | Virtual network name | Existing virtual network name. | No | If empty, driver will update all the subnets under the cluster virtual network. | | `vnetResourceGroup` | Specify virtual network resource group where virtual network is defined. | Existing resource group name. | No | If empty, driver uses the `vnetResourceGroup` value in Azure cloud config file. | > [!IMPORTANT] > The `tags` storage class parameter is applied to the storage account when the Azure Files CSI driver provisions the volume. After the persistent volume is created, the `PersistentVolume` spec is immutable, so editing or patching the PV to change tags or other volume attributes fails. Updating the storage class later affects only newly provisioned volumes. > > To update tags on an existing volume, change them on the underlying storage account in Azure. If your storage class uses an existing storage account, update tags on that account. This operation doesn't interrupt existing mounts, pods, or data access, and updated Azure tags aren't synchronized back to the Kubernetes PV YAML or metadata. For example: > > ```azurecli-interactive > az storage account update \ > --name mystorageaccount \ > --resource-group MC_myResourceGroup_myAKSCluster_eastus \ > --set tags.abc=ABC123 > ``` > [!NOTE] > If the storage account is created by the driver, then you only need to specify `networkEndpointType: privateEndpoint` parameter in storage class. The CSI driver creates the private endpoint and private DNS zone (named `privatelink.file.core.windows.net`) together with the account. If you bring your own storage account, then you need to [create the private endpoint][storage-account-private-endpoint] for the storage account. If you're using Azure Files storage in a network isolated cluster, you must create a custom storage class with "networkEndpointType: privateEndpoint". You can use the following example manifest as a reference: > > ```yaml > apiVersion: storage.k8s.io/v1 > kind: StorageClass > metadata: > name: azurefile-csi-private-custom > provisioner: file.csi.azure.com > allowVolumeExpansion: true > parameters: > skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS, Premium_ZRS, StandardV2_LRS, Standard_LRS > networkEndpointType: privateEndpoint > reclaimPolicy: Delete > volumeBindingMode: Immediate > mountOptions: > # Canonical permissions: 0755/uid=1000/gid=1000 for least privilege. > # Use 0777/uid=0/gid=0 only if app requires root or broad write access. > - dir_mode=0755 > - file_mode=0755 > - uid=1000 > - gid=1000 > - mfsymlinks > - cache=strict # https://linux.die.net/man/8/mount.cifs > - nosharesock # reduce probability of reconnect race > - actimeo=30 # reduce latency for metadata-heavy workload > - nobrl # disable sending byte range lock requests to the server and for applications which have challenges with posix locks > ``` ## Create a PVC with Azure Files A PVC uses the storage class object to dynamically provision an Azure file share. You can use the example YAML manifest in this section to create a PVC that's _100 GB_ in size with _ReadWriteMany_ access. For more information on access modes, see [Kubernetes PV access modes][access-modes]. 1. Create a file named `azure-file-pvc.yaml` and paste in the following YAML. Make sure the `storageClassName` matches the name of your existing storage class. ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: my-azurefile spec: accessModes: - ReadWriteMany storageClassName: my-azurefile resources: requests: storage: 100Gi ``` > [!NOTE] > If using the `Premium_LRS` SKU for your storage class, the minimum value for `storage` must be `100Gi`. 1. Create the PVC using the [`kubectl apply`][kubectl-apply] command. ```bash kubectl apply -f azure-file-pvc.yaml ``` 1. View the status of the PVC using the [`kubectl get`][kubectl-get] command: ```bash kubectl get pvc my-azurefile ``` Your output should resemble the following example output, which shows that the PVC is in a `Bound` state, and a PV was dynamically created to satisfy the claim: ```output NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE my-azurefile Bound pvc-aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb 100Gi RWX my-azurefile 5m ``` ## Use a PVC with Azure Files in a pod The example YAML manifest in this section creates a pod that uses the PVC _my-azurefile_ to mount the Azure Files file share at the _/mnt/azure_ path. For Windows Server containers, specify a `mountPath` using the Windows path convention, such as _'D:'_. 1. Create a file named `azure-pvc-files.yaml`, and paste in the following YAML. Make sure the `claimName` matches the name of your existing PVC. ```yaml kind: Pod apiVersion: v1 metadata: name: mypod spec: containers: - name: mypod image: mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine resources: requests: cpu: 100m memory: 128Mi limits: cpu: 250m memory: 256Mi volumeMounts: - mountPath: /mnt/azure name: volume readOnly: false volumes: - name: volume persistentVolumeClaim: claimName: my-azurefile ``` 1. Create the pod using the [`kubectl apply`][kubectl-apply] command. ```bash kubectl apply -f azure-pvc-files.yaml ``` 1. View the status of the pod using the [`kubectl describe`][kubectl-describe] command: ```bash kubectl describe pod mypod ``` Your output should resemble the following example output, which shows that the pod is running and the volume is mounted at the correct path: ```output Containers: mypod: Container ID: docker://BB22CC33DD44EE55FF66AA77BB88CC99DD00EE11 Image: mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine Image ID: docker-pullable://nginx@sha256:AA11BB22CC33DD44EE55FF66AA77BB88CC99DD00 State: Running Started: Fri, 01 Mar 2019 23:56:16 +0000 Ready: True Mounts: /mnt/azure from volume (rw) /var/run/secrets/kubernetes.io/serviceaccount from default-token-8rv4z (ro) [...] Volumes: volume: Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace) ClaimName: my-azurefile ReadOnly: false [...] ``` ## Mount options for Azure Files The location to configure mount options (`mountOptions`) depends on whether you're provisioning dynamic or static persistent volumes: - If you're dynamically provisioning a volume with a storage class, specify the mount options on the storage class object (kind: StorageClass). - If you're statically provisioning a volume, specify the mount options on the PV object (kind: PersistentVolume). - If you're [mounting the file share as an inline volume](#mount-file-share-as-an-inline-volume), specify the mount options on the Pod object (kind: Pod). For more information, see [Mount options](https://kubernetes.io/docs/concepts/storage/storage-classes/#mount-options). The default value for `fileMode` and `dirMode` is _0777_ for Kubernetes versions 1.13.0 and above. The following example sets _0777_: ```yaml kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: my-azurefile provisioner: file.csi.azure.com allowVolumeExpansion: true mountOptions: # Canonical permissions: 0755/uid=1000/gid=1000 for least privilege. # Use 0777/uid=0/gid=0 only if app requires root or broad write access. - dir_mode=0755 - file_mode=0755 - uid=1000 - gid=1000 - mfsymlinks - cache=strict - actimeo=30 - nobrl # disable sending byte range lock requests to the server and for applications which have challenges with posix locks parameters: skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS (SSD v1), StandardV2_LRS (HDD v2), Standard_LRS (HDD pay-as-you-go) ``` ### Recommended mount options for SMB shares Recommended mount options for SMB shares are provided in the following storage class example: ```yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: azurefile-csi-premiumv2-custom provisioner: file.csi.azure.com allowVolumeExpansion: true parameters: skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS, Premium_ZRS, StandardV2_LRS, Standard_LRS, Standard_ZRS reclaimPolicy: Delete volumeBindingMode: Immediate mountOptions: # Canonical permissions: 0755/uid=1000/gid=1000 for least privilege. # Use 0777/uid=0/gid=0 only if app requires root or broad write access. - dir_mode=0755 - file_mode=0755 - uid=1000 - gid=1000 - mfsymlinks # support symbolic links - cache=strict # https://linux.die.net/man/8/mount.cifs - nosharesock # reduces probability of reconnect race - actimeo=30 # reduces latency for metadata-heavy workload - nobrl # disable sending byte range lock requests to the server and for applications which have challenges with posix locks ``` If you're using premium (SSD) file shares with the SMB protocol and your workload is metadata heavy, enroll to use the [metadata caching](/azure/storage/files/smb-performance?tabs=portal#metadata-caching-for-ssd-file-shares) feature to improve performance. For more information, see [Improve performance for SMB Azure file shares](/azure/storage/files/smb-performance). ### Recommended mount options for NFS shares Recommended mount options for NFS shares are provided in the following storage class example: ```yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: azurefile-csi-premiumv2-custom provisioner: file.csi.azure.com parameters: protocol: nfs skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS, Premium_ZRS, PremiumV2_ZRS reclaimPolicy: Delete volumeBindingMode: Immediate allowVolumeExpansion: true mountOptions: - nconnect=4 # improves performance by enabling multiple connections to share - noresvport # improves availability - actimeo=30 # reduces latency for metadata-heavy workloads ``` Increase [read-ahead size](/azure/storage/files/nfs-performance#increase-read-ahead-size-to-improve-read-throughput) to improve read throughput. While Azure Files supports setting nconnect up to the maximum setting of 16, we recommend configuring the mount options with the optimal setting of nconnect=4. Currently, there are no gains beyond four channels for the Azure Files implementation of nconnect. ## Create a volume snapshot from a PVC with Azure Files The Azure Files CSI driver supports creating [snapshots of persistent volumes](https://kubernetes-csi.github.io/docs/snapshot-restore-feature.html) and the underlying file shares. 1. Create a [volume snapshot class](https://github.com/kubernetes-sigs/azurefile-csi-driver/blob/master/deploy/example/snapshot/volumesnapshotclass-azurefile.yaml) using the [`kubectl apply`][kubectl-apply] command: ```bash kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/azurefile-csi-driver/master/deploy/example/snapshot/volumesnapshotclass-azurefile.yaml ``` Your output should resemble the following example output: ```output volumesnapshotclass.snapshot.storage.k8s.io/csi-azurefile-vsc created ``` 1. Create a [volume snapshot](https://github.com/kubernetes-sigs/azurefile-csi-driver/blob/master/deploy/example/snapshot/volumesnapshot-azurefile.yaml) from the dynamic PVC you created earlier in this tutorial using the [`kubectl apply`][kubectl-apply] command: ```bash kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/azurefile-csi-driver/master/deploy/example/snapshot/volumesnapshot-azurefile.yaml ``` Your output should resemble the following example output: ```output volumesnapshot.snapshot.storage.k8s.io/azurefile-volume-snapshot created ``` 1. View the status of the volume snapshot using the [`kubectl describe`][kubectl-describe] command: ```bash kubectl describe volumesnapshot azurefile-volume-snapshot ``` Your output should resemble the following example output, which shows that the volume snapshot isn't ready to use because the driver is still creating the snapshot of the underlying Azure file share: ```output Name: azurefile-volume-snapshot Namespace: default Labels: <none> Annotations: API Version: snapshot.storage.k8s.io/v1beta1 Kind: VolumeSnapshot Metadata: Creation Timestamp: 2020-08-27T22:37:41Z Finalizers: snapshot.storage.kubernetes.io/volumesnapshot-as-source-protection snapshot.storage.kubernetes.io/volumesnapshot-bound-protection Generation: 1 Resource Version: 955091 Self Link: /apis/snapshot.storage.k8s.io/v1beta1/namespaces/default/volumesnapshots/azurefile-volume-snapshot UID: 00aa00aa-bb11-cc22-dd33-44ee44ee44ee Spec: Source: Persistent Volume Claim Name: pvc-azurefile Volume Snapshot Class Name: csi-azurefile-vsc Status: Bound Volume Snapshot Content Name: snapcontent-00aa00aa-bb11-cc22-dd33-44ee44ee44ee Ready To Use: false Events: <none> ``` ## Resize a persistent volume with Azure Files > [!NOTE] > Shrinking persistent volumes isn't currently supported. Trying to patch an existing PVC with a smaller size than the current one leads to the following error message: > > `The persistentVolumeClaim "pvc-azurefile" is invalid: spec.resources.requests.storage: Forbidden: field can not be less than previous value.` You can request a larger volume for a PVC by editing the PVC object to specify a larger size. This change triggers the expansion of the underlying volume that backs the PV. A new PV is never created to satisfy the claim. Instead, an existing volume is resized. In AKS, the built-in `azurefile-csi` storage class supports expansion. Custom storage classes must set `allowVolumeExpansion: true`. The PVC requested a 100 GiB file share. 1. Verify the current size of the PVC and the filesystem inside the pod using the `kubectl exec` command to run the `df -h` command inside the pod: ```bash kubectl exec -it nginx-azurefile -- df -h /mnt/azurefile ``` Your output should resemble the following example output, which shows that the filesystem is 100 GB in size: ```output Filesystem Size Used Avail Use% Mounted on //a123b4c567de89fghi01jk2.file.core.windows.net/pvc-00aa00aa-bb11-cc22-dd33-44ee44ee44ee 100G 128K 100G 1% /mnt/azurefile ``` 1. Expand the PVC by increasing the `spec.resources.requests.storage` field using the `kubectl patch` command. In this example, we increase the file share to 200 GiB: ```bash kubectl patch pvc pvc-azurefile --type merge --patch '{"spec": {"resources": {"requests": {"storage": "200Gi"}}}}' ``` Your output should resemble the following example output, which shows that the PVC was patched successfully: ```output persistentvolumeclaim/pvc-azurefile patched ``` 1. Verify the PVC was successfully resized and the new size is reflected in the pod by using the `kubectl get pvc` command and the `df -h` command inside the pod: ```bash kubectl get pvc pvc-azurefile ``` Your output should resemble the following example output, which shows that the PVC is still in a `Bound` state, and the capacity was updated to 200 GiB: ```output NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc-azurefile Bound pvc-00aa00aa-bb11-cc22-dd33-44ee44ee44ee 200Gi RWX azurefile-csi 64m ``` 1. Verify the new size of the filesystem inside the pod using the `kubectl exec` command to run the `df -h` command inside the pod: ```bash kubectl exec -it nginx-azurefile -- df -h /mnt/azurefile ``` Your output should resemble the following example output, which shows that the filesystem is now 200 GB in size: ```output Filesystem Size Used Avail Use% Mounted on //a123b4c567de89fghi01jk2.file.core.windows.net/pvc-bbbbbbbb-1111-2222-3333-cccccccccccc 200G 128K 200G 1% /mnt/azurefile ``` ## Use a persistent volume with private Azure Files storage (private endpoint) If your Azure Files resources are protected with a private endpoint, you must create your own storage class. Make sure that you've [configured your DNS settings to resolve the private endpoint IP address to the FQDN of the connection string][azure-private-endpoint-dns]. When you create the storage class using the Azure Files CSI driver, you need to specify the `networkEndpointType` parameter with the value `privateEndpoint`, and provide the following parameters: - `resourceGroup`: The resource group where the storage account is deployed. - `storageAccount`: The storage account name. - `server`: The FQDN of the storage account's private endpoint. 1. Create a file named `private-azure-file-sc.yaml` and then paste in the following manifest. Make sure you replace the placeholders for `<resourceGroup>` and `<storageAccountName>`. ```yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: azurefile-csi-private-custom provisioner: file.csi.azure.com allowVolumeExpansion: true parameters: skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS, StandardV2_LRS resourceGroup: <resourceGroup> storageAccount: <storageAccountName> server: <storageAccountName>.file.core.windows.net networkEndpointType: privateEndpoint reclaimPolicy: Delete volumeBindingMode: Immediate mountOptions: # Canonical permissions: 0755/uid=1000/gid=1000 for least privilege. # Use 0777/uid=0/gid=0 only if app requires root or broad write access. - dir_mode=0755 - file_mode=0755 - uid=1000 - gid=1000 - mfsymlinks - cache=strict # https://linux.die.net/man/8/mount.cifs - nosharesock # reduce probability of reconnect race - actimeo=30 # reduce latency for metadata-heavy workload - nobrl ``` 1. Create the storage class using the `kubectl apply` command: ```bash kubectl apply -f private-azure-file-sc.yaml ``` Your output should resemble the following example output: ```output storageclass.storage.k8s.io/private-azurefile-csi created ``` 1. Create a file named `private-pvc.yaml` and paste in the following manifest. Make sure the `storageClassName` matches the name of your existing storage class. ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: private-azurefile-pvc spec: accessModes: - ReadWriteMany storageClassName: private-azurefile-csi resources: requests: storage: 100Gi ``` 1. Create the PVC using the [`kubectl apply`][kubectl-apply] command. ```bash kubectl apply -f private-pvc.yaml ``` ## Use Azure Files with Windows containers The Azure Files CSI driver also supports Windows nodes and containers. To use Windows containers, follow the [Windows containers quickstart](./learn/quick-windows-container-deploy-cli.md) to add a Windows node pool. After you have a Windows node pool, you can use the built-in storage classes like `azurefile-csi` or create a custom one. The example [Windows-based stateful set](https://github.com/kubernetes-sigs/azurefile-csi-driver/blob/master/deploy/example/windows/statefulset.yaml) in this section saves timestamps into a file `data.txt` every second, which is mounted to an Azure file share using the Azure Files CSI driver. 1. Create the stateful set using the [`kubectl apply`][kubectl-apply] command: ```bash kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/azurefile-csi-driver/master/deploy/example/windows/statefulset.yaml ``` Your output should resemble the following example output: ```output statefulset.apps/busybox-azurefile created ``` 1. Validate the timestamps are being written to the file share using the following `kubectl exec` commands to run the `cat` command inside the pod: ```bash kubectl exec -it busybox-azurefile-0 -- cat c:\\mnt\\azurefile\\data.txt # on Linux/MacOS Bash kubectl exec -it busybox-azurefile-0 -- cat c:\mnt\azurefile\data.txt # on Windows Powershell/CMD ``` Your output should resemble the following example output, which shows that timestamps are being written to the file share every second: ```output 2020-08-27 22:11:01Z 2020-08-27 22:11:02Z 2020-08-27 22:11:04Z (...) ``` ## Use NFS protocol with Azure Files [Azure Files supports the NFS v4.1 protocol](/azure/storage/files/storage-files-how-to-create-nfs-shares). NFS version 4.1 support for Azure Files provides you with a fully managed NFS file system as a service built on a highly available and highly durable distributed resilient storage platform. This option is optimized for random access workloads with in-place data updates and provides full POSIX file system support. This section shows you how to use NFS shares with the Azure Files CSI driver on an AKS cluster. ### Prerequisites for using NFS shares with Azure Files - NFS requires SSD file shares (such as `PremiumV2_LRS`, `PremiumV2_ZRS`, `Premium_LRS`, or `Premium_ZRS`) and a virtual network-enabled storage account. - Your AKS cluster _control plane_ identity (that is, your AKS cluster name) is added to the [Contributor](/azure/role-based-access-control/built-in-roles#contributor) role on the VNet and NetworkSecurityGroup. - Your AKS cluster's service principal or managed identity must be added to the Contributor role on the storage account. > [!NOTE] > You can use a private endpoint instead of allowing access to the selected VNet. ### Optimize read and write size options This section provides information about how to approach performance tuning NFS with the Azure Files CSI driver with the `rsize` and `wsize` options. The `rsize` and `wsize` options set the maximum transfer size of an NFS operation. If `rsize` or `wsize` aren't specified on mount, the client and server negotiate the largest size supported by the two. Currently, both Azure Files and modern Linux distributions support read and write sizes as large as 1,048,576 Bytes (1 MiB). Optimal performance is based on efficient client-server communication. Increasing or decreasing the **mount** read and write option size values can improve NFS performance. The default size of the read/write packets transferred between client and server are 8 KB for NFS version 2, and 32 KB for NFS version 3 and 4. These defaults might be too large or too small. Reducing the `rsize` and `wsize` might improve NFS performance in a congested network by sending smaller packets for each NFS-read reply and write request. However, this can increase the number of packets needed to send data across the network, increasing total network traffic and CPU utilization on the client and server. It's important that you perform testing to find an `rsize` and `wsize` that sustain efficient packet transfer and don't decrease throughput and increase latency. The following example manifest configures the `mountOptions` section in a storage class to a maximum `rsize` and `wsize` of 256-KiB: ```yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: azurefile-csi-premiumv2-custom provisioner: file.csi.azure.com allowVolumeExpansion: true parameters: protocol: nfs skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS, Premium_ZRS, PremiumV2_ZRS mountOptions: - nconnect=4 - noresvport - actimeo=30 - rsize=262144 - wsize=262144 ``` For a list of supported `mountOptions`, see [NFS mount options][nfs-file-share-mount-options]. ### Create NFS file share storage class > [!NOTE] > `vers`, `minorversion`, `sec` are configured by the Azure Files CSI driver. Specifying a value in your manifest for these properties isn't supported. 1. Create a file named `nfs-sc.yaml` and paste in the following manifest. Make sure to specify `protocol: nfs` in the parameters section, and adjust the `mountOptions` as needed for your workload. ```yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: azurefile-csi-premiumv2-custom provisioner: file.csi.azure.com allowVolumeExpansion: true parameters: protocol: nfs skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS, Premium_ZRS, PremiumV2_ZRS mountOptions: - nconnect=4 - noresvport - actimeo=30 ``` 1. After editing and saving the file, create the storage class using the [`kubectl apply`][kubectl-apply] command. ```bash kubectl apply -f nfs-sc.yaml ``` Your output should resemble the following example output: ```output storageclass.storage.k8s.io/azurefile-csi-nfs created ``` ### Create a stateful set with an NFS-backed file share 1. Create a file named `nfs-ss.yaml` and paste in the following manifest. This configuration saves timestamps into a file `data.txt`. ```yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: statefulset-azurefile labels: app: nginx spec: podManagementPolicy: Parallel # default is OrderedReady serviceName: statefulset-azurefile replicas: 1 template: metadata: labels: app: nginx spec: nodeSelector: "kubernetes.io/os": linux containers: - name: statefulset-azurefile image: mcr.microsoft.com/oss/nginx/nginx:1.19.5 command: - "/bin/bash" - "-c" - set -euo pipefail; while true; do echo $(date) >> /mnt/azurefile/outfile; sleep 1; done volumeMounts: - name: persistent-storage mountPath: /mnt/azurefile updateStrategy: type: RollingUpdate selector: matchLabels: app: nginx volumeClaimTemplates: - metadata: name: persistent-storage spec: storageClassName: azurefile-csi-premiumv2-custom accessModes: ["ReadWriteMany"] resources: requests: storage: 100Gi ``` 1. Create the stateful set using the [`kubectl apply`][kubectl-apply] command. ```bash kubectl apply -f nfs-ss.yaml ``` Your output should resemble the following example output: ```output statefulset.apps/statefulset-azurefile created ``` 1. Validate the contents of the volume using the following `kubectl exec` command to run the `df -h` command inside the pod: ```bash kubectl exec -it statefulset-azurefile-0 -- df -h ``` Your output should resemble the following example output, which shows that the NFS file share is mounted at the correct path with the correct size: ```output Filesystem Size Used Avail Use% Mounted on ... /dev/sda1 29G 11G 19G 37% /etc/hosts accountname.file.core.windows.net:/accountname/pvc-cccccccc-2222-3333-4444-dddddddddddd 100G 0 100G 0% /mnt/azurefile ... ``` Because the NFS file share is in a Premium storage account, the minimum file share size is 100 GiB. If you create a PVC with a small storage size, you might encounter an error similar to the following: _failed to create file share ... size (5)..._. ### Encryption in transit (EiT) for NFS file shares > [!NOTE] > The EiT feature is available starting with AKS version 1.33. Ubuntu 20.04 and Windows nodes aren't currently supported. > > The feature is supported in all Azure regions that [support SSD Azure file shares](/azure/storage/files/redundancy-premium-file-shares). [Encryption in Transit (EiT)](/azure/storage/files/encryption-in-transit-for-nfs-shares) ensures that all read and writes to the NFS file shares within the virtual network are encrypted, providing an extra layer of security. By setting `encryptInTransit: "true"` in StorageClass parameters or PersistentVolume `volumeAttributes`, you can enable data encryption in transit for NFS Azure file shares. #### Dynamic provisioning (StorageClass) ```yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: azurefile-csi-premiumv2-eit provisioner: file.csi.azure.com allowVolumeExpansion: true parameters: protocol: nfs skuName: PremiumV2_LRS encryptInTransit: "true" mountOptions: - nconnect=4 - noresvport - actimeo=30 ``` #### Existing persistent volumes For PVs that were created without encryption in transit, set `encryptInTransit: "true"` in `volumeAttributes`: ```yaml apiVersion: v1 kind: PersistentVolume metadata: name: pv-azurefile-eit spec: capacity: storage: 100Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain mountOptions: - nconnect=4 - noresvport - actimeo=30 csi: driver: file.csi.azure.com volumeHandle: "{resource-group-name}#{account-name}#{file-share-name}" volumeAttributes: protocol: nfs encryptInTransit: "true" resourceGroup: "{resource-group-name}" storageAccount: "{account-name}" shareName: "{file-share-name}" ``` ## Use managed identity to access Azure Files storage Azure Files now supports managed identity based authentication for SMB access. This enables your applications to securely access Azure Files without storing or managing credentials. > [!NOTE] > Managed identity support for Azure Files in AKS is available starting with AKS version 1.34 on Linux nodes. ### Prerequisites for using managed identity to access Azure Files storage - Ensure the [user-assigned Kubelet identity](use-managed-identity.md#create-a-kubelet-managed-identity) has the `Storage File Data SMB MI Admin` role on the storage account. - If you use your own storage account, you need to assign `Storage File Data SMB MI Admin` role to the user-assigned Kubelet identity on that storage account. - If the storage account is created by the CSI driver, grant `Storage File Data SMB MI Admin` role to the resource group where the storage account resides. ### Enable managed identity for dynamic PVs with Azure Files To enable managed identity for dynamically provisioned volumes, you need to create a new storage class with `mountWithManagedIdentity`: `"true"` and deploy your stateful set using this storage class. The following example manifest configures a storage class to use managed identity to access Azure Files: ```yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: azurefile-csi provisioner: file.csi.azure.com parameters: resourceGroup: EXISTING_RESOURCE_GROUP_NAME # optional, node resource group by default if it's not provided storageAccount: EXISTING_STORAGE_ACCOUNT_NAME # optional, a new account will be created if it's not provided mountWithManagedIdentity: "true" # optional, clientID of the managed identity, kubelet identity would be used by default if it's not provided clientID: "xxxxx-xxxx-xxx-xxx-xxxxxxx" reclaimPolicy: Delete volumeBindingMode: Immediate allowVolumeExpansion: true mountOptions: # Canonical permissions: 0755/uid=1000/gid=1000 for least privilege. # Use 0777/uid=0/gid=0 only if app requires root or broad write access. - dir_mode=0755 - file_mode=0755 - uid=1000 - gid=1000 - mfsymlinks - cache=strict # https://linux.die.net/man/8/mount.cifs - nosharesock # reduce probability of reconnect race - actimeo=30 # reduce latency for metadata-heavy workload - nobrl # disable sending byte range lock requests to the server ``` ### Enable managed identity for static PVs with Azure Files To use managed identity with statically provisioned Azure Files persistent volumes, ensure the following configuration: 1. Enable the SMBOauth on the storage account by running: ```bash az storage account update --name <account-name> --resource-group <resource-group-name> --enable-smb-oauth true ``` 1. Create a PV with `mountWithManagedIdentity`: `"true"` and mount the PV to your application pod. The following example manifest configures a PV to use managed identity to access Azure Files: ```yaml apiVersion: v1 kind: PersistentVolume metadata: name: pv-azurefile spec: capacity: storage: 100Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: azurefile-csi mountOptions: # Canonical permissions: 0755/uid=1000/gid=1000 for least privilege. # Use 0777/uid=0/gid=0 only if app requires root or broad write access. - dir_mode=0755 - file_mode=0755 - uid=1000 - gid=1000 - mfsymlinks - cache=strict # https://linux.die.net/man/8/mount.cifs - nosharesock # reduce probability of reconnect race - actimeo=30 # reduce latency for metadata-heavy workload - nobrl # disable sending byte range lock requests to the server csi: driver: file.csi.azure.com # make sure volumeHandle is unique for every identical share in the cluster volumeHandle: "{resource-group-name}#{account-name}#{file-share-name}" volumeAttributes: resourceGroup: EXISTING_RESOURCE_GROUP_NAME # optional, node resource group by default if it's not provided storageAccount: EXISTING_STORAGE_ACCOUNT_NAME # optional, a new account will be created if it's not provided shareName: EXISTING_FILE_SHARE_NAME mountWithManagedIdentity: "true" # optional, clientID of the managed identity, kubelet identity would be used by default if it's empty clientID: "xxxxx-xxxx-xxx-xxx-xxxxxxx" ``` ## Use workload identity to access Azure Files storage Azure Files now supports workload identity-based authentication for SMB access. Workload identity enables pod-level, least-privilege access to Azure Files without tying application identity to the node lifecycle. > [!NOTE] > Workload identity support for Azure Files in AKS is available starting with AKS version 1.35.0 on Linux nodes. ### Prerequisites for using workload identity to access Azure Files storage Before using workload identity to access Azure Files from AKS, complete the following prerequisites. #### 1. Create a cluster with oidc-issuer enabled and get the AKS cluster credential Create a new AKS cluster with the OIDC issuer enabled, or verify that it's already enabled. Follow the official [documentation](use-oidc-issuer.md#create-an-aks-cluster-with-the-oidc-issuer) for creating a new AKS cluster with the `--enable-oidc-issuer` parameter and retrieve the cluster credentials. And set the following environment variables: ```bash export RESOURCE_GROUP=<your resource group name> export CLUSTER_NAME=<your cluster name> export REGION=<your region> ``` #### 2. Prepare the storage account Create a new storage account and file share, or use an existing one. Refer to the Azure Files [documentation](/azure/storage/files/storage-how-to-use-files-portal) for detailed instructions. Set the following environment variables: ```bash export STORAGE_RESOURCE_GROUP=<your storage account resource group> export ACCOUNT=<your storage account name> export SHARE=<your fileshare name> # optional ``` #### 3. Create or reuse a managed identity and grant required permissions Create a user‑assigned managed identity, or reuse an existing one (for example, a [managed identity](managed-identity-overview.md) associated with the AKS node resource group). And retrieve the required identity and resource details: ```bash export UAMI=<your managed identity name> az identity create --name $UAMI --resource-group $RESOURCE_GROUP export USER_ASSIGNED_CLIENT_ID="$(az identity show -g $RESOURCE_GROUP --name $UAMI --query 'clientId' -o tsv)" export IDENTITY_TENANT=$(az aks show --name $CLUSTER_NAME --resource-group $RESOURCE_GROUP --query identity.tenantId -o tsv) export ACCOUNT_SCOPE=$(az storage account show --name $ACCOUNT --query id -o tsv) ``` Grant the `Storage File Data SMB MI Admin` role to the managed identity. This role enables Azure Files mounting using workload identity tokens only, without relying on storage account keys. ```bash az role assignment create --role "Storage File Data SMB MI Admin" --assignee $USER_ASSIGNED_CLIENT_ID --scope $ACCOUNT_SCOPE ``` #### 4. Create a Kubernetes ServiceAccount Create a Kubernetes ServiceAccount that your workload will use. ```bash export SERVICE_ACCOUNT_NAME=<your sa name> export SERVICE_ACCOUNT_NAMESPACE=<your sa namespace> cat <<EOF | kubectl apply -f - apiVersion: v1 kind: ServiceAccount metadata: name: ${SERVICE_ACCOUNT_NAME} namespace: ${SERVICE_ACCOUNT_NAMESPACE} EOF ``` #### 5. Create the federated identity credential Create the federated identity credential between the managed identity, service account issuer, and subject using the `az identity federated-credential create` command. ```bash export FEDERATED_IDENTITY_NAME=<your federated identity name> export AKS_OIDC_ISSUER="$(az aks show --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME --query "oidcIssuerProfile.issuerUrl" -o tsv)" az identity federated-credential create --name $FEDERATED_IDENTITY_NAME \ --identity-name $UAMI \ --resource-group $RESOURCE_GROUP \ --issuer $AKS_OIDC_ISSUER \ --subject system:serviceaccount:${SERVICE_ACCOUNT_NAMESPACE}:${SERVICE_ACCOUNT_NAME} ``` After completing these steps, workloads running with the specified ServiceAccount can authenticate to Azure Files using Microsoft Entra workload identity, without using storage account keys or node‑level managed identities. ### Enable workload identity for dynamic PVs with Azure Files To use workload identity with dynamically provisioned Azure Files persistent volumes, ensure the following configuration: 1. Grant permissions to the CSI driver control plane identity - Assign the `Storage Account Contributor` role to the identity used by the CSI driver control plane for the target storage account. - If the storage account is created dynamically by the CSI driver, grant `Storage Account Contributor` role to the node resource group. - By default, AKS cluster control plane identity is already assigned the `Storage Account Contributor` role on the node resource group for the storage account creation. 1. Create a new storage class with `mountWithWorkloadIdentityToken`: `"true"` and deploy your stateful set using this storage class. The following example manifest configures a storage class to use workload identity to access Azure Files: ```yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: azurefile-csi-wi provisioner: file.csi.azure.com parameters: resourceGroup: EXISTING_RESOURCE_GROUP_NAME # optional, node resource group by default if it's not provided storageAccount: EXISTING_STORAGE_ACCOUNT_NAME # optional, a new account will be created if it's not provided mountWithWorkloadIdentityToken: "true" # optional, clientID of the managed identity, kubelet identity would be used by default if it's not provided clientID: "xxxxx-xxxx-xxx-xxx-xxxxxxx" reclaimPolicy: Delete volumeBindingMode: Immediate allowVolumeExpansion: true mountOptions: - dir_mode=0777 # modify this permission if you want to enhance the security - file_mode=0777 - mfsymlinks - cache=strict # https://linux.die.net/man/8/mount.cifs - nosharesock # reduce probability of reconnect race - actimeo=30 # reduce latency for metadata-heavy workload - nobrl # disable sending byte range lock requests to the server ``` ### Enable workload identity for static PVs with Azure Files To use workload identity with statically provisioned Azure Files persistent volumes, ensure the following configuration: 1. Enable the SMBOauth on the storage account by running: ```bash az storage account update --name <account-name> --resource-group <resource-group-name> --enable-smb-oauth true ``` 1. Create a PV with `mountWithWorkloadIdentityToken`: `"true"` specified and mount the PV to your application pod. The following example manifest configures a PV to use workload identity to access Azure Files: ```yaml apiVersion: v1 kind: PersistentVolume metadata: name: pv-azurefile spec: capacity: storage: 100Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: azurefile-csi mountOptions: - dir_mode=0777 # modify this permission if you want to enhance the security - file_mode=0777 - uid=0 - gid=0 - mfsymlinks - cache=strict # https://linux.die.net/man/8/mount.cifs - nosharesock # reduce probability of reconnect race - actimeo=30 # reduce latency for metadata-heavy workload - nobrl # disable sending byte range lock requests to the server csi: driver: file.csi.azure.com # make sure volumeHandle is unique for every identical share in the cluster volumeHandle: "{resource-group-name}#{account-name}#{file-share-name}" volumeAttributes: resourceGroup: EXISTING_RESOURCE_GROUP_NAME # optional, node resource group by default if it's not provided storageAccount: EXISTING_STORAGE_ACCOUNT_NAME # optional, a new account will be created if it's not provided shareName: EXISTING_FILE_SHARE_NAME mountWithWorkloadIdentityToken: "true" # optional, clientID of the managed identity, kubelet identity would be used by default if it's empty clientID: "xxxxx-xxxx-xxx-xxx-xxxxxxx" ``` ## Create a static PV with Azure Files The following sections provide instructions for creating a static PV with Azure Files. A static PV is a persistent volume that an administrator creates manually. This PV is available for use by pods in the cluster. To use a static PV, you create a PVC that references the PV, and then create a pod that references the PVC. ### Storage class parameters for static PVs with Azure Files The following table includes parameters you can use to define a custom storage class for your static PVCs with Azure Files: | Name | Meaning | Available values | Required | Default value | | ---- | ------- | ---------------- | -------- | ------------- | | `volumeAttributes.resourceGroup` | Specify an Azure resource group name. | myResourceGroup | No | If empty, driver uses the same resource group name as current cluster. | | `volumeAttributes.storageAccount` | Specify an existing Azure storage account name. | storageAccountName | Yes | | | `volumeAttributes.shareName` | Specify an Azure file share name. | fileShareName | Yes | | | `volumeAttributes.folderName` | Specify a folder name in Azure file share. | folderName | No | If folder name doesn't exist in file share, mount would fail. | | `volumeAttributes.protocol` | Specify file share protocol. | `smb`, `nfs` | No | `smb` | | `volumeAttributes.server` | Specify Azure storage account server address | Existing server address, for example `accountname.privatelink.file.core.windows.net`. | No | If empty, driver uses default `accountname.file.core.windows.net` or other sovereign cloud account address. | | --- | **The following parameters are only for SMB protocol** | --- | --- | --- | | `volumeAttributes.secretName` | Specify a secret name that stores storage account name and key. | | No | | | `volumeAttributes.secretNamespace` | Specify a secret namespace. | `default`,`kube-system`, etc. | No | PVC namespace (`csi.storage.k8s.io/pvc/namespace`) | | `nodeStageSecretRef.name` | Specify a secret name that stores storage account name and key. | Existing secret name. | No | If empty, driver uses kubelet identity to get account key. | | `nodeStageSecretRef.namespace` | Specify a secret namespace. | Kubernetes namespace | No | | | --- | **The following parameters are only for NFS protocol** | --- | --- | --- | | `volumeAttributes.fsGroupChangePolicy` | Indicates how the driver changes a volume's ownership. Pod `securityContext.fsGroupChangePolicy` is ignored. | `OnRootMismatch` (default), `Always`, `None` | No | `OnRootMismatch` | | `volumeAttributes.mountPermissions` | Specify mounted folder permissions. The default is `0777` | | No | | ### Create an Azure file share Before you can use an Azure Files file share as a Kubernetes volume, you must create an Azure Storage account and the file share. 1. Get the node resource group name of your AKS cluster using the [`az aks show`][az-aks-show] command with the `--query nodeResourceGroup` parameter. ```azurecli-interactive az aks show --resource-group myResourceGroup --name myAKSCluster --query nodeResourceGroup -o tsv ``` The output of the command resembles the following example: ```azurecli-interactive MC_myResourceGroup_myAKSCluster_eastus ``` 1. Create a storage account using the [`az storage account create`][az-storage-account-create] command with the `--sku` parameter. The following command creates a storage account using the `Standard_LRS` SKU. Make sure to replace the following placeholders: - `myAKSStorageAccount` with the name of the storage account - `nodeResourceGroupName` with the name of the resource group that the AKS cluster nodes are hosted in - `location` with the name of the region to create the resource in. It should be the same region as the AKS cluster nodes. ```azurecli-interactive az storage account create --name myAKSStorageAccount --resource-group nodeResourceGroupName --location location --sku Standard_LRS ``` 1. Export the connection string as an environment variable, which you use to create the file share, using the [`az storage account show-connection-string`][az-storage-account-show-connection-string] command. Make sure to replace `storageAccountName` and `resourceGroupName` with your storage account name and resource group name. ```azurecli-interactive export AZURE_STORAGE_CONNECTION_STRING=$(az storage account show-connection-string --name storageAccountName --resource-group resourceGroupName -o tsv) ``` > [!NOTE] > Connection strings must be protected using key rotation or storage in an Azure Key Vault. For more information about connection strings, see [Configure Azure Storage connection strings](/azure/storage/common/storage-configure-connection-string) and [Manage storage account access keys](/azure/storage/common/storage-account-keys-manage). For production environments, Microsoft recommends using Microsoft Entra ID authentication. For more information, see [Authorize access to data in Azure Storage](/azure/storage/blobs/authorize-access-azure-active-directory). 1. Create the file share using the [`az storage share create`][az-storage-share-create] command. Make sure to replace `shareName` with your share name. ```azurecli-interactive az storage share create --name shareName --connection-string $AZURE_STORAGE_CONNECTION_STRING ``` 1. Export the storage account key as an environment variable using the [`az storage account keys list`][az-storage-account-keys-list] command. Make sure to replace `storageAccountName` and `resourceGroupName` with your storage account name and resource group name. ```azurecli-interactive STORAGE_KEY=$(az storage account keys list --resource-group nodeResourceGroupName --account-name myAKSStorageAccount --query "[0].value" -o tsv) ``` 1. Echo the storage account name and key using the following command. Make note of the storage account key, which you use to create a Kubernetes secret in the next step. ```bash echo Storage account key: $STORAGE_KEY ``` ### Create a Kubernetes secret Kubernetes needs credentials to access the file share created in the previous step. These credentials are stored in a [Kubernetes secret][kubernetes-secret], which is referenced when you create a Kubernetes pod. - Create the secret using the `kubectl create secret` command. The following example creates a secret named _azure-secret_ and populates the _azurestorageaccountname_ and _azurestorageaccountkey_ from the previous step. To use an existing Azure storage account, provide the account name and key. ```bash kubectl create secret generic azure-secret --from-literal=azurestorageaccountname=myAKSStorageAccount --from-literal=azurestorageaccountkey=$STORAGE_KEY ``` ### Mount file share as a persistent volume 1. Create a new file named `azurefiles-pv.yaml` and copy in the following contents. Under `csi`, update `resourceGroup`, `volumeHandle`, and `shareName`. For mount options, the default value for `fileMode` and `dirMode` is _0777_. ```yaml apiVersion: v1 kind: PersistentVolume metadata: annotations: pv.kubernetes.io/provisioned-by: file.csi.azure.com name: azurefile spec: capacity: storage: 5Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: azurefile-csi csi: driver: file.csi.azure.com volumeHandle: "{resource-group-name}#{account-name}#{file-share-name}" # make sure this volumeid is unique for every identical share in the cluster volumeAttributes: shareName: aksshare nodeStageSecretRef: name: azure-secret namespace: default mountOptions: # Canonical permissions: 0755/uid=1000/gid=1000 for least privilege. # Use 0777/uid=0/gid=0 only if app requires root or broad write access. - dir_mode=0755 - file_mode=0755 - uid=1000 - gid=1000 - mfsymlinks - cache=strict - nosharesock - actimeo=30 - nobrl # disable sending byte range lock requests to the server and for applications which have challenges with posix locks ``` 1. Create the PV using the [`kubectl create`][kubectl-create] command. ```bash kubectl create -f azurefiles-pv.yaml ``` 1. Create a new file named _azurefiles-mount-options-pvc.yaml_ and paste in the following contents. Make sure the `storageClassName` matches the name of your existing storage class, and the `volumeName` matches the name of the PV you created in the previous step. ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: azurefile spec: accessModes: - ReadWriteMany storageClassName: azurefile-csi volumeName: azurefile resources: requests: storage: 5Gi ``` 1. Create the PVC using the [`kubectl apply`][kubectl-apply] command. ```bash kubectl apply -f azurefiles-mount-options-pvc.yaml ``` 1. Verify your PVC is created and bound to the PV using the [`kubectl get`][kubectl-get] command. ```bash kubectl get pvc azurefile ``` Your output should resemble the following example output, which shows that the PVC is in a `Bound` state, and it's bound to the PV named _azurefile_: ```console NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE azurefile Bound azurefile 5Gi RWX azurefile 5s ``` 1. Update your container spec to reference your PVC and your pod in the YAML file. For example: ```yaml ... volumes: - name: azure persistentVolumeClaim: claimName: azurefile ``` 1. A pod spec can't be updated in place, so delete the pod using the [`kubectl delete`][kubectl-delete] command and recreate it using the [`kubectl apply`][kubectl-apply] command. ```bash kubectl delete pod mypod kubectl apply -f azure-files-pod.yaml ``` ### Mount file share as an inline volume > [!NOTE] > To avoid performance issue, we recommend you use a persistent volume instead of an inline volume when numerous pods are accessing the same file share. Inline volume can only access secrets in the same namespace as the pod. To specify a different secret namespace, use a [persistent volume][persistent-volume]. To mount the Azure Files file share into your pod, you configure the volume in the container spec. 1. Create a new file named `azure-files-pod.yaml` and copy in the following contents. If you changed the name of the file share or secret name, update the `shareName` and `secretName`. You can also update the `mountPath`, which is the path where the Files share is mounted in the pod. For Windows Server containers, specify a `mountPath` using the Windows path convention, such as _'D:'_. ```yaml apiVersion: v1 kind: Pod metadata: name: mypod spec: nodeSelector: kubernetes.io/os: linux containers: - image: 'mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine' name: mypod resources: requests: cpu: 100m memory: 128Mi limits: cpu: 250m memory: 256Mi volumeMounts: - name: azure mountPath: /mnt/azure readOnly: false volumes: - name: azure csi: driver: file.csi.azure.com volumeAttributes: secretName: azure-secret # required shareName: aksshare # required mountOptions: 'dir_mode=0755,file_mode=0755,uid=1000,gid=1000,cache=strict,actimeo=30,nosharesock,nobrl' # optional ``` 1. Create the pod using the [`kubectl apply`][kubectl-apply] command. ```bash kubectl apply -f azure-files-pod.yaml ``` 1. View the status of the pod using the [`kubectl describe`][kubectl-describe] command: ```bash kubectl describe pod mypod ``` ## Related content - [Use Azure tags in Azure Kubernetes Service (AKS)][use-tags] <!-- LINKS --> [kubernetes-secret]: https://kubernetes.io/docs/concepts/configuration/secret/ [smb-overview]: /windows/desktop/FileIO/microsoft-smb-protocol-and-cifs-protocol-overview [kubectl-apply]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#apply [kubectl-get]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#get [kubectl-create]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#create [data-plane-api]: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/storage/azblob [kubectl-describe]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#describe [kubectl-delete]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#delete [access-modes]: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes [azure-storage-account]: /azure/storage/common/storage-introduction [install-azure-cli]: /cli/azure/install-azure-cli [persistent-volume]: #mount-file-share-as-a-persistent-volume [use-tags]: use-tags.md [node-resource-group]: faq.yml [storage-skus]: /azure/storage/common/storage-redundancy [az-aks-show]: /cli/azure/aks#az-aks-show [az-storage-share-create]: /cli/azure/storage/share#az-storage-share-create [storage-tiers]: /azure/storage/files/storage-files-planning#storage-tiers [access-tiers-overview]: /azure/storage/blobs/access-tiers-overview [tag-resources]: /azure/azure-resource-manager/management/tag-resources [azure-files-usage]: /azure/storage/files/understand-performance#choosing-a-performance-tier-based-on-usage-patterns [az-storage-account-create]: /cli/azure/storage/account#az-storage-account-create [storage-account-private-endpoint]: /azure/storage/common/storage-private-endpoints [nfs-overview]:/windows-server/storage/nfs/nfs-overview [persistent-volume-claim-overview]: concepts-storage.md#persistent-volume-claims [azure-private-endpoint-dns]: /azure/private-link/private-endpoint-dns#azure-services-dns-zone-configuration [nfs-file-share-mount-options]: /azure/storage/files/storage-files-how-to-mount-nfs-shares#mount-options
Success! Branch created successfully. Create Pull Request on GitHub
Error: