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.
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 Bool(b bool) *bool
- func String(s string) *string
- type Client
- type Config
- type Credential
- type DeleteOptions
- type Error
- type Group
- type GroupCategory
- type GroupClient
- 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) Update(ctx context.Context, id Identity, spec GroupSpec) (*Group, error)
- type GroupScope
- type GroupSpec
- type Identity
- type Kind
- type Logger
- 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) Update(ctx context.Context, id Identity, spec OUSpec) (*OU, error)
- type OUSpec
- type OptTime
- type ReplicationConfig
- type Result
- type RetryConfig
- type Secret
- 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) 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} )
Kind sentinels for errors.Is.
Functions ¶
Types ¶
type Client ¶
type Client struct {
OU *OUClient
Group *GroupClient
User *UserClient
// 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 Config ¶
type Config struct {
// Transport is how PowerShell reaches the jump box. Required.
Transport Transport
// 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 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 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 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) 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.
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 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 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 )
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 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) 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 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 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 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) 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 |
|---|---|
|
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. |
|
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. |
|
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. |
|
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. |