PowerShell Variables: Complete Guide with Types, Scoping & Examples [2024]
Master PowerShell variables—the foundation of all scripts. This comprehensive guide covers data types, scoping rules, expansion techniques, special variables, and production-grade patterns.
Table of Contents
- What are Variables?
- Variable Naming Rules
- Variable Types
- Strings vs Variable Expansion
- Numeric Variables
- Boolean Variables
- Array Variables
- Hashtable Variables
- Object Variables
- Special Variables
- Variable Scope
- Casting & Type Conversion
- Read-Only Variables
- Variable Operations
- Common Mistakes
- Best Practices
- Troubleshooting
- FAQs
- Real-World Examples
- Conclusion
What are Variables? {#what-are-variables}
A PowerShell variable is a named storage location that holds a value. Variables are fundamental building blocks—they store data, cmdlet output, calculations, and object references.
Key Characteristics:
- ✅ Dynamically typed (type determined at assignment)
- ✅ Case-insensitive by default (best practice: follow conventions)
- ✅ Prefixed with dollar sign
$ - ✅ Can hold any .NET data type
- ✅ Scoped (local, script, global, environment)
- ✅ Can be cast to specific types
Variable Naming Rules {#naming-rules}
Valid Names
$number = 42
$name = "John"
$serverList = @("Server1", "Server2")
$_currentValue = "Value"
$result2024 = "Data"
Invalid Names
$-number = 42 # ❌ Special char at start
$123start = 42 # ❌ Can't start with number
$my-var = 42 # ❌ Hyphens invalid (use camelCase)
$ space = 42 # ❌ Spaces invalid
Naming Best Practices
- Use camelCase:
$serverName,$processCount - Use descriptive names:
$usernot$u - Avoid single letters except in loops:
foreach ($item in $items) - Use plural for collections:
$services,$users
Variable Types {#types}
| Type | Example | Description |
|---|---|---|
[string] | $name = "John" | Text values |
[int] | $count = 42 | Whole numbers |
[double] | $price = 19.99 | Decimal numbers |
[boolean] | $enabled = $true | True/False |
[array] | $items = @(1, 2, 3) | Collections |
[hashtable] | $config = @{...} | Key-value pairs |
[PSCustomObject] | [PSCustomObject]@{...} | Custom objects |
[DateTime] | [DateTime]::Now | Dates and times |
Strings vs Variable Expansion {#expansion}
Single Quotes (Literal - No Expansion)
$name = "John"
Write-Output 'Hello $name' # Output: Hello $name
Write-Output 'Value is $5.00' # Output: Value is $5.00
Double Quotes (Interpolation - Expansion)
$name = "John"
Write-Output "Hello $name" # Output: Hello John
Write-Output "2 + 2 = $((2 + 2))" # Output: 2 + 2 = 4
Property Access in Strings
$file = Get-Item -Path "C:\test.txt"
Write-Output "File: $($file.Name)" # Output: File: test.txt
Write-Output "Modified: $($file.LastWriteTime.ToString('yyyy-MM-dd'))"
Numeric Variables {#numeric}
Integer Variables
[int]$count = 100
$count++
Write-Output "Count: $count" # Output: Count: 101
# Automatic type detection
$num = 42 # Becomes [int]
$num = 42.5 # Becomes [double]
Double/Decimal Variables
[double]$pi = 3.14159
$price = 19.99 # Automatic double
# Math operations
$total = $price * 2
$average = ($count1 + $count2) / 2
Rounding and Formatting
$value = 3.14159
[Math]::Round($value, 2) # Output: 3.14
"{0:F2}" -f $value # Output: 3.14
Boolean Variables {#boolean}
True/False Values
$enabled = $true
$isAdmin = $false
if ($enabled) {
Write-Output "Feature is enabled"
}
# Negation
if (-not $isAdmin) {
Write-Output "User is not admin"
}
Converting to Boolean
[bool]"true" # $true
[bool]"false" # $true (any non-empty string is $true!)
[bool]0 # $false
[bool]1 # $true
Array Variables {#array-variables}
Creating Arrays
# Using @() operator
$numbers = @(1, 2, 3, 4, 5)
$names = @("Alice", "Bob", "Charlie")
# Implicit array
$colors = "red", "green", "blue"
# Empty array
$empty = @()
Accessing Array Elements
$services = @("BITS", "wuauserv", "WinDefend")
$services[0] # First: BITS
$services[-1] # Last: WinDefend
$services[0, 2] # Multiple: BITS, WinDefend
$services[1..2] # Range: wuauserv, WinDefend
Array Operations
$list = @(1, 2, 3)
# Add item
$list += 4
# Count items
$list.Count # 4
# Loop
foreach ($item in $list) { Write-Output $item }
# Filter
$list | Where-Object { $_ -gt 2 }
Hashtable Variables {#hashtable-variables}
Creating Hashtables
# Using @{} syntax
$config = @{
Server = "localhost"
Port = 8080
Debug = $true
}
# Accessing values
$config["Server"] # localhost
$config.Port # 8080
Modifying Hashtables
$user = @{
Name = "John"
Age = 30
}
# Add new key
$user["Email"] = "john@example.com"
# Update existing
$user.Age = 31
# Remove key
$user.Remove("Age")
Object Variables {#object-variables}
Creating Custom Objects
$person = [PSCustomObject]@{
Name = "John"
Age = 30
Department = "IT"
}
# Access properties
$person.Name # John
$person["Age"] # 30
# Modify properties
$person.Age = 31
Collections of Objects
$users = @(
[PSCustomObject]@{Name = "John"; Age = 30},
[PSCustomObject]@{Name = "Jane"; Age = 28},
[PSCustomObject]@{Name = "Bob"; Age = 35}
)
# Filter objects
$users | Where-Object { $_.Age -gt 30 }
# Get properties
$users | Select-Object -ExpandProperty Name
Special Variables {#special}
| Variable | Meaning | Example |
|---|---|---|
$_ | Current pipeline object | Get-Service | ForEach-Object { $_.Name } |
$args | Script arguments | Access function parameters |
$null | No value | $x = $null |
$true | Boolean true | if ($enabled) |
$false | Boolean false | if (-not $disabled) |
$? | Last command success | if ($?) { "Success" } |
$LASTEXITCODE | Last command exit code | External program results |
$Host | PowerShell host | $Host.UI.WriteErrorLine() |
$PSVersionTable | PowerShell version | $PSVersionTable.PSVersion |
$env: | Environment variables | $env:USERNAME, $env:APPDATA |
Using $env Variables
$username = $env:USERNAME # Current user
$computerName = $env:COMPUTERNAME # Computer name
$tempPath = $env:TEMP # Temp directory
# Modify environment variable
$env:CUSTOMVAR = "MyValue"
Variable Scope {#scope}
Local Scope (Default)
$globalVar = "I'm global"
function Test-Local {
$localVar = "I'm local"
Write-Output $globalVar # Can access global
}
Test-Local
Write-Output $localVar # Error: $localVar doesn't exist here
Script Scope
$script:sharedVar = "Shared across script"
function Modify {
$script:sharedVar = "Modified"
}
Modify
Write-Output $script:sharedVar # Modified
Global Scope
$global:globalVar = "Truly global"
function Access {
Write-Output $global:globalVar
}
Access
Casting & Type Conversion {#casting}
Explicit Casting
[string]$text = 42 # "42"
[int]$number = "100" # 100
[double]$decimal = "3.14" # 3.14
[bool]$flag = 1 # $true
[datetime]$date = "2024-02-05" # Date object
# Array casting
[string[]]$names = "Alice", "Bob"
Parsing vs Casting
# Safe parsing with try-catch
try {
$num = [int]::Parse("123")
} catch {
Write-Error "Invalid number"
}
# Direct casting (throws error on failure)
$num = [int]"456" # Works
$num = [int]"not-a-number" # Error!
Read-Only Variables {#readonly}
Creating Read-Only Variables
$readOnly = "Initial value"
Set-Variable -Name readOnly -Value "New value" -Option ReadOnly
# Now this will error:
$readOnly = "Cannot change" # Error: Variable is read-only
Constant Variables
Set-Variable -Name PI -Value 3.14159 -Option Constant
# Cannot be changed, even with Set-Variable
Set-Variable -Name PI -Value 3.14 # Error!
Variable Operations {#operations}
Testing Variables
# Test if exists
if (Test-Path Variable:myVar) { Write-Output "Exists" }
# Get variable value
$value = Get-Variable -Name myVar -ValueOnly
# List all variables
Get-Variable | Where-Object { $_.Name -like "my*" }
Clearing Variables
# Remove single variable
Remove-Variable -Name myVar
# Remove all user variables (keeps system variables)
Remove-Variable -Name * -ErrorAction SilentlyContinue
Common Mistakes {#mistakes}
❌ Mistake 1: Forgetting Dollar Sign
# WRONG: No $ prefix
name = "John" # Error or creates new command
# RIGHT: Use $ prefix
$name = "John" # Correct
❌ Mistake 2: Spaces Around Operators
# WRONG: Spaces cause issues
$x= 5 # May not work as expected
$y =10 # Inconsistent
# RIGHT: Consistent spacing
$x = 5
$y = 10
❌ Mistake 3: String Expansion Surprises
# WRONG: Expects expansion, won't happen
$path = 'C:\Users\$env:USERNAME\Documents'
# RIGHT: Use double quotes for expansion
$path = "C:\Users\$env:USERNAME\Documents"
❌ Mistake 4: Null Handling
# WRONG: Doesn't check for null
$data = Get-Item -Path "nonexistent" -ErrorAction SilentlyContinue
Write-Output $data.Name # Error if null!
# RIGHT: Check before use
if ($data) { Write-Output $data.Name }
Best Practices {#best-practices}
✅ Use Descriptive Names
$serverNamenot$s$processCountnot$pc
✅ Type Hints When Appropriate
[string]$username = "admin"[int]$port = 8080
✅ Initialize Before Use
$total = 0before$total += $value
✅ Use Variable Scope Explicitly
$script:sharedfor script scope$global:everywherefor global
✅ Validate Input
- Check for
$null - Validate type with
GetType()
Troubleshooting {#troubleshooting}
Issue: “Cannot find variable”
- Cause: Typo or scope issue
- Solution: Check spelling, use proper scope prefix
Issue: “Variable is read-only”
- Cause: Variable was set with ReadOnly option
- Solution: Create new variable or use Set-Variable -Force
Issue: String not expanding
- Cause: Using single quotes
- Solution: Use double quotes for interpolation
FAQs {#faqs}
Q: Are PowerShell variables case-sensitive?
A: Variables are case-insensitive ($name = $NAME), but best practice is consistent casing.
Q: What’s the difference between $null and empty string?
A: $null = no value, "" = empty string
Q: Can I use hyphens in variable names?
A: No. Use $variable-name wrapping or camelCase: $variableName
Q: How do I clear all variables?
A: Remove-Variable -Name * -ErrorAction SilentlyContinue (preserves system variables)
Real-World Examples {#real-world-examples}
Example 1: Configuration Management
$config = @{
DatabaseServer = "db.contoso.com"
DatabasePort = 5432
Username = "admin"
Debug = $false
}
$connectionString = "Server=$($config.DatabaseServer);Port=$($config.DatabasePort)"
Example 2: Data Processing
$users = @(
[PSCustomObject]@{Name = "John"; Department = "IT"},
[PSCustomObject]@{Name = "Jane"; Department = "HR"}
)
$itUsers = $users | Where-Object { $_.Department -eq "IT" }
Example 3: Logging
$logFile = "C:\Logs\app_$(Get-Date -Format 'yyyyMMdd').log"
$logLevel = "INFO"
$message = "Application started"
"[$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss'))] [$logLevel] $message" | Add-Content -Path $logFile
Conclusion
PowerShell variables are fundamental to every script. Understanding types, scoping, and best practices enables you to write robust, maintainable automation.
Key Takeaways:
- Variables store any .NET data type
- Use clear naming conventions
- Understand variable scope
- Double quotes for string expansion, single quotes for literals
- Always validate input and handle null values
For related concepts, explore PowerShell Arrays, PowerShell Hashtables, and the Complete PowerShell Guide.
Related Articles
Variable Types & Structures
- PowerShell Arrays - Array variables and indexing
- PowerShell Hashtables - Key-value variable storage
- PowerShell Collections - Collection types
- Custom Objects - Object variables
- Type Casting - Convert variable types
Variable Scoping & Management
- Variable Scope - Scope rules and inheritance
- Environment Variables - System variables
- Remove Environment Variable - Delete environment vars
- Boolean Variables - True/false variables
String & Data Processing
- PowerShell Strings - String variable operations
- String Formatting - Format string variables
- Null Values - Handle null variables
- Numeric Operations - Math with variables
Control & Execution
- PowerShell If-Else Statements - Conditional variables
- PowerShell Loops - Loop with variables
- PowerShell Functions - Function parameters/variables
- PowerShell Error Handling - Error variables
Advanced Usage
- PowerShell Operators - Operator variables
- Regular Expressions - Regex with variables
- PowerShell Remoting - Remote variables
- Date/Time Variables - Date variables
Comprehensive Guides
- Complete PowerShell Guide - Full variables section
- Active Directory Guide - Variables in AD automation
- PowerShell Best Practices - Variable naming conventions