|
| 1 | +--- |
| 2 | +title: Getting started - Entra ID based access for Azure Blob Storage SFTP |
| 3 | +author: Jeevan Manoj |
| 4 | +ms.date: 02/24/2026 |
| 5 | +ms.author: normesta |
| 6 | + |
| 7 | +--- |
| 8 | +Getting started - Entra ID based access for Azure Blob Storage SFTP |
| 9 | + |
| 10 | +**This feature is currently in public preview and once enabled it is applicable across all storage accounts within the entire subscription. This feature is currently in public preview and once enabled it is applicable across all storage accounts within the entire subscription. ** |
| 11 | + |
| 12 | +Azure Blob Storage SFTP now supports Entra ID-based access in public preview. Previously, Azure Blob Storage SFTP only supported local user-based access, requiring either a password or an SSH private key for authentication. With this new feature, users can leverage their Entra ID or Entra External Identities to connect to Azure storage accounts via SFTP without the need to create and maintain local users. |
| 13 | + |
| 14 | +Entra ID-based access brings a host of benefits, including Role Based Access Control (RBAC), Multi-factor Authentication, and Entra ID Access Control Lists (ACLs) to Azure Blob Storage SFTP. |
| 15 | + |
| 16 | +# Key benefits |
| 17 | + |
| 18 | +1. Eliminate Local User Management |
| 19 | + |
| 20 | +With Entra ID-based access, there is no need to create, rotate, or maintain local SFTP users per storage account. Authentication is handled entirely by Entra ID, significantly reducing operational overhead and configuration sprawl |
| 21 | + |
| 22 | +1. Enterprisegrade Identity & Security |
| 23 | + |
| 24 | +SFTP access is backed by Entra ID, enabling: |
| 25 | + |
| 26 | +- Centralized identity lifecycle management |
| 27 | +- Strong authentication (including MFA via Entra ID) |
| 28 | +- Consistent security posture aligned with enterprise IAM standards |
| 29 | +- This improves security compared to static, longlived local credentials |
| 30 | + |
| 31 | +1. Native Azure RBAC, ABAC & ACL Integration |
| 32 | + |
| 33 | +Authorization for SFTP mirrors Azure Blob Storage's existing access control model: |
| 34 | + |
| 35 | +- RoleBased Access Control (RBAC) |
| 36 | +- AttributeBased Access Control (ABAC) |
| 37 | +- POSIXstyle Access Control Lists (ACLs) |
| 38 | +- Users can apply the same roles and permissions used for REST, SDK, and Portal access—now extended seamlessly to SFTP. |
| 39 | + |
| 40 | +1. Faster SFTP Onboarding |
| 41 | + |
| 42 | +Because Entra ID accounts are ubiquitous, users can: |
| 43 | + |
| 44 | +- Reuse existing users, groups, and service principals |
| 45 | +- Avoid timeconsuming local user creation and key distribution |
| 46 | +- Get SFTP up and running faster with fewer setup steps |
| 47 | +- This significantly shortens timetovalue for SFTPbased workflows. |
| 48 | + |
| 49 | +1. Secure External Collaboration |
| 50 | + |
| 51 | +Using Entra ID External Identities, customers can securely grant SFTP access to partners and vendors without managing separate identity systems—while maintaining full control and auditability. |
| 52 | + |
| 53 | +## Overview |
| 54 | + |
| 55 | +Below is a high-level overview of the key steps involved in this process. In summary, you'll first authenticate using Entra ID, then obtain an OpenSSH certificate, and finally connect to Azure Blob Storage SFTP using a compatible client or SDK. Each of these steps is outlined in more detail in the following sections. |
| 56 | + |
| 57 | + |
| 58 | + |
| 59 | +1. Authenticate with Entra ID via Azure CLI, PS, SDK etc. |
| 60 | +2. Get an OpenSSH Certificate from Entra ID by passing a public key |
| 61 | +3. Use any SFTP Client/SDK which supports OpenSSH certificates to connect to Azure Storage with the OpenSSH Certificate and the public key from step 2. |
| 62 | + |
| 63 | +> [!NOTE] |
| 64 | +> For Step 3 Password based authentication won't be supported since there are no SFTP clients that have native Entra ID integration to allow an Entra ID UX to accept the passwords. |
| 65 | +
|
| 66 | +:::image type="content" source="media/secure-file-transfer-protocol-support-entra-id-based-access/overview-flow-chart.png" alt-text="Flow chart demonstrating the Open SSH certificate workflow"::: |
| 67 | + |
| 68 | + |
| 69 | + |
| 70 | +# **Connecting to Azure Blob Storage with Entra IDs ** |
| 71 | + |
| 72 | +## **Register for the preview feature 'SFTP Entra ID Support' on your Azure subscription** |
| 73 | + |
| 74 | +You can register for public preview features by following this [guide](/azure/azure-resource-manager/management/preview-features). Looks for a preview feature named" SFTP Entra ID Support". |
| 75 | + |
| 76 | +## **Generate OpenSSH certificate** |
| 77 | + |
| 78 | +# [Azure CLI](#tab/azurecli) |
| 79 | + |
| 80 | +Generate the OpenSSH certificate with az cli [az sftp](/cli/azure/sftp) as below |
| 81 | + |
| 82 | + az login |
| 83 | + az sftp cert --file /my_cert.pub |
| 84 | + |
| 85 | +> [!NOTE] |
| 86 | +> The certificate will be valid only for 65 minutes for security reasons & the command will have to be rerun to obtain certificate again. |
| 87 | +
|
| 88 | +> [!NOTE] |
| 89 | +> Currently, retrieving SSH certificates is only supported with [az cli](/cli/azure/ssh) or Azure PowerShell. We do not yet have Azure Portal support for downloading SSH certificates. |
| 90 | +
|
| 91 | + |
| 92 | +Optionally, you can generate your own SSH key pair and use them while downloading the certificate as follows. |
| 93 | + |
| 94 | +Generate SSH key pair: You must use RSA keys, since Entra only supports generating RSA certificates. |
| 95 | + |
| 96 | + ssh-keygen -t rsa |
| 97 | +1. key files will be generated once the above is executed. They are: |
| 98 | + |
| 99 | +| **File Name** | **Key Type** | |
| 100 | +|---|---| |
| 101 | +| **id_rsa** | Private key | |
| 102 | +| **id_rsa.pub** | Public key | |
| 103 | + |
| 104 | + |
| 105 | +Use the command below to generate the SSH certificate with the keys generated |
| 106 | + |
| 107 | +`az login`<br>az sftp cert --public-key-file /id_rsa.pub --file /my_cert.pub |
| 108 | + |
| 109 | +If you are using a Service Principal, you can login either using a Client Secret or a Certificate: |
| 110 | + |
| 111 | + Certificate: |
| 112 | + az login --service-principal -u <application_id_or_client_id> --tenant <tenant_id> --certificate <path_to_certificate> |
| 113 | + Client Secret: |
| 114 | + az login --service-principal -u <application_id_or_client_id> -p <secret_value> --tenant <tenant_id> |
| 115 | + Afer this you can just run the same command to download the certificate: |
| 116 | +az sftp cert --public-key-file /id_rsa.pub --file /my_cert.pub |
| 117 | + |
| 118 | +# [Azure PowerShell](#tab/azurepowershell) |
| 119 | + |
| 120 | +Generate the OpenSSH certificate with [PowersShell Az.Sftp](https://www.powershellgallery.com/packages/Az.Sftp/0.1.0) as below |
| 121 | + |
| 122 | + Connect-AzAccount |
| 123 | + New-AzSftpCertificate -CertificatePath "\my_cert.cert" |
| 124 | +Optionally, use the command below to generate the OpenSSH certificate with your SSH keys |
| 125 | + |
| 126 | + New-AzSftpCertificate -PublicKeyFile "\id_rsa.pub" -CertificatePath "\my_cert.cert" |
| 127 | +Learn more about the PowerShell module [here](/powershell/module/az.sftp/). |
| 128 | + |
| 129 | +> [!NOTE] |
| 130 | +> Powershell currently does not support Service Principals and managed Identity Sign ins. |
| 131 | +
|
| 132 | +### MSAL.NET |
| 133 | + |
| 134 | +```dotnetcli |
| 135 | +using Microsoft.Identity.Client; |
| 136 | +using Microsoft.Identity.Client.SSHCertificates; |
| 137 | +using Newtonsoft.Json; |
| 138 | +using System.Security.Cryptography; |
| 139 | +using System.Text; |
| 140 | +public class Program |
| 141 | +{ |
| 142 | + private const string AZURE_CLI_CLIENT_ID = "<your-azure-cli-client-id>"; |
| 143 | + private const string MY_TENANT_ID = "<your-tenant-id>"; |
| 144 | + public static async Task Main(string[] args) |
| 145 | + { |
| 146 | + var options = new PublicClientApplicationOptions |
| 147 | + { |
| 148 | + ClientId = AZURE_CLI_CLIENT_ID, |
| 149 | + }; |
| 150 | + var app = PublicClientApplicationBuilder.CreateWithApplicationOptions(options) |
| 151 | + .WithTenantId(MY_TENANT_ID) |
| 152 | + .WithDefaultRedirectUri() |
| 153 | + .Build(); |
| 154 | + var scopes = new string[] |
| 155 | + { |
| 156 | + "`<https://pas.windows.net/CheckMyAccess/Linux/.default>`", |
| 157 | + }; |
| 158 | + var keyId = new byte[32]; |
| 159 | + Random.Shared.NextBytes(keyId); |
| 160 | + var rsa = RSA.Create(); |
| 161 | + var key = rsa.ExportParameters(includePrivateParameters: true); |
| 162 | + if (key.Modulus == null || key.Exponent == null) |
| 163 | + throw new InvalidOperationException("RSA key generation failed: Modulus or Exponent is null."); |
| 164 | + var localKey = new |
| 165 | + { |
| 166 | + kty = "RSA", |
| 167 | + n = Convert.ToBase64String(key.Modulus).Replace("+", "-").Replace("/", "_"), |
| 168 | + e = Convert.ToBase64String(key.Exponent).Replace("+", "-").Replace("/", "_"), |
| 169 | + kid = BitConverter.ToString(keyId).Replace("-", string.Empty).ToLower(), |
| 170 | + }; |
| 171 | + var localKeyJson = JsonConvert.SerializeObject(localKey); |
| 172 | + Console.WriteLine("RSA Key:"); |
| 173 | + Console.WriteLine(localKeyJson); |
| 174 | + Console.WriteLine(); |
| 175 | +
|
| 176 | + // Get SSH certificate |
| 177 | +
|
| 178 | + AuthenticationResult result = await app.AcquireTokenInteractive(scopes) |
| 179 | + .WithSSHCertificateAuthenticationScheme(localKeyJson, localKey.kid) |
| 180 | + .ExecuteAsync(); |
| 181 | +
|
| 182 | + // Define output directory and certificate path |
| 183 | +
|
| 184 | + var sshDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".ssh", "entra"); |
| 185 | + Directory.CreateDirectory(sshDir); |
| 186 | + var certPath = Path.Combine(sshDir, "id_rsa-cert.pub"); |
| 187 | +
|
| 188 | + // Remove read-only attribute if certificate already exists so it can be overwritten |
| 189 | +
|
| 190 | + if (File.Exists(certPath)) |
| 191 | + { |
| 192 | + File.SetAttributes(certPath, FileAttributes.Normal); |
| 193 | + } |
| 194 | + // Save the certificate |
| 195 | + var cert = `[$"[email protected]](mailto:$%[email protected])` {result.AccessToken}"; |
| 196 | + await File.WriteAllTextAsync(certPath, cert); |
| 197 | + File.SetAttributes(certPath, FileAttributes.ReadOnly); |
| 198 | + // Dump certificate content to console |
| 199 | + Console.WriteLine("Cert"); |
| 200 | + Console.WriteLine(cert); |
| 201 | + Console.WriteLine(); |
| 202 | + } |
| 203 | +} |
| 204 | +``` |
| 205 | + |
| 206 | +## Verify the contents of the OpenSSH certificate [Optional] |
| 207 | + |
| 208 | +Use the following command to view the OpenSSH certificate. |
| 209 | + |
| 210 | +`ssh-keygen -L -f my_cert.pub` |
| 211 | +Username is captured in the _Principals_ section highlighted in red |
| 212 | + |
| 213 | +:::image type="content" source="media/secure-file-transfer-protocol-support-entra-id-based-access/verify-opensshcert.jpg" alt-text="Screenshot of the OpenSSH certificate output showing the Principals section highlighted in red."::: |
| 214 | + |
| 215 | +For security reasons, the OpenSSH certificate is valid for 65 minutes. After this period, you will need to request a new certificate to initiate any further transactions. For security reasons, the OpenSSH certificate is valid for 65 minutes. After this period, you will need to request a new certificate to initiate any further transactions. |
| 216 | + |
| 217 | +## Connect to the Storage Account with OpenSSH |
| 218 | + |
| 219 | +### SFTP command |
| 220 | + |
| 221 | + |
| 222 | + C:\Users\username> sftp -o PubkeyAcceptedKeyTypes="[email protected],rsa-sha2-256" -o IdentityFile="C:\path\to\key\.ssh\id_rsa" -o CertificateFile="C:\path\to\cert\.ssh\my_cert.pub" storageaccountname.username@storageaccountname.blob.core.windows.net |
| 223 | + Connected to storageaccountname.blob.core.windows.net. |
| 224 | + sftp> |
| 225 | + |
| 226 | +> [!NOTE] |
| 227 | +> If the Principal is in the format [[email protected]](mailto:[email protected]), make sure you don't add the domain section in the command and only use the username portion. |
| 228 | +
|
| 229 | +> [!NOTE] |
| 230 | +> [Both User and Service principals](/entra/identity-platform/app-objects-and-service-principals) are supported. In the case of Service principals, use the service principal id in place of the username in the connection string. |
| 231 | +
|
| 232 | +> [!NOTE] |
| 233 | +> Adding the container name directly into the connection string or setting it up via Home Directory is currently NOT supported. |
| 234 | +
|
| 235 | +Once connected, use the following to upload a file to the Azure Storage via SFTP. |
| 236 | + |
| 237 | +`sftp> put 'C:\path\to\blob\blog.jpeg'` |
| 238 | + |
| 239 | +If you get a permission denied error, please make sure you have the necessary RBAC roles such as Storage Blob Data Contributor or Storage Blob Data Owner |
| 240 | + |
| 241 | +### SFTP desktop clients |
| 242 | + |
| 243 | +OpenSSH based login is supported by SFTP clients such as WinSCP and PuTTY. Steps to connect via WinSCP below. |
| 244 | + |
| 245 | +1. WinSCP: Support for OpenSSH certificates for user authentication was implemented in version 6.0. (<https://winscp.net/tracker/1873>) |
| 246 | +2. Obtain the OpenSSH certificate from step 3 above (3. Generate OpenSSH certificate) |
| 247 | +3. In WinSCP enter the Host name Username and click on Advanced |
| 248 | + |
| 249 | +:::image type="content" source="media/secure-file-transfer-protocol-support-entra-id-based-access/winscp-login.png" alt-text="Screenshot of the WinSCP login dialog showing the Host name, Username fields, and the Advanced button."::: |
| 250 | + |
| 251 | +1. Navigate to the Authentication section in the SSH Tab on the left and attach the Private Key and Certificate files obtained from the earlier sections and Click 'Ok.' |
| 252 | + |
| 253 | +:::image type="content" source="media/secure-file-transfer-protocol-support-entra-id-based-access/winscp-advanced-settings.png" alt-text="Screenshot of the WinSCP Advanced Site Settings dialog showing the Authentication section with Private Key and Certificate file fields."::: |
| 254 | + |
| 255 | +1. Click 'Login' to Login with the Entra ID account and OpenSSH certificate |
| 256 | + |
| 257 | +:::image type="content" ssource="media/secure-file-transfer-protocol-support-entra-id-based-access/winscp-login-highlight.png" alt-text="Screenshot of the WinSCP login dialog with the Login button to connect using the Entra ID account and OpenSSH certificate."::: |
| 258 | + |
| 259 | +# [Azure CLI](#tab/azurecli) |
| 260 | + |
| 261 | +Use the following command to connect by using the OpenSSH certificate obtained in the previous steps |
| 262 | + |
| 263 | +az sftp connect --storage-account `<<account_name>>` --certificate-file /my_cert.pub |
| 264 | + |
| 265 | +Additionally, you can also get the OpenSSH certificate and connect to SFTP with a single command as follows |
| 266 | + |
| 267 | +`az sftp connect` |
| 268 | +`az sftp connect --storage-account <<account_name>>` |
| 269 | + |
| 270 | +More information regarding the commands, see [here](/cli/azure/sftp) |
| 271 | + |
| 272 | +# [Azure PowerShell](#tab/azurepowershell) |
| 273 | + |
| 274 | +Use the following command to connect by using the OpenSSH certificate obtained in the previous steps |
| 275 | + |
| 276 | +Connect-AzSftp -StorageAccount `"<<account_name>>"` -CertificateFile "/my_cert.pub" |
| 277 | + |
| 278 | +Additionally, you can also get the OpenSSH certificate and connect to SFTP with a single command as follows |
| 279 | + |
| 280 | +Connect-AzAccount |
| 281 | +Connect-AzSftp –StorageAccount "<<account_name>>" |
| 282 | + |
| 283 | +More information regarding the commands, see [here](/powershell/module/az.sftp/connect-azsftp) |
| 284 | + |
| 285 | +## Entra ID based access control model in Azure Blob Storage SFTP |
| 286 | + |
| 287 | +| **Mechanism** | **Status** | **Tutorial** | |
| 288 | +|---|---|---| |
| 289 | +| Role-based access control (Azure RBAC) | Supported | [Access control model for Azure Data Lake Storage - Azure Storage | Microsoft Learn](/azure/storage/blobs/data-lake-storage-access-control-model) | |
| 290 | +| Access control lists (ACLs) | Supported | [Access control model for Azure Data Lake Storage - Azure Storage | Microsoft Learn](/azure/storage/blobs/data-lake-storage-access-control-model) | |
| 291 | +| Attribute-based access control (Azure ABAC) | Not supported in private preview. If any ABAC rule exists, for SFTP it will be ignored. | | |
| 292 | + |
| 293 | +## How permissions are evaluated |
| 294 | + |
| 295 | +**SFTP mirrors the Azure Blob Storage's access control explained** [**here**](/azure/storage/blobs/data-lake-storage-access-control-model)** except that during the private preview, ABAC support is partial. Learn more in the Known issues and limitations section. ** |
| 296 | + |
| 297 | +## Sharing access to users outside of the home Entra ID tenant |
| 298 | + |
| 299 | +Organizations often need to share Azure Blob Storage SFTP access with external partners and customers. Entra External Identities can address this requirement by allowing Azure Blob Storage SFTP to provide secure access to external collaborators. This feature enables efficient and secure connections and interactions with storage resources. By using Entra ID External Identity capabilities, organizations can maintain strong access control and security measures while enabling collaboration with external entities. Learn more [here](/entra/external-id/b2b-quickstart-add-guest-users-portal). |
| 300 | + |
| 301 | + |
| 302 | +## Known issues & limitations |
| 303 | + |
| 304 | +Entra ID support is limited to SSH certificates and public key authentication. |
| 305 | + |
| 306 | +Only RSA certificates are supported. ECDSA is not supported. |
| 307 | + |
| 308 | +1. [ABAC](/azure/storage/blobs/storage-auth-abac-attributes) behavior is inconsistent when using with the Storage Blob Data Owner role (RBAC) and may lead to timeout errors. To use ABAC, choose the Storage Blob Data Contributor role, or use the Storage Blob Data Owner role without ABAC. |
| 309 | + |
| 310 | +ABAC [sub-operations](/azure/storage/blobs/storage-auth-abac-attributes) are unsupported and will behave incorrectly. Specific behaviours of the sub operations are listed below. |
| 311 | + |
| 312 | +List blobs (Blob.List): Users can list Blobs without any restrictions, and the ABAC condition expression(s) are ignored. |
| 313 | + |
| 314 | +Read a blob (NOT Blob.List): Works as expected on the given ABAC condition expression(s). However, for all the other cases, List blobs (Blob.List) action will also inadvertently fail in addition to the expected failure of Read a blob (NOT Blob.List). |
| 315 | + |
| 316 | +_(Deprecated)_ Read content from a blob with tag conditions (Blob.Read.WithTagConditions): The ABAC condition expression(s) are ignored. |
| 317 | + |
| 318 | +Sets the access tier on a blob (Blob.Write.Tier): The ABAC condition expression(s) are ignored. |
| 319 | + |
| 320 | +Write to a blob with blob index tag (Blob.Write.WithTagHeaders): The ABAC condition expression(s) are ignored. |
| 321 | + |
| 322 | +Setting a home directory is not supported. |
| 323 | + |
| 324 | +The connection string cannot include the container name. The user will connect to the root of the Storage Account and then navigate to the destination container and directories with 'change directory' (cd) commands. |
| 325 | + |
| 326 | +Currently, `chown` & `chgrp` require either superuser and manage ownership, or manage ownership, read, and write. In the future, only manage ownership or superuser roles will suffice. |
| 327 | + |
| 328 | +For `chmod`, the current requirement is either superuser and modify permissions, or modify permissions, read, and write. It will later require only modify permissions or superuser. |
| 329 | + |
| 330 | +## Troubleshooting |
| 331 | + |
| 332 | +- Connections to Storage Accounts via WinSCP works and the list of containers are visible after logging in. However, opening any container fails with Access denied |
| 333 | + - **Why**: WinSCP automatically tries to **canonicalize every directory** it enters. That means — for _every_ `cd` or directory listing, it sends one or more extra protocol requests to figure out the "true" absolute path. |
| 334 | + - The **root directory** shows _containers_ |
| 335 | + - Each container is **a virtual chroot** — once inside it, you can't go above or outside it. |
| 336 | + - Paths are **virtual**, not physical, and Azure doesn't support `/`-based absolute traversal above containers. |
| 337 | + - **Fix**: There are two options to resolve this issue |
| 338 | + - Disable "Resolve Symbolic Links" |
| 339 | + - Advanced->Environment->Directories -> Untick "Resolve Symbolic Links" |
| 340 | + - Set Remote Directory |
| 341 | + - Advanced->Environment->Directories -> Set "Remote Directory" to "\\<container-name>" |
| 342 | + - By setting this you will directly enter the specified container after logging in |
0 commit comments