Documentation
¶
Overview ¶
Package adclient interacts with AD domain controllers
Index ¶
- Variables
- func AssignTemporaryPassword(s Client, args PasswordArgs) (*string, error)
- func IsAccessDenied(err error) bool
- func IsAccountLocked(s Client, args UnlockArgs) (bool, error)
- func SetVerboseLogging(enabled bool)
- func StateProtocolVersion() string
- func UnlockAccount(s Client, args UnlockArgs) error
- func Verbosef(format string, args ...any)
- func VerifyAccounts(s Client, scope Scope, server string, immutableIDs []string) (map[string]AccountState, error)
- type ADGroups
- type AccountState
- type Client
- type Domain
- type GetADGroupArgs
- type GetADUserArgs
- type Group
- type IneligibleReason
- type ListADUsersArgs
- type ListADUsersDiagnostics
- type ListADUsersResult
- type PasswordArgs
- type PasswordPolicy
- type PowershellClient
- type PrimaryGroups
- type ProviderState
- type Scope
- type ServerInfo
- type UnlockArgs
- type User
- type Users
Constants ¶
This section is empty.
Variables ¶
var ErrEnumerationInvalidated = errors.New("the enumeration cannot be continued")
ErrEnumerationInvalidated means a cursor or durable state cannot be reused, for example after a controller incarnation or scope change. Callers use the sentinel to classify the wire error.
Functions ¶
func AssignTemporaryPassword ¶
func AssignTemporaryPassword(s Client, args PasswordArgs) (*string, error)
AssignTemporaryPassword generates a password no shorter than minTemporaryPasswordLength and no shorter than the resolved AD minimum, assigns it, and best-effort requests a change at the account's next logon.
The policy reads and reset writes deliberately leave -Server unset. The provider's configured controller is for consistency-sensitive directory reads and may be an RODC, where Set-ADAccountPassword cannot run. The controller that accepts the reset enforces the authoritative policy; if a policy read from another replica was stale and allowed a password that is too short, the reset fails rather than committing a password weaker than that controller permits.
func IsAccessDenied ¶ added in v0.1.28
IsAccessDenied reports whether err is the directory refusing an operation the agent is not permitted to perform.
This exists so that the one failure every AD deployment hits - a service account that was never delegated the Reset Password right - says so, instead of arriving as a wall of PowerShell. A false negative costs nothing but the better message.
func IsAccountLocked ¶
func IsAccountLocked(s Client, args UnlockArgs) (bool, error)
IsAccountLocked will return a boolean indicating account lockout status.
This read and UnlockAccount deliberately leave -Server unset. The provider's configured controller is for consistency-sensitive directory reads and may be an RODC, while Unlock-ADAccount requires a writable target. Replica disagreement can make the gate ask the caller to retry, but the write remains authoritative: no successful unlock is inferred from this read alone.
func SetVerboseLogging ¶ added in v0.1.28
func SetVerboseLogging(enabled bool)
SetVerboseLogging enables high-volume diagnostic logging. Operational logs remain enabled regardless; healthy per-request and per-object details do not.
func StateProtocolVersion ¶ added in v0.1.28
func StateProtocolVersion() string
StateProtocolVersion identifies the cursor and provider-state encodings. Agents serving one directory must report the same value because requests have no agent affinity.
func UnlockAccount ¶
func UnlockAccount(s Client, args UnlockArgs) error
UnlockAccount will unlock a user account. See IsAccountLocked for why it does not name the provider's configured read controller.
func Verbosef ¶ added in v0.1.28
Verbosef logs diagnostic detail only when the operator explicitly requested it. Keep object-level logging out even here: summaries are both faster and easier to use on a large directory.
func VerifyAccounts ¶ added in v0.1.28
func VerifyAccounts(s Client, scope Scope, server string, immutableIDs []string) (map[string]AccountState, error)
VerifyAccounts reports the current state of accounts by immutable ID.
The server uses this result before pruning accounts omitted by enumeration. Batched filter queries report absence by set difference.
Types ¶
type ADGroups ¶
type ADGroups []*Group
ADGroups is a list of groups returned by a group listing.
func GetADGroups ¶
func GetADGroups(s Client, args GetADGroupArgs) (ADGroups, *string, error)
GetADGroups returns one bounded page matching a name prefix. AD orders names case-insensitively using en-US collation, with ObjectGUID as the tie-breaker. Pagination assumes LDAP name-range comparisons agree at page boundaries.
type AccountState ¶ added in v0.1.28
type AccountState string
AccountState is what verification found for one account.
const ( // AccountEligible means the account exists and a full enumeration would // return it. AccountEligible AccountState = "eligible" // AccountSuspended means the account exists but is one a full enumeration // would not have returned - disabled, or otherwise excluded by our own rules. // It is distinguished from AccountRemoved only so the server can say which of // the two it acted on; both are grounds for removing the entry. AccountSuspended AccountState = "suspended" // AccountRemoved means the account does not exist, or is outside the // configured scope. AccountRemoved AccountState = "removed" // AccountUnknown means we could not tell. Callers must not act on it. AccountUnknown AccountState = "unknown" )
The states verification can report.
type Domain ¶
type Domain struct {
Forest string `json:"Forest"`
NetBIOSName string `json:"NetBIOSName"`
DNSRoot string `json:"DNSRoot"`
Name string `json:"Name"`
// DomainSID is the domain's security identifier, in its S-1-5-21-... string
// form.
//
// This is the only name for the domain that is actually stable. The other
// four are all renameable - a domain rename changes the DNS root, the
// NetBIOS name and the flat name together - and the SID is assigned when the
// domain is created and survives every one of them. Anything asking "is this
// the same directory as the one I saw before" has to ask it of this field.
DomainSID string `json:"DomainSID"`
}
Domain represents the domain response
func GetADDomain ¶
GetADDomain returns domain information
type GetADGroupArgs ¶
type GetADGroupArgs struct {
NamePrefix *string
MaxCount *int64
Cursor *string
// Server is the controller a first page is read from. A continuation uses the
// one its cursor names, so that every page of one listing sees a single
// replica's view. Empty means discover.
//
// It is taken as already validated: it reaches here from the agent's own
// configuration, where it was checked once, and re-checking it per page would
// spend exactly the command this field exists to avoid.
Server string
// Scope restricts which containers groups are listed from. An empty scope
// means the whole domain.
//
// Deliberately its own scope rather than the one that restricts accounts.
// Groups conventionally live in a container of their own, so applying the
// account bases here would list nothing at all in an ordinary domain. The same
// scope controls membership projection, so the groups offered for policy and
// the memberships those policies evaluate cannot disagree.
Scope Scope
}
GetADGroupArgs is a struct of request args
type GetADUserArgs ¶
type GetADUserArgs struct {
Identity string
// Server is the domain controller to read from. Empty means whichever one
// PowerShell picks, which is only safe where the request makes no other
// read to disagree with.
Server string
}
GetADUserArgs contains arguments for an indexed single-account lookup.
type Group ¶ added in v0.1.28
Group is the identity of an Active Directory group used by both group listings and account membership projection.
func GetADUserGroupsWithPrimary ¶ added in v0.1.28
func GetADUserGroupsWithPrimary(s Client, server string, groupScope Scope, user *User, primary PrimaryGroups) ([]Group, error)
GetADUserGroupsWithPrimary resolves an account's memberships using a previously populated primary-group lookup. AD does not include transitive parent groups in memberOf, and primaryGroupID is resolved separately because memberOf omits it. Resolution is complete-or-error and must use the controller that supplied the account so recovery policy cannot consume a partial membership set. Keep this limitation in sync with devdocs/content/docs/diragentad/index.md.
type IneligibleReason ¶ added in v0.1.28
type IneligibleReason string
IneligibleReason says why an account is not syncable. It is a small closed set rather than free text so the counts can be logged per reason.
const ( ReasonEligible IneligibleReason = "" ReasonNoObject IneligibleReason = "no object" ReasonSystemObject IneligibleReason = "system object" ReasonDisabled IneligibleReason = "disabled" ReasonOutOfScope IneligibleReason = "out of scope" ReasonNoDN IneligibleReason = "no distinguished name" ReasonBadDN IneligibleReason = "unreadable distinguished name" )
Reasons an account is not syncable.
func (IneligibleReason) Unplaceable ¶ added in v0.1.28
func (r IneligibleReason) Unplaceable() bool
Unplaceable reports whether the reason means we could not decide where the account sits, rather than that we decided it sits outside the scope.
Verification treats unplaceable accounts as unknown rather than removed so malformed projection data cannot authorize deletion.
type ListADUsersArgs ¶
type ListADUsersArgs struct {
// Cursor continues an enumeration in progress.
Cursor *string
// ProviderState is the watermark from the last completed enumeration. Its
// absence means a full reconciliation is being asked for.
ProviderState *string
// Scope restricts which accounts are eligible.
Scope Scope
// GroupScope restricts which direct group memberships this directory stores
// and enforces. Empty means every group in the account's local domain.
GroupScope Scope
// Server is the controller a *new* walk starts against. A walk already in
// progress, or one resuming above a stored watermark, uses the controller its
// cursor or state names instead - sequence numbers mean nothing on another
// one, so that is not a preference to be overridden. Empty means discover.
//
// So changing which controller this agent prefers does not move an
// enumeration that already has a watermark. startOrResumeWalk says so in the
// log when the two disagree, because the remedy - a full synchronization -
// is not something an operator would guess at.
Server string
// Deadline is the instant the caller stops waiting for the request this page
// belongs to. The zero time means unbounded.
//
// It exists because overrunning it is not a slow answer but a discarded
// one: the server closes the connection and everything the walk learned goes
// with it. Rather than risk that, a page stops early and returns a cursor -
// the protocol already handles a short page, and the cursor carries the
// window the walk had arrived at.
//
// An instant rather than a duration, because a duration is only the right
// bound if the walk starts counting the moment the caller does. It does not:
// resolving the scope, the group containers and the pinned controller all
// happen first, and each of those is a round trip bounded only by
// commandTimeout. Measured from here, a page could grant itself a deadline
// minutes past the one the server was actually keeping, spend the whole
// reserve before its first range, and lose the finished page to a recycled
// connection. The caller stamps this on arrival so both sides bound the same
// interval.
Deadline time.Time
}
ListADUsersArgs is request args to user functions
type ListADUsersDiagnostics ¶ added in v0.1.28
type ListADUsersDiagnostics struct {
Skipped map[IneligibleReason]int `json:"skipped,omitempty"`
MissingIdentifiers int `json:"missing_identifiers,omitempty"`
InvalidTimestamps int `json:"invalid_timestamps,omitempty"`
UnresolvedMemberships int `json:"unresolved_memberships,omitempty"`
// Separate from UnresolvedMemberships because the two have opposite
// remedies. An unresolved membership was asked about and not answered, which
// a re-synchronization commonly fixes on its own; an unclassifiable one was
// never asked about, because its distinguished name would not parse here.
// Reported as one number, the second sent an operator to check replication
// for a fault that only editing the object in AD can clear.
UnclassifiableMemberships int `json:"unclassifiable_memberships,omitempty"`
RecoveredRangeTimeouts int `json:"recovered_range_timeouts,omitempty"`
PreferredDCReported bool `json:"preferred_dc_reported,omitempty"`
}
ListADUsersDiagnostics contains bounded, page-level counts for conditions that should be summarized once by the provider instead of logged per object.
type ListADUsersResult ¶ added in v0.1.28
type ListADUsersResult struct {
Users Users
Diagnostics ListADUsersDiagnostics
// NextCursor continues this enumeration. Nil on the final page.
NextCursor *string
// NextProviderState is the watermark to remember for next time. Set only on
// the final page, because only then is the enumeration known to have
// completed - and set even when the page is empty, since a nil state tells
// the server to reconcile again.
NextProviderState *string
// contains filtered or unexported fields
}
ListADUsersResult is one page of an enumeration.
func ListADUsers ¶
func ListADUsers(s Client, args ListADUsersArgs) (*ListADUsersResult, error)
ListADUsers returns one page of accounts by walking closed ranges of uSNChanged.
Each closed range is consumed whole because multiple objects may share a USN. The numeric cursor does not depend on PowerShell session state.
func (*ListADUsersResult) AddProjectionDiagnostics ¶ added in v0.1.28
func (r *ListADUsersResult) AddProjectionDiagnostics(missingIdentifiers, invalidTimestamps int) error
AddProjectionDiagnostics adds counts discovered while the provider projects AD users into protocol accounts. When another page follows, the counts are written into its opaque cursor so they survive cross-agent pagination.
The position is re-rendered from the cursor this result kept, not parsed back out of the token. Decoding a string this process produced a moment earlier meant two failure paths for a thing that cannot fail, and it left the counts written twice - once here and once into the decoded copy - so the result and its own cursor could disagree if either write were missed. The cursor takes r.Diagnostics whole instead, which makes them the same number by construction.
type PasswordArgs ¶
type PasswordArgs struct {
UserImmutableID string
}
PasswordArgs is the args to password functions
type PasswordPolicy ¶
type PasswordPolicy struct {
MinPasswordLength *int `json:"MinPasswordLength,omitempty"`
}
PasswordPolicy is the representation of the policy in the system
func GetPasswordPolicy ¶
func GetPasswordPolicy(s Client, immutableID string) (*PasswordPolicy, error)
GetPasswordPolicy gets the password policy that applies to the given user: the fine grained policy AD itself resolves for the account, or the default domain policy when no fine grained policy applies.
AD resolves fine-grained policy precedence; this function falls back to the default domain policy when none applies or the resultant policy cannot be read.
type PowershellClient ¶
type PowershellClient struct {
// contains filtered or unexported fields
}
PowershellClient is used for invoking commands in an underlying powershell process.
func (*PowershellClient) Close ¶
func (s *PowershellClient) Close() error
Close terminates the connection
func (*PowershellClient) Execute ¶
func (s *PowershellClient) Execute(cmd string) (string, error)
Execute runs the cmd
PowerShell errors are classified from their ErrorRecords rather than from the process pipe they happened to use. stdout is returned with an error so callers that can make use of a partial result may inspect it.
func (*PowershellClient) ResetPassword ¶ added in v0.1.28
func (s *PowershellClient) ResetPassword(identity, password string) error
ResetPassword runs the secret-bearing command in a short-lived PowerShell process. The fixed script is supplied through -Command, while the password is supplied through redirected stdin. Consequently the password is data, not PowerShell source, and Script Block Logging cannot record it as Event 4104.
type PrimaryGroups ¶ added in v0.1.28
type PrimaryGroups struct {
// contains filtered or unexported fields
}
PrimaryGroups holds primary-group results that can be reused across account lookups.
func ResolvePrimaryGroups ¶ added in v0.1.28
func ResolvePrimaryGroups(s Client, server string, users Users) (PrimaryGroups, error)
ResolvePrimaryGroups resolves distinct primary groups in one or more batched AD queries. This live lookup is unbounded because callers require a complete answer.
type ProviderState ¶ added in v0.1.28
type ProviderState struct {
V int `json:"v"`
// DC and Inv identify the domain controller the watermark came from.
// Sequence numbers are per-DC and are not replicated, so a watermark is only
// meaningful against the same incarnation of the same DC.
DC string `json:"dc"`
Inv string `json:"inv,omitempty"`
// CompletedHigh is the sequence number the last completed enumeration
// snapshotted. The next one starts immediately above it.
//
// It is deliberately the snapshot the enumeration *started* with rather than
// the highest number it saw. An account written while the enumeration was
// running lands above the snapshot and is therefore picked up next time, at
// the cost of occasionally re-reporting one - which is free, because applying
// an account twice is idempotent.
CompletedHigh int64 `json:"completed_high"`
// Scope is the hash of the search bases that enumeration ran with.
//
// Without this, widening the scope loses accounts permanently and silently:
// every account newly in scope already has a sequence number below the
// watermark, so no incremental enumeration would ever return it.
Scope string `json:"scope"`
// GroupScope is the hash of the group bases used when membership was last
// synchronized. A change requires every account's memberships to be rebuilt.
GroupScope string `json:"group_scope"`
}
ProviderState is what the agent remembers between synchronizations.
It is the completed-enumeration watermark; walkCursor tracks position within one enumeration. Its absence requests a full reconciliation.
func DecodeProviderState ¶ added in v0.1.28
func DecodeProviderState(encoded string, sc Scope, groupScope Scope) (ProviderState, error)
DecodeProviderState parses and validates durable state the server handed back.
Invalid state is an error because silently changing between full and incremental semantics could cause incorrect deletions. Full scopes are passed so mismatch errors can describe the active configuration.
type Scope ¶ added in v0.1.28
type Scope struct {
// SearchBases are distinguished names whose subtrees are in scope.
//
// Provider paths resolve configured bases to AD's canonical distinguished
// names before using them.
SearchBases []string
}
Scope identifies directory subtrees. Empty SearchBases means the local domain.
func ResolveScope ¶ added in v0.1.28
ResolveScope replaces every configured search base with the distinguished name the directory itself reports for it, and reports an error unless every base names a container that exists.
Account and group scopes use the same rules. Rejecting unresolved or leaf objects prevents a valid-but-empty query from masquerading as an empty scope.
The name is read back rather than echoed because AD accepts spellings the local parser in contains() does not treat identically - spaces after commas, hex escapes, quoted relative values. Carrying the directory's own spelling forward keeps containment evaluated against the name that scoped the query.
The check uses AD's selected controller, so very recent containers may require a retry after replication.
func (Scope) Describe ¶ added in v0.1.28
Describe renders the scope for an operator-facing message.
Bases are quoted and use the canonical spellings returned by AD.
func (Scope) Eligible ¶ added in v0.1.28
func (sc Scope) Eligible(user *User) (bool, IneligibleReason)
Eligible reports whether user should become a directory entry, and if not, why.
This is the single eligibility predicate. Enumeration, verification of candidates for deletion, and the precondition on a recovery operation all have to agree: a verification looser than enumeration makes filtered objects immortal, and one that is tighter deletes live accounts.
func (Scope) Hash ¶ added in v0.1.28
Hash identifies the scope, so a walk can be abandoned when the configuration changes underneath it.
Provider state includes this hash because scope changes require a full walk.
func (Scope) Restricted ¶ added in v0.1.28
Restricted reports whether any usable search base is configured.
Empty entries are ignored consistently with Hash and query construction.
type ServerInfo ¶ added in v0.1.28
type ServerInfo struct {
// DC is the host name to pass to -Server for every query in the walk.
DC string `json:"DC"`
// HighestUSN is the top of this DC's sequence counter. Any write after this
// point lands above it, which is what bounds the walk.
HighestUSN int64 `json:"HighestUSN"`
// InvocationID identifies this DC's incarnation. A change means it was
// restored from backup and its sequence numbers are no longer comparable.
// Empty if it could not be read, which is not fatal - it only means that
// particular check cannot be made.
InvocationID string `json:"InvocationID"`
}
ServerInfo identifies a domain controller and snapshots its highest committed USN and invocation ID.
func GetADServerInfo ¶ added in v0.1.28
func GetADServerInfo(s Client) (*ServerInfo, error)
GetADServerInfo pins a domain controller and snapshots its change counter.
func GetPinnedADServerInfo ¶ added in v0.1.28
func GetPinnedADServerInfo(s Client, server string) (*ServerInfo, error)
GetPinnedADServerInfo validates an already-chosen DC against the current domain before contacting it, then re-reads its counter.
type UnlockArgs ¶
type UnlockArgs struct {
UserImmutableID string
}
UnlockArgs is request arguments to unlock functions
type User ¶
type User struct {
SamAccountName string `json:"SamAccountName"`
DistinguishedName string `json:"DistinguishedName"`
Name string `json:"Name"`
EmailAddress string `json:"EmailAddress"`
ObjectGUID string `json:"ObjectGUID"`
MemberOf []string `json:"MemberOf"`
LockedOut bool `json:"LockedOut"`
WhenChanged string `json:"whenChanged"`
// UserPrincipalName is the logon name, which looks like an email address and
// usually is one.
//
// It is projected because it is the identifier a person actually types.
// mail is populated by Exchange rather than by account creation, so an
// on-prem domain without it leaves every account with no email attribute at
// all - while the UPN is what those same people sign in with and what a
// federated login carries as its SAML NameID. Storing only mail and
// sAMAccountName filed such an account under an identifier nobody would enter.
UserPrincipalName string `json:"UserPrincipalName"`
// DisplayName, GivenName and Surname are the other places AD keeps a person's
// name. They are projected because Name alone is a poor answer to the only
// question the server asks of it - see PersonName.
DisplayName string `json:"DisplayName"`
GivenName string `json:"GivenName"`
Surname string `json:"Surname"`
// USNChanged is this DC's sequence number for the last change to the object.
// It is the walk's indexed integer range key. Several objects may share the
// same value, so pagination consumes complete USN ranges rather than treating
// it as a unique object cursor.
USNChanged int64 `json:"USNChanged"`
// Enabled is a pointer so that a query which did not ask for the property is
// distinguishable from an account AD reported as disabled.
Enabled *bool `json:"Enabled"`
// AdminCount is Active Directory's own marker for an administratively
// privileged account. SDProp stamps it on the built-in Administrator, on
// krbtgt, and on every member of a protected administrative group - including
// members that reach one through a nested group, which memberOf does not
// report at all.
//
// A pointer for the same reason Enabled is one, but the absence is read the
// other way round. AD leaves the attribute unset on an ordinary account, so
// "no value" and "not asked for" would be the same JSON null;
// pointReadAttributes coerces it to an integer precisely so that a projection
// which asked always emits a number, and nil means nobody asked.
// requireEligibleAccount refuses on nil. See PrivilegeKnown.
AdminCount *int `json:"AdminCount"`
// SID is the account's own security identifier, in its S-1-5-21-... string
// form.
//
// Projected for one reason: it carries the domain prefix that turns
// PrimaryGroupID from a relative identifier into a group this agent can look
// up. See primaryGroupSID. Reading the prefix off the account rather than off
// GetADDomain keeps the derivation correct by construction - a primary group
// is a RID in the account's own domain - and means nothing has to cache a
// domain SID across the requests an enumeration is spread over.
SID string `json:"SID"`
// PrimaryGroupID is the relative identifier of the account's primary group.
//
// Active Directory does not list the primary group in memberOf, and does
// not list the account in that group's member attribute either, so this is
// the only place the membership appears at all. Without it a recovery policy
// written against a group that is somebody's primary group matches nobody -
// which fails open for a deny rule.
//
// A pointer for the reason Enabled is one: every security principal has a
// primaryGroupID, so nil means the projection did not ask rather than that
// the account has no primary group. A live recovery-time read refuses an
// account it cannot classify; see GetADUserGroupsWithPrimary.
PrimaryGroupID *int `json:"PrimaryGroupID"`
// Groups is the membership in MemberOf resolved to the identities the server
// stores. Excluded from the wire format on purpose: it is filled in from a
// second query, never parsed out of the first one.
Groups []Group `json:"-"`
}
User is info from AD on a certain user
func (*User) ExternalIDs ¶ added in v0.1.28
ExternalIDs returns the identifiers a person or identity provider may use for this account, in primary-ID order and deduplicated case-insensitively.
func (*User) IsDisabled ¶ added in v0.1.26
IsDisabled reports whether AD said this account is disabled.
An account we have no answer for counts as enabled: a projection that forgets to ask for the property should not silently make every account in the domain unrecoverable.
func (*User) IsPrivileged ¶ added in v0.1.28
IsPrivileged reports whether AD marked this account as administratively privileged. It answers false for an account nothing was asked about; callers that must fail closed check PrivilegeKnown first.
func (*User) PersonName ¶ added in v0.1.28
PersonName is the account's name as a human name. It is what the server stores for the account, and so the string it weighs against the name on a government ID.
Prefer complete structured names, then displayName, then the mandatory CN. A lone given name or surname is not combined because recovery name matching treats incomplete names conservatively.
func (*User) PrivilegeKnown ¶ added in v0.1.28
PrivilegeKnown reports whether the projection this account was read through asked AD about privilege at all.
Separate from IsPrivileged, and deliberately not folded into it the way IsDisabled folds its own absence rule in. IsDisabled can decide once for everyone because there is only one safe reading of a missing Enabled. Here the safe reading differs by caller: a recovery operation must refuse an account it cannot classify, while enumeration must not drop one. Neither answer can be baked into the accessor without being wrong somewhere, so the accessors stay neutral and each caller states its own rule where a reader will see it.
func (*User) UnmarshalJSON ¶ added in v0.1.28
UnmarshalJSON treats WhenChanged as optional metadata. AD normally emits a timestamp string, but a missing, null, or wrongly typed value must not prevent the identity-bearing parts of the account from being decoded. ListAccounts reports such an account without an UpdatedAt value and counts it in the page diagnostics.
type Users ¶
type Users []*User
Users is a list of User values.
func FindADUsersByIdentifier ¶ added in v0.1.28
FindADUsersByIdentifier resolves accounts by an identifier a person would type, such as an email address, a UPN or a logon name.
A filter supports email/UPN and represents absence as an empty result. Values are not necessarily unique. Empty server lets AD select a controller; callers making related reads should pass their pinned controller.
func GetADUser ¶
func GetADUser(s Client, args GetADUserArgs) (Users, error)
GetADUser retrieves one account by an identity AD indexes directly.
-Identity accepts a distinguished name, an ObjectGUID, a SID or a sAMAccountName, and nothing else. Callers holding an identifier a person typed want FindADUsersByIdentifier instead: an email address or a UPN passed here does not return an empty result, it raises ADIdentityNotFoundException.