guide · static export
Security · powershell

Set Up a PowerShell Profile and a Safe Execution Policy

Your shortcuts, helper functions and prompt can load with every shell — if the profile loads at all. The execution policy decides that, and the per-user scope is the one to change.

by@guidox updated:10/31/26 time:20 min level:Beginner tested on:Windows 11 24H2 works on:Windows 10, Windows 11 tools:PowerShell
TL;DR

Create the file at **$PROFILE**, set **Set-ExecutionPolicy -Scope CurrentUser RemoteSigned**, and unblock scripts downloaded from the web. Read **Get-ExecutionPolicy -List** to see which scope wins.

Before you start

Requirements

  • A normal user account
  • PowerShell 7 or Windows PowerShell 5.1
  • 20 minutes
Step by step

Instructions

01

Find the profile path that applies

**$PROFILE** expands to the file for the current host and user. `$PROFILE.AllUsersAllHosts` exists too, but a per-user file keeps your shortcuts out of other accounts.

$PROFILE
$PROFILE | Select-Object *
Test-Path $PROFILE
Get-ExecutionPolicy -List
02

Create the file and test that it loads

Nothing loads until the file exists. Creating it, writing one variable and starting a new shell proves the wiring before you add real logic.

New-Item -ItemType File -Path $PROFILE -Force | Out-Null
Add-Content -Path $PROFILE -Value '$env:GUIDOX_PROFILE = "loaded"'
pwsh -NoLogo -Command '$env:GUIDOX_PROFILE'
tip**-NoProfile** skips the file entirely. That switch is also the fastest way to tell whether a strange behaviour comes from your profile.
03

Change the policy in the right scope

**RemoteSigned** allows local scripts and requires a signature for downloaded ones. MachinePolicy and UserPolicy come from Group Policy and override every other scope, which is why the list matters.

Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned -Force
Get-ExecutionPolicy -List
Get-ExecutionPolicy -Scope CurrentUser
⚠ warningSetting Unrestricted or Bypass for the whole machine removes the only barrier against a downloaded script running unattended. Scope it to the current user.
04

Unblock a file that came from the internet

A downloaded script carries a zone marker in an alternate data stream and RemoteSigned refuses it. **Unblock-File** removes that marker without changing the policy.

Get-Item .\tools\deploy.ps1 -Stream * | Select-Object Stream,Length
Unblock-File .\tools\deploy.ps1
Get-Item .\tools\deploy.ps1 -Stream * | Select-Object Stream,Length
05

Add something worth loading

A profile is for convenience, not for state. Keep it fast: a function, a PSReadLine option and an alias cost milliseconds, a network call at startup costs seconds.

Add-Content -Path $PROFILE -Value @'
function Get-BigFiles { param($Path = "C:\", $Top = 10)
  Get-ChildItem $Path -Recurse -File -ErrorAction SilentlyContinue |
    Sort-Object Length -Descending | Select-Object -First $Top FullName,Length }
Set-PSReadLineOption -PredictionViewStyle ListView
'@
. $PROFILE
Get-BigFiles -Path C:\Windows -Top 3
06

Measure the startup cost

A profile that shells out to a module import or a network share makes every terminal slow. Time a clean start against a profiled one before you blame the shell.

Measure-Command { pwsh -NoProfile -Command 'exit' } | Select-Object TotalMilliseconds
Measure-Command { pwsh -Command 'exit' } | Select-Object TotalMilliseconds
gc $PROFILE | Measure-Object -Line
Troubleshooting

Common issues

The profile does not load

The file name or folder must match what $PROFILE prints exactly, including the host-specific prefix. Check Test-Path again after creating it.

Scripts still refuse to run

A policy scope wins over yours. Get-ExecutionPolicy -List shows MachinePolicy at the top when a domain controls it.

The terminal takes seconds to start

Something in the profile is slow. Comment out half of it, restart, and bisect until the guilty line is obvious.

Edge cases break after the profile change

Aliases shadow cmdlets in scripts too. Use full cmdlet names in shared scripts and keep aliases in interactive-only sections.

FAQ

Frequently asked

Does this affect Windows PowerShell and PowerShell 7?

Each host has its own file. Windows PowerShell uses Documents\WindowsPowerShell, PowerShell 7 uses Documents\PowerShell.

Is RemoteSigned safe enough?

For a single-user machine it blocks the common drive-by case. On a shared machine keep AllSigned and sign the scripts you own.

How to undo this

Delete the profile file or empty it, and set the policy back with Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Undefined. Both changes apply to the next shell only.

Changelog · 1 entry
  1. 2026-10-31
    First version
Interactive version: checklist, feedback, related guides →

Free · no account needed · tracks your progress