A hash table (also known as a dictionary or associative array) is a compact data structure that stores data as key-value pairs. Under the hood, PowerShell hash tables are built on the .NET System.Collections.Hashtable class. Unlike standard arrays indexed by sequential numbers, hash tables allow you to look up values using unique descriptive keys (such as strings or identifiers), enabling fast lookups without scanning the entire collection.
The most common operations on hash tables in PowerShell are explained below.
Declaring Hash Tables (@{})
Hash tables are declared using the @{} syntax. Key-value pairs are separated by semicolons (;) on a single line or placed on separate lines without semicolons.
$ServerConfig = @{ Hostname = "web01"; IP = "192.168.1.50"; Port = 443 }
$DatabaseConfig = @{
ServerName = "sql-prod-01"
Database = "ProductionDB"
MaxPoolSize = 100
Encryption = $true
}
$DatabaseConfig.GetType().FullNameSystem.Collections.HashtableHash table keys can be declared as unquoted identifiers (barewords) if they contain no whitespace or special characters. If a key includes spaces or hyphens, wrap it in quotation marks (e.g., 'Max-Pool-Size' = 100).
Another example using a user profile:
$user = @{
Name = "Tom"
Age = 30
Role = "Administrator"
}
$userName Value
---- -----
Role Administrator
Age 30
Name Tomwhere:
- Each
Key = Valuepair is separated by a newline or semicolon. - Keys are typically strings, but can be other data types as well.
Key-Value Access
You can access and inspect values stored in a hash table using either property dot notation or the array index operator ([]).
Property and Indexer Syntax
$Server = @{
Name = "AppServer01"
Role = "ApplicationHost"
Cores = 8
}
$Server.Name
$Server["Role"]AppServer01
ApplicationHost- Dot Notation (
$hash.Key): Convenient and clean; ideal when keys are known, fixed identifiers without spaces. - Indexer Notation (
$hash['Key']or$hash[$variable]): Necessary when the key contains spaces, special characters, or is stored inside another variable.
Checking Key Existence
Querying a non-existent key returns $null without generating an error. To verify whether a key or value actually exists in the collection, use the built-in boolean inspection methods:
$Server.ContainsKey("Cores")
$Server.ContainsValue("ActiveDirectory")True
FalseAdding and Modifying Keys
Hash tables are dynamic and mutable, allowing you to add, modify, or delete key-value pairs at runtime.
You can add entries via direct assignment or by using the .Add() method:
$Config = @{
Environment = "Staging"
Timeout = 30
}
$Config["MaxRetries"] = 3
$Config.Environment = "Production"
$Config.Add("EnableLog", $true)
$Configwhere:
$Config["MaxRetries"] = 3creates a new keyMaxRetries.$Config.Environment = "Production"overwrites the existingEnvironmentvalue.$Config.Add(...)safely inserts a new key but will throw a runtime exception if the key is already present.
Name Value
---- -----
Environment Production
Timeout 30
EnableLog True
MaxRetries 3Another example adding multiple keys to a user object:
$user = @{ Name = "Tom" }
$user["Department"] = "IT"
$user.Add("Email", "tutorialreference@example.com")
$userName Value
---- -----
Email tutorialreference@example.com
Department IT
Name Tomwhere:
- Assigning to a new key with bracket notation (
$user["Department"] = ...) creates the key if it doesn't already exist. - The
.Add()method achieves the same result but throws an error if the key already exists.
Removing Keys
Use the .Remove() method to delete an individual key-value pair, or .Clear() to empty the entire table:
$Config = @{ Environment = "Production"; Timeout = 30; MaxRetries = 3 }
$Config.Remove("Timeout")
$Config.ContainsKey("Timeout")FalseRemoving a key from a user object:
$user = @{ Name = "Tom"; Department = "IT"; Email = "tutorialreference@example.com" }
$user.Remove("Email")
$userName Value
---- -----
Department IT
Name TomLooping Through Hash Tables
Unlike arrays, passing a hash table directly to the pipeline emits the entire hash table as a single object. To process each key-value pair individually, you must enumerate its keys or entries.
1. Enumerating Keys with .Keys
$Services = @{ Web = "nginx"; DB = "mongod"; Cache = "redis" }
foreach ($key in $Services.Keys) {
Write-Output "Role: $key maps to Service: $($Services[$key])"
}Role: Web maps to Service: nginx
Role: DB maps to Service: mongod
Role: Cache maps to Service: redis2. Enumerating Entries with .GetEnumerator()
To pipe key-value pairs through pipeline cmdlets (Where-Object, ForEach-Object, Sort-Object), call the .GetEnumerator() method. This streams each pair as a DictionaryEntry object with .Key and .Value properties:
$user = @{ Name = "Tom"; Age = 30; Role = "Administrator" }
foreach ($entry in $user.GetEnumerator()) {
Write-Output "$($entry.Key): $($entry.Value)"
}where:
.GetEnumerator()exposes each key-value pair as an object with.Keyand.Valueproperties.- Looping directly over
$userwithout.GetEnumerator()iterates over the keys only in most contexts, so.GetEnumerator()is the safer, explicit approach.
Role: Administrator
Age: 30
Name: Tom3. Filtering Entries Through the Pipeline
$PortMap = @{
HTTP = 80
HTTPS = 443
SSH = 22
RDP = 3389
}
$PortMap.GetEnumerator() |
Where-Object { $_.Value -gt 100 } |
Sort-Object -Property Valuewhere:
.GetEnumerator()unrolls the hash table into distinct stream elements.$_.Keyrepresents the current port identifier.$_.Valuerepresents the numeric port number.
Name Value
---- -----
HTTPS 443
RDP 3389Ordered Hash Tables ([ordered]@{})
Standard hash tables use a hashing algorithm that does not guarantee insertion order. When enumerating keys, the output order often differs from the declaration order.
To preserve the exact sequence in which keys were declared, prefix the literal with the [ordered] type accelerator. This instantiates an underlying System.Collections.Specialized.OrderedDictionary:
$StandardHash = @{
First = 1
Second = 2
Third = 3
}
$OrderedHash = [ordered]@{
First = 1
Second = 2
Third = 3
}
Write-Output "--- Standard Keys ---"
$StandardHash.Keys -join ", "
Write-Output "--- Ordered Keys ---"
$OrderedHash.Keys -join ", "where [ordered]@{} instructs the parser to maintain key sequence upon creation.
--- Standard Keys ---
First, Third, Second
--- Ordered Keys ---
First, Second, ThirdA practical configuration example:
$config = [ordered]@{
Server = "WEB01"
Port = 8080
Protocol = "HTTPS"
}
$configName Value
---- -----
Server WEB01
Port 8080
Protocol HTTPSPractical Example: Parameter Splatting and Server Configuration
A primary use of hash tables in production administration is splatting. Splatting allows you to define a cmdlet's parameters and values inside a hash table and pass them cleanly using the @ symbol instead of long, single-line command invocations.
The following script builds an ordered system configuration map and demonstrates parameter splatting:
$AuditProfile = [ordered]@{
TargetRole = "WebFrontend"
Environment = "Production"
CheckDate = (Get-Date -Format "yyyy-MM-dd")
StatusReport = "Compliant"
}
$FileParams = @{
Path = "$PSScriptRoot\system-audit.json"
Value = ($AuditProfile | ConvertTo-Json)
Force = $true
ErrorAction = "Stop"
}
Set-Content @FileParams
if (Test-Path -Path $FileParams.Path) {
[PSCustomObject]@{
DestinationFile = $FileParams.Path
ProfileWritten = $AuditProfile.TargetRole
ConfigEntries = $AuditProfile.Count
}
}where:
$AuditProfilestores structured operational configuration preserving layout order.$FileParamsis a hash table matching the parameter names ofSet-Content(-Path,-Value,-Force,-ErrorAction).@FileParams(splatting syntax) unpacks the key-value pairs directly into the cmdlet's parameters.Test-Pathvalidates target file generation.
Execute the script from the console:
pwsh ./splatting-demo.ps1DestinationFile ProfileWritten ConfigEntries
--------------- -------------- -------------
C:\Scripts\system-audit.json WebFrontend 4