Choosing a Windows Service Account — LocalSystem, Virtual Accounts, and gMSA
· Go Komura · Windows, Windows Service, Service Accounts, gMSA, LocalSystem, Virtual Accounts, Security, Active Directory, Least Privilege
“An in-house service we had been running as LocalSystem for now was flagged in a security audit as ‘excessive privilege’. What should we change it to?” “The service could not access a shared folder, so we are running it as a domain user. When the password expires the service stops, so we made it never expire and wrote it in plaintext in the runbook.” — Among consultations around customers’ Windows services, these two are staples.
What both sites have in common is that the service’s logon account is frozen as “the setting that happened to work”, not as a design decision. A Windows service always runs in the security context of some account, and that account decides all of what it can do locally, who it looks like from the far side of the network, and who manages the password. Leave this at the default and a vulnerability in one service leads directly to taking over the whole machine, and plaintext passwords scatter into runbooks and scripts.
flowchart TB
accTitle: Three things the logon account decides
accDescr: A service always runs in the security context of some account, and that account decides all of what it can do locally, who it looks like from the far side of the network, and who manages the password
acct["The service's logon account"] --> local["What it can do locally"]
acct --> net["Who it looks like from the far side of the network"]
acct --> pwd["Who manages the password"]
Figure 1: Choosing the logon account is a design decision that simultaneously decides local privileges, network identity, and password management.
There are effectively six choices — LocalSystem, LocalService, NetworkService, a virtual account (NT SERVICE\
How to build the service itself (choosing between Task Scheduler and a service, implementing with a .NET Worker Service) is covered in “How to Build and Operate Windows Services”. This article concentrates on the “logon account”, which is where the most accidents happen.
1. The Bottom Line First
- When in doubt, a virtual account is the first candidate for a service that finishes inside a single machine, and a gMSA is the first candidate for a service that accesses a resource inside the domain with a service-specific identity. Microsoft also gives the guidance to use a managed account (MSA / virtual account) wherever possible.12
- Do not choose LocalSystem because “it works”. The token includes SYSTEM and BUILTIN\Administrators and holds strong privileges such as SeDebugPrivilege, so a takeover loses almost everything on that machine. The default of
sc.exe createbeing LocalSystem is the breeding ground for this accident.34 - The difference between LocalService and NetworkService is network identity. Local privileges are minimal for both, but to the remote side LocalService looks anonymous and NetworkService looks like the computer account.5
- **A virtual account (NT SERVICE\
) is the modern default that can separate identity per service while needing no password management.** You can specify "NT SERVICE\\service-name" directly on an ACL, and SQL Server's default service account is this as well.[^understand-service-accounts][^sql-service-accounts] - When LocalSystem, NetworkService, or a virtual account goes onto the network, it becomes the computer account (DOMAIN\computer-name$). Granting PC$ on the ACL of a shared folder or SQL Server often lets you do without a domain user.36
- A configuration that uses a domain user for a service becomes debt on both password operations and Kerberoasting. SCM logs on with the stored password, so expiry becomes a start failure, and the “never expire + plaintext memo” that avoids that becomes a gift to an attacker.78
- A gMSA has Active Directory generate and rotate the password automatically. The requirements are a domain and a KDS root key, and you set the service to “DOMAIN\account-name$” with the password field empty. Some apps do not support it, so you need to validate in advance.910
- Changing the account changes the assumptions of the profile, %TEMP%, and DPAPI. Data protected with the old account’s DPAPI cannot be decrypted by the new account.
- An inventory of the current state can be confirmed from the logon accounts of the service list and from event ID 4624 (logon type 5).11
In one sentence, this article’s conclusion is: make a configuration that “does not give a service a human password” (built-in accounts, a virtual account, a gMSA) the default, and treat a domain user as a last resort.
2. The Big Picture of the Choices — Six Logon Accounts in a Single Table
One step of review first. At service start, the Service Control Manager (SCM) logs on as the configured account and, on success, creates an access token and assigns it to the service process. Thereafter, every resource access — files, pipes, and the like — is decided by matching this token against the ACL.7 So choosing the logon account is a design that decides the contents of the token passed to the service process. Here are the six choices.
flowchart TB
accTitle: What SCM does at service start
accDescr: SCM logs on as the configured account, on success creates an access token and assigns it to the service process, and thereafter resource access is decided by matching the token against the ACL
scm["SCM"] --> logon["Log on as the configured account"]
logon --> token["Create an access token"]
token --> proc["Assign it to the service process"]
proc --> access["Access to a file or a pipe"]
access --> check{"Does the ACL permit it?"}
check -->|Yes| ok["Access succeeds"]
check -->|No| deny["Access is denied"]
Figure 2: Every resource access of the service is decided by matching the token SCM created at start against the ACL.
| Account | Local privileges | Network identity | Password management | Typical use |
|---|---|---|---|---|
| LocalSystem | Almost unlimited (SYSTEM+Administrators) | Computer account (PC$) | Not needed (no password) | Exceptional services that run as one with the OS |
| LocalService | Minimal (Users-class) | Anonymous | Not needed | Local processing that does not need a network identity |
| NetworkService | Minimal (Users-class) | Computer account (PC$) | Not needed | Low-privilege processing where a machine-level identity is enough |
| Virtual account NT SERVICE\ |
Minimal + grant individually on the ACL | Computer account (PC$) | Not needed (managed automatically) | The default for a business service that runs on a single server |
| Domain user | Only what you grant | That user itself | Manual (expiry, leak, and rotation are all left to people) | A last resort for an app that does not support a gMSA |
| gMSA | Only what you grant | That gMSA itself | AD generates and rotates automatically | When a domain environment needs a service-specific identity |
LocalSystem, LocalService, NetworkService, and a virtual account all have no concept of a password at all. The only ones that log on with a password stored in SCM (= expiry and leak are possible) are a domain user and a local user.73
Below we dig into this table one row at a time.
3. What Is Wrong with LocalSystem
3.1. Stronger Still Than “Run as Administrator”
LocalSystem (display name Local System, NT AUTHORITY\SYSTEM) is a predefined account SCM uses, and it holds extensive privileges on the local computer. The token includes the SIDs of NT AUTHORITY\SYSTEM and BUILTIN\Administrators, and it can access most objects on the system. Further, SeDebugPrivilege, which can debug other processes, and SeTcbPrivilege, which acts as part of the OS, are enabled by default.3
This strength is synonymous with the size of the damage when it is taken over. If a service running as LocalSystem has one arbitrary-code-execution vulnerability, an attacker reaches, in one breath, reading and tampering with every user’s files on that machine (SYSTEM has Full Control by default on NTFS5), reading other processes’ memory via SeDebugPrivilege, and stealing credentials and moving laterally from there (a starting point for Pass-the-Hash and the like). The chain of credential theft and lateral movement is as covered in “NTLM and Kerberos Explained with Diagrams” and “A Practical Guide to Windows LAPS”.
flowchart TB
accTitle: The damage when a LocalSystem service is taken over
accDescr: If a service running as LocalSystem has one arbitrary-code-execution vulnerability, an attacker reaches reading and tampering with every user's files, reading other processes' memory, and stealing credentials and moving laterally
vuln["One arbitrary-code-execution vulnerability"] --> sys["The attacker obtains SYSTEM privileges"]
sys --> files["Reading and tampering with files"]
sys --> mem["Reading other processes' memory"]
sys --> cred["Stealing credentials"]
cred --> lateral["Lateral movement to another machine"]
Figure 3: One vulnerability in a LocalSystem service lets an attacker reach, in one breath, taking the whole machine and a starting point for lateral movement.
3.2. Why It Is Still Chosen
The reason is simple: it is the default, and an access-denied never appears. The default when you omit obj= on sc.exe create is LocalSystem,4 and many old sample-code and installer templates still assume LocalSystem. Because you can stay free of privilege errors during development, the structure that mass-produces “it worked, so leave it” is there. Microsoft’s own documentation also states that most services do not need this high a privilege level, and that if you do not need it you should consider using LocalService or NetworkService.3
flowchart TB
accTitle: The structure that keeps LocalSystem being chosen
accDescr: The default of sc.exe create is LocalSystem, and old sample code and templates also assume LocalSystem, so no access-denied appears during development and a configuration of it worked so leave it is mass-produced
def["The default of sc.exe create"] --> lsys["Created as LocalSystem"]
old["Old samples and templates"] --> lsys
lsys --> noerr["No access-denied during development"]
noerr --> asis["It worked, so leave it"]
asis --> mass["Services with excessive privilege are mass-produced"]
Figure 4: The default and a development experience of “no access-denied” mass-produce services frozen as LocalSystem.
3.3. The Difference from TrustedInstaller — LocalSystem Is Not Unlimited Either
Calling LocalSystem “Windows’ strongest account” is not accurate. Windows Resource Protection (WRP) since Windows Vista permits changes to important OS system files, folders, and registry keys only to TrustedInstaller (the Windows Modules Installer service), and even SYSTEM or an administrator gets access denied on a rewrite.12 Explorer’s “You need permission from TrustedInstaller” is this mechanism. Put the other way around, LocalSystem can reach almost everything outside the WRP-protected area, and there is usually no reason to give that to a business service.
flowchart TB
accTitle: The relationship between the WRP-protected area and TrustedInstaller
accDescr: Changes to important system files and registry keys that WRP protects are permitted only to TrustedInstaller, and even SYSTEM or an administrator gets access denied
ti["TrustedInstaller"] -->|Can change| wrp["WRP-protected system files and the like"]
sysadm["SYSTEM and administrators"] -->|Access denied| wrp
sysadm -->|Almost everything is allowed| other["Outside the WRP-protected area"]
Figure 5: LocalSystem is not unlimited either; changes to the WRP-protected area are permitted only to TrustedInstaller.
3.4. Cases Where LocalSystem Is Reasonable
What is exceptionally reasonable is a service whose required privileges exceed administrator-class in the first place — working closely with a device driver, operating the OS security foundation, managing other services or sessions, and the like. Software such as a backup agent or an EDR applies. Even then, it is worth confirming that there is a code path that truly uses that privilege, and considering whether the work that needs the privilege can be separated (for how to tell, see “When Do You Actually Need Administrator Privileges on Windows?”).
4. LocalService and NetworkService — Built-in Accounts with Least Privilege
LocalService (NT AUTHORITY\LOCAL SERVICE, SID: S-1-5-19) and NetworkService (NT AUTHORITY\NETWORK SERVICE, SID: S-1-5-20) are built-in accounts prepared for low-privilege services. Both hold only minimal privileges locally, and can do little more than a member of the Users group.51
The difference between the two is one point: who they look like from the far side of the network.5
- LocalService: Connects to the remote side with anonymous credentials. It cannot access a resource that requires authentication.
- NetworkService: Presents the computer’s credentials to the remote side (in a domain environment, DOMAIN\computer-name$).
The split is LocalService if “it does not go onto the network, or if it does it does not need an identity”, and NetworkService if “you want to access a resource inside the domain with the machine’s identity”.
flowchart TB
accTitle: The difference between LocalService and NetworkService
accDescr: Local privileges are minimal for both, but to the remote side LocalService connects with anonymous credentials and NetworkService presents the computer's credentials
ls["LocalService"] --> anon["Connects with anonymous credentials"]
anon -.-> ng["A resource that requires authentication is not possible"]
ns["NetworkService"] --> comp["Presents the computer's credentials"]
comp -.-> pc["In a domain environment it looks like PC$"]
Figure 6: Local privileges are the same minimum, but the identity visible from the far side of the network splits into anonymous or the computer account.
These two, however, have a weakness from a modern point of view. The same account is shared by many services. If five services run as LocalService, then as long as the ACL is per-account, the five can access one another’s resources. SQL Server does not support the Local Service account for the same reason: it is a shared account and cannot be separated from other services.1
flowchart TB
accTitle: A shared account cannot be separated
accDescr: If several services share the same LocalService, then as long as the ACL is per-account they can access one another's resources
sva["Service A"] --> acct["The same LocalService"]
svb["Service B"] --> acct
svc["Service C"] --> acct
acct --> mutual["Can access one another's resources"]
mutual -.-> reason["Because the ACL is per-account"]
Figure 7: Services that share the same account cannot be separated from one another’s resources by an ACL.
Solving this “stay low-privilege, but separate per service” is the next topic, the virtual account.
5. Virtual Accounts (NT SERVICE\) — The Modern Default
5.1. You Can Have a Per-Service Identity with No Password
A virtual account is a “managed local account” available from Windows Server 2008 R2 / Windows 7 onward. There are three characteristics.6
- The account is managed automatically; neither creation nor setting a password is needed
- The name is
NT SERVICE\<service-name>, and it becomes an identity unique to each service - In a domain environment, it can access the network with the computer account’s credentials (DOMAIN\computer-name$)
In other words, it keeps the “no password management” merit of LocalService/NetworkService and removes the “cannot separate because the account is shared” demerit. That is also why SQL Server setup defaults to a virtual account such as NT SERVICE\MSSQLSERVER.1
flowchart TB
accTitle: What a virtual account makes compatible
accDescr: A virtual account keeps the no-password-management merit of LocalService and NetworkService, removes the cannot-separate-because-shared demerit, and has an identity unique to each service
merit["Merit (no password management)"] -->|Keep| va["Virtual account"]
demerit["Demerit (cannot separate because shared)"] -->|Remove| va
va --> ident["An identity unique to each service"]
va --> auto["Neither creation nor setting a password is needed"]
Figure 8: A virtual account keeps the merits of the built-in accounts and removes only the cannot-separate-because-shared demerit.
5.2. You Can Write “NT SERVICE\service-name” Directly on an ACL
The practical convenience is that you can add only that service to an ACL by name. “Only this service can write this data folder” can be realized with neither creating a group nor managing a password.
# Change the service's logon account to a virtual account
# The value of obj= is "NT SERVICE\service-name". Do not specify a password
sc.exe config MyAppService obj= "NT SERVICE\MyAppService"
# Confirm the configuration (check SERVICE_START_NAME)
sc.exe qc MyAppService
# Grant modify rights on the data folder to this service only
icacls "C:\ProgramData\MyApp" /grant "NT SERVICE\MyAppService:(OI)(CI)M"
In the GUI, in services.msc open the service’s properties → the “Log On” tab → enter NT SERVICE\service-name in “This account”, and leave the password fields empty (for a virtual account or an MSA, not specifying a password is SCM’s specification). After the change, a service restart applies it.
5.3. The Constraint — Outside the Machine It Is Not “That Service”
A virtual account’s identity is machine-local and is not recognized from the domain. On the network it collapses to the computer account as described later, so the remote side cannot tell “which service it is”, and you also cannot share the same identity across several servers.10
flowchart TB
accTitle: A virtual account's identity collapses outside the machine
accDescr: A virtual account that is unique per service inside the machine also collapses to the computer account on the network, and the remote side cannot tell which service it is
vaa["Virtual account A"] --> pc["Computer account PC$"]
vab["Virtual account B"] --> pc
pc --> remote["The identity visible to the remote side"]
remote -.-> nodist["Cannot tell which service it is"]
Figure 9: Even with a unique identity inside the machine, on the far side of the network every service looks like the same PC$.
The moment this constraint — needing a service-specific identity on the far side of the network, needing the same identity on several servers — becomes a problem is when a gMSA (Chapter 8) is called for.
6. Identity When Going onto the Network — The Practice of the Computer Account (PC$)
6.1. “A Service Cannot Access a Shared Folder” Is a Misunderstanding
On a domain-joined machine, when a service running as LocalSystem, NetworkService, or a virtual account accesses a remote resource, it authenticates as the computer account (DOMAIN\computer-name$).36 Many of the opening consultations of “it could not access a shared folder, so we made it a domain user” are in fact solved by this. The destination ACL simply was not permitting PC$.
flowchart TB
accTitle: Remote access as the computer account
accDescr: A LocalSystem, NetworkService, or virtual-account service on a domain-joined machine authenticates to the remote side as the computer account, and if the destination ACL permits PC$ it can access
svc["Service (LocalSystem, a virtual account, and the like)"] --> auth["Authenticate as PC$"]
auth --> acl{"Does the destination ACL permit PC$?"}
acl -->|Yes| ok["Access to a shared folder or a DB succeeds"]
acl -->|No| ng["Access is denied"]
Figure 10: In a domain environment, granting PC$ on the destination ACL alone establishes remote access without a domain user.
Granting on the file-server side is the same as an ordinary ACL operation; specify computer-name$ as the account name (in the GUI object-picker dialog, include “Computers” in the object types).
# On the file-server side: grant a service on APPSV01 modify rights on the shared folder
# You need to grant both share permissions and NTFS permissions
Grant-SmbShareAccess -Name "AppData" -AccountName "CORP\APPSV01$" -AccessRight Change -Force
icacls "D:\Shares\AppData" /grant "CORP\APPSV01$:(OI)(CI)M"
SQL Server is the same: create the computer account as a login and the connection string goes through with Integrated Security=true and no password.
-- On the DB-server side: permit Windows integrated authentication from a service on APPSV01
CREATE LOGIN [CORP\APPSV01$] FROM WINDOWS;
6.2. Know the Limits of the PC$ Approach
This approach has two limits.
- Granularity is per machine. LocalSystem, NetworkService, and every virtual-account service running on the same machine all look like the same PC$ from the remote side. You cannot “permit only this service” on the destination, and you also cannot audit which service used that account.2
- It cannot be used in a workgroup environment. A computer account is an Active Directory object, so a machine that is not domain-joined does not have one. You need a design that handles the destination account’s credentials explicitly.
When you want to go beyond limit 1, the 2026 answer is not the next chapter’s domain user… but to skip that problem and proceed to a gMSA.
flowchart TB
accTitle: Two limits of the PC$ approach
accDescr: Authentication as PC$ has machine-level granularity so neither per-service permission nor audit is possible, and in a workgroup environment the computer account itself does not exist so it cannot be used
pcs["The PC$ approach"] --> lim1["Limit 1: per machine"]
pcs --> lim2["Limit 2: no workgroup"]
lim1 -.-> noaudit["No per-service permit"]
noaudit -.-> noaudit2["no per-service audit"]
lim2 -.-> nocred["Use explicit creds"]
lim1 --> gmsa["Beyond this: a gMSA"]
Figure 11: When you want to go beyond the two limits of machine-level granularity and a domain prerequisite, skip the domain user and proceed to a gMSA.
7. The Problem of Using a Domain User for a Service
7.1. The Structural Problem of the Password
If you assign a domain user (or a local user) to a service, SCM stores that password and uses it to log on at every start. SCM does not manage expiry, so when the password expires the logon fails and the service will not start.7
From there, the negative spiral you often see in the field begins.
- An accident of the service stopping because of expiry occurs
- As recurrence prevention, “password never expires” is set
- A change procedure is never established, and the same password is written in plaintext into the runbooks, scripts, and Task Scheduler of several servers
- Even when someone leaves, the password does not change (if you change it, you do not know what will stop)
flowchart TB
accTitle: The negative spiral of operating with a domain user
accDescr: The password expires and the service stops, never-expire is set as recurrence prevention, a plaintext password spreads into runbooks and scripts, and even when someone leaves it cannot be changed
expire["1. Expiry stops the service"] --> forever["2. Never-expire is set as recurrence prevention"]
forever --> spread["3. A plaintext password spreads"]
spread -.-> where["Runbooks, scripts, tasks"]
spread --> stuck["4. Even when someone leaves, it cannot be changed"]
Figure 12: Starting from an expiry accident, never-expire and the spread of a plaintext password become fixed.
Microsoft also points out that a configuration that uses a domain account for a service costs considerable operational effort in the manual management of the password and the SPN, and that maintenance can lead to a service stop.1
7.2. Kerberoasting — A Service Account Is Targeted
Another attack specific to a domain-user service account is Kerberoasting. A service that receives Kerberos authentication registers an SPN (service principal name) on the logon account. Any authenticated user in the domain can request a service ticket to an account that has an SPN registered, so an attacker obtains the ticket and tries an offline brute-force of the password. A 10-to-16-character password a human decided will not withstand this attack.
flowchart TB
accTitle: The flow of Kerberoasting
accDescr: A service ticket to a service account that has an SPN registered can be requested by any authenticated user, so an attacker obtains the ticket and tries an offline brute-force of the password
atk["An authenticated user in the domain"] --> req["Request a ticket for the SPN"]
req --> tkt["Obtain a service ticket"]
tkt --> brute["Offline brute-force"]
brute --> weak["10 to 16 characters or so will be cracked"]
Figure 13: Any authenticated user can request a ticket, and a password of a length a human decided will not withstand an offline brute-force.
The effective response is to make the password a strength a human cannot guess or crack. Microsoft also lists forcing a long password, and using a gMSA whose password becomes a long machine-generated random value.8 The same document also mentions Kerberos armoring (FAST), but FAST protects pre-authentication data and resistance to KDC spoofing; it does not prevent an authenticated user requesting a service ticket to an SPN, so it is not a substitute for the password strength of a service account. The relationship between SPNs and Kerberos, and the conditions under which authentication falls back to NTLM, are diagrammed in “NTLM and Kerberos Explained with Diagrams”.
7.3. If You Still Use a Domain User
If you have no choice but to use a domain user, for reasons such as the application not supporting a gMSA, treat the following as the minimum mitigation.
- Make the password a randomly generated 25 characters or more, and do not write it anywhere other than a password-management tool (runbooks, scripts, a shared Excel)
- Make it a service-dedicated account and split it per service (do not share it with a human account2)
- Deny interactive logon and Remote Desktop, and permit only “Log on as a service”
- Minimize the groups it belongs to (adding it to Domain Admins is out of the question)
- Establish a periodic-rotation procedure and put the places a change will affect into a ledger
Doing all of this is less safe and less easy than migrating to a gMSA — that is the next chapter.
8. gMSA — Leaving Password Management to Active Directory
8.1. The Mechanism and the Effect
A gMSA (group Managed Service Account) is a domain account that leaves password management to the domain controller. The password is computed by the domain controller from the KDS (Key Distribution Service) root key, and only permitted hosts obtain it.13
flowchart TB
accTitle: How a gMSA manages the password
accDescr: The domain controller computes the password from the KDS root key, only permitted hosts obtain it and use it to run the service, and the password is rotated automatically every 30 days by default
kds["KDS root key"] --> dc["The DC computes the password"]
dc --> host["A permitted host obtains it"]
host --> svc["Used to run the service"]
dc -.-> rot["Automatic rotation every 30 days by default"]
Figure 14: The domain controller takes on generating, distributing, and updating the password, and humans can operate without knowing the password.
The effects are clear.9
- A 240-byte randomly generated password: brute-force and dictionary attacks become unrealistic, and Kerberoasting resistance rises substantially
- Automatic rotation every 30 days by default: a human does not need to plan a change, and the service does not need to be stopped
- The same identity can be shared across several servers: a server farm under load balancing can mutually authenticate as the same principal
- Simpler SPN management: registration and management of SPNs can also be delegated and simplified
Humans can operate without knowing the password — if you take it as the mechanism that does for a service account what Windows LAPS does for a local administrator password, the placement is easier to grasp.
8.2. Requirements
A gMSA has prerequisites.10
- An Active Directory domain environment (not possible in a workgroup)
- Domain and forest functional levels of Windows Server 2012 or higher
- A KDS root key has already been created
- The gMSA name is unique in the forest, not merely in the domain
- The password-change interval can be set only at creation time
Creating the KDS root key is a one-time task, but for up to 10 hours after creation you cannot create a gMSA, because you wait for replication to every domain controller. It is a safety device to prevent the accident of password retrieval failing before replication has finished.14
flowchart TB
accTitle: From creating the KDS root key to creating a gMSA
accDescr: After the KDS root key is created you wait for replication to every domain controller, so for up to 10 hours you cannot create a gMSA; after replication completes you can create one
add["Create the KDS root key"] --> wait["Up to 10 hours of waiting for replication"]
wait -.-> why["A safety device to prevent a retrieval-failure accident"]
wait --> done["Replication to every DC has completed"]
done --> ok["You can create a gMSA"]
Figure 15: The up-to-10-hour wait after creating the root key is waiting time to prevent a retrieval failure while replication has not finished.
# Run as a domain administrator, on a domain controller (or an administrative
# workstation with the AD PowerShell module)
# Confirm whether a KDS root key exists, and create one if not (once per forest)
Get-KdsRootKey
Add-KdsRootKey -EffectiveImmediately # Actually usable after up to 10 hours
8.3. The Procedure from Creation to Configuration
The procedure is four stages: “① create a group permitted to retrieve → ② create the gMSA → ③ install it on the servers → ④ set it on the service”.10
flowchart TB
accTitle: The four stages of introducing a gMSA
accDescr: Introduce it in four stages: creating a group permitted to retrieve the password, creating the gMSA, installing it on each server, and setting it as the service's logon account
st1["① Create a group permitted to retrieve"] --> st2["② Create the gMSA"]
st1 -.-> add["Add the servers' PC$"]
st2 --> st3["③ Install on each server"]
st3 -.-> test["Validate retrieval with the Test command"]
st3 --> st4["④ Set it on the service"]
Figure 16: From creating the group to setting the service, introducing a gMSA proceeds in four stages.
# ① Create a security group permitted to retrieve the password,
# and add the computer accounts of the servers that will run the service
New-ADGroup -Name "GG-SvcBatchHosts" -GroupScope Global
Add-ADGroupMember -Identity "GG-SvcBatchHosts" -Members "APPSV01$", "APPSV02$"
# Group membership is evaluated at computer logon, so
# restarting the target servers after adding is the reliable approach
# ② Create the gMSA
New-ADServiceAccount -Name "svc-batch" `
-DNSHostName "svc-batch.corp.example.com" `
-PrincipalsAllowedToRetrieveManagedPassword "GG-SvcBatchHosts"
# ③ On each server that will run the service, install the gMSA and validate
Install-ADServiceAccount -Identity "svc-batch"
Test-ADServiceAccount -Identity "svc-batch" # True means retrieval is working
# ④ Set it as the service's logon account. Append $ to the name, and do not specify a password
sc.exe config MyBatchService obj= "CORP\svc-batch$"
Restart-Service MyBatchService
When setting from services.msc as well, the account name is like CORP\svc-batch$ — append $ at the end, and leave the password fields empty. An MSA-family account cannot be used for an interactive sign-in.1 After that, grant CORP\svc-batch$ on the ACL of a shared folder or SQL Server in place of PC$, and network access with a service-specific identity is complete, passwordless.
8.4. Some Apps Do Not Support It
As a caveat, not every piece of software will run as a gMSA. Things that configure the logon identity through a standard mechanism — a Windows service, an IIS application pool, a Task Scheduler task — are widely supported, but there are constraints such as failover clustering itself not supporting a gMSA, and an app whose internals demand a password cannot use it.10 Microsoft also states plainly that you should confirm behavior as a gMSA in a test environment before production.9
flowchart TB
accTitle: Telling whether something supports a gMSA
accDescr: An app that configures the logon identity through a standard mechanism widely supports a gMSA, but failover clustering and an app whose internals demand a password cannot use it, so confirm in a test environment before production
app["The target app"] --> how{"How is logon set?"}
how -->|Standard mechanism| okapp["gMSA supported"]
okapp -.-> ex1["Service, IIS, a task"]
how -->|Password demanded| ngapp["gMSA not possible"]
ngapp -.-> ex2["Failover clustering"]
okapp --> test["Test before production"]
Figure 17: An app that configures logon through a standard mechanism is widely supported, but some designs are unsupported, so validation before production is indispensable.
There are also siblings: the sMSA (standalone Managed Service Account) for a single server, and the dMSA (delegated Managed Service Account, introduced in Windows Server 2025, which ties to device identity to counter credential theft). For a new build, take a gMSA as the baseline and consider according to the requirements.6
9. Accompanying Design — Logon Rights, the Profile, DPAPI, and Auditing
Four more things that change with the account, to keep.
9.1. The “Log on as a Service” Right (SeServiceLogonRight)
To start as a service, the account needs the “Log on as a service” user right. LocalSystem, LocalService, and NetworkService have it built in, but any other account (a domain user, a gMSA, and the like) needs an explicit assignment.15
If you set it from the “Log On” tab of the services.msc GUI, the snap-in grants this right automatically. On the other hand, CreateService / ChangeServiceConfig (the APIs sc.exe config calls) do not verify that the specified account has this right. The typical cause of a service configured by a script stopping at start with “the service did not start due to a logon failure” is this. Do not rely on a tool’s side effect; include in the deploy procedure, explicitly, adding to “Log on as a service” in Local Security Policy (secpol.msc), or configuration via GPO/Intune (in an environment that configures this right with Group Policy, a local grant is overwritten when the policy applies, so that needs attention as well). Conversely, the standard move for a service-dedicated account is to set “Deny log on locally” together with it.
flowchart TB
accTitle: The difference by the configuration path of the "Log on as a service" right
accDescr: The services.msc GUI grants the right automatically, but the API sc.exe config calls does not verify the right, so an account without the right stops the service with a logon failure at start
gui["Set in services.msc"] --> auto["The right is granted automatically"]
auto --> okgui["The service can start"]
cli["Set with sc.exe config"] --> noval["The right is not verified"]
noval --> has{"Does it have the right?"}
has -->|Yes| okcli["The service can start"]
has -->|No| stop["Stops with a logon failure"]
stop -.-> fix["Grant explicitly with secpol.msc or a GPO"]
Figure 18: The GUI grants the right automatically, but a scripted configuration does not verify it, so you need to include an explicit grant in the procedure.
9.2. The Profile, %TEMP%, and HKEY_CURRENT_USER Change
SCM loads that account’s user profile at service start.7 So the real %TEMP%, %APPDATA%, and HKEY_CURRENT_USER are a different thing per logon account, and when you switch accounts, settings and caches saved in the old account’s profile look as if they “disappeared”.
The design response is simple: put the service’s data not under the profile but on an explicit path such as C:\ProgramData\<app-name>, and grant that ACL to the logon account. That way an account change does not come with a data migration.
flowchart TB
accTitle: Profile dependence and the response of data placement
accDescr: The real profile is a different thing per logon account, so switching accounts makes the old profile's data look as if it disappeared, but placing the data on an explicit path and granting the ACL makes migration unnecessary
sw["Switching the logon account"] --> newprof["A different profile is loaded"]
newprof --> lost["The old data looks as if it disappeared"]
lost -.->|Response| fix["Place it under ProgramData"]
fix --> acl["Grant the ACL to the logon account"]
acl --> nomig["No migration even when the account changes"]
Figure 19: Avoid the profile and put the data on an explicit path, and an account change no longer comes with a data migration.
9.3. Data Protected with DPAPI Is Bound to the Account
Easier still to miss is DPAPI. Data encrypted with user-scope DPAPI (CryptProtectData or .NET’s ProtectedData) can, in principle, be decrypted only by the same account that protected it. The moment you change the account, a stored connection string or API key can no longer be read — that is DPAPI doing its job correctly, but if it is not in the migration procedure it becomes an incident.
flowchart TB
accTitle: The relationship between DPAPI-protected data and an account change
accDescr: Data protected with user-scope DPAPI can be decrypted only by the same account that protected it, so after you change the logon account you need to re-enter the secrets
protect["DPAPI-protect with the old account"] --> data["A protected connection string and the like"]
data --> who{"Which account is decrypting?"}
who -->|The same old account| okdec["Can decrypt"]
who -->|The new account| ngdec["Cannot decrypt"]
ngdec --> re["Re-enter the secrets"]
Figure 20: DPAPI-protected data is bound to the account that protected it, and after an account switch you need to re-enter.
The response is to include in the migration plan the procedure of “re-enter the secrets after the account switch” (for the design of where to store them, see “Storing Secrets in Windows Apps”). Also, a configuration that can finish with Windows integrated authentication as a gMSA or PC$ can eliminate storing the secret itself. The correct order is to consider “can we do without storing it” before “where do we store it”.
And if the service wants to process “with the calling user’s privileges”, you use impersonation rather than making the account stronger. For that, see “Handling Windows Impersonation Tokens Correctly”.
9.4. Auditing — Look at 4624 Logon Type 5
A service start is recorded in the Security event log as event ID 4624 (An account was successfully logged on) with logon type 5 (Service: SCM started a service). The “Virtual Account” field in the event indicates whether the logon was by an MSA / virtual account, so it can also be used to watch the use of managed accounts.11
flowchart TB
accTitle: The flow of auditing a service start
accDescr: SCM starting a service is recorded as event ID 4624 logon type 5, and the Virtual Account field can identify whether the logon was by a managed account
start["SCM starts a service"] --> ev["Record event ID 4624"]
ev --> type5["Logon type 5 (Service)"]
type5 --> vafield["Virtual Account field"]
vafield --> watch["Watching managed accounts"]
Figure 21: A service start is recorded as a 4624 of logon type 5, and you can even track the use of managed accounts.
For an inventory of the current state, aggregating the logon accounts of the service list is the quick method.
# Aggregate which services are running as which account
Get-CimInstance Win32_Service |
Group-Object StartName |
Sort-Object Count -Descending |
Select-Object Count, Name
# Inventory non-standard services running as LocalSystem (tell in-house / third-party by the path)
Get-CimInstance Win32_Service |
Where-Object { $_.StartName -eq 'LocalSystem' -and $_.PathName -notlike '*\Windows\*' } |
Select-Object Name, DisplayName, PathName
If this output lines up “a business service running as LocalSystem” and “a service running as a domain user”, the next chapter’s decision flow is called for.
10. Decision Flow — Decide with Four Questions
Here is the content so far, as a selection procedure. Answer four questions in order.
flowchart TB
accTitle: The decision flow for the logon account
accDescr: Decide the logon account by answering in order the four questions of whether there is network access, domain join, whether a machine-level identity is enough, and gMSA support
q1{"Win auth to a peer?"} -->|No| va["Virtual account"]
va -.-> sys["LocalSystem if needed"]
q1 -->|Yes| q2{"Domain-joined?"}
q2 -->|No| cred["Protect stored creds"]
q2 -->|Yes| q3{"Machine-level enough?"}
q3 -->|Yes| pcacl["Virtual acct + PC$"]
q3 -->|No| q4{"App supports a gMSA?"}
q4 -->|Yes| gmsa["gMSA"]
q4 -->|No| du["User + mitigations"]
Figure 22: Answer the four questions in order and which of the six choices you should use is decided.
Question 1: Does that service access another machine on the network (a shared folder, a DB, an API, and the like) with Windows authentication?
If not, a virtual account is the default. Only if a special local privilege is needed, confirm that need and then consider LocalSystem.
Question 2: (If it does access) Is the machine domain-joined?
In a workgroup, neither PC$ nor a gMSA can be used. Use a design that handles the destination account’s credentials explicitly (protect the store with DPAPI or the like), or consider joining the domain.
Question 3: (In a domain) Is a machine-level identity (PC$) enough?
If it is, a virtual account (or NetworkService) + granting PC$ on the destination ACL is complete. If you need a service-specific identity, or a common identity across several servers, go to question 4.
Question 4: Does the application support a gMSA?
If it does (things that configure logon through a standard mechanism — SCM, an IIS app pool, Task Scheduler — generally do), a gMSA. Do not forget a behavior check in a validation environment. If it is unsupported no matter what, use a dedicated domain user after applying every mitigation in section 7.3.
In a table it is as follows.
| Situation | Recommendation | Notes |
|---|---|---|
| Local-only, ordinary privileges | Virtual account | Grant the ACL to NT SERVICE\<name> |
| Local-only, privilege beyond administrator is required | LocalSystem | Validate the need for the privilege first |
| Local processing that does not need a network identity | LocalService | Acceptable for keeping an existing service as-is |
| Access a resource inside the domain with the machine’s identity | Virtual account (or NetworkService) | Grant PC$ on the destination ACL |
| Access a resource inside the domain with a service-specific identity | gMSA | KDS root key + confirm support |
| The same identity on several servers (load balancing and the like) | gMSA | Not possible with a virtual account |
| An app that does not support a gMSA + a specific identity is needed | A dedicated domain user | The mitigations in section 7.3 are required |
| A workgroup + remote access is needed | Protect and store explicit credentials | Also consider revisiting the design |
11. Summary
- A service’s logon account is a design decision that simultaneously decides local privileges, network identity, and password management. Do not leave it at the default (LocalSystem).
- LocalSystem holds a SYSTEM+Administrators token and strong privileges, and the damage when it is taken over is maximized. Most business services do not need this privilege.
- LocalService and NetworkService are both low-privilege; the difference is network identity (anonymous, or the computer account). Because the account is shared by several services, however, they cannot be separated.
- A virtual account (NT SERVICE\
) is the modern default that can separate per service while needing no password management. You can specify it directly on an ACL, and configuration is only changing the logon-account name. - LocalSystem, NetworkService, and a virtual account go onto the network as DOMAIN\PC$ in a domain environment. Granting PC$ on the ACL of a shared folder or SQL Server often lets you do without a domain user.
- Using a domain user for a service has the structural problems of a stop from expiry, the spread of a plaintext password, and Kerberoasting. If you use one, a dedicated account + a long random password + logon restrictions are required.
- A gMSA is a mechanism in which AD generates and rotates the password automatically; the requirements are a domain, functional level 2012 or higher, and a KDS root key. Set the service to “DOMAIN\name$” with the password field empty.
- When you change the account, include the “Log on as a service” right, moving the profile and %TEMP%, and re-entering DPAPI-protected data in the migration procedure. Auditing can be confirmed with event ID 4624 logon type 5.
The next time you install a service, stop for a moment on the logon-settings screen and ask this again. As whom, and how far, should this service be able to access? The answer should be some row of this article’s decision table.
Related Articles
- How to Build and Operate Windows Services ── From Choosing Between Task Scheduler and Services to Turning a BackgroundService into a Windows Service
- When Do You Actually Need Administrator Privileges on Windows? - UAC, Protected Areas, and How to Tell by Design
- Handling Windows Impersonation Tokens Correctly — Borrowing Privileges per Thread and Reverting Safely
- NTLM and Kerberos Explained with Diagrams — Why Authentication Falls Back to NTLM
- A Practical Guide to Windows LAPS — Retiring the Shared Local Administrator Password Across All PCs
- Storing Secrets in Windows Apps - Avoiding Plaintext Configuration with DPAPI
Related Consulting Areas
KomuraSoft LLC handles logon-account design and least-privilege hardening for Windows services and resident apps, migrating existing services built on the assumption of LocalSystem to a virtual account or a gMSA, and investigating failures caused by access denied, DPAPI, and the profile after an account change. Starting from the stage of “we were flagged in an audit, but we do not know where to begin” is fine.
- Windows Application Development
- Bug Investigation & Root-Cause Analysis
- Technical Consulting & Design Review
- Contact Us
References
-
Microsoft Learn, Configure Windows service accounts and permissions. That SQL Server’s default service account is a virtual account (NT SERVICE\MSSQLSERVER and the like), that when specifying a virtual account or an MSA you leave the password field empty, that an MSA is a name with a trailing $ and cannot be used for an interactive sign-in, that Local Service is a shared account so it cannot be separated and is not supported by SQL Server, that using a domain account costs effort in the manual management of the password and the SPN and maintenance can lead to a service stop, and that you should always run a service as a least-privilege account. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Securing on-premises service accounts. The priority of first a gMSA for an on-premises service, then an sMSA if that cannot be used, then a computer account, and finally a user account; that when you use a computer account you cannot tell which service is using that account and cannot audit a change; and the roles of a service account (identifying, authenticating, and starting the service). ↩ ↩2 ↩3
-
Microsoft Learn, LocalSystem Account. That LocalSystem holds extensive privileges on the local computer and the token includes the SIDs of NT AUTHORITY\SYSTEM and BUILTIN\Administrators, that it has no password, that it presents the computer’s credentials to a remote server, a list of privileges including SE_DEBUG_NAME and SE_TCB_NAME, and that most services do not need this privilege level and should consider using LocalService/NetworkService. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, sc.exe config. That you specify the service’s logon account with the obj= parameter, that the default is LocalSystem, and the password= parameter when using a user account other than LocalSystem. ↩ ↩2
-
Microsoft Learn, Local accounts. That SYSTEM (S-1-5-18) has Full Control by default on an NTFS volume, that NETWORK SERVICE (S-1-5-20) presents the computer’s credentials to a remote server, and that LOCAL SERVICE (S-1-5-19) holds minimal privileges locally and presents anonymous credentials to the network. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Service accounts. That a virtual account is an automatically managed local account that needs no password management, that the name is in the NT SERVICE<SERVICENAME> form, that in a domain environment it accesses the network with the computer account’s credentials (
\ ↩ ↩2 ↩3 ↩4$), and the criteria for choosing among sMSA, gMSA, dMSA, and a virtual account. -
Microsoft Learn, Service User Accounts. That a service runs in the security context of a user account, that SCM logs on to the account at start and associates an access token with the service process, that SCM loads the user profile, and that SCM does not manage password expiry so expiry makes the logon fail and the service will not start. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Protect SMB traffic from interception. Recommendations including a gMSA as a service-account protection (a long machine-generated random password making password cracking by brute-force or a dictionary attack unrealistic), forcing a long password, and a mention of Kerberos armoring (FAST). ↩ ↩2
-
Microsoft Learn, Secure group managed service accounts. That a gMSA password is a 240-byte random generation that is hard to brute-force or dictionary-attack, that the Windows OS changes the password every 30 days so an administrator does not need to plan a change or stop the service, deploying to a server farm and simpler SPN management, that if a service does not support a gMSA you use an sMSA and if that is also not possible a standard user account with strong password management, and that you should confirm behavior as a gMSA in a test environment before production. ↩ ↩2 ↩3
-
Microsoft Learn, Manage group Managed Service Accounts. The gMSA prerequisites (domain/forest functional level 2012 or higher, creating a KDS root key), that the gMSA name must be unique in the forest, that the password-change interval can be set only at creation, specifying the group permitted to retrieve the password with New-ADServiceAccount’s -PrincipalsAllowedToRetrieveManagedPassword, the Install-ADServiceAccount/Test-ADServiceAccount procedure, that a virtual account’s identity is machine-local and is not recognized from the domain, that a failover cluster does not support a gMSA, and that SCM, an IIS app pool, and Task Scheduler support configuring logon as a gMSA. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, 4624(S): An account was successfully logged on. That event 4624 is recorded on the accessed computer when a logon session is created, that logon type 5 means a service (SCM starting a service), and that the “Virtual Account” field can identify a logon by an MSA or a virtual account and can be used to watch managed service accounts. ↩ ↩2
-
Microsoft Learn, About Windows Resource Protection. That Windows Resource Protection (WRP) prevents replacement of important system files, folders, and registry keys, that full access to a WRP-protected resource is restricted to TrustedInstaller and a change can be made only through the supported replacement mechanism via the Windows Modules Installer service, and that an application that tries to change a protected resource receives access denied. ↩
-
Microsoft Learn, Group Managed Service Accounts overview. That a gMSA is a domain account that leaves password management to Windows, that the domain controller computes the password from the Key Distribution Service (kdssvc.dll) shared secret and a member host queries the domain controller for the current and previous passwords, and that it enables mutual authentication as the same principal in a server farm. ↩
-
Microsoft Learn, Create a Key Distribution Service (KDS) root key. That a root key is needed for the domain controller to begin generating gMSA passwords, the creation procedure with Add-KdsRootKey -EffectiveImmediately, that for up to 10 hours after creation you cannot create a gMSA because you wait for AD replication to converge, and that incomplete replication can make password retrieval fail. ↩
-
Microsoft Learn, Policy CSP - UserRights: LogOnAsService. That the “Log on as a service” right lets a security principal log on as a service, that Local System, Local Service, and Network Service have this right built in, that a service run as any other account needs this right assigned, and the Group Policy configuration path. ↩
Related Articles
Recent articles sharing the same tags. Deepen your understanding with closely related topics.
A Practical Guide to Windows LAPS — Retiring the Shared Local Administrator Password Across All PCs
A shared local administrator password across every PC is fertile ground for Pass-the-Hash attacks, where the compromise of one machine sp...
SMB Signing and LDAP Channel Binding — Closing the "Other Half" of Your NTLM Defences in Practice
SMB signing and LDAP signing/channel binding are the defences that limit the damage from relay attacks while you work towards retiring NT...
NTLM and Kerberos Explained with Diagrams — Why Authentication Falls Back to NTLM
An illustrated comparison of NTLM and Kerberos: challenge/response, TGTs and service tickets, the conditions under which Negotiate falls ...
Will NTLM Deprecation Stop Your Business Apps? — How to Collect Audit Logs, and the Order in Which to Kill Dependencies
A practical procedure for finding out where your Windows environment and business applications depend on NTLM ahead of its retirement: au...
The Depths of Windows Virtualization (Part 2) — Memory Even the Kernel Cannot See: How VBS, HVCI, and Credential Guard Work
On a clean install to compatible hardware, VBS is enabled by default and uses the hypervisor and SLAT to create isolation stronger than t...
Related Topics
These topic pages place the article in a broader service and decision context.
Windows Technical Topics
Topic hub for KomuraSoft LLC's Windows development, investigation, and legacy-asset articles.
Where This Topic Connects
This article connects naturally to the following service pages.
Windows App Development
We support Windows desktop applications that involve resident processing, device integration, operational logging, and maintainable structure.
Frequently Asked Questions
Common questions about the topic of this article.
- Should I change a service that I have been running as LocalSystem for now, immediately?
- An immediate change is not always the right answer for every case. First confirm whether that service truly needs LocalSystem-class local privileges (strong privileges beyond an administrator). If it is only file read/write and network communication, moving to a virtual account (NT SERVICE\service-name) is the first candidate. At migration time, confirm granting access to the folders and registry keys you need, how data that depends on a profile or DPAPI is handled, and whether the "Log on as a service" right is present. Confirm start and the main functions in a validation environment, then switch production.
- Should I choose a virtual account or NetworkService?
- For a new choice, we recommend a virtual account. On the network both appear as the computer account (DOMAIN\computer-name$), and both have small local privileges. NetworkService, however, is shared by several services, so you cannot separate with an ACL that "permits only this service". A virtual account has an identity unique to each service, and you can specify NT SERVICE\service-name directly on an ACL. Recent Microsoft products such as SQL Server also default to a virtual account.
- Can I use a gMSA in a workgroup environment (no domain)?
- No. A gMSA is a mechanism in which an Active Directory domain controller generates and manages the password, and a domain plus creating a KDS root key are prerequisites. In a workgroup environment, the baseline is to finish local processing with a virtual account or LocalService/NetworkService. If you need access to another machine, you need a different design such as explicitly using credentials of an account prepared on the destination. Network access as the computer account (PC$) is also a story that holds only in a domain environment.
- After I changed the service's logon account, settings and credentials I had saved can no longer be read. Why?
- Because each logon account is tied to its own user profile, %TEMP%, HKEY_CURRENT_USER, and DPAPI key. In particular, data protected with user-scope DPAPI (CryptProtectData and the like) can, in principle, be decrypted only by the same account that protected it. Files saved under the profile (AppData and the like) are also a different path from the new account. Before you switch accounts, plan the procedure for recreating DPAPI-protected data (re-entering API keys and the like) and migrating files under the profile.
- If I only want the service to access a shared folder, do I need a domain user?
- In many cases, no. In a domain environment, a service running as LocalSystem, NetworkService, or a virtual account authenticates to the remote side as the computer account (DOMAIN\computer-name$). Add that PC$ to the share permissions and the NTFS permissions of the shared folder and it can read and write. If you want access control with a service-specific identity, or the same identity on several servers, consider a gMSA rather than a domain user.