Practical tools for system, network, security, and operations administrators.

SYS-C0104 · Windows Server & PowerShell

PowerShell Scripting Basics

Write PowerShell that passes objects, supports -WhatIf, and does not treat execution policy as a boundary.

Updated 25 October 2026 · Pillar guide

This is an original planning guide. It is not a vendor manual, an official standard, or legal advice. Example addresses use documentation ranges. Review every command before you paste it.

Commands return objects, and policy is not a lock

PowerShell commands are verb-noun. Get-Help Verb-Noun -Examples is the page you read before you invent a flag. Get-Command finds what is installed. A script that assumes a module is present should say which module, and the host should have it installed on purpose, not imported from a random path.

Output is objects. The columns you see are a view. The next command can take the object. Text parsing is what you do when you have left PowerShell. Select-Object, Where-Object, and ForEach-Object are the ordinary tools. A long one-liner you cannot read on Monday is a script with names.

Execution policy controls whether a local script is allowed to start. It is not an access-control boundary. A person who can run powershell can bypass a policy. Do not describe the policy as the thing that keeps a server safe. The thing that keeps a server safe is who can log on, what the script does, and the account it runs as. Sign scripts if your organisation already does. Do not invent a signing ceremony in the middle of an outage.

-WhatIf and -Confirm are how you see a change before it happens, on cmdlets that support them. A script that changes users, services, or files should show the same preview. If a cmdlet has no -WhatIf, the script writes the plan and waits. The template generator drafts a skeleton. You delete every sample that is not this task.

A lab query, then a change you preview

The lab starts with a query that cannot change anything: a Get- command whose output you understand, filtered to one name you planted. You keep the output. The command builder on this site can draft that Get- line. Compare it to Get-Help before you run it on a server.

The change, when there is one, is a Set- or a New- against that same one name, with -WhatIf first. The WhatIf text should name the object you planted and no other. If it names a second object, the filter is wrong and you do not remove -WhatIf.

The script file lives in a path the operator account can read, and it is not world-writable in the Windows sense that every user can edit it. The account that runs it is named in the ticket. A scheduled task that runs as a domain admin because that was convenient is the mistake the Windows roles guide is trying to stop.

Errors stop the script. $ErrorActionPreference = 'Stop' is a choice you can explain, similar to a shell that exits on failure. A script that prints an error and continues through a loop of accounts will half-finish a change. Prefer a loop of one account until the one account is boring.

A change you can finish

Query in this window. Change one named object with -WhatIf, then without it, then Get- the object again and keep both outputs. A loop over a whole OU is a later window, after the one object was right.

Rollback is the previous value you recorded from the first Get-. If you did not record it, you are not ready to Set-.

  1. Get-Help the cmdlet and read the examples.
  2. Run a Get- that matches one planted object.
  3. Record the current values you might change.
  4. Run the change with -WhatIf and read every name it prints.
  5. Run it for real only if the names match the ticket.
  6. Get- the object again and keep the after line.
  7. Put the script where the run account can read it and others cannot edit it.

Mistakes that outlive the command

PowerShell fails in the same few ways. Each one looks finished on the day you type it. The list is here so the ticket can forbid them before the window, not so you can recognise them in an incident review.

Decisions to write down before the window

One decision for PowerShell: The cmdlet is the one Get-Help described. The module name is in the ticket. The first run is a Get- of one object. The output is saved. A change supports a preview, or the script prints the plan and you approve that plan. -WhatIf output names only the objects in the ticket. The run account is named and is not a domain admin unless the cmdlet cannot work otherwise. Execution policy is set the way the organisation already requires. It is not described as a boundary. Errors stop the script. A loop over many objects waits until one object is done. The previous value is the rollback. The template was trimmed. Sample users from the generator are gone. The script path is not editable by every user who can log on. And a second person read the filter aloud before -WhatIf was removed.

PowerShell checks before a change runs without -WhatIf

Confirm this before you call the change done: Get-Help was read for this cmdlet. A one-object Get- output is in the ticket. The previous value is written down. -WhatIf names only the intended object. The run account is the account in the ticket. The script stops on errors. No downloaded module was imported as part of this change. Execution policy is not the control you are relying on. The generator sample names are gone. The after Get- matches the intended value. Rollback is the previous value, and you know the Set- that puts it back. A second person heard the filter. The scheduled task, if any, uses the same script path you tested. The hostname in the prompt matches the hostname in the ticket for PowerShell. The result line includes the time and the person, not only the command. You can point at the rollback value without searching a chat history. And done is a sentence a second person can repeat: the proof for PowerShell happened, and the next host is not started early. If one of those fails, stop.

Tools for this guide

These tools run in the browser on this site. They do not log into your machines.

Guides in this cluster

These supporting pages belong to this pillar.