Your First Script
Note
Step-by-step tutorial for creating your first PowerShell script from scratch, covering everything from basics to best practices.
Overview
This tutorial will walk you through creating a complete, practical PowerShell script. You'll learn how to:
- Set up a script file
- Accept user input
- Process data
- Handle errors
- Save output
- Run your script
We'll build a File Organizer Script that sorts files into folders based on their extension - a real-world task you'll actually use!
What You'll Build
A script that: 1. Asks the user for a folder path 2. Groups files by extension (.txt, .pdf, .jpg, etc.) 3. Creates folders for each extension 4. Moves files into their respective folders 5. Reports what was organized
Step 1: Create the Script File
Create a New .ps1 File
# Option 1: Use PowerShell to create the file
New-Item -Path "C:\Scripts\Organize-Files.ps1" -ItemType File
# Option 2: Use your favorite text editor
# Create a new file called "Organize-Files.ps1"
# Save it in a folder like C:\Scripts\
Naming Convention
- Use Verb-Noun format (e.g.,
Organize-Files,Get-Report,Set-Config) - Use
.ps1extension for PowerShell scripts - Avoid spaces in file names (use hyphens or CamelCase)
Set Execution Policy (If Needed)
# Check current execution policy
Get-ExecutionPolicy
# If it's "Restricted", you can't run scripts
# Set it to RemoteSigned to allow local scripts
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Step 2: Add the Script Header
Start your script with comments that explain what it does:
<#
.SYNOPSIS
Organizes files in a directory by moving them into folders based on their extension.
.DESCRIPTION
This script scans a specified directory, groups files by their extension,
creates folders for each extension type, and moves the files accordingly.
.PARAMETER Path
The path to the directory containing files to organize.
.EXAMPLE
.\Organize-Files.ps1 -Path "C:\Downloads"
.NOTES
Author: Your Name
Created: 2025-12-27
Version: 1.0
#>
Why Add Comments?
- Helps you remember what the script does months later
- Makes it easier for others to understand your code
- Enables
Get-Helpto show information about your script
Step 3: Add Parameters
Accept input from the user when they run the script:
param(
[Parameter(Mandatory = $false)]
[string]$Path,
[Parameter(Mandatory = $false)]
[switch]$WhatIf
)
Explanation:
param()defines what inputs the script accepts[Parameter(Mandatory = $false)]- this input is optional[string]$Path- a text parameter called "Path"[switch]$WhatIf- an on/off flag (no value needed)
Step 4: Validate Input
Check if the user provided a path, and verify it exists:
# If no path provided, ask for one
if (-not $Path) {
$Path = Read-Host "Enter the path to organize"
}
# Verify the path exists
if (-not (Test-Path -Path $Path)) {
Write-Error "Path not found: $Path"
exit 1 # Exit with error code
}
# Verify it's a directory, not a file
$item = Get-Item -Path $Path
if (-not $item.PSIsContainer) {
Write-Error "Path must be a directory, not a file"
exit 1
}
Always Validate Input
- Check if required data is provided
- Verify paths exist before using them
- Confirm data is the right type (file vs folder, number vs text)
Step 5: Main Processing Logic
Get files and group them by extension:
# Get all files (not directories) from the path
Write-Output "Scanning $Path for files..."
$files = Get-ChildItem -Path $Path -File
# Report what we found
Write-Output "Found $($files.Count) files to organize"
# Group files by extension
$fileGroups = $files | Group-Object -Property Extension
# Report the groups
Write-Output "`nFile breakdown:"
foreach ($group in $fileGroups) {
$extName = if ($group.Name) { $group.Name } else { "No Extension" }
Write-Output " $extName : $($group.Count) files"
}
Step 6: Create Folders and Move Files
# Process each group
foreach ($group in $fileGroups) {
# Determine folder name
$folderName = if ($group.Name) {
$group.Name.TrimStart('.') # Remove the leading dot from extension
} else {
"NoExtension"
}
# Create full folder path
$targetFolder = Join-Path -Path $Path -ChildPath $folderName
# Create folder if it doesn't exist
if (-not (Test-Path -Path $targetFolder)) {
Write-Output "`nCreating folder: $folderName"
if (-not $WhatIf) {
New-Item -Path $targetFolder -ItemType Directory | Out-Null
}
}
# Move files
Write-Output "Moving $($group.Count) $folderName files..."
foreach ($file in $group.Group) {
$destination = Join-Path -Path $targetFolder -ChildPath $file.Name
if ($WhatIf) {
Write-Output " [WHATIF] Would move: $($file.Name)"
} else {
Move-Item -Path $file.FullName -Destination $destination
Write-Output " Moved: $($file.Name)"
}
}
}
Step 7: Add Error Handling
Wrap the main logic in try/catch to handle errors gracefully:
try {
# Get all files (not directories) from the path
Write-Output "Scanning $Path for files..."
$files = Get-ChildItem -Path $Path -File -ErrorAction Stop
# ... rest of the processing logic ...
} catch {
Write-Error "An error occurred: $_"
Write-Error $_.Exception.Message
exit 1
} finally {
Write-Output "`nScript completed!"
}
The Complete Script
Here's the full script put together:
<#
.SYNOPSIS
Organizes files in a directory by moving them into folders based on their extension.
.DESCRIPTION
This script scans a specified directory, groups files by their extension,
creates folders for each extension type, and moves the files accordingly.
.PARAMETER Path
The path to the directory containing files to organize.
.PARAMETER WhatIf
Preview what would happen without actually moving files.
.EXAMPLE
.\Organize-Files.ps1 -Path "C:\Downloads"
.EXAMPLE
.\Organize-Files.ps1 -Path "C:\Downloads" -WhatIf
.NOTES
Author: Your Name
Created: 2025-12-27
Version: 1.0
#>
param(
[Parameter(Mandatory = $false)]
[string]$Path,
[Parameter(Mandatory = $false)]
[switch]$WhatIf
)
# Validate input
if (-not $Path) {
$Path = Read-Host "Enter the path to organize"
}
if (-not (Test-Path -Path $Path)) {
Write-Error "Path not found: $Path"
exit 1
}
$item = Get-Item -Path $Path
if (-not $item.PSIsContainer) {
Write-Error "Path must be a directory, not a file"
exit 1
}
# Main logic with error handling
try {
# Get all files
Write-Output "Scanning $Path for files..."
$files = Get-ChildItem -Path $Path -File -ErrorAction Stop
if ($files.Count -eq 0) {
Write-Output "No files found to organize."
exit 0
}
Write-Output "Found $($files.Count) files to organize"
# Group by extension
$fileGroups = $files | Group-Object -Property Extension
# Show breakdown
Write-Output "`nFile breakdown:"
foreach ($group in $fileGroups) {
$extName = if ($group.Name) { $group.Name } else { "No Extension" }
Write-Output " $extName : $($group.Count) files"
}
# Process each group
foreach ($group in $fileGroups) {
# Determine folder name
$folderName = if ($group.Name) {
$group.Name.TrimStart('.')
} else {
"NoExtension"
}
# Create full folder path
$targetFolder = Join-Path -Path $Path -ChildPath $folderName
# Create folder if needed
if (-not (Test-Path -Path $targetFolder)) {
Write-Output "`nCreating folder: $folderName"
if (-not $WhatIf) {
New-Item -Path $targetFolder -ItemType Directory | Out-Null
}
}
# Move files
Write-Output "Moving $($group.Count) $folderName files..."
foreach ($file in $group.Group) {
$destination = Join-Path -Path $targetFolder -ChildPath $file.Name
if ($WhatIf) {
Write-Output " [WHATIF] Would move: $($file.Name)"
} else {
try {
Move-Item -Path $file.FullName -Destination $destination -ErrorAction Stop
Write-Output " Moved: $($file.Name)"
} catch {
Write-Warning " Failed to move $($file.Name): $_"
}
}
}
}
Write-Output "`nOrganization complete!"
} catch {
Write-Error "An error occurred: $_"
Write-Error $_.Exception.Message
exit 1
} finally {
Write-Output "`nScript finished."
}
How to Run Your Script
Option 1: Run from PowerShell
# Navigate to the script directory
cd C:\Scripts
# Run the script with a parameter
.\Organize-Files.ps1 -Path "C:\Downloads"
# Preview without making changes
.\Organize-Files.ps1 -Path "C:\Downloads" -WhatIf
# Run without parameter (will prompt)
.\Organize-Files.ps1
Option 2: Run from File Explorer
# Right-click the .ps1 file
# Select "Run with PowerShell"
# Note: This may not work if execution policy is restrictive
Option 3: Run with Full Path
Testing Your Script
Always test with -WhatIf first!
# Test without making changes
.\Organize-Files.ps1 -Path "C:\Test" -WhatIf
# Review the output to see what WOULD happen
# If it looks good, run without -WhatIf
.\Organize-Files.ps1 -Path "C:\Test"
Always Test First
- Use
-WhatIfto preview changes - Test on a small sample folder first
- Make backups of important data before running scripts
Improving Your Script
Add Logging
# Add at the start of your script
$logPath = "C:\Logs\Organize-Files-$(Get-Date -Format 'yyyyMMdd-HHmmss').log"
Start-Transcript -Path $logPath
# Your script here...
# Add at the end
Stop-Transcript
Add Progress Indicators
# Show progress when processing many files
$counter = 0
foreach ($file in $files) {
$counter++
Write-Progress -Activity "Organizing files" `
-Status "Processing $($file.Name)" `
-PercentComplete (($counter / $files.Count) * 100)
# Process file...
}
Add Verbose Output
# Change Write-Output to Write-Verbose for detailed messages
Write-Verbose "Processing file: $($file.Name)" -Verbose
# Run with -Verbose to see these messages
.\Organize-Files.ps1 -Path "C:\Downloads" -Verbose
Common Script Patterns
Pattern 1: Validate, Process, Report
# 1. Validate input
if (-not (Test-Path $Path)) { exit }
# 2. Process data
$results = Get-ChildItem $Path | Where-Object {...}
# 3. Report results
Write-Output "Processed $($results.Count) items"
Pattern 2: Try-Catch-Finally
try {
# Main logic that might fail
$data = Get-Content $FilePath
} catch {
# Handle errors
Write-Error $_
} finally {
# Always runs (cleanup)
Write-Output "Done"
}
Pattern 3: Pipeline Processing
Get-ChildItem -Path $Path |
Where-Object {$_.Length -gt 1MB} |
ForEach-Object {
# Process each item
Move-Item $_.FullName -Destination $TargetPath
}
Next Steps
Now that you've created your first script:
- Experiment - Modify the script to do something different
- Add features - What if you wanted to organize by size instead? Or date?
- Learn more - Explore the related topics below
- Share - Put your scripts on GitHub to share with others
Common Mistakes
Forgetting to Save Changes
Running from the Wrong Directory
Hardcoding Paths
Tips & Tricks
Use ISE or VS Code
Test Parameters Separately
Use Descriptive Variable Names
Common Mistakes
Not Saving Your Script Before Running
Running Wrong Version of Script
Forgetting Execution Policy
Not Using Parameters
Related Topics
- Script Structure - Organizing larger scripts
- Functions - Reusable code blocks
- Error Handling - Robust error management
- Variables & Data Types - Working with data in scripts
Additional Resources
- Microsoft Docs: About Scripts
- Microsoft Docs: About Comment Based Help
- PowerShell Gallery - Browse thousands of PowerShell scripts