PowerShell Hashtable: Create, Access, and Iterate Examples

Whether you’re storing configuration settings, building a lookup table, or passing structured data to a function, PowerShell hashtables are the right tool. A hashtable maps keys to values — you give it a name, and you get back the value associated with that name in O(1) time. This guide covers creating, reading, updating, iterating, and ordering hashtables, plus when to prefer them over PSCustomObjects.
Creating a Hashtable
Use the @{} syntax to define a hashtable literal:
# Basic hashtable creation
$config = @{
Server = 'db01.corp.local'
Port = 5432
Database = 'AppDB'
Timeout = 30
}
# Empty hashtable
$lookup = @{}
# Verify the type
$config.GetType().Name
Hashtable
Keys and values can be any type. Keys are typically strings, but they can be integers or other types. Values can be strings, numbers, arrays, other hashtables, or even script blocks.
Reading and Writing Values
Two equivalent ways to access values — dot notation and bracket notation:
$config = @{ Server = 'db01'; Port = 5432 }
# Dot notation
$config.Server
# Bracket notation — required when key name has spaces or special characters
$config['Port']
# Update an existing value
$config.Port = 5433
$config['Server'] = 'db02.corp.local'
# Display the entire hashtable
$config
db01
5432
Name Value
---- -----
Port 5433
Server db02.corp.local
Bracket notation is required when key names contain spaces or characters that conflict with PowerShell syntax. Dot notation is cleaner for simple alphanumeric keys.
Adding and Removing Keys
$settings = @{ Theme = 'Dark'; Language = 'en-US' }
# Add a new key
$settings['MaxRetries'] = 3
$settings.Add('LogLevel', 'Info') # .Add() throws if key already exists
# Remove a key
$settings.Remove('Theme')
# Check if key exists before accessing
if ($settings.ContainsKey('Language')) {
Write-Output "Language: $($settings['Language'])"
}
# All keys and all values
$settings.Keys
$settings.Values
Language: en-US
MaxRetries
Language
LogLevel
3
en-US
Info
Always use ContainsKey() before accessing a key you’re not sure exists. Accessing a missing key returns $null rather than throwing an error, which can lead to silent bugs downstream.
Iterating with GetEnumerator
To loop through all key-value pairs in a hashtable, use GetEnumerator():
$config = @{
Server = 'db01'
Port = 5432
Database = 'AppDB'
}
foreach ($entry in $config.GetEnumerator()) {
Write-Output "$($entry.Key) = $($entry.Value)"
}
# Alternatively, pipe to ForEach-Object
$config.GetEnumerator() | ForEach-Object {
"$($_.Key): $($_.Value)"
}
Server = db01
Port = 5432
Database = AppDB
Do not try to modify a hashtable while iterating over it with GetEnumerator() — that throws an error. Collect the changes first, then apply them after the loop.
Ordered Hashtables
Regular hashtables don’t preserve insertion order. When order matters (for display, config serialization, or matching expected output), use the [ordered] type accelerator:
# Standard hashtable — key order is unpredictable
$unordered = @{ A = 1; B = 2; C = 3 }
# Ordered dictionary — preserves insertion order
$ordered = [ordered]@{
Step1 = 'Initialize'
Step2 = 'Process'
Step3 = 'Cleanup'
}
$ordered.Keys
Step1
Step2
Step3
The [ordered] cast creates a System.Collections.Specialized.OrderedDictionary instead of a System.Collections.Hashtable. Both support the same access patterns.
Hashtable vs PSCustomObject
Hashtables and PSCustomObjects are related but serve different purposes:
- Hashtable — best for dynamic key-value storage, config data, and lookup tables. Keys can be added and removed at runtime. Faster to create.
- PSCustomObject — best for structured records that flow through a pipeline, format nicely in tables, and are exported to CSV. Created from a hashtable with
[PSCustomObject]@{}.
# Convert hashtable to PSCustomObject for pipeline-friendly output
$person = [PSCustomObject]@{
Name = 'Alice'
Department = 'Engineering'
StartDate = '2023-03-15'
}
$person | Format-Table
Name Department StartDate
---- ---------- ---------
Alice Engineering 2023-03-15
Common Errors and Fixes
-
Key not found throws exception — use ContainsKey first: Calling
$hash.Add('key', 'value')when the key already exists throws"An item with the same key has already been added". Check with$hash.ContainsKey('key')first, or use the indexer assignment$hash['key'] = 'value'which silently overwrites. -
Ordered hashtable requires [ordered] cast: Writing
@{ordered; A=1; B=2}is not valid syntax. The correct form is[ordered]@{ A=1; B=2 }. Forgetting the cast means your output order will be unpredictable, which can break tests or serialization.
Related Cmdlets / See Also
Wrapping Up
Hashtables are the most versatile data structure in PowerShell scripting. Use them for config, lookups, and structured data; use [ordered] when insertion order matters; convert to PSCustomObject when you need pipeline-friendly output. Use ContainsKey() defensively before reading or adding keys. Your next step: try building a configuration hashtable for one of your existing scripts and pass it as a parameter.


