Cloudek.

powershell

Before You Run That PowerShell Script: A Safety Checklist for Admins

A practical PowerShell script safety checklist: why execution policy is not a security boundary, reading before running, -WhatIf, test tenants and signing.

The script is running with your permissions

A PowerShell script runs with whatever rights the signed-in account or app has. If you run it as a Global Administrator in a production tenant, a mistake or a hidden line can change or delete things at the scale of that role, and it will do it quickly. Most script incidents are not clever attacks. They are an old snippet copied from a forum, a wildcard that matched more than expected, or a script run against the wrong tenant because the wrong window was in front. This PowerShell script safety checklist is for administrators who run scripts from colleagues, vendors and the internet. None of it is exotic; the aim is to build a habit of a few minutes of checking before each run.

If you want a quick first pass, paste the script into the PowerShell script risk reviewer. It flags patterns such as downloads, encoded commands and Invoke-Expression. It is a pattern scanner, not a verdict, and a clean result does not mean a script is safe.

Execution policy is not a security boundary

Many admins treat execution policy as the protection. Microsoft's own documentation is clear that it is not: the execution policy is described as a safety feature and defense in depth, and the documentation notes that a user can easily bypass it by typing the script contents at the command line. It stops you from running scripts by accident. It does not stop a determined person or a malicious script.

Understand what the settings do, and check what is effective on your machine with Get-ExecutionPolicy -List. On Windows, RemoteSigned requires a trusted publisher's signature on scripts downloaded from the internet, while local scripts do not need one. AllSigned requires a signature on everything. Bypass blocks nothing and prompts nothing. The check for downloaded files depends on a zone marker that browsers add, and Microsoft notes that some download methods, including curl.exe, Invoke-WebRequest and Invoke-RestMethod, may not add it. So a script fetched that way can look local. Never run Set-ExecutionPolicy Bypass just to make an error go away; if you must change it for a test, scope it to the current process only.

The checklist

1. Know where the script came from. A script from your own repository with review history is different from one pasted from a chat message. Prefer sources with an owner you can ask. Do not run anything because a web page told you to paste a one-line command.

2. Read it before you run it. Open the whole file. If you cannot explain what each section does, you are not ready to run it. Read it in an editor, not by running it to see what happens.

3. Look for the lines that deserve suspicion. Search for Invoke-Expression or iex, -EncodedCommand, FromBase64String, Invoke-WebRequest or Invoke-RestMethod followed by execution, Start-Process, registry writes, scheduled task creation, and anything that sends data to an address you do not recognise. Look for hard-coded credentials and tokens, and for commands that disable logging or security tools. None of these is automatically malicious, but each needs an explanation.

4. Check the scope of every change. Find each command that deletes, disables, removes, resets or sets. Look for wildcards, -Force, -Recurse, and pipelines that feed a filtered list into a destructive command. Check the filter is correct on its own by running just the selection part and examining the output, before it is connected to the action.

5. Use -WhatIf and read its output. Cmdlets that support ShouldProcess accept -WhatIf and show what they would have done without making the change, and -Confirm to ask first. Two cautions from Microsoft's documentation: it only works where a command implements it, so a script that calls the Graph API directly or runs a native program gets no protection; and -WhatIf is not always passed through to functions in other script modules. Check that -WhatIf really printed lines for the objects you expected, and treat silence as a warning, not as success.

6. Test somewhere that can be broken. Run the script first in a separate test tenant, a lab virtual machine or a test organizational unit, with a test account and a copy of representative data. Check the result, including the side effects, before production. If you have no test tenant, narrow the first production run to a single named object.

7. Use the least privilege that works. Do not run as Global Administrator when a narrower role or a read-only permission will do. With Microsoft Graph PowerShell, request only the scopes the script needs when you connect, and read the consent prompt. Use a separate admin account rather than your daily account.

8. Check the target before you press enter. Confirm which tenant, subscription or server you are connected to. Print it at the start of the script or run the relevant "who am I connected to" command and read it. Most wrong-tenant mistakes happen in a session that was left open from earlier work.

9. Keep a record and a way back. Export the current state of what you will change, such as a CSV of the settings or the list of objects, before the run. Write down the rollback step in advance. For deletions, check whether the service has a recycle bin or a soft-delete period, and do not assume it does.

10. Log what you did. Use Start-Transcript, or at least save the command and its output, so you can say later what ran, when, and against what. Keep secrets out of transcripts.

11. Consider signing for scripts you reuse. Signing a script with a code-signing certificate lets AllSigned machines run it and shows who signed it. Microsoft's documentation recommends using a timestamp server so the signature outlasts the certificate's expiry. Be clear about what a signature proves: that the file has not changed since it was signed, and who signed it. It does not prove the code is safe, and the documentation itself notes that AllSigned still risks running signed but malicious scripts. Protect the signing key, because malware that can use your key can sign scripts as you.

12. Run it once, small, then at scale. After a clean test, run against a small production set, check, and only then run the rest. Stop at the first surprise.

When a script arrives from AI

Generated scripts need the same treatment, with extra care, because a model can invent a parameter or misread a tenant. See the earlier article on human approval before AI-generated PowerShell runs. The same checklist applies; being fluent text is not evidence of being correct.

A one-page version

Before you run: know the source, read all of it, search for the risky lines, check scope and filters, run with -WhatIf where supported, test elsewhere, use the least privilege, confirm the target, back up the state, log the run, and go small first. If any item is a no, stop and fix that item first. Check current Microsoft documentation for the version of PowerShell you use, since behaviour differs between Windows PowerShell 5.1 and PowerShell 7.