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 ¶
- Variables
- func ACLEndpointHelpers() string
- func And(terms ...string) string
- func Bool(b bool) *bool
- func Equal(attr, value string) string
- func EscapeFilter(value string) string
- func Int(i int) *int
- func ParseEnvelope(op string, r Result) (json.RawMessage, error)
- func String(s string) *string
- type ACE
- type ACESpec
- type ACEType
- type ACLClient
- type Client
- type Computer
- type ComputerClient
- func (cc *ComputerClient) Create(ctx context.Context, spec ComputerSpec) (*Computer, error)
- func (cc *ComputerClient) Delete(ctx context.Context, id Identity) error
- func (cc *ComputerClient) Get(ctx context.Context, id Identity) (*Computer, error)
- func (cc *ComputerClient) Search(ctx context.Context, q Query) ([]Computer, error)
- func (cc *ComputerClient) Update(ctx context.Context, id Identity, spec ComputerSpec) (*Computer, error)
- type ComputerSpec
- type Config
- type Credential
- type DelegationClient
- type DelegationTask
- type DeleteOptions
- type Dialect
- type Error
- type GMSA
- type GMSASpec
- type Group
- type GroupCategory
- type GroupClient
- func (g *GroupClient) AddMembers(ctx context.Context, group Identity, members []Identity) error
- func (g *GroupClient) Create(ctx context.Context, spec GroupSpec) (*Group, error)
- func (g *GroupClient) Delete(ctx context.Context, id Identity) error
- func (g *GroupClient) Get(ctx context.Context, id Identity) (*Group, error)
- func (g *GroupClient) IsMember(ctx context.Context, group, member Identity) (bool, error)
- func (g *GroupClient) Members(ctx context.Context, group Identity) ([]Member, error)
- func (g *GroupClient) MembersRecursive(ctx context.Context, group Identity) ([]Member, error)
- func (g *GroupClient) RemoveMembers(ctx context.Context, group Identity, members []Identity) error
- func (g *GroupClient) Search(ctx context.Context, q Query) ([]Group, error)
- func (g *GroupClient) Update(ctx context.Context, id Identity, spec GroupSpec) (*Group, error)
- type GroupScope
- type GroupSpec
- type Identity
- type Inheritance
- type Kind
- type Logger
- type Member
- type OU
- type OUClient
- func (o *OUClient) Create(ctx context.Context, spec OUSpec) (*OU, error)
- func (o *OUClient) Delete(ctx context.Context, id Identity, opts DeleteOptions) error
- func (o *OUClient) Get(ctx context.Context, id Identity) (*OU, error)
- func (o *OUClient) Search(ctx context.Context, q Query) ([]OU, error)
- func (o *OUClient) Update(ctx context.Context, id Identity, spec OUSpec) (*OU, error)
- type OUSpec
- type OptTime
- type Query
- type ReplicationConfig
- type Result
- type RetryConfig
- type Right
- type SchemaClient
- type SchemaRef
- type SchemaRefKind
- type SearchScope
- type Secret
- type ServiceAccountClient
- func (s *ServiceAccountClient) Create(ctx context.Context, spec GMSASpec) (*GMSA, error)
- func (s *ServiceAccountClient) Delete(ctx context.Context, id Identity) error
- func (s *ServiceAccountClient) Get(ctx context.Context, id Identity) (*GMSA, error)
- func (s *ServiceAccountClient) Search(ctx context.Context, q Query) ([]GMSA, error)
- func (s *ServiceAccountClient) Update(ctx context.Context, id Identity, spec GMSASpec) (*GMSA, error)
- type Transport
- type User
- type UserClient
- func (u *UserClient) Create(ctx context.Context, spec UserSpec) (*User, error)
- func (u *UserClient) Delete(ctx context.Context, id Identity) error
- func (u *UserClient) Get(ctx context.Context, id Identity) (*User, error)
- func (u *UserClient) Search(ctx context.Context, q Query) ([]User, error)
- func (u *UserClient) SetPassword(ctx context.Context, id Identity, pw Secret) error
- func (u *UserClient) Update(ctx context.Context, id Identity, spec UserSpec) (*User, error)
- type UserSpec
Examples ¶
Constants ¶
This section is empty.
Variables ¶
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
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 Equal ¶ added in v0.4.0
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
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 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.
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.
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
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
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
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 ¶
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) DefaultNamingContext ¶
DefaultNamingContext returns the domain's naming context, e.g. "DC=corp,DC=local".
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) Search ¶ added in v0.11.0
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 ¶
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.
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.
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
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 ¶
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) IsMember ¶ added in v0.3.0
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
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
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
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.
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 ByGUID ¶
ByGUID identifies an object by objectGUID. This is the canonical form: it survives rename and move, which DN and sAMAccountName do not.
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 )
type Logger ¶
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 ¶
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 ¶
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) Search ¶ added in v0.4.0
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 ¶
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".
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 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 ¶ added in v0.5.0
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.
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 (Secret) MarshalJSON ¶
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.
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
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) Search ¶ added in v0.10.0
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 ¶
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) Search ¶ added in v0.4.0
Search returns every user under q.SearchBase matching q.Filter.
func (*UserClient) SetPassword ¶
SetPassword resets the account's password through Set-ADAccountPassword -Reset. The error it returns never echoes the value.
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.
Source Files
¶
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. |