# Least privilege for IIS and Windows services: app pools, gMSA and ACLs

> Pick the right service identity, grant only the NTFS rights the app needs, and use gMSA when a domain service account must be managed.

**Tested on**

- Build: 26100.33650
- Edition: Standard / Datacenter
- Architecture: x64
- Evidence: E1
- Verified: 2026-10-29

## The short answer

Default to **ApplicationPoolIdentity** for IIS. For a Windows service that needs a domain identity, use **gMSA** (or a tightly scoped service account), not a domain admin and not LocalSystem.

Grant:

- read/execute on the app binaries and static files;
- modify on one upload/cache directory if needed;
- read on config/secrets the app must read;
- nothing on `C:\`, `C:\Windows` or another app’s folder.

## IIS app pool

```powershell
New-WebAppPool -Name 'apppool'
Set-ItemProperty IIS:\AppPools\apppool -Name processModel.identityType -Value ApplicationPoolIdentity
icacls C:\sites\app /grant "IIS AppPool\apppool:(OI)(CI)RX"
```

For uploads:

```powershell
icacls C:\sites\app\uploads /grant "IIS AppPool\apppool:(OI)(CI)M"
```

`RX` read/execute; `M` modify. Do not grant `F` (full) because a vendor installer said so.

## Windows service

If the app runs as a service (Node/Go/Python), create a dedicated local/domain account or gMSA:

- log on as a service right;
- read/execute on app dir;
- write on logs/uploads/temp;
- no interactive logon;
- no local admin.

## gMSA

```powershell
New-ADServiceAccount -Name svc-app -DNSHostName svc-app.example.com `
  -PrincipalsAllowedToRetrieveManagedPassword 'IIS-Servers'
Install-ADServiceAccount -Identity svc-app
Test-ADServiceAccount -Identity svc-app
```

Then set the app pool/service to `EXAMPLE\svc-app$` with no password. gMSA requires AD and the KDS root key; it is not a standalone-server feature.

## Verify effective access

```powershell
icacls C:\sites\app
Get-IISAppPool apppool | Select-Object Name,State,ProcessModel
whoami /priv
```

For a service, check `Get-CimInstance Win32_Service -Filter "Name='api'" | Select-Object StartName`.

> **Caution: LocalSystem is not a shortcut**
>
> LocalSystem can read/write the whole host and impersonate. A web app that needs it usually has a design problem (files, registry, network) that should be fixed, not elevated.

## Common over-privilege

- `Everyone` read on the site root because one folder failed.
- App pool in local Administrators “to access a share”.
- Domain admin for a service that only reads one folder.
- Shared service account across unrelated apps.
- Write access to `web.config` or the PHP/Node binaries.

## Lab boundary

E1. The lab created `labpool` with ApplicationPoolIdentity on Windows 11 but did not configure gMSA/AD. The gMSA steps are the domain pattern to validate on Server 2025.

## Sources

- [Microsoft Learn — Application pool identities](https://learn.microsoft.com/en-us/iis/manage/configuring-security/application-pool-identities)
- [Microsoft Learn — Group Managed Service Accounts](https://learn.microsoft.com/en-us/windows-server/security/group-managed-service-accounts/group-managed-service-accounts-overview)
- [Microsoft Learn — icacls](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/icacls)

---

Source: https://winbook.fyi/en/server/security/least-privilege/
