PowerShell Genie

PowerShell Least privilege automation

Automation accounts accumulate privilege because it is easier to add Domain Admin than to work out which four rights a script needs. That is how one compromised scheduled task becomes a domain-wide problem.

Lesson 2 min read Windows PowerShell 5.1 and PowerShell 7

Documentation: Microsoft Learn

Overview

Group Managed Service Accounts are the answer on Windows. Active Directory holds and rotates the password, so it never appears in a script, a config file or a password manager. Create with New-ADServiceAccount, allow the target machines to retrieve the password with -PrincipalsAllowedToRetrieveManagedPassword, then install it on each machine with Install-ADServiceAccount.

Where a gMSA does not fit, use SecretManagement with a vault rather than a credential in a file. Export-Clixml encrypts with DPAPI tied to one user on one machine, which is better than plaintext but is not a secret store and does not survive a machine rebuild.

Rights should be scoped to the OU or the resource, and read-only where reading is all that is needed. Deny interactive logon on service accounts so a stolen credential cannot be used at a console. Log what the account does, so you can tell normal from abnormal.

Start by auditing what your existing automation accounts actually use. It is usually far less than what they hold.

Inspect this example’s structure

Read the command and its supplied arguments. This example requires real PowerShell or its module; this diagram does not execute it.

$account = [pscustomobject]@{ Type = 'gMSA' Name = 'svc-Reporting$' Password = 'managed by AD, rotated automatically' Rights = 'read-only on the reporting OU' Interactive = 'denied' } $account | Format-List
  1. Stage 1$accountPositional values or no named parameters
  2. Stage 2Format-ListPositional values or no named parameters

    An example to inspect

    Preview the example. Sign in for the full lesson and activities.

    $account = [pscustomobject]@{
      Type = 'gMSA'
      Name = 'svc-Reporting$'
      Password = 'managed by AD, rotated automatically'
      Rights = 'read-only on the reporting OU'
      Interactive = 'denied'
    }
    $account | Format-List

    What happens

    This prints the shape of a properly scoped service account. The trailing dollar sign in the name marks it as a machine-type account, which is how gMSAs appear in AD. The three properties after it are the ones that matter: no stored password, rights limited to one OU, and no interactive logon.

    Worth remembering

    • gMSA passwords are held and rotated by AD, never by you.
    • Use SecretManagement with a vault where a gMSA does not fit.
    • Export-Clixml is DPAPI-bound to one user on one machine.
    • Scope rights to the OU, and deny interactive logon.

    Primary documentation

    Active Directory PowerShell documentation

    Try it

    Illustrative output · values vary by environment

    1PS> $account = [pscustomobject]@{
    2PS> Type = 'gMSA'
    3PS> Name = 'svc-Reporting$'
    4PS> Password = 'managed by AD, rotated automatically'
    5PS> Rights = 'read-only on the reporting OU'
    6PS> Interactive = 'denied'
    7PS> }
    8PS> $account | Format-List

    Example reader

    Inspect this page's example

    Fictional browser data when supported

    Run supported commands against the practice data, or inspect the example without executing it.

    Ready to inspect.

      Prints the properties of a least-privilege service account.

      Show reference output

      Illustrative output · values vary by environment

      Type        : gMSA
      Name        : svc-Reporting$
      Password    : managed by AD, rotated automatically
      Rights      : read-only on the reporting OU
      Interactive : denied

      Keep going

      Next: PowerShell JEA
      Genie guidePowerShell Least privilege automation

      Automation accounts accumulate privilege because it is easier to add Domain Admin than to work out which four rights a script needs. That is how one compromised scheduled task becomes a domain-wide problem.