User accounts in Active Directory

A domain user account is an object in the directory with a few hundred possible attributes. A handful matter every day, because they decide how a person signs in and how Windows recognizes them:

Attribute Example What it is for
sAMAccountName lromero The pre-Windows 2000 logon name, used as AD\lromero; at most 20 characters and unique in the domain.
userPrincipalName lucia.romero@example.com The user principal name (UPN), an email-style sign-in name. The suffix can differ from the domain name, so it can match the email address and the cloud sign-in name.
objectSid S-1-5-21-…-1107 The security identifier (SID) that access control lists and tokens actually use.
objectGUID a8f3… A globally unique identifier that never changes, also not when the account is renamed or moved; sync tools use it to match objects.
distinguishedName CN=Lucia Romero,OU=Billing,DC=ad,DC=example,DC=com Where the object sits in the tree; it changes when the account moves to another OU (see Directory Services and LDAP).
userAccountControl 512 A set of flags: disabled, password never expires, smart card required, and more (512 means a normal, enabled account).

SIDs and why names do not matter

A domain account's SID is the domain's SID followed by a relative identifier (RID), a number handed out by the DC from a pool it got from the RID master (see Active Directory Fundamentals). When a user signs in, the DC puts the user's SID and the SIDs of all their groups into an access token; every permission check compares those SIDs with the access control lists on files, shares and objects. You can see your own token with whoami /groups, as shown on Users and Permissions.

The consequence is practical. If someone deletes lromero and creates a new account with the same name, the new account has a new RID, and none of the old permissions, mailbox links or group memberships apply. That is why leavers are disabled first and only deleted after a retention period, and why the AD Recycle Bin is worth enabling: it restores a deleted object with its original SID and attributes. The full joiner-mover-leaver process is on Identity Lifecycle.

Computer accounts and the secure channel

A computer account is the directory object that represents a domain-joined machine, with its own SID and its own password, so that the computer itself can authenticate to domain controllers. Its sAMAccountName ends with a dollar sign: the laptop PC-BIL-014 is PC-BIL-014$ in AD. Group Policy, Kerberos tickets for services and the DNS registration of a member all rely on the computer being able to prove who it is.

The machine password is random and changed automatically by the computer, by default every 30 days. The computer keeps the current and the previous password; the DC keeps the same. The authenticated connection a member sets up with a DC using that password is called the secure channel. When the two sides disagree, for example after a virtual machine is reverted to a snapshot from two months ago, or a PC was off the network for a long time and then reimaged with an old image, users see:

The trust relationship between this workstation and the primary domain failed.

The fix is to reset the machine password rather than to delete and recreate the computer account (which would lose its group memberships and delegated permissions). Sign in with a local administrator account, then run Test-ComputerSecureChannel in Windows PowerShell 5.1 (the cmdlet is not part of PowerShell 7; there, start powershell.exe or use netdom resetpwd):

Test-ComputerSecureChannel -Verbose                # is the channel broken?
Test-ComputerSecureChannel -Repair -Credential (Get-Credential AD\helpdesk-admin)
VERBOSE: Performing the operation "Test-ComputerSecureChannel" on target "PC-BIL-014".
VERBOSE: The secure channel between the local computer and the domain ad.example.com is broken.
False
True

Groups: type and scope

Every AD group has two independent properties: a type (what the group is for) and a scope (who can be a member and where it can be used).

Security groups and distribution groups

A security group has a SID, ends up in members' access tokens and can therefore be given permissions. A distribution group is a group without a security identity, used only as an email distribution list; it never appears in a token and cannot be used to grant access. A security group can also have an email address, so a mail-enabled security group does both jobs. A team that asks for "a group for the billing share" needs a security group, however much it looks like an email list in the address book.

Group scopes

A group scope is the property of an Active Directory group that decides which accounts and groups can be its members and in which domains it can be given permissions. In a forest with a single domain the differences seem academic; they matter as soon as there is a second domain or a trust with another forest.

Scope Can contain Can be given permissions in Typical use
Domain local Accounts and global groups from any trusted domain; universal groups from the same forest; domain local groups from its own domain Its own domain only Access to one resource (a share, a printer, an application)
Global Accounts and global groups from its own domain only Any domain in the forest and any trusting domain People with the same role or department
Universal Accounts, global and universal groups from any domain in the forest Any domain in the forest (and in trusting forests) Roles that span domains

Universal group memberships are stored in the global catalog, so every change replicates to every global catalog server in the forest. That is one reason to put global groups, not individual users, into universal groups: the membership of the universal group then rarely changes.

When a membership change takes effect

Group SIDs are collected into the token when the user signs in (and into the Kerberos ticket the user receives). Adding Lucía to a group therefore does nothing for a session that is already open: she has to sign out and in again, and a server that she already has a session with may keep the old view until that session ends. This is one of the most common "I added her but it still says access denied" tickets. For computer accounts the same applies: a computer added to a group sees the change after a restart.

Nesting groups with AGDLP

AGDLP is Microsoft's long-standing pattern for nesting groups: put user Accounts in Global groups that describe a role, put those global groups in Domain Local groups that describe access to one resource, and grant the Permissions only to the domain local groups. Each layer answers one question:

AGDLP. Accounts amara and mei are members of the global group GG-Service-Desk. That global group is a member of the domain local group DL-BillingShare-Read. Only the domain local group appears in the access list of the billing share, with Read permission. A · Accounts G · Global group DL · Domain local group P · Permission amara mei GG-Service-Desk DL-BillingShare-Read \\fs01\billing Read who which role which access to what only DL groups here AGUDLP: in a multi-domain forest, a universal group sits between G and DL.
The data owner approves membership of the domain local group; the manager of a team maintains the global group. Nobody edits the access list of the share again.

Why bother with two layers? Because roles and resources change for different reasons. When a new service desk analyst starts, only GG-Service-Desk changes, and the analyst gets every resource the role needs at once. When the billing share must also be readable by auditors, only DL-BillingShare-Read changes. Access lists on servers stay small and stable, and an access review can ask the clear question "should the service desk have read access to billing?" instead of reading a list of fifty names. This is role-based access control built from AD groups; the models behind it are on Authorization Models.

AGUDLP adds a Universal group between the global and the domain local layer when a role has members in several domains of a forest: global groups from each domain go into one universal group, which goes into the domain local groups. In a single-domain forest, AGDLP is enough.

Effective access through nested groups

To answer "why can this person open that share?", a tool has to follow nesting in both directions and must not loop forever when two groups contain each other, which AD allows. This sketch resolves every group a user is in, directly or indirectly, with a breadth-first search that remembers which groups it has seen, then collects the permissions the share's access list grants to any of them.

# Effective permissions through nested AD groups (AGDLP), safe against loops.
from collections import deque

# Group -> direct members (user accounts or other groups).
MEMBERS = {
    # Global groups: people with the same role.
    "GG-Billing-Clerks": ["lucia"],
    "GG-Billing-Managers": ["femke"],
    "GG-Service-Desk": ["amara", "mei"],
    # Domain local groups: one per resource and access level.
    "DL-BillingShare-Read": ["GG-Service-Desk", "GG-Billing-Managers"],
    "DL-BillingShare-Modify": ["GG-Billing-Clerks", "GG-Billing-Managers"],
    # A mistake from years ago: two groups that contain each other.
    "GG-Finance-All": ["GG-Billing-Managers", "GG-Finance-Legacy"],
    "GG-Finance-Legacy": ["GG-Finance-All"],
}

# The share's access list names domain local groups only.
SHARE_ACL = {
    "DL-BillingShare-Read": {"read"},
    "DL-BillingShare-Modify": {"read", "write"},
}


def groups_of(principal):
    """Every group that contains principal, directly or through nesting."""
    found = set()
    queue = deque([principal])
    while queue:
        current = queue.popleft()
        for group, members in MEMBERS.items():
            if current in members and group not in found:
                found.add(group)  # seen once, never queued again: loops end
                queue.append(group)
    return found


for user in ["amara", "femke", "lucia", "ravi"]:
    groups = groups_of(user)
    rights = set()
    for group in groups:
        rights |= SHARE_ACL.get(group, set())
    print(f"{user:<6} {', '.join(sorted(rights)) or 'no access'}")
    for group in sorted(groups):
        print(f"         member of {group}")
amara  read
         member of DL-BillingShare-Read
         member of GG-Service-Desk
femke  read, write
         member of DL-BillingShare-Modify
         member of DL-BillingShare-Read
         member of GG-Billing-Managers
         member of GG-Finance-All
         member of GG-Finance-Legacy
lucia  read, write
         member of DL-BillingShare-Modify
         member of GG-Billing-Clerks
ravi   no access

The two finance groups that contain each other are visited once each and the search ends. Without the found check, the loop would run until memory ran out. Real tools do the same: PowerShell's Get-ADGroupMember -Recursive (below) and the "effective access" tab in a folder's advanced security settings.

Service accounts and managed service accounts

Many Windows services, scheduled tasks and applications need a domain identity to reach a database or a file share. The classic answer is a service account: an ordinary user account, such as svc-portal, whose password is typed into the service configuration. In practice those accounts collect risks:

  • The password is set by hand, is known to several people and is set to "never expires", because changing it means updating every server that uses it.
  • The account often has far more rights than it needs, sometimes Domain Admin, "because the installer asked for it".
  • If it has a service principal name (SPN), any domain user can request a Kerberos service ticket for it and try to crack the password offline: Kerberoasting (MITRE ATT&CK T1558.003), explained with its defenses on Attacks on Identity and Kerberos and NTLM. A long random password defeats it; a human-chosen one often does not.

Group Managed Service Accounts

A group Managed Service Account (gMSA) is an AD account type for services whose long, random password is generated and rotated automatically by the domain controllers, and which only the computers you list may retrieve. Nobody knows or types the password, it changes every 30 days by default, and the same account can run a service on several servers (a web farm, for example). gMSAs arrived with Windows Server 2012; the older standalone managed service account worked on one computer only.

# Once per forest: the KDS root key the DCs derive gMSA passwords from
Add-KdsRootKey -EffectiveImmediately

# The account, and which servers may fetch its password
New-ADServiceAccount -Name svc-portal -DNSHostName svc-portal.ad.example.com `
  -PrincipalsAllowedToRetrieveManagedPassword GG-Portal-Servers

# On each portal server (a member of GG-Portal-Servers):
Install-ADServiceAccount -Identity svc-portal
Test-ADServiceAccount -Identity svc-portal

The service is then configured to run as AD\svc-portal$ with an empty password field; Windows fetches the current password itself. Add-KdsRootKey -EffectiveImmediately still waits for AD replication before the key can be used, so in a lab with several DCs the first gMSA may fail for a while.

Delegated Managed Service Accounts (Windows Server 2025)

Windows Server 2025 adds the delegated Managed Service Account (dMSA). It is designed to replace an existing classic service account: the dMSA takes over its role, is bound to specific machines and uses random keys, and the old account's password is disabled, which removes the Kerberoasting target. Unlike a gMSA, a dMSA is tied to the machine identity. Migration works from a classic service account only, not from an MSA or gMSA.

For workloads in the cloud the same idea, an identity without a password anyone handles, is called a managed identity; see Identity for Applications and Machines.

The Protected Users group

The Protected Users group is a built-in global security group whose members automatically get stricter authentication protections that cannot be switched off per account. With a domain functional level of Windows Server 2012 R2 or higher, members:

  • cannot authenticate with NTLM, only with Kerberos;
  • cannot use DES or RC4 in Kerberos preauthentication;
  • cannot be delegated (constrained or unconstrained);
  • get Kerberos ticket-granting tickets limited to 4 hours, with no renewal beyond that.

Their credentials are also not cached on the computers they sign in to, which means a member cannot sign in to a laptop that is offline. That makes Protected Users a strong fit for administrator accounts, which should never sign in offline or be delegated anyway, and a poor fit for ordinary mobile users. Do not add service or computer accounts: services that depend on NTLM or delegation break. Add a few admin accounts first, test the tools they use, then widen. Privileged accounts are covered further on Privileged Access Management.

PowerShell for bulk work

Active Directory Users and Computers (dsa.msc) is fine for one account. For fifty, or for a question such as "who in Billing has not signed in for 90 days?", use the ActiveDirectory module for PowerShell (part of the Remote Server Administration Tools). Commands are illustrative; there is no domain controller behind this page.

Get-ADUser -Filter "Department -eq 'Billing' -and Enabled -eq 'True'" `
  -Properties Department, LastLogonDate |
  Sort-Object LastLogonDate |
  Select-Object SamAccountName, UserPrincipalName, LastLogonDate
SamAccountName UserPrincipalName          LastLogonDate
-------------- -----------------          -------------
fjansen-old    femke.jansen2@example.com  3/14/2026 8:02:11 AM
lromero        lucia.romero@example.com   9/30/2026 7:58:40 AM
fjansen        femke.jansen@example.com   10/1/2026 8:15:03 AM

-Filter is evaluated on the DC, so only matching users cross the network; avoid Get-ADUser -Filter * followed by Where-Object in a large domain. The old duplicate account at the top of this list is exactly what such a query is for. Creating accounts from an HR export and adding them to their role group:

Import-Csv .\new-starters.csv | ForEach-Object {
    New-ADUser -Name $_.DisplayName -GivenName $_.GivenName -Surname $_.Surname `
      -SamAccountName $_.SamAccountName `
      -UserPrincipalName "$($_.Alias)@example.com" `
      -Department $_.Department `
      -Path "OU=Users,OU=Billing,DC=ad,DC=example,DC=com" `
      -Enabled $false        # enabled on day one, with a one-time sign-in method
    Add-ADGroupMember -Identity GG-Billing-Clerks -Members $_.SamAccountName
}

The accounts are created disabled and without a password in the file: a CSV with initial passwords is a credential leak waiting to happen. Most *-AD* commands that change something accept -WhatIf; run a bulk script with it first. To see who really ends up in a group through nesting:

Get-ADGroupMember -Identity DL-BillingShare-Modify -Recursive |
  Select-Object Name, SamAccountName, objectClass
Name          SamAccountName objectClass
----          -------------- -----------
Lucía Romero  lromero        user
Femke Jansen  fjansen        user

Accounts in Microsoft Entra ID, the cloud directory, are managed with the Microsoft Graph PowerShell SDK (Connect-MgGraph, Get-MgUser, New-MgGroupMember); the older MSOnline and AzureAD modules have been retired and no longer work (2025). See Microsoft Entra ID.

Common mistakes

  • Nesting nobody understands. Five levels of groups, some created for a project that ended years ago, and loops between them. Keep to two layers (role and resource), use a naming convention that shows the scope and purpose (GG-, DL-), and fill in the description and the "managed by" field of every group.
  • Distribution groups for access. They have no SID in the token, so the permission silently does nothing.
  • Delete and recreate. A new account with the same name is a different SID; disable instead, and use the Recycle Bin for mistakes.
  • Accounts left in the default containers. The built-in CN=Users and CN=Computers containers are not OUs, so OU-linked Group Policy does not reach them; see Group Policy.
  • Service accounts with interactive rights. A service account that can sign in to desktops is a target and an audit headache. Deny interactive sign-in, and move it to a gMSA.