The azure-native:storage:LocalUser resource, part of the Pulumi Azure Native provider, defines local users for Azure Storage accounts, controlling authentication methods and access permissions for SFTP and NFSv3 protocols. This guide focuses on three capabilities: SSH key authentication for SFTP, NFSv3 access with Unix-style groups, and permission scoping to file shares.
Local users belong to storage accounts and reference file shares or containers that must exist separately. The examples are intentionally small. Combine them with your own storage accounts and access control policies.
Create a user with SSH access and file share permissions
Teams building SFTP workflows provision users who authenticate with SSH keys and access specific Azure File shares with granular permissions.
import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";
const localUser = new azure_native.storage.LocalUser("localUser", {
accountName: "sto2527",
allowAclAuthorization: true,
groupId: 2000,
hasSshPassword: true,
homeDirectory: "homedirectory",
permissionScopes: [
{
permissions: "rwd",
resourceName: "share1",
service: "file",
},
{
permissions: "rw",
resourceName: "share2",
service: "file",
},
],
resourceGroupName: "res6977",
sshAuthorizedKeys: [{
description: "key name",
key: "ssh-rsa keykeykeykeykey=",
}],
username: "user1",
});
import pulumi
import pulumi_azure_native as azure_native
local_user = azure_native.storage.LocalUser("localUser",
account_name="sto2527",
allow_acl_authorization=True,
group_id=2000,
has_ssh_password=True,
home_directory="homedirectory",
permission_scopes=[
{
"permissions": "rwd",
"resource_name": "share1",
"service": "file",
},
{
"permissions": "rw",
"resource_name": "share2",
"service": "file",
},
],
resource_group_name="res6977",
ssh_authorized_keys=[{
"description": "key name",
"key": "ssh-rsa keykeykeykeykey=",
}],
username="user1")
package main
import (
storage "github.com/pulumi/pulumi-azure-native-sdk/storage/v3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := storage.NewLocalUser(ctx, "localUser", &storage.LocalUserArgs{
AccountName: pulumi.String("sto2527"),
AllowAclAuthorization: pulumi.Bool(true),
GroupId: pulumi.Int(2000),
HasSshPassword: pulumi.Bool(true),
HomeDirectory: pulumi.String("homedirectory"),
PermissionScopes: storage.PermissionScopeArray{
&storage.PermissionScopeArgs{
Permissions: pulumi.String("rwd"),
ResourceName: pulumi.String("share1"),
Service: pulumi.String("file"),
},
&storage.PermissionScopeArgs{
Permissions: pulumi.String("rw"),
ResourceName: pulumi.String("share2"),
Service: pulumi.String("file"),
},
},
ResourceGroupName: pulumi.String("res6977"),
SshAuthorizedKeys: storage.SshPublicKeyArray{
&storage.SshPublicKeyArgs{
Description: pulumi.String("key name"),
Key: pulumi.String("ssh-rsa keykeykeykeykey="),
},
},
Username: pulumi.String("user1"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;
return await Deployment.RunAsync(() =>
{
var localUser = new AzureNative.Storage.LocalUser("localUser", new()
{
AccountName = "sto2527",
AllowAclAuthorization = true,
GroupId = 2000,
HasSshPassword = true,
HomeDirectory = "homedirectory",
PermissionScopes = new[]
{
new AzureNative.Storage.Inputs.PermissionScopeArgs
{
Permissions = "rwd",
ResourceName = "share1",
Service = "file",
},
new AzureNative.Storage.Inputs.PermissionScopeArgs
{
Permissions = "rw",
ResourceName = "share2",
Service = "file",
},
},
ResourceGroupName = "res6977",
SshAuthorizedKeys = new[]
{
new AzureNative.Storage.Inputs.SshPublicKeyArgs
{
Description = "key name",
Key = "ssh-rsa keykeykeykeykey=",
},
},
Username = "user1",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.storage.LocalUser;
import com.pulumi.azurenative.storage.LocalUserArgs;
import com.pulumi.azurenative.storage.inputs.PermissionScopeArgs;
import com.pulumi.azurenative.storage.inputs.SshPublicKeyArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var localUser = new LocalUser("localUser", LocalUserArgs.builder()
.accountName("sto2527")
.allowAclAuthorization(true)
.groupId(2000)
.hasSshPassword(true)
.homeDirectory("homedirectory")
.permissionScopes(
PermissionScopeArgs.builder()
.permissions("rwd")
.resourceName("share1")
.service("file")
.build(),
PermissionScopeArgs.builder()
.permissions("rw")
.resourceName("share2")
.service("file")
.build())
.resourceGroupName("res6977")
.sshAuthorizedKeys(SshPublicKeyArgs.builder()
.description("key name")
.key("ssh-rsa keykeykeykeykey=")
.build())
.username("user1")
.build());
}
}
resources:
localUser:
type: azure-native:storage:LocalUser
properties:
accountName: sto2527
allowAclAuthorization: true
groupId: 2000
hasSshPassword: true
homeDirectory: homedirectory
permissionScopes:
- permissions: rwd
resourceName: share1
service: file
- permissions: rw
resourceName: share2
service: file
resourceGroupName: res6977
sshAuthorizedKeys:
- description: key name
key: ssh-rsa keykeykeykeykey=
username: user1
When a user connects via SFTP, Azure validates their SSH key against sshAuthorizedKeys and enforces permissions defined in permissionScopes. Each scope targets a specific file share (resourceName) and grants read, write, or delete access (permissions: “rwd” or “rw”). The hasSshPassword property enables password authentication alongside SSH keys, and homeDirectory sets the user’s landing directory after login.
Enable NFSv3 access with supplementary group membership
NFS workflows require POSIX-style group membership to control file system access based on Unix permissions.
import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";
const localUser = new azure_native.storage.LocalUser("localUser", {
accountName: "sto2527",
extendedGroups: [
1001,
1005,
2005,
],
isNFSv3Enabled: true,
resourceGroupName: "res6977",
username: "user1",
});
import pulumi
import pulumi_azure_native as azure_native
local_user = azure_native.storage.LocalUser("localUser",
account_name="sto2527",
extended_groups=[
1001,
1005,
2005,
],
is_nf_sv3_enabled=True,
resource_group_name="res6977",
username="user1")
package main
import (
storage "github.com/pulumi/pulumi-azure-native-sdk/storage/v3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := storage.NewLocalUser(ctx, "localUser", &storage.LocalUserArgs{
AccountName: pulumi.String("sto2527"),
ExtendedGroups: pulumi.IntArray{
pulumi.Int(1001),
pulumi.Int(1005),
pulumi.Int(2005),
},
IsNFSv3Enabled: pulumi.Bool(true),
ResourceGroupName: pulumi.String("res6977"),
Username: pulumi.String("user1"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;
return await Deployment.RunAsync(() =>
{
var localUser = new AzureNative.Storage.LocalUser("localUser", new()
{
AccountName = "sto2527",
ExtendedGroups = new[]
{
1001,
1005,
2005,
},
IsNFSv3Enabled = true,
ResourceGroupName = "res6977",
Username = "user1",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.storage.LocalUser;
import com.pulumi.azurenative.storage.LocalUserArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var localUser = new LocalUser("localUser", LocalUserArgs.builder()
.accountName("sto2527")
.extendedGroups(
1001,
1005,
2005)
.isNFSv3Enabled(true)
.resourceGroupName("res6977")
.username("user1")
.build());
}
}
resources:
localUser:
type: azure-native:storage:LocalUser
properties:
accountName: sto2527
extendedGroups:
- 1001
- 1005
- 2005
isNFSv3Enabled: true
resourceGroupName: res6977
username: user1
Setting isNFSv3Enabled to true allows the user to mount storage via NFSv3 protocol. The extendedGroups property assigns supplementary group IDs (1001, 1005, 2005) that determine file system access rights. This mirrors traditional Unix group membership, where a user belongs to multiple groups for different permission levels.
Beyond these examples
These snippets focus on specific local user features: SSH key authentication and password management, NFSv3 protocol access with group membership, and file share permission scoping. They’re intentionally minimal rather than full access control configurations.
The examples reference pre-existing infrastructure such as storage accounts with SFTP or NFSv3 enabled, and Azure File shares or blob containers. They focus on configuring the user rather than provisioning the storage infrastructure.
To keep things focused, common local user patterns are omitted, including:
- ACL authorization (allowAclAuthorization)
- Shared key authentication (hasSharedKey)
- Primary group ID configuration (groupId)
- Blob container permission scopes
These omissions are intentional: the goal is to illustrate how each local user feature is wired, not provide drop-in identity modules. See the LocalUser resource reference for all available configuration options.
Let's configure Azure Storage Local Users
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Resource Management & Updates
username, accountName, and resourceGroupName properties are immutable. Changing any of these requires recreating the resource.hasSshPassword, hasSshKey, or hasSharedKey. This removes the existing credential without recreating the user.Authentication & Authorization
hasSshPassword), SSH key (hasSshKey), and shared key (hasSharedKey). You can enable multiple methods simultaneously.hasSshKey to true and provide SSH public keys in the sshAuthorizedKeys array. Each key can include a description and the public key string.allowAclAuthorization to false disallows ACL-based authorization for the user, restricting access to only the configured permissionScopes.Access Protocols
isNFSv3Enabled set to true and use extendedGroups for supplementary group membership. SFTP users use SSH-based authentication (hasSshPassword or sshAuthorizedKeys) and don’t require NFSv3 enablement.Permissions & File Access
permissionScopes with an array of objects specifying permissions (r, w, d combinations), resourceName (share name), and service (typically “file”).