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 POSIX group membership, and authentication method management.
Local users belong to storage accounts and reference file shares that must exist separately. The examples are intentionally small. Combine them with your own storage accounts, file shares, and security 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 Files 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
The sshAuthorizedKeys property defines public keys for authentication. The permissionScopes array grants access to specific file shares, where permissions uses a string of letters (r=read, w=write, d=delete), resourceName identifies the share, and service specifies “file” for Azure Files. Setting hasSshPassword to true enables password authentication alongside SSH keys.
Enable NFSv3 access with supplementary group membership
NFS workflows require POSIX-style group membership to control file system access.
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
The isNFSv3Enabled property activates NFSv3 protocol support. The extendedGroups array defines supplementary group IDs beyond the primary group, allowing the user to access files owned by multiple groups. This mirrors traditional Unix group membership for NFS mounts.
Disable authentication methods and update group membership
Security policies may require disabling authentication methods or updating group membership without recreating the user.
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: false,
extendedGroups: [
1001,
1005,
2005,
],
groupId: 3000,
hasSharedKey: false,
hasSshKey: false,
hasSshPassword: false,
homeDirectory: "homedirectory2",
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",
allow_acl_authorization=False,
extended_groups=[
1001,
1005,
2005,
],
group_id=3000,
has_shared_key=False,
has_ssh_key=False,
has_ssh_password=False,
home_directory="homedirectory2",
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"),
AllowAclAuthorization: pulumi.Bool(false),
ExtendedGroups: pulumi.IntArray{
pulumi.Int(1001),
pulumi.Int(1005),
pulumi.Int(2005),
},
GroupId: pulumi.Int(3000),
HasSharedKey: pulumi.Bool(false),
HasSshKey: pulumi.Bool(false),
HasSshPassword: pulumi.Bool(false),
HomeDirectory: pulumi.String("homedirectory2"),
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",
AllowAclAuthorization = false,
ExtendedGroups = new[]
{
1001,
1005,
2005,
},
GroupId = 3000,
HasSharedKey = false,
HasSshKey = false,
HasSshPassword = false,
HomeDirectory = "homedirectory2",
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")
.allowAclAuthorization(false)
.extendedGroups(
1001,
1005,
2005)
.groupId(3000)
.hasSharedKey(false)
.hasSshKey(false)
.hasSshPassword(false)
.homeDirectory("homedirectory2")
.isNFSv3Enabled(true)
.resourceGroupName("res6977")
.username("user1")
.build());
}
}
resources:
localUser:
type: azure-native:storage:LocalUser
properties:
accountName: sto2527
allowAclAuthorization: false
extendedGroups:
- 1001
- 1005
- 2005
groupId: 3000
hasSharedKey: false
hasSshKey: false
hasSshPassword: false
homeDirectory: homedirectory2
isNFSv3Enabled: true
resourceGroupName: res6977
username: user1
Setting hasSharedKey, hasSshKey, and hasSshPassword to false disables those authentication methods. The groupId property sets the primary POSIX group, while extendedGroups adds supplementary groups. The allowAclAuthorization property controls whether the user can use ACL-based authorization instead of permission scopes.
Beyond these examples
These snippets focus on specific local user features: SSH and NFSv3 authentication, file share permission scopes, and POSIX group membership. They’re intentionally minimal rather than full identity management solutions.
The examples reference pre-existing infrastructure such as storage accounts with SFTP or NFSv3 enabled, Azure Files shares, and resource groups. They focus on configuring the local user rather than provisioning the storage infrastructure.
To keep things focused, common local user patterns are omitted, including:
- Shared key generation and rotation (hasSharedKey)
- Home directory path configuration (homeDirectory)
- ACL authorization controls (allowAclAuthorization)
- SSH password management (hasSshPassword)
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
Configuration & Constraints
accountName, username, and resourceGroupName properties are immutable. Changing any of these requires recreating the resource.Authentication & Access
sshAuthorizedKeys with your SSH public keys, set hasSshPassword to true if using password authentication, and define permissionScopes to grant access to specific file shares.isNFSv3Enabled to true. You can optionally configure extendedGroups to specify supplementary group membership for the user.hasSshKey to remove SSH keys, hasSshPassword to remove SSH passwords, or hasSharedKey to remove shared keys.isNFSv3Enabled property and supports extendedGroups for group membership. SFTP access uses sshAuthorizedKeys and hasSshPassword for authentication. These are distinct access patterns for different protocols.Permissions & Authorization
permissionScopes property to define access. Each scope specifies a service (e.g., “file”), resourceName (the share name), and permissions (e.g., “rwd” for read/write/delete).