PowerShell
Step 9. Execution Policy and Your First Script — Check, Run, and Restore
Level 0 — Understanding Computer Operation and Structure | Difficulty ★★☆☆☆ | Estimated time 2–3 hours
Prerequisite: Step 8. Use Windows PowerShell 5.1 or PowerShell 7 on Windows. No internet connection is needed.
- Tools: a personal Windows practice PC, PowerShell, and a text editor. Administrator rights are not required.
- Scope: new practice files and the Process policy of this PowerShell window. If organizational Group Policy is configured, use an approved personal lab.
- Key idea: execution policy sets conditions for running scripts; it is not a security boundary. Permission to run, or a signature, does not establish that a script is harmless.
A script is a program saved as commands. It can change files or make network requests with the running user’s permissions. Today you create short files that query the date and PC state, then compare blocked and allowed execution of the same file. Do not assume that your existing policy is Restricted.
1. Learning Objectives
- Save and execute a .ps1 file using its correct path.
- Read effective and per-scope policies, and distinguish policy errors from path errors.
- Change only the current session and restore its recorded original value.
- Understand script encoding and comments, including non-ASCII text.
- Run checkup.ps1 and assess completion from its actual output.
2. Background — Files, Policies, and Scope
2-1. Saving Commands as a File
Interactive commands can leave files and history behind. A script saves the commands themselves as a .ps1 file that you can inspect and run again. The same file can produce different results as the time, process count, or permissions change. It supports reuse, sharing, and automation; read its contents before running it.
2-2. What Policy Controls
| Policy | Meaning |
|---|---|
| Restricted | Allows interactive commands but blocks script files such as .ps1. |
| RemoteSigned | Requires a trusted signature for scripts marked as coming from the internet. Local files without that mark may run unsigned, regardless of who wrote them. |
| AllSigned | Requires trusted signatures even for scripts written locally. |
| Unrestricted | Permits unsigned scripts, with possible warnings based on origin. |
| Bypass | Omits execution-policy blocks and warnings. Not used in this lesson. |
| Undefined | No value is assigned at that scope. Query the effective policy separately. |
A digital signature helps identify the signer and detect changes after signing; it does not certify harmless behavior. Internet-origin marking depends on the download method and file system. Being on a USB drive alone neither establishes internet origin nor makes a file trustworthy.
2-3. Scope and Precedence
Precedence is MachinePolicy → UserPolicy → Process → CurrentUser → LocalMachine; the strictest value does not automatically win. Process applies to this process and can be inherited by child processes. Its setting disappears when that process closes. Other already-running windows are separate.
CurrentUser and LocalMachine changes persist after closing the window. Omitting Scope targets LocalMachine and can cause an administrator-permission error. Leave both unchanged today. Windows PowerShell 5.1 and PowerShell 7 manage settings separately; check the actual shell you are using.
3. Follow Along
Before you begin. Execution policy helps prevent accidental execution; it is not a security boundary. Keep changes in the shown process scope and restore the previous value. Do not override organization policy.
3-1. Record the Current State
Open a new PowerShell window and keep using it until restoration. Save the original Process value in $previousProcessPolicy, and record the table and effective value. The check stops without changing policy if Group Policy is configured.
$previousProcessPolicy = Get-ExecutionPolicy -Scope Process
Get-ExecutionPolicy -List
Get-ExecutionPolicy
if ((Get-ExecutionPolicy -Scope MachinePolicy) -ne 'Undefined' -or
(Get-ExecutionPolicy -Scope UserPolicy) -ne 'Undefined') {
throw 'Group Policy is configured. Use an approved personal lab.'
}
The table shows scope settings; the final single value is effective policy. It may already be RemoteSigned. Even if every scope is Undefined, an effective default still applies. Use the query result rather than guessing from the Windows or PowerShell version.
3-2. Create a Practice Folder and hello.ps1
$scriptlab = Join-Path $HOME ('infosec-step09-' + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $scriptlab -ErrorAction Stop
Set-Location -LiteralPath $scriptlab
pwd
Record the displayed path. Its random name creates a fresh folder without overwriting existing files. The next block is file content to save as hello.ps1, not an instruction to run Get-Date directly in the terminal.
Get-Date
- In Notepad, create a document and paste the content. Use Save As, the practice folder, file name
hello.ps1, file type All Files, and encoding UTF-8 with BOM. Menu wording may vary by editor. - Another text editor or file-writing tool is also valid. Verify the same .ps1 extension, content, and encoding below.
- The ASCII-only Get-Date line also works without a BOM. UTF-8 with BOM supports later non-ASCII text in both 5.1 and 7. Saving the file and executing it are separate steps.
Get-Item -LiteralPath .\hello.ps1 | Select-Object Name, FullName, Extension Get-Content -LiteralPath .\hello.ps1 -Raw -Encoding UTF8
Name should be hello.ps1 and Extension should be .ps1. Correct hello.ps1.txt before continuing. The content should be one Get-Date line. Inspecting it does not execute it.
3-3. Reproduce a Policy Block in This Session
Do not assume the first attempt must fail. Temporarily set only Process to Restricted, then try the real file. Paste the entire block into the terminal. try attempts execution and finally restores the original value even on error. Here -Force suppresses the confirmation question; it does not grant administrator rights or override Group Policy.
try {
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Restricted -Force -ErrorAction Stop
Get-ExecutionPolicy
.\hello.ps1
} finally {
Set-ExecutionPolicy -Scope Process -ExecutionPolicy $previousProcessPolicy -Force -ErrorAction Stop
}
Expect Restricted followed by a script-execution block with PSSecurityException, commonly UnauthorizedAccess. No date should be emitted. Wording, paths, and display language vary. The same error ID can have other causes, so read the full message along with policy state.
3-4. Allow Execution in This Session and Run the File
Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned -Force -ErrorAction Stop Get-ExecutionPolicy -List Get-ExecutionPolicy .\hello.ps1
Confirm Process and effective policy are RemoteSigned. The date should follow. The same file now runs under different policy. Continue in this window and restore it in Section 5. If stopping early, closing this practice window also discards its Process setting.
Read the path: .\ in .\hello.ps1 means the current folder. .hello.ps1 is a different name. A bare hello.ps1 normally is not found because the current folder is not searched automatically; PATH configuration can be an exception. Use the & call operator for a quoted path. Dot sourcing, . .\hello.ps1, is a different operation that runs in the current scope and can leave variables there.
3-5. Expand the File to Several Lines
Replace hello.ps1 with the following content and save as UTF-8 with BOM. The two blocks are respectively file content and the terminal command to execute it.
$today = Get-Date "Today is $today" "This script ran from a file."
.\hello.ps1
The first of the three code lines assigns a variable; the next two emit strings. Expect two output lines. Date values and formatting depend on the environment. Rerunning the file obtains a new current date.
3-6. Add Comments
Save the following as hello.ps1 and execute it with the next command. Its comments state the date, purpose, and what to check before running.
# Created: 2026-09-10 # Purpose: print the current time # Before running: use hello.ps1 in the practice folder $today = Get-Date "Today is $today" # Print the date
.\hello.ps1
In ordinary code, # starts a comment through the end of the line. Inside a quoted string it is a literal character. Run the following in the terminal; it emits one line, tag#value.
"tag#value" # comment
3-7. Mission — checkup.ps1
Create checkup.ps1 that prints the current time, running process count, and free space on C:. Add a message only when the count exceeds 200. This is an exercise threshold, not a malware or PC-health verdict. The result is console output, not a separate report file.
4. Exercises
- Why can you type Get-Date interactively under Restricted?
- If hello.ps1 exists but is not found, which path and extension should you check?
- Why might Set-ExecutionPolicy fail for permissions when Scope is omitted?
- Why can RemoteSigned block an internet-downloaded script, and what should you inspect first?
- How should you restore a Process policy that was originally Undefined?
Answers & completion criteria · expand/collapse
5. Model Answers and Completion Criteria
Mission Model Answer — File Content
Save this as checkup.ps1 in the practice folder using UTF-8 with BOM.
# checkup.ps1 - Query this PC's time, process count, and free disk space
"Current time: $(Get-Date)"
$proc = @(Get-Process -ErrorAction Stop).Count
"Running processes: $proc"
if ($proc -gt 200) { "Many processes (practice threshold: more than 200)" }
$drive = Get-PSDrive -Name C -PSProvider FileSystem -ErrorAction Stop
if ($null -eq $drive.Free) { throw 'Unable to read free space on C:.' }
$freeGiB = [math]::Round($drive.Free / 1GB, 1)
"C: free space: $freeGiB GiB"
Run it in the same PowerShell window.
.\checkup.ps1
Verify: expect the time, process count, and free-space lines, plus one message only when the count exceeds 200. @(...).Count counts an array. PowerShell’s 1GB is 1,073,741,824, so the displayed unit is GiB. A missing C: drive or unavailable free-space reading stops with an error; unknown is not reported as zero.
Also run hello.ps1 using a full path string. Since it now contains the commented version, expect one date string.
& "$scriptlab\hello.ps1"
Finish the Practice — Restore the Recorded Original Value
Set-ExecutionPolicy -Scope Process -ExecutionPolicy $previousProcessPolicy -Force -ErrorAction Stop Get-ExecutionPolicy -List Get-ExecutionPolicy
Compare with your original table. Process should match its recorded value; CurrentUser and LocalMachine should be unchanged. Restore Undefined if that was the original. Setting Restricted unconditionally is not restoration. If you lost the variable or window context, close this dedicated practice window and query a new one. Created .ps1 files remain on disk.
Exercise Explanations
- 1. Restricted does not prohibit all interactive commands. Distinguish direct command input from .ps1 file execution.
- 2. Check pwd, FullName, and Extension. Specify the current-folder path as .\hello.ps1.
- 3. The default scope is LocalMachine. An access error is not a reason to automatically elevate; verify that the intended scope for this lesson is Process.
- 4. Internet-origin marking and signature requirements may explain it. Inspect the message, content, source, and signature. Do not execute without a basis for trust. Consider unblocking only an approved file, never an entire downloads folder.
- 5. Restore using $previousProcessPolicy. Closing this process also discards its Process setting. Do not try to override organizational controls through other scopes.
Completion Checklist
- [ ] I verified the real extensions and contents of both .ps1 files.
- [ ] I observed a real file blocked under Restricted and the original value restored.
- [ ] RemoteSigned allowed the date and checkup.ps1 outputs.
- [ ] I distinguish comments from # inside a string.
- [ ] I can explain why execution policy is not a security boundary or safety guarantee.
- [ ] I compared initial and final policies and left persistent scopes unchanged.
6. Common Pitfalls
1. Missing file
Check for hello.ps1.txt or the wrong folder. Show file extensions in Explorer and verify the terminal’s actual path.
2. Command not found
Use .\hello.ps1, not a dot followed immediately by the name. Relaxing policy does not repair a wrong path.
3. Policy-change permission error
Check for Scope Process and inspect Group Policy with Get-ExecutionPolicy -List. Use an approved lab when organizational policy is configured.
4. Another window still blocks it
Process applies to that process. Query separate windows and 5.1/7 independently. RemoteSigned can still block an unsigned internet-marked file.
5. Non-ASCII text is garbled or fails parsing
Windows PowerShell 5.1 reads BOM-less scripts using the system ANSI code page. A mismatch with UTF-8 can corrupt text or cause parsing errors. PowerShell 7 supports BOM-less UTF-8, so failure is not universal. Save the correct visible text as UTF-8 with BOM, then verify it. Adding a BOM to already-corrupted text does not restore the original characters.
6. Double-click opens an editor
Double-click behavior depends on file associations. It is not a guarantee that a script can never execute that way. For this lesson, inspect the file and run its explicit path from PowerShell.
7. Summary
You now have two reusable scripts and a before-and-after verification habit: inspect the file → record policy → change only Process → execute the real .ps1 → restore the original. Permission to execute and evidence that the code does what you intend are separate checks.
Practice Verification Record
Historical record. The dated record below describes the earlier lesson version. The current instructions include subsequent editorial corrections; that record does not certify every revised example.
On 2026-09-10, Korean and English files were saved and executed through real .ps1 paths in Windows PowerShell 5.1.26100.9444 and PowerShell 7.6.5. Checks covered Restricted blocking, RemoteSigned execution, UTF-8 BOM text, comments, checkup.ps1 output, and restoration. Files were saved with a file-writing tool; the Notepad Save As dialog was not exercised. CurrentUser and LocalMachine were left unchanged.
Microsoft: Execution policies · Set-ExecutionPolicy · Scripts and scope · Character encoding
ONE STEP FURTHER
Finished this lesson?
Check the completion criteria, then mark your progress.