Skip to content

winnet

Windows NetAPI and related bindings: local accounts, groups, SMB shares and connections, and domain membership.

This API is VFS-aware: every operation goes through the VFS in scope for the current strand, so it works transparently under remote and elevated Windows contexts.

Domain join

join_domain, unjoin_domain, rename_machine and apply_offline_join change how the machine is identified on the network. Every one of them takes effect only after the machine is restarted, so a provisioning script must sequence a reboot before the change is usable. None of them return a status; use join_status to read the recorded membership.

provision_computer and apply_offline_join split a join in two: the first runs where a domain controller is reachable and mints a blob, the second applies that blob to a Windows installation that has never talked to the domain. This is the path for image-based and first-boot provisioning, where the online join would need network, a reachable domain controller and credentials at exactly the wrong moment.

Types

TypeDescription
AccountPolicy The local password and account-lockout policy.
Connection A connection to a remote SMB resource.
ConnectionInfo A snapshot of an SMB client connection.
Group A Windows local group, identified by immutable SID.
GroupInfo A snapshot of a Windows local group.
JoinStatus The machine's workgroup or domain membership.
MachineInfo Machine identity and server role.
ServerType Native Windows server-role flags.
Share A local SMB share, identified by its immutable name.
ShareInfo A snapshot of a local SMB share.
User A Windows local user, identified by immutable SID.
UserFlags Native Windows user-account flags.
UserInfo A snapshot of a Windows user account's mutable state.
ConnectionKind The kind of resource a network connection reaches.
ShareKind The kind of resource a share exposes.

ConnectionKind = (:DISK: | :PRINT: | :ANY:)

The kind of resource a network connection reaches.

ShareKind = (:DISKTREE: | :PRINTQ: | :DEVICE: | :IPC:)

The kind of resource a share exposes.

Symbol Resource
:DISKTREE: Disk drive
:PRINTQ: Print queue
:DEVICE: Communication device
:IPC: Interprocess communication

Functions

account_policy() -> AccountPolicy

Reads the local password and lockout policy.

apply_offline_join blob :windows_path …

Applies a provisioning blob to a Windows installation, joining it to the domain without contacting a domain controller.

Takes effect only after the target installation is started or restarted -- see Domain join.

Parameters

NameTypeDescription
blob Bin Blob from provision_computer.
:windows_path fs.Path Windows directory of the installation to modify -- the mounted image's Windows directory for an offline image, or the running system's own when online is set.
:online? Bool The target installation is the running system.

Example

let blob = fs.Path("ws01.blob").read "b"
winnet.apply_offline_join $blob
  windows_path: (fs.windows.Path r"D:\Windows")

connect remote … -> Connection

Connects to a remote SMB resource, optionally redirecting a local device.

With a trailing func the connection is scoped: it is disconnected when func returns, including when func throws, so a failure partway through does not leave a mapping behind. Without one the connection is returned and the caller decides when -- or whether -- to disconnect it.

Connections belong to a logon session. One made under a given account is not visible to a process running as another.

Parameters

NameTypeDescription
remote Str Remote resource in UNC form, such as r"\\server\share".
:local? Str Local device to redirect, such as "Z:".
:user? Str Account to authenticate as.
:password? Str Password for user.
:kind? ConnectionKind Resource kind.
:persistent? Bool Record the connection so it is restored at logon.
:save_credentials? Bool Override whether credentials are saved for the server.
:persistent

A persistent connection must redirect a local device: Windows only remembers connections that redirect one. Asking for persistent without local raises ValueError rather than silently producing a mapping that disappears at the next logon.

:save_credentials

Supplied credentials are saved for the target server by default when the connection is persistent, because a restored connection has nothing to authenticate with otherwise. false suppresses that; true saves them for a non-persistent connection. Saving requires user.

Saved credentials are per-account, the same way the connection itself is, so a connection provisioned for a service account must be made under that account to be restored for it.

Errors

Exception Condition
ValueError persistent requested without local
sys.AlreadyExistsError The local device is already redirected
sys.PermissionDeniedError Credentials were rejected
sys.NotFoundError The remote resource does not exist

Example

# Scoped: disconnected however the block exits
connect \\build\artifacts local: Z: do |conn|
  fs.copy $release $conn.path()

# Unbound: the caller owns teardown
let conn = connect r"\\build\artifacts"
try
  publish $conn.remote
finally
  conn.disconnect()

# Persistent, surviving a reboot
connect \\build\artifacts
  local: Z:
  user: r"CORP\ci"
  password: $secret
  persistent: true

connect[R] remote func … -> R

Connects to a network resource and calls func with the connection, which is disconnected when func returns.

Parameters

NameTypeDescription
remote Str Remote resource in UNC form, such as r"\\server\share".
:local? Str Local device to redirect, such as "Z:".
:user? Str Account to authenticate as.
:password? Str Password for user.
:kind? ConnectionKind Resource kind.
:persistent? Bool Record the connection so it is restored at logon.
:save_credentials? Bool Override whether credentials are saved for the server.
func ((Connection) -> R) Called with the Connection.

connection name_or_info -> Connection

Obtains a handle to an existing connection.

Parameters

NameTypeDescription
name_or_info (Str | ConnectionInfo) Local device, remote name, or a prior snapshot.

connections() -> Iter[ConnectionInfo]

Lists every connection, including ones saved in the profile that are not currently connected.

Example

for entry = connections()
  echo $entry.local $entry.remote $entry.state

create_group name … -> Group

Creates a local group.

Parameters

NameTypeDescription
name Str Group name.
:comment? Str Administrative comment.

create_share name :path … -> Share

Creates a local SMB share.

Parameters

NameTypeDescription
name Str Share name.
:path fs.Path Local path to share.
:kind? ShareKind Resource kind. Defaults to :DISKTREE:.
:comment? Str Comment.
:max_uses? Int Connection limit. Defaults to unlimited.
:special? Bool Mark the share special.
:temporary? Bool Mark the share temporary.
:sec_desc? (security.windows.SecDesc | Bin | Dict[{...}]) Security descriptor, self-relative packet, or declarative descriptor. Defaults to the one Windows supplies.

create_user name :password … -> User

Creates a normal enabled local user.

Parameters

NameTypeDescription
name Str Account name.
:password Str Initial password.
:full_name? Str Display name.
:comment? Str Administrative account comment.
:user_comment? Str User-facing account comment.
:home_dir? fs.Path Home directory.
:home_dir_drive? Str Drive designator assigned to the home directory at logon, such as "Z:".
:profile? fs.Path Profile path.
:script_path? fs.Path Logon script path.
:account_expires? (time.DateTime | nil) Expiration instant. nil means the account never expires.
:disabled? Bool Whether the account is disabled.
:password_never_expires? Bool Whether the password is exempt from expiry.
:password_cannot_change? Bool Whether the user may change the password.

Example

let user = create_user build-user password: $password
  full_name: "Build User"
  comment: "Automation account"
  disabled: true
  password_never_expires: true

group principal -> Group

Obtains a handle to an existing local group.

Parameters

NameTypeDescription
principal (Str | security.windows.Sid | GroupInfo) Group name, SID, or a prior snapshot.

groups() -> Iter[GroupInfo]

Lists local groups.

join_domain domain …

Joins the machine to a domain.

Takes effect only after a restart -- see Domain join.

Supply either account and password or machine_password; the two are mutually exclusive, because joining with a pre-created computer account password leaves no room for a user account.

Parameters

NameTypeDescription
domain Str Domain to join.
:ou? Str Organizational unit for the computer account.
:account? Str Domain account authorized to join, such as "CORP\\joiner". Must be given together with password; omit both to use the caller's own credentials.
:password? Str Password for account.
:machine_password? Str Password of a pre-created computer account, used instead of account and password.
:create_account? Bool Create the computer account as part of the join.
:join_if_joined? Bool Proceed even if already joined to a domain.
:unsecure? Bool Perform an unsecured join.
:defer_spn? Bool Defer setting the service principal name until a later rename.
:force_spn? Bool Force the service principal name to be set.
:dc_account? Bool Join using a domain controller account.
:with_new_name? Bool Join under a rename that has not taken effect yet.
:readonly? Bool Join against a read-only domain controller.
:ambiguous_dc? Bool Allow an ambiguous domain controller name.
:no_netlogon_cache? Bool Do not write the netlogon cache.
:no_account_reuse? Bool Fail rather than reuse an existing computer account.

Errors

Exception Condition
sys.AlreadyExistsError Already joined and join_if_joined was not set
sys.InvalidInputError Credentials are incomplete, or both credential forms given
sys.PermissionDeniedError Caller may not join the machine to the domain

Example

winnet.join_domain corp.example.com
  ou: "OU=Servers,DC=corp,DC=example,DC=com"
  account: r"CORP\joiner"
  password: $password
  create_account: true
echo "restart to complete the join"

join_status() -> JoinStatus

Reads the machine's current workgroup or domain membership.

machine_info() -> MachineInfo

Reads the computer name, domain membership, OS level and server role.

provision_computer domain :machine … -> Bin

Creates a computer account in the domain and returns the blob that joins a machine to it.

Run this where a domain controller is reachable, then apply the blob with apply_offline_join.

The blob contains the computer account password. Treat it as a secret: anyone holding it can join a machine as that account.

Parameters

NameTypeDescription
domain Str Domain to create the computer account in.
:machine Str Computer name to provision.
:ou? Str Organizational unit for the computer account.
:dc? Str Domain controller to provision against.
:reuse? Bool Reuse an existing computer account.
:default_password? Bool Use the default computer account password.
:skip_account_search? Bool Skip searching for an existing account.
:root_ca_certs? Bool Include root CA certificates in the blob.
:downlevel_priv_support? Bool Support provisioning by a down-level privileged user.

Example

let blob = winnet.provision_computer corp.example.com
  machine: WS01
  ou: "OU=Workstations,DC=corp,DC=example,DC=com"
fs.Path("ws01.blob").write $blob

rename_machine name …

Renames the machine within its domain.

Takes effect only after a restart -- see Domain join.

Parameters

NameTypeDescription
name Str New computer name.
:account? Str Domain account authorized to rename. Must be given together with password; omit both to use the caller's own credentials.
:password? Str Password for account.
:create_account? Bool Create the renamed computer account if it is missing.

share name_or_info -> Share

Obtains a handle to an existing local SMB share.

Parameters

NameTypeDescription
name_or_info (Str | ShareInfo) Share name or a prior snapshot.

shares() -> Iter[ShareInfo]

Lists every local SMB share, administrative and non-disk shares included.

universal_name path -> Str

Resolves a path on a redirected device to its UNC form.

Parameters

NameTypeDescription
path fs.Path Path on a redirected device.

Example

assert_eq (universal_name (Path r"Z:\release")) r"\\build\artifacts\release"

unjoin_domain …

Removes the machine from its domain.

Takes effect only after a restart -- see Domain join.

Parameters

NameTypeDescription
:account? Str Domain account authorized to unjoin. Must be given together with password; omit both to use the caller's own credentials.
:password? Str Password for account.
:delete_account? Bool Disable the computer account in the domain.

Errors

Exception Condition
sys.NotFoundError The machine is not joined to a domain
sys.InvalidInputError The machine is a domain controller
sys.PermissionDeniedError Caller may not unjoin the machine

update_account_policy … -> AccountPolicy

Updates the supplied local password and lockout settings.

Omitted settings are left unchanged.

Parameters

NameTypeDescription
:min_password_length? Int Minimum password length.
:max_password_age? (time.Duration | nil) Maximum password age. nil selects no limit, so passwords never expire.
:min_password_age? time.Duration Minimum password age.
:force_logoff? (time.Duration | nil) Forced-logoff delay. nil disables forced logoff.
:password_history_length? Int Number of prior passwords retained.
:lockout_duration? time.Duration Account lockout duration.
:lockout_observation_window? time.Duration Failed-logon counter observation window.
:lockout_threshold? Int Failed logons allowed before lockout. Zero disables lockout.

Returns

The resulting policy.

user principal -> User

Obtains a handle to an existing local user.

Parameters

NameTypeDescription
principal (Str | security.windows.Sid | UserInfo) Account name, SID, or a prior snapshot.

users() -> Iter[UserInfo]

Lists local user accounts.