adpwsh

package module
v0.21.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 16, 2026 License: MIT Imports: 11 Imported by: 0

README

go-adpwsh

A Go library that drives Active Directory through the ActiveDirectory PowerShell module — on the Windows host the caller runs on, on a Windows jump box reached over SSH, or on a Windows host reached over WinRM/PSRP.

It is a separate repository from the Terraform provider that consumes it for one reason: managing AD from Go is useful without Terraform, and a library that cannot return a diag.Diagnostics is a library whose correctness rules cannot quietly become someone else's problem. A test in this module fails the build if any Terraform package enters the import graph, including through test imports.

Topology

Three transports, one contract. All three invoke the same fixed command with the same JSON payload on stdin, and all three hand stdout, stderr and the exit code back verbatim.

On-host — transport/local. The caller already runs on a domain-joined Windows host, so the process holds a Kerberos TGT for whoever launched it and there is no hop to authenticate.

caller (Windows host)
   └─ pwsh -EncodedCommand …   (payload on stdin)
        └─ Import-Module ActiveDirectory
             └─ ADWS :9389 ──▶ pinned DC

Remote — transport/ssh. The caller runs anywhere and reaches a Windows jump box over SSH.

caller (anywhere)
   └─ ssh ──▶ jump box
                └─ pwsh -EncodedCommand …   (payload on stdin)
                     └─ Import-Module ActiveDirectory
                          └─ ADWS :9389 ──▶ pinned DC

On Windows, an SSH session authenticated by public key receives a network logon token carrying no delegatable credentials, so onward authentication to ADWS fails — the classic double hop. Over SSH that is worked around with an explicit Config.Credential, which becomes -Credential on every cmdlet. On-host execution removes the problem instead of working around it, and Config.Credential remains available there for the case where the operations must authenticate as some account other than the one that launched the process.

Remote — transport/psrp. The caller runs anywhere and reaches a Windows host over WinRM/PSRP. By default it authenticates with Kerberos over HTTP (port 5985), using the caller's own ambient Kerberos ticket; UseTLS switches it to HTTPS on port 5986. The target host needs the PowerShell.7 WinRM endpoint registered (Enable-PSRemoting run from pwsh 7) and RSAT-AD-PowerShell installed, and a non-admin connecting account must belong to the local Remote Management Users group. The AD cmdlets still reach the domain controller over ADWS (port 9389), so this transport is pointed either straight at a DC or at a member/management host together with domain credentials to cross the same Kerberos double hop transport/ssh works around.

Cost per operation, stated so it is not a surprise. Every operation pays a fresh Import-Module ActiveDirectory, roughly 1–3 seconds on Windows. This is inherent to the one-shot-per-operation execution contract, and it is the same for every transport. Concurrency bounds how many run at once — 4 by default, because each is a real process with real memory cost.

Example

dir := fake.NewDirectory()
client, err := adpwsh.New(context.Background(), adpwsh.Config{Transport: dir.Transport()})
if err != nil {
    panic(err)
}
defer client.Close()

ou, err := client.OU.Create(context.Background(), adpwsh.OUSpec{
    Name:      "Staff",
    Container: client.DefaultNamingContext(),
})
if err != nil && !errors.Is(err, adpwsh.ErrReplication) {
    panic(err)
}
fmt.Println(ou.DN, ou.Protected)
// Output: OU=Staff,DC=corp,DC=local true

That example runs in this module's test suite against transport/fake, so the whole library — and any consumer built on it — is testable with no Windows VM.

What the module guarantees

These are enforced at the module boundary. A consumer cannot opt out of them.

  • Read-back after write. Create and Update return the result of the same read Get performs, so an inconsistent result after apply is impossible by construction.
  • Delete verification. Delete returns nil only after a re-read confirms the object is gone. A Remove-AD* that returns cleanly while the deletion was refused is an error, not a success.
  • A pinned domain controller. New resolves one DC and every cmdlet for the client's lifetime carries -Server <that DC>. Without it a create lands on DC-A and the read-back hits DC-B and reports "not found".
  • Serialized writes per target. A read-then-write delta has no compare-and-swap, so writes naming the same object are serialized. Writes naming different objects still run concurrently.
  • Fail-closed classification. An unrecognized (exception type, error code) pair is KindUnknown and is never retried. Only KindTransient is retried: guessing that an unknown error is transient turns a permission problem into a hang.
  • No value ever becomes script text. Scripts are constants selected by a closed set of op names and embedded at build time. Every value travels as JSON on stdin and is splatted into the cmdlet. There is no code path that formats a caller's value into PowerShell.
  • Secrets cannot be printed or marshalled. Secret renders as REDACTED under every fmt verb and its MarshalJSON always fails, so a struct walk into a log line or a state file is a loud error rather than a leak. The payload is masked before a log line is constructed.
  • A replication timeout returns the model and the error. The object exists; only the wait did not finish. Erroring without the model orphans the object, so Create and Update may return a non-nil model beside a non-nil ErrReplication. Persist the model and surface the error.

Extension seams

  • Transport is the only I/O seam. Four ship: transport/local (pwsh as a child process of the caller), transport/ssh (a Windows jump box), transport/psrp (WinRM/PSRP to a Windows host), and transport/fake (a programmable double plus fake.Directory, a small in-memory AD). Envelope parsing, error classification, retry and the replication wait all live above it, which is why transport/local inherited every property above without restating one of them, and why transport/psrp did too. No transport can reinterpret an AD refusal as a transport failure.
  • Catalog will be the schema seam. The types have landed — schema.Catalog and its reader — and make schema produces the catalog they read; Config.Catalog has not, and adding it later is additive.

The schema catalog

make schema writes schema/catalog.json, a machine-readable catalog of an Active Directory schema: every attribute's type and constraints, and, for each class it covers, that class's effective set of allowed attributes. schema.Baseline() reads the copy committed with this module, so a consumer reaches a stock catalog with nothing beyond an import — no domain, no transport, no separate download.

Effective, not declared, is the whole point. An object's legal attributes are the union — across the entire inheritance closure — of mayContain, systemMayContain, mustContain and systemMustContain. The closure follows subClassOf up to top and, at every step, auxiliaryClass and systemAuxiliaryClass transitively. Against a stock Windows Server 2025 schema, reading mayContain off the class itself reports 31 attributes for organizationalUnit where the answer is 160, 25 for group where it is 192, and 159 for user where it is 406 — and it under-reports, so a validator built that way rejects attributes Active Directory accepts.

via records which class contributed each attribute, so "why is this attribute allowed here?" is answered by the file rather than by re-deriving the closure.

The committed catalog is a stock baseline, not an authority

It was exported from an unextended forest. Exchange, Skype for Business and Configuration Manager each add hundreds of attributes and several auxiliary classes to the schema they extend — Exchange alone adds roughly a thousand attributes. A consumer that trusts this baseline on an extended forest will under-report. source names the domain, forest mode, schema objectVersion and export time, so a stale or foreign catalog is identifiable without diffing it.

Regenerating

Regenerate after any schema extension, and to produce a catalog for your own forest. Run the exporter directly on the Windows host that has RSAT-AD-PowerShell — Windows generally has no make, so the instruction there is go run, not make schema:

go run ./cmd/adschema export --transport local \
  --server dc01.corp.local --out schema/catalog.json

--transport ssh can now carry this export, and every other operation this library sends. transport/ssh sends short commands inline as pwsh -EncodedCommand <base64>; base64-of-UTF-16 runs about 2.7x the size of the source, and every script this library sends — the preamble and epilogue alone are already close to 4.5KB of source before an op is added — exceeds the roughly 8,191-character command-line limit of cmd.exe, the shell behind sshd's DefaultShell on a Windows host; the schema fetch is merely the largest of them. Once the base64 reaches largeCommandThreshold (7,000 characters — the same cutoff scripts/lab/psrun.sh uses), transport/ssh instead writes the script to a randomly named file under Config.RemoteTempDir (default C:\Windows\Temp) over SFTP on the same SSH connection, runs it with -File, and removes it afterward. This path is covered by an in-process fake SSH server that also serves the sftp subsystem; it has not yet been exercised against a real Windows jump box.

make schema-check regenerates to a temporary file and diffs, which proves the committed catalog still matches the domain — but make itself is generally not on the Windows host, so proving the property today still means running the exporter by hand, twice, passing --exported-at set to the committed file's own source.exportedAt both times (so the diff is of the schema, not the clock), and comparing the two results byte for byte.

adschema export --classes all resolves every structural class instead of the three the provider manages (organizationalUnit, group, user). It costs nothing beyond output size, and the committed baseline deliberately uses the default. A password is read from the environment variable named by --ad-password-env, never from a flag, because argv is visible in the host's process list.

The exporter only reads. Extending a schema is irreversible and forest-wide, and no tool here makes it easy.

Windows requirements

Both transports need the same things of the Windows machine that runs pwsh:

  • A Windows member server — not a domain controller.
  • RSAT-AD-PowerShell installed.
  • PowerShell 7 (pwsh) or Windows PowerShell 5.1 (powershell.exe) on PATH. The scripts no longer use anything 6+ only — no ConvertFrom-Json -AsHashtable, no ?. null-conditional operator — so 5.1 is a supported engine, not a fallback.
  • TCP 9389 open to the domain controller — the AD Web Services port the cmdlets use.

5.1 support is proven, but not uniformly across transports. It is verified over transport/psrp: 34 acceptance-suite runs against a live domain (ten batches, logged batch by batch in the consuming provider's LAB.md), including non-ASCII input in the hostile-input batch, all on Windows PowerShell 5.1. transport/local and transport/ssh deliver the payload by writing raw UTF-8 to the process's stdin, which the script reads with [Console]::In.ReadToEnd(); pwsh defaults that stream to UTF-8, but powershell.exe reads it through the console code page instead. Non-ASCII payloads over local/ssh on 5.1 are therefore unverified — say so rather than claiming 5.1 works identically everywhere. This is not fixed by setting [Console]::InputEncoding in the preamble: that rebuilds Console.In from the real stdin handle, which would break psrp's payload delivery, since it does not deliver input that way.

transport/local needs nothing further: the caller is already on that machine.

transport/ssh additionally needs OpenSSH Server running on it, and TCP 22 open from wherever the caller runs. Host key verification is on by default; insecure_ignore_host_key is an explicit opt-out, and setting two host-key sources is a validation error rather than a silent precedence surprise.

Stability

v0.x. Group membership (Members/MembersRecursive/AddMembers/RemoveMembers/IsMember) is present; Members reads the full member attribute (the ActiveDirectory module performs ranged retrieval internally — the cmdlets reject the LDAP ;range= option), MembersRecursive resolves nesting via Get-ADGroupMember -Recursive (leaf user/computer accounts only — group objects, even empty nested ones, are never returned; primary-group-only membership excluded), and SetMembers is deferred. Bounded, class-scoped search (OU.Search/Group.Search/User.Search) is present as of v0.4.0: each takes a Query — an LDAP filter built through the exported EscapeFilter/Equal/And helpers, a search base, a scope and a size limit — and errors with KindTooManyResults rather than truncating past the limit. The module takes v1 only after the acceptance suite passes against a real domain. Until then, minor versions may change the surface.

Access control is present via ACL.Get, ACL.Grant and ACL.Revoke over explicit ACEs on an object's DACL. Schema resolution (Schema.Resolve) maps friendly schema names to their GUIDs with a well-known fast path. Delegation task expansion (Delegation.Template and Delegation.Tasks) is a pure function that expands a curated delegation task into the ACEs that implement it, with no I/O side effects.

Search is still not an arbitrary directory API: there is no generic object search, and object mutation remains get-by-identity only. The Catalog interface, the generic Object sub-client, and tier-2 Attributes map[string]any are deliberately absent from this release.

ServiceAccount (group Managed Service Account) provides Create, Get, Update, Delete, and Search via ServiceAccountClient. Supported attributes include description, enabled, DNS hostname, service principal names, principals allowed to retrieve the managed password, Kerberos encryption types, and account expiration; managed password interval and trusted for delegation are also present.

Computer provides Create, Get, Update, Delete, and Search via ComputerClient. Supported attributes include description, enabled, DNS hostname, service principal names, constrained delegation (msDS-AllowedToDelegateTo), RBCD (principals allowed to delegate to), Kerberos encryption types, and account expiration; operating system and version are read-only, and trusted for delegation is present.

Licence

MIT.

Documentation

Overview

Package adpwsh drives Active Directory through the ActiveDirectory PowerShell module running on a Windows jump box.

It knows nothing about Terraform. Every correctness rule it enforces — read-back after write, delete verification, pinned domain controller, serialized writes, fail-closed error classification, and the invariant that no value ever becomes PowerShell script text — is a guarantee made at the module boundary, so no consumer can opt out of it.

The read surface includes bounded, class-scoped search: OU.Search, Group.Search and User.Search each take a Query (an LDAP filter built through the exported EscapeFilter/Equal/And helpers, a search base, a scope and a size limit) and return typed results. It is not an arbitrary directory API — there is no generic object search, and object mutation remains get-by-identity only.

As of this version the library also manages object access control: ACL.Get/Grant/Revoke over explicit ACEs (never the whole DACL), Schema.Resolve (friendly name to schema GUID, with a well-known fast path), and Delegation which expands a curated task into the ACEs that implement it.

Example

Example shows the whole contract in one page: a client is a transport plus a pinned DC; every write returns the read path's result; a replication timeout returns the model and an error together.

package main

import (
	"context"
	"errors"
	"fmt"

	adpwsh "github.com/nemethhh/go-adpwsh"
	"github.com/nemethhh/go-adpwsh/transport/fake"
)

func main() {
	dir := fake.NewDirectory()
	client, err := adpwsh.New(context.Background(), adpwsh.Config{Transport: dir.Transport()})
	if err != nil {
		panic(err)
	}
	defer client.Close()

	ou, err := client.OU.Create(context.Background(), adpwsh.OUSpec{
		Name:      "Staff",
		Container: client.DefaultNamingContext(),
	})
	if err != nil && !errors.Is(err, adpwsh.ErrReplication) {
		panic(err)
	}
	fmt.Println(ou.DN, ou.Protected)
}
Output:
OU=Staff,DC=corp,DC=local true

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrNotFound         error = kindSentinel{KindNotFound}
	ErrAlreadyExists    error = kindSentinel{KindAlreadyExists}
	ErrDenied           error = kindSentinel{KindDenied}
	ErrConstraint       error = kindSentinel{KindConstraint}
	ErrPassword         error = kindSentinel{KindPassword}
	ErrReferral         error = kindSentinel{KindReferral}
	ErrTransient        error = kindSentinel{KindTransient}
	ErrTransport        error = kindSentinel{KindTransport}
	ErrInvalidAttribute error = kindSentinel{KindInvalidAttribute}
	ErrSchema           error = kindSentinel{KindSchema}
	ErrReplication      error = kindSentinel{KindReplication}
	ErrTooManyResults   error = kindSentinel{KindTooManyResults}
	ErrUnsupported      error = kindSentinel{KindUnsupported}
)

Kind sentinels for errors.Is.

Functions

func ACLEndpointHelpers added in v0.18.0

func ACLEndpointHelpers() string

ACLEndpointHelpers returns the PowerShell -FunctionDefinitions block that a ConstrainedLanguage management-host endpoint must install so the provider can run ACL delegation in constrained mode. The Terraform provider embeds a copy in its endpoint-registration script and drift-tests it against this value.

func And added in v0.4.0

func And(terms ...string) string

And composes a conjunction. Zero terms is the empty filter (the caller decides the default); one term is returned unwrapped; two or more are joined under a single "&".

func Bool

func Bool(b bool) *bool

Bool is the pointer helper for optional booleans.

func Equal added in v0.4.0

func Equal(attr, value string) string

Equal builds an equality assertion "(attr=<escaped value>)". The attribute name is a caller-controlled schema identifier and is not escaped; only the value is.

func EscapeFilter added in v0.4.0

func EscapeFilter(value string) string

EscapeFilter escapes one LDAP assertion value per RFC 4515. Every value the provider puts inside a filter goes through here; hand-rolled quoting is the defect class this exists to retire.

func Int added in v0.10.0

func Int(i int) *int

Int is the pointer helper for optional integers.

func ParseEnvelope added in v0.2.2

func ParseEnvelope(op string, r Result) (json.RawMessage, error)

ParseEnvelope turns one raw Result into either the operation's data or a classified *Error. A non-zero exit or a missing envelope is KindTransport: the script exits 0 even when AD refuses, so anything else means the transport or the pwsh process itself failed.

It is exported for the build-time tooling in this module that needs a query the op set does not expose — cmd/adschema — so that such a tool inherits this library's error classification instead of inventing its own. It confers no ability to run script text: the caller must already hold a Result.

func String

func String(s string) *string

String is the pointer helper for the tri-state spec fields. Named for how it reads at the call site: Description: adpwsh.String("x").

Types

type ACE added in v0.5.0

type ACE struct {
	Trustee             string // SID
	Type                ACEType
	Rights              []Right
	ObjectType          string // GUID; "" = all
	InheritedObjectType string // GUID; "" = all child classes
	Inheritance         Inheritance
	Inherited           bool // read-only: system-stamped copy; never managed
}

ACE is one explicit access-control entry as the library reads and writes it. Object types are GUIDs at this layer (already resolved from friendly names).

type ACESpec added in v0.5.0

type ACESpec struct {
	Rights      []Right
	ObjectType  string
	Scope       Inheritance
	ObjectClass string
	Type        ACEType
}

ACESpec is the friendly, unresolved form a delegation template emits and the provider maps to config. ObjectType and ObjectClass are friendly names or GUIDs.

type ACEType added in v0.5.0

type ACEType string

ACEType is an access-control entry's allow/deny sense.

const (
	ACEAllow ACEType = "Allow"
	ACEDeny  ACEType = "Deny"
)

type ACLClient added in v0.5.0

type ACLClient struct {
	// contains filtered or unexported fields
}

ACLClient reads and writes the discretionary ACL of a directory object. It is never authoritative over the whole DACL: Grant adds explicit ACEs and Revoke subtracts the rights it names from the matching explicit ACE, leaving inherited entries and other trustees untouched.

func (*ACLClient) Get added in v0.5.0

func (a *ACLClient) Get(ctx context.Context, target Identity) ([]ACE, error)

Get returns every ACE on target's DACL, including inherited ones (the Inherited flag distinguishes them). The caller matches the explicit ACE it owns and ignores the rest.

func (*ACLClient) Grant added in v0.5.0

func (a *ACLClient) Grant(ctx context.Context, target Identity, aces []ACE) error

Grant adds each ACE to target's DACL. Adding an ACE that is already present is a no-op on the DC, so Grant is idempotent.

func (*ACLClient) Revoke added in v0.5.0

func (a *ACLClient) Revoke(ctx context.Context, target Identity, aces []ACE) error

Revoke subtracts the rights named by each ACE from target's DACL. It uses .NET RemoveAccessRule, so it removes the named rights from the matching explicit ACE (same trustee, type and scope) even when Active Directory has coalesced several grants into one larger ACE — the case RemoveAccessRuleSpecific could not reach, because AddAccessRule unions masks on grant. For an ACE that stands alone, subtracting its whole mask empties and drops it, so the common case is unchanged. Revoking rights that are already absent is a no-op, so Revoke stays idempotent.

type Client

type Client struct {
	OU             *OUClient
	Group          *GroupClient
	User           *UserClient
	ServiceAccount *ServiceAccountClient
	Computer       *ComputerClient
	Schema         *SchemaClient
	ACL            *ACLClient
	Delegation     *DelegationClient
	// contains filtered or unexported fields
}

Client is the entry point. Its sub-clients map one method to one provider resource operation.

func New

func New(ctx context.Context, cfg Config) (*Client, error)

New validates the configuration, resolves the domain controller this client will pin for its lifetime, and proves the jump box can import the ActiveDirectory module. It performs one round trip.

func (*Client) Close

func (c *Client) Close() error

Close releases the transport.

func (*Client) DefaultNamingContext

func (c *Client) DefaultNamingContext() string

DefaultNamingContext returns the domain's naming context, e.g. "DC=corp,DC=local".

func (*Client) Server

func (c *Client) Server() string

Server returns the pinned domain controller.

type Computer added in v0.11.0

type Computer struct {
	GUID                       string
	DN                         string
	Name                       string
	SamAccountName             string
	Container                  string
	SID                        string
	Enabled                    bool
	DNSHostName                string
	Description                string
	DisplayName                string
	Location                   string
	ManagedBy                  string // read back as a DN
	TrustedForDelegation       bool
	ServicePrincipalNames      []string
	AllowedToDelegateTo        []string // msDS-AllowedToDelegateTo (SPNs)
	PrincipalsAllowed          []string // RBCD principals, as objectGUIDs
	KerberosEncryptionType     []string
	AccountExpiration          *time.Time
	OperatingSystem            string
	OperatingSystemVersion     string
	OperatingSystemServicePack string
}

Computer is an Active Directory computer account (objectClass "computer"). OperatingSystem* are read-only: the joined machine owns them.

type ComputerClient added in v0.11.0

type ComputerClient struct {
	// contains filtered or unexported fields
}

ComputerClient is the computer account (objectClass "computer") sub-client.

func (*ComputerClient) Create added in v0.11.0

func (cc *ComputerClient) Create(ctx context.Context, spec ComputerSpec) (*Computer, error)

Create makes a computer account and returns the result of the same read Get performs. It may return a non-nil Computer together with a non-nil error on a replication timeout; the caller must persist the model and surface the error. OperatingSystem/OperatingSystemVersion/OperatingSystemServicePack are never written here: they are read-only (the joined machine owns them) and are not on ComputerSpec.

func (*ComputerClient) Delete added in v0.11.0

func (cc *ComputerClient) Delete(ctx context.Context, id Identity) error

Delete removes a computer account and returns nil only after a re-read confirms it is gone.

func (*ComputerClient) Get added in v0.11.0

func (cc *ComputerClient) Get(ctx context.Context, id Identity) (*Computer, error)

Get reads one computer account.

func (*ComputerClient) Search added in v0.11.0

func (cc *ComputerClient) Search(ctx context.Context, q Query) ([]Computer, error)

Search returns every computer account under q.SearchBase matching q.Filter.

func (*ComputerClient) Update added in v0.11.0

func (cc *ComputerClient) Update(ctx context.Context, id Identity, spec ComputerSpec) (*Computer, error)

Update folds the attribute write, the rename and the move into one round trip.

type ComputerSpec added in v0.11.0

type ComputerSpec struct {
	Name                   string // CN; required
	SamAccountName         string // required; "$" is added by AD; length is NOT capped here
	Container              string // parent DN; required
	DNSHostName            *string
	Description            *string
	DisplayName            *string
	Location               *string
	ManagedBy              *string
	Enabled                *bool
	TrustedForDelegation   *bool
	ServicePrincipalNames  *[]string  // nil leaves alone, non-nil (incl. empty) replaces
	AllowedToDelegateTo    *[]string  // nil leaves alone, non-nil (incl. empty) replaces
	PrincipalsAllowed      []Identity // full-replace; nil leaves alone, non-nil (incl. empty) replaces
	KerberosEncryptionType *[]string  // nil leaves alone, non-nil replaces
	AccountExpiration      OptTime
}

ComputerSpec is the desired state of a computer account. Pointer fields follow the same tri-state convention as GMSASpec: nil leaves the attribute alone, a pointer to "" clears it, a pointer to a value sets it.

Unlike GMSASpec, SamAccountName has no length cap here: the 15-char NetBIOS limit gMSA enforces is a gMSA-specific constraint (the "$" AD appends must still fit in 20 bytes downlevel-logon-name space), not a general AD rule — AD accepts a computer sAMAccountName well past 15 characters, so validate must not reject one. There are also no create-only fields: DNSHostName, unlike a gMSA's, is settable any time.

type Config

type Config struct {
	// Transport is how PowerShell reaches the jump box. Required.
	Transport Transport

	// Dialect selects the script set. The zero value, DialectADWS, preserves
	// the historical behaviour exactly.
	Dialect Dialect

	// Server pins the domain controller every cmdlet targets. When empty it is
	// discovered once in New and never changes for this client's lifetime.
	Server string

	// Credential, when set, becomes the -Credential passed to every cmdlet on
	// the jump box. Omit it to use the transport session's own identity.
	Credential *Credential

	// Retry governs re-attempts, and applies only to errors classified
	// transient.
	Retry RetryConfig

	// Replication governs the post-write wait.
	Replication ReplicationConfig

	// Log is an optional output port. It is not an extension seam: redaction
	// cannot be the caller's job, because the caller never sees the payload.
	// The library masks credential-bearing keys before anything reaches Log.
	Log Logger
}

Config configures a Client. Transport is the only required field.

type Credential

type Credential struct {
	Username string
	Password Secret
}

Credential is a username and password for the AD cmdlets.

type DelegationClient added in v0.5.0

type DelegationClient struct{}

DelegationClient expands a curated delegation task into the concrete ACEs that implement it. It performs no directory I/O: the expansion is pure, so a consumer can compute a plan from it without a round trip. Object types are friendly names; the caller resolves them to GUIDs.

func (*DelegationClient) Template added in v0.5.0

func (d *DelegationClient) Template(task DelegationTask) ([]ACESpec, error)

Template returns the ACE specs a task expands into. These mirror the AD "Delegate Control" wizard's common tasks; the object/attribute names are cross-checked against the schema well-known table and intended to be verified end-to-end on the lab.

type DelegationTask added in v0.5.0

type DelegationTask string

DelegationTask names a curated bundle of ACEs.

const (
	TaskResetUserPasswords    DelegationTask = "reset_user_passwords"
	TaskManageUsers           DelegationTask = "manage_users"
	TaskModifyGroupMembership DelegationTask = "modify_group_membership"
	TaskManageGroups          DelegationTask = "manage_groups"
)

func Tasks added in v0.5.0

func Tasks() []DelegationTask

Tasks returns every task name, in a stable order.

type DeleteOptions

type DeleteOptions struct {
	// Unprotect lifts ProtectedFromAccidentalDeletion before deleting. Without
	// it, deleting an OU created with AD's own default fails.
	Unprotect bool
}

DeleteOptions is taken only by OU.Delete. Making the unprotect step an explicit option keeps the destructive part visible at the call site.

type Dialect added in v0.21.0

type Dialect int

Dialect selects which PowerShell script set an operation runs.

The zero value is DialectADWS: Microsoft's ActiveDirectory module over AD Web Services, which requires a Windows host. DialectPSOpenAD runs the PSOpenAD module over LDAP and works anywhere PowerShell 7.4 does, including Linux.

There is deliberately no auto-detection. A dialect decides which module executes, and guessing it is the same class of mistake as guessing a transport.

const (
	DialectADWS Dialect = iota
	DialectPSOpenAD
)

func (Dialect) String added in v0.21.0

func (d Dialect) String() string

type Error

type Error struct {
	Kind          Kind   // the only thing callers switch on
	Op            string // "User.Create"
	Identity      string // the identity acted on, in form:value notation
	ExceptionType string // verbatim, e.g. …ADIdentityNotFoundException
	Code          int    // Win32 code; decode via MS-ERREF
	ServerMessage string // the DC's own words, via IHasServerErrorMessage
	FQID          string // cmdlet-specific; diagnostics only
	Target        string
	Tombstoned    bool // set when an already-exists was traced to a deleted object
	Err           error
}

Error is the single error type this library returns. AD's raw detail is carried alongside the normalized Kind so a caller can render an exact message without switching on Microsoft's type names.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Is

func (e *Error) Is(target error) bool

Is matches the Kind sentinels, so errors.Is(err, ErrNotFound) works without exposing the sentinel's concrete type.

func (*Error) Unwrap

func (e *Error) Unwrap() error

type GMSA added in v0.10.0

type GMSA struct {
	GUID                          string
	DN                            string
	Name                          string
	SamAccountName                string
	Container                     string
	SID                           string
	DNSHostName                   string
	Description                   string
	DisplayName                   string
	Enabled                       bool
	TrustedForDelegation          bool
	PrincipalsAllowed             []string // objectGUIDs, resolved from the DNs AD returns
	ServicePrincipalNames         []string
	KerberosEncryptionType        []string
	ManagedPasswordIntervalInDays int
	AccountExpiration             *time.Time // nil means never
}

GMSA is a group Managed Service Account as this library reads it back.

type GMSASpec added in v0.10.0

type GMSASpec struct {
	Name                          string // CN; required
	SamAccountName                string // required; <= 15 chars, "$" is added by AD
	Container                     string // parent DN; required
	DNSHostName                   *string
	Description                   *string
	DisplayName                   *string
	Enabled                       *bool
	TrustedForDelegation          *bool
	PrincipalsAllowed             []Identity // full-replace; nil leaves alone, non-nil (incl. empty) replaces
	ServicePrincipalNames         *[]string  // nil leaves alone, non-nil (incl. empty) replaces
	KerberosEncryptionType        *[]string  // nil leaves alone, non-nil replaces
	AccountExpiration             OptTime
	ManagedPasswordIntervalInDays *int // create-only; ignored on Update
}

GMSASpec is the desired state of a group Managed Service Account. Pointer fields follow the tri-state convention: nil leaves the attribute alone, a pointer to "" clears it, a pointer to a value sets it.

type Group

type Group struct {
	GUID           string
	DN             string
	Name           string
	SamAccountName string
	Container      string
	Scope          GroupScope
	Category       GroupCategory
	Description    string
	ManagedBy      string
	SID            string
}

Group is a security or distribution group.

type GroupCategory

type GroupCategory string

GroupCategory distinguishes a security principal from a distribution list.

const (
	GroupCategorySecurity     GroupCategory = "security"
	GroupCategoryDistribution GroupCategory = "distribution"
)

type GroupClient

type GroupClient struct {
	// contains filtered or unexported fields
}

GroupClient is the group sub-client.

func (*GroupClient) AddMembers added in v0.3.0

func (g *GroupClient) AddMembers(ctx context.Context, group Identity, members []Identity) error

AddMembers adds each member to the group. It is idempotent: a member already present is not an error. A member that does not exist is a real error and is surfaced.

func (*GroupClient) Create

func (g *GroupClient) Create(ctx context.Context, spec GroupSpec) (*Group, error)

Create makes a group and returns the result of the same read Get performs. It may return a non-nil Group together with a non-nil error on a replication timeout; the caller must persist the model and surface the error.

func (*GroupClient) Delete

func (g *GroupClient) Delete(ctx context.Context, id Identity) error

Delete removes a group and returns nil only after a re-read confirms it is gone.

func (*GroupClient) Get

func (g *GroupClient) Get(ctx context.Context, id Identity) (*Group, error)

Get reads one group.

func (*GroupClient) IsMember added in v0.3.0

func (g *GroupClient) IsMember(ctx context.Context, group, member Identity) (bool, error)

IsMember reports whether member is a direct member of group without enumerating the group. A not-found group or member reads as "false": the edge cannot exist, which is drift the caller reconciles rather than an error.

func (*GroupClient) Members added in v0.3.0

func (g *GroupClient) Members(ctx context.Context, group Identity) ([]Member, error)

Members reads a group's full membership. It pages the multivalued member attribute so the result is correct for a group of any size.

func (*GroupClient) MembersRecursive added in v0.7.0

func (g *GroupClient) MembersRecursive(ctx context.Context, group Identity) ([]Member, error)

MembersRecursive reads a group's effective membership: the leaf accounts (users and computers) reachable through nested groups, matching Get-ADGroupMember -Recursive. Group objects are traversed but never returned — not even an empty nested group (confirmed against a real domain). Primary-group-only membership (e.g. a user's primary Domain Users) is not included — it is stored via primaryGroupID, not the member attribute.

func (*GroupClient) RemoveMembers added in v0.3.0

func (g *GroupClient) RemoveMembers(ctx context.Context, group Identity, members []Identity) error

RemoveMembers removes each member from the group. It is idempotent: a member not present is not an error, and a not-found group is success — the edges are gone regardless.

func (*GroupClient) Search added in v0.4.0

func (g *GroupClient) Search(ctx context.Context, q Query) ([]Group, error)

Search returns every group under q.SearchBase matching q.Filter.

func (*GroupClient) Update

func (g *GroupClient) Update(ctx context.Context, id Identity, spec GroupSpec) (*Group, error)

Update folds the attribute write, the rename and the move into one round trip. Where AD refuses a scope conversion, AD's own error is surfaced.

type GroupScope

type GroupScope string

GroupScope is the group's replication and membership scope.

const (
	GroupScopeGlobal      GroupScope = "global"
	GroupScopeDomainLocal GroupScope = "domainlocal"
	GroupScopeUniversal   GroupScope = "universal"
)

type GroupSpec

type GroupSpec struct {
	Name           string // the CN; a change means Rename-ADObject
	SamAccountName string // required; changes through Set-ADGroup
	Container      string // parent DN; a change means Move-ADObject
	Scope          GroupScope
	Category       GroupCategory // defaults to security
	Description    *string
	ManagedBy      *string
}

GroupSpec is the desired state of a group.

type Identity

type Identity interface {
	String() string
	// contains filtered or unexported methods
}

Identity is an AD -Identity argument. The interface is sealed by an unexported method: the only values that satisfy it come from the four constructors below, so there is no constructor taking an arbitrary string as an identity and no caller can hand the library a value that becomes script text.

func ByDN

func ByDN(dn string) Identity

ByDN identifies an object by distinguished name.

func ByGUID

func ByGUID(guid string) Identity

ByGUID identifies an object by objectGUID. This is the canonical form: it survives rename and move, which DN and sAMAccountName do not.

func BySAM

func BySAM(sam string) Identity

BySAM identifies a security principal by sAMAccountName.

func BySID

func BySID(sid string) Identity

BySID identifies a security principal by SID.

type Inheritance added in v0.5.0

type Inheritance string

Inheritance is how an ACE propagates. It maps onto System.DirectoryServices.ActiveDirectorySecurityInheritance.

const (
	InheritanceThis        Inheritance = "this"        // this object only
	InheritanceDescendants Inheritance = "descendants" // all descendants, scoped by InheritedObjectType
	InheritanceChildren    Inheritance = "children"    // immediate children only
)

type Kind

type Kind int

Kind is the normalized condition a caller switches on. Adding a condition later adds a Kind, not a type: the public surface does not track Microsoft's exception list.

const (
	KindUnknown Kind = iota
	KindNotFound
	KindAlreadyExists
	KindDenied
	KindConstraint
	KindPassword
	KindReferral
	// KindTransient means the operation provably did not execute: the
	// failure occurred before the script (or, for a transport, the request
	// carrying it) was ever sent, so re-issuing the identical script and
	// payload cannot duplicate a side effect. This is the bar a producer of
	// KindTransient must clear — not "this looks like it might be worth
	// retrying," but "nothing happened, by construction, every time." An AD
	// result code that comes back through the envelope (RPC_S_SERVER_BUSY
	// and friends in classByCode) clears it: the script ran, the cmdlet was
	// attempted, and AD refused it before doing anything. A transport-level
	// failure clears it only when it is raised by a pre-send check (a
	// semaphore, a circuit breaker, a not-yet-connected guard) that runs
	// before any bytes carrying the script leave the process.
	//
	// Anything that merely *might* not have executed is not transient, and
	// must map to KindTransport (or another non-retryable Kind) instead. In
	// particular, a context cancellation or deadline observed while awaiting
	// a response is not transient: depending on the transport, the script
	// may already have reached the server before the deadline fired, so the
	// failure and a completed execution are indistinguishable from here.
	// This is the exact bug that once made transport/winrm/wrap.go's
	// mapExecuteError treat context.Canceled/context.DeadlineExceeded as
	// KindTransient — up to Retry.MaxAttempts re-issues of an operation that
	// may have already run. See mapExecuteError's doc for the WSMan-specific
	// mechanics and why a caller-side cancellation loses nothing by staying
	// non-retryable (core.backoff already aborts on the caller's own
	// ctx.Done()).
	KindTransient
	KindTransport
	KindInvalidAttribute
	KindSchema
	// KindReplication means the write succeeded and the replication wait did
	// not complete. It is never retried, and the caller must persist the model
	// it was returned alongside.
	KindReplication
	// KindTooManyResults means a search matched more than its size limit. It is
	// never retried; the caller narrows the filter or raises the limit.
	KindTooManyResults
	// KindUnsupported means the operation cannot run against this transport's
	// endpoint — e.g. an ACL op against a ConstrainedLanguage endpoint, whose
	// .NET DirectoryServices calls are unavailable. Never retried.
	KindUnsupported
)

func Classify

func Classify(exceptionType string, code int) Kind

Classify normalizes an AD exception into a Kind. It fails closed: an unrecognized (type, code) pair is KindUnknown and is never retried.

func (Kind) String

func (k Kind) String() string

type Logger

type Logger interface {
	Debug(ctx context.Context, msg string, kv ...any)
}

Logger is the output port. A three-line adapter satisfies it from tflog, which is how the provider gets logging without this module importing anything from Terraform.

type Member added in v0.3.0

type Member struct {
	GUID  string
	DN    string
	Class string // user, group, computer, foreignSecurityPrincipal, …
	SID   string
}

Member is one entry read back from a group's membership.

type OU

type OU struct {
	GUID        string
	DN          string
	Name        string
	Container   string // derived from DN; never echoed by the script
	Description string
	Protected   bool
}

OU is an organizational unit as this library reads it back.

type OUClient

type OUClient struct {
	// contains filtered or unexported fields
}

OUClient is the organizational-unit sub-client.

func (*OUClient) Create

func (o *OUClient) Create(ctx context.Context, spec OUSpec) (*OU, error)

Create makes an organizational unit and returns the result of the same read Get performs, so an inconsistent result after apply is impossible by construction.

It may return a non-nil OU together with a non-nil error: that is the replication-timeout contract. The object exists and the wait did not complete, so the caller must persist the model and surface the error. Ignoring the model orphans the object.

func (*OUClient) Delete

func (o *OUClient) Delete(ctx context.Context, id Identity, opts DeleteOptions) error

Delete removes an organizational unit and returns nil only after a re-read confirms it is gone. A non-empty OU is never deleted recursively: the error names the child count. Remove-ADOrganizationalUnit -Recursive exists and is deliberately not reachable from this API.

func (*OUClient) Get

func (o *OUClient) Get(ctx context.Context, id Identity) (*OU, error)

Get reads one organizational unit.

func (*OUClient) Search added in v0.4.0

func (o *OUClient) Search(ctx context.Context, q Query) ([]OU, error)

Search returns every organizational unit under q.SearchBase matching q.Filter. It errors with KindTooManyResults rather than truncating: the script requests one row over the limit, and finding it means more exist.

func (*OUClient) Update

func (o *OUClient) Update(ctx context.Context, id Identity, spec OUSpec) (*OU, error)

Update folds the attribute write, the rename and the move into one round trip, in the order that keeps the DN valid. It never deletes and recreates, because that destroys the object's SID and with it every ACL referencing it.

Like Create, it may return a non-nil OU with a non-nil error on a replication timeout.

type OUSpec

type OUSpec struct {
	Name        string // the RDN; required on create, a change means Rename-ADObject
	Container   string // parent DN; required on create, a change means Move-ADObject
	Description *string
	Protected   *bool // ProtectedFromAccidentalDeletion
}

OUSpec is the desired state of an organizational unit. A nil pointer leaves the attribute alone; a pointer to "" clears it; a pointer to a value sets it.

type OptTime

type OptTime struct {
	// contains filtered or unexported fields
}

OptTime is the three-state carrier a *time.Time cannot express: time.Time has no empty sentinel the way string does. The zero value leaves the attribute alone.

func ClearTime

func ClearTime() OptTime

ClearTime clears accountExpires, which in AD means "never expires".

func SetTime

func SetTime(t time.Time) OptTime

SetTime writes accountExpires.

func (OptTime) IsClear

func (o OptTime) IsClear() bool

IsClear reports whether the attribute should be cleared.

func (OptTime) IsSet

func (o OptTime) IsSet() bool

IsSet reports whether a value should be written.

func (OptTime) Value

func (o OptTime) Value() time.Time

Value is meaningful only when IsSet reports true.

type Query added in v0.4.0

type Query struct {
	Filter     string      // a COMPLETE LDAP filter; "" ⇒ "(objectClass=*)"
	SearchBase string      // DN; "" ⇒ the pinned domain's defaultNamingContext
	Scope      SearchScope // "" ⇒ subtree
	SizeLimit  int         // ≤ 0 ⇒ defaultSizeLimit
}

Query is a directory search. The zero value searches the whole domain subtree for every object of the sub-client's class, capped at the default limit.

type ReplicationConfig

type ReplicationConfig struct {
	Wait         bool
	Targets      []string // DC host names, or the single element "all"
	ForceSync    bool
	Timeout      time.Duration
	PollInterval time.Duration
}

ReplicationConfig governs the wait that follows a write. Replication is a property of domain topology, not of any single object, so it is configured once on the client.

type Result

type Result struct {
	Stdout   string
	Stderr   string
	ExitCode int
}

Result is the raw outcome of one pwsh invocation.

type RetryConfig

type RetryConfig struct {
	MaxAttempts    int
	InitialBackoff time.Duration
	MaxBackoff     time.Duration
	Jitter         float64 // fraction of the backoff, 0..1
}

RetryConfig is values, not code.

type Right string

Right is one System.DirectoryServices.ActiveDirectoryRights value, by name.

type SchemaClient added in v0.5.0

type SchemaClient struct {
	// contains filtered or unexported fields
}

SchemaClient resolves friendly schema names to GUIDs.

func (*SchemaClient) Resolve added in v0.5.0

func (s *SchemaClient) Resolve(ctx context.Context, refs []SchemaRef) (map[SchemaRef]string, error)

Resolve returns a GUID for each ref. Well-known names are answered from the in-process table; the rest go to the directory in one round trip. A name that resolves to nothing is returned as an empty string (the caller decides whether that is an error).

type SchemaRef added in v0.5.0

type SchemaRef struct {
	Kind SchemaRefKind
	Name string
}

SchemaRef is a friendly name to resolve to a GUID.

type SchemaRefKind added in v0.5.0

type SchemaRefKind string

SchemaRefKind is which schema partition a name is resolved against.

const (
	RefAttribute     SchemaRefKind = "attribute"
	RefClass         SchemaRefKind = "class"
	RefExtendedRight SchemaRefKind = "extended_right"
)

type SearchScope added in v0.4.0

type SearchScope string

SearchScope is the -SearchScope argument to a directory search.

const (
	SearchScopeBase     SearchScope = "base"
	SearchScopeOneLevel SearchScope = "onelevel"
	SearchScopeSubtree  SearchScope = "subtree"
)

type Secret

type Secret struct {
	// contains filtered or unexported fields
}

Secret carries a password without letting it reach a log line, a state file, or a %v verb. Its plaintext is readable only inside this package, through reveal, which the payload builders call deliberately at the moment of serialization. This is the structural answer to the archived provider's credential leak: the guarantee is on the type, not on each call site.

func NewSecret

func NewSecret(s string) Secret

NewSecret wraps a plaintext password.

func (Secret) GoString

func (Secret) GoString() string

GoString makes %#v safe.

func (Secret) IsZero

func (s Secret) IsZero() bool

IsZero reports whether the secret was never set.

func (Secret) MarshalJSON

func (Secret) MarshalJSON() ([]byte, error)

MarshalJSON always fails. A Secret must be revealed deliberately by a payload builder; it must never be serialized by a struct walk into a log line or a state file.

func (Secret) String

func (Secret) String() string

String makes %v, %s and the print helpers safe.

type ServiceAccountClient added in v0.10.0

type ServiceAccountClient struct {
	// contains filtered or unexported fields
}

ServiceAccountClient is the group Managed Service Account (gMSA) sub-client.

func (*ServiceAccountClient) Create added in v0.10.0

func (s *ServiceAccountClient) Create(ctx context.Context, spec GMSASpec) (*GMSA, error)

Create makes a group Managed Service Account and returns the result of the same read Get performs. It may return a non-nil GMSA together with a non-nil error on a replication timeout; the caller must persist the model and surface the error.

func (*ServiceAccountClient) Delete added in v0.10.0

func (s *ServiceAccountClient) Delete(ctx context.Context, id Identity) error

Delete removes a group Managed Service Account and returns nil only after a re-read confirms it is gone.

func (*ServiceAccountClient) Get added in v0.10.0

func (s *ServiceAccountClient) Get(ctx context.Context, id Identity) (*GMSA, error)

Get reads one group Managed Service Account.

func (*ServiceAccountClient) Search added in v0.10.0

func (s *ServiceAccountClient) Search(ctx context.Context, q Query) ([]GMSA, error)

Search returns every group Managed Service Account under q.SearchBase matching q.Filter.

func (*ServiceAccountClient) Update added in v0.10.0

func (s *ServiceAccountClient) Update(ctx context.Context, id Identity, spec GMSASpec) (*GMSA, error)

Update folds the attribute write, the rename and the move into one round trip. ManagedPasswordIntervalInDays is never referenced here: it is create-only, since Set-ADServiceAccount has no such parameter.

type Transport

type Transport interface {
	Run(ctx context.Context, encodedCommand string, payload []byte) (Result, error)
	Close() error
}

Transport runs one PowerShell command on the jump box.

Implementations must invoke:

<pwsh> -NoProfile -NonInteractive -EncodedCommand <encodedCommand>

with payload written to the process's standard input and closed, and must return its stdout, stderr and exit code verbatim.

Run returns a non-nil error only when the process could not be run to completion — dial, authentication, channel exhaustion, context cancellation. A non-zero exit is reported through Result.ExitCode, never as an error: the distinction between "AD said no" and "we could not reach AD" is decided above this interface, not inside it. An implementation that can classify its own failure should return an *Error with the appropriate Kind (KindTransient for an exhausted channel, KindTransport for a dial or auth failure); any other error is treated as KindTransport.

type User

type User struct {
	GUID                  string
	DN                    string
	Name                  string
	SamAccountName        string
	UserPrincipalName     string
	DisplayName           string
	GivenName             string
	Surname               string
	Description           string
	Container             string
	Enabled               bool
	SID                   string
	ChangePasswordAtLogon bool
	CanChangePassword     bool
	PasswordExpires       bool
	AccountExpiration     *time.Time // nil means the account never expires
}

User is a user account.

type UserClient

type UserClient struct {
	// contains filtered or unexported fields
}

UserClient is the user sub-client.

func (*UserClient) Create

func (u *UserClient) Create(ctx context.Context, spec UserSpec) (*User, error)

Create makes a user account and returns the result of the same read Get performs. It may return a non-nil User together with a non-nil error on a replication timeout; the caller must persist the model and surface the error.

AD refuses to enable an account with no password satisfying domain policy, so Enabled: true without a Password fails with AD's own error rather than being papered over by silently creating a disabled account.

func (*UserClient) Delete

func (u *UserClient) Delete(ctx context.Context, id Identity) error

Delete removes a user and returns nil only after a re-read confirms it is gone.

func (*UserClient) Get

func (u *UserClient) Get(ctx context.Context, id Identity) (*User, error)

Get reads one user.

func (*UserClient) Search added in v0.4.0

func (u *UserClient) Search(ctx context.Context, q Query) ([]User, error)

Search returns every user under q.SearchBase matching q.Filter.

func (*UserClient) SetPassword

func (u *UserClient) SetPassword(ctx context.Context, id Identity, pw Secret) error

SetPassword resets the account's password through Set-ADAccountPassword -Reset. The error it returns never echoes the value.

func (*UserClient) Update

func (u *UserClient) Update(ctx context.Context, id Identity, spec UserSpec) (*User, error)

Update folds the attribute write, the rename and the move into one round trip. It never changes the password: -AccountPassword does not exist on Set-ADUser, so rotation goes through SetPassword.

type UserSpec

type UserSpec struct {
	SamAccountName    string  // required on create
	Container         string  // required on create; a change means Move-ADObject
	Name              *string // the CN; defaults to SamAccountName on create; a change means Rename-ADObject
	UserPrincipalName *string
	DisplayName       *string
	GivenName         *string
	Surname           *string
	Description       *string

	Enabled               *bool
	Password              *Secret
	ChangePasswordAtLogon *bool
	CanChangePassword     *bool
	PasswordExpires       *bool
	AccountExpiration     OptTime
}

UserSpec is the desired state of a user account.

Directories

Path Synopsis
cmd
adschema command
Command adschema exports an Active Directory schema catalog: every attribute's type and constraints, and every requested class's effective set of allowed attributes.
Command adschema exports an Active Directory schema catalog: every attribute's type and constraints, and every requested class's effective set of allowed attributes.
internal
addn
Package addn implements the slice of RFC 4514 (distinguished names) and RFC 4515 (search filters) this library needs: parsing and case-insensitive comparison of DNs, and escaping of filter assertion values.
Package addn implements the slice of RFC 4514 (distinguished names) and RFC 4515 (search filters) this library needs: parsing and case-insensitive comparison of DNs, and escaping of filter assertion values.
adschema
Package adschema is the schema exporter's guts: one fetch over a transport, the inheritance closure that turns what was fetched into effective attribute sets, and the deterministic serialiser that writes the catalog.
Package adschema is the schema exporter's guts: one fetch over a transport, the inheritance closure that turns what was fetched into effective attribute sets, and the deterministic serialiser that writes the catalog.
adscript
Package adscript holds the constant PowerShell this library runs, the encoder that hands it to pwsh, and the builder for the attribute half of a Set-AD* payload.
Package adscript holds the constant PowerShell this library runs, the encoder that hands it to pwsh, and the builder for the attribute half of a Set-AD* payload.
oop
Package oop drives PowerShell's OutOfProcess PSRP framing over an arbitrary byte stream (an SSH subsystem channel to `pwsh -sshs`, or a local child process's stdio to `pwsh -SSHServerMode`), exposing an io.ReadWriter + MultiplexedTransport for github.com/smnsjas/go-psrpcore/runspace.
Package oop drives PowerShell's OutOfProcess PSRP framing over an arbitrary byte stream (an SSH subsystem channel to `pwsh -sshs`, or a local child process's stdio to `pwsh -SSHServerMode`), exposing an io.ReadWriter + MultiplexedTransport for github.com/smnsjas/go-psrpcore/runspace.
psrun
Package psrun is the shared runspace-executor core for the warm transports that drive a pwsh -sshs/-SSHServerMode server over the out-of-proc adapter.
Package psrun is the shared runspace-executor core for the warm transports that drive a pwsh -sshs/-SSHServerMode server over the out-of-proc adapter.
warm
Package warm is the transport-agnostic warm-runspace engine: a pool of persistent PSRP executors with an idle reaper and a one-shot, pre-execution-only retry.
Package warm is the transport-agnostic warm-runspace engine: a pool of persistent PSRP executors with an idle reaper and a one-shot, pre-execution-only retry.
Package schema holds the Active Directory schema catalog: every attribute's type and constraints, and every exported class's effective set of allowed attributes.
Package schema holds the Active Directory schema catalog: every attribute's type and constraints, and every exported class's effective set of allowed attributes.
transport
fake
Package fake provides a Transport double: it synthesizes result envelopes, injects AD exceptions, and records what was asked of it.
Package fake provides a Transport double: it synthesizes result envelopes, injects AD exceptions, and records what was asked of it.
local
Package local is the transport that runs PowerShell on the machine the caller itself runs on.
Package local is the transport that runs PowerShell on the machine the caller itself runs on.
localwarm
Package localwarm runs go-adpwsh operations in a persistent local PowerShell 7 runspace (pwsh -SSHServerMode) driven over the out-of-proc adapter, pooled by internal/warm.
Package localwarm runs go-adpwsh operations in a persistent local PowerShell 7 runspace (pwsh -SSHServerMode) driven over the out-of-proc adapter, pooled by internal/warm.
ssh
Package ssh is the transport that carries go-adpwsh to a Windows jump box.
Package ssh is the transport that carries go-adpwsh to a Windows jump box.
sshwarm
Package sshwarm runs go-adpwsh operations in a persistent pwsh -sshs runspace on a Windows jump box, reached over an SSH subsystem channel and driven by the out-of-proc adapter, pooled by internal/warm.
Package sshwarm runs go-adpwsh operations in a persistent pwsh -sshs runspace on a Windows jump box, reached over an SSH subsystem channel and driven by the out-of-proc adapter, pooled by internal/warm.
winrm
Package winrm runs go-adpwsh's PowerShell commands over PSRP/WinRM using github.com/smnsjas/go-psrp.
Package winrm runs go-adpwsh's PowerShell commands over PSRP/WinRM using github.com/smnsjas/go-psrp.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL