By Davy Rogers. Use these techniques only in environments you own or have explicit permission to test.

PowerShell: Variables and Data Types

Variables are containers for storing data in your scripts. PowerShell is dynamically typed but provides strong typing when needed. This lesson covers variable declaration, scoping, data types, arrays, hashtables, and custom objects - essential for writing powerful scripts.

What You'll Learn

  • Creating and using variables
  • Variable naming conventions
  • Data types
  • Type conversion and casting
  • Arrays and array operations
  • Hashtables (dictionaries)
  • Custom objects (PSCustomObject)
  • Variable scope
  • Environment variables

Creating Variables

Basic Declaration

# Simple assignment ($ prefix)
$name = "Alice"
$age = 25
$isAdmin = $true
$price = 19.99

# No type declaration needed!
$var = "string"
$var = 123          # Now it's a number
$var = $true        # Now it's a boolean

Variable Naming Rules

✓ Must start with $
✓ Can contain letters, numbers, underscores
✓ Case-insensitive ($Name = $name = $NAME)
✓ Use descriptive names

Examples:
$userName
$user_name
$userName123
$_private

Data Types

Common Types

# String
$text = "Hello, World!"
$text.GetType().Name    # String

# Integer
$number = 42
$number.GetType().Name  # Int32

# Double (decimal)
$price = 19.99
$price.GetType().Name   # Double

# Boolean
$isActive = $true
$isActive.GetType().Name  # Boolean

# DateTime
$date = Get-Date
$date.GetType().Name    # DateTime

# Array
$array = @(1, 2, 3)
$array.GetType().Name   # Object[]

# Hashtable
$hash = @{Name="Alice"; Age=25}
$hash.GetType().Name    # Hashtable

Check Variable Type

$var = 42

# Method 1
$var.GetType()

# Method 2
$var -is [int]          # True
$var -is [string]       # False

# Get type name
$var.GetType().Name     # Int32
$var.GetType().FullName # System.Int32

Type Conversion and Casting

Explicit Casting

# Cast to specific type
[int]$number = "42"       # String to Int
[string]$text = 123       # Int to String
[double]$decimal = 10     # Int to Double
[datetime]$date = "2025-10-08"  # String to DateTime

# Type enforcement
[int]$age = 25
$age = "thirty"           # ERROR! Type mismatch

Implicit Conversion

# PowerShell converts automatically
$result = "10" + 20       # 1020 (string concatenation)
$result = 10 + "20"       # 30 (numeric addition)

# Be careful!
"5" + 10                  # "510" (string)
5 + "10"                  # 15 (number)

Convert Methods

# Convert strings to numbers
[Convert]::ToInt32("42")
[Convert]::ToDouble("3.14")

# Convert to specific types
[int]::Parse("123")
[double]::Parse("3.14")
[datetime]::Parse("2025-10-08")

# Try parse (safer)
$result = 0
if ([int]::TryParse("123", [ref]$result)) {
    Write-Host "Parsed: $result"
} else {
    Write-Host "Failed to parse"
}

Arrays

Creating Arrays

# Method 1: @() syntax
$array = @(1, 2, 3, 4, 5)

# Method 2: Comma operator
$array = 1, 2, 3, 4, 5

# Method 3: Range operator
$array = 1..10              # 1 to 10

# Empty array
$empty = @()

# Single item array
$single = ,1                # Force single item array
$single = @(1)             # Alternative

Accessing Elements

$array = @("a", "b", "c", "d", "e")

# By index (0-based)
$array[0]           # "a"
$array[2]           # "c"
$array[-1]          # "e" (last item)
$array[-2]          # "d" (second to last)

# Range
$array[0..2]        # a, b, c
$array[1..3]        # b, c, d

# Multiple specific indices
$array[0,2,4]       # a, c, e

# Array length
$array.Length       # 5
$array.Count        # 5

Array Operations

# Add element (creates new array)
$array = @(1, 2, 3)
$array += 4              # 1, 2, 3, 4

# Add multiple
$array += 5, 6, 7

# Combine arrays
$a = @(1, 2)
$b = @(3, 4)
$c = $a + $b             # 1, 2, 3, 4

# Remove element (filter)
$array = @(1, 2, 3, 4, 5)
$array = $array | Where-Object { $_ -ne 3 }  # Remove 3

# Check if contains
$array -contains 3       # True/False
3 -in $array            # True/False

# Sort
$array | Sort-Object
$array | Sort-Object -Descending

# Unique values
$array | Select-Object -Unique

Array Methods

$array = @(3, 1, 4, 1, 5, 9, 2, 6)

# Sort (modifies original)
$array.Sort()

# Reverse (modifies original)
$array.Reverse()

# Index of value
$array.IndexOf(4)

# Clear array
$array.Clear()

ArrayList (Resizable)

# Create ArrayList
$list = New-Object System.Collections.ArrayList

# Add items
$list.Add("item1") | Out-Null
$list.Add("item2") | Out-Null
$list.AddRange(@("item3", "item4"))

# Remove items
$list.Remove("item2")
$list.RemoveAt(0)      # Remove by index

# Access
$list[0]
$list.Count

Hashtables

Creating Hashtables

# Method 1: @{} syntax
$user = @{
    Name = "Alice"
    Age = 25
    City = "NYC"
}

# Method 2: Explicit
$hash = @{}
$hash["key1"] = "value1"
$hash["key2"] = "value2"

# Ordered hashtable
$ordered = [ordered]@{
    First = 1
    Second = 2
    Third = 3
}

Accessing Hashtable

$user = @{Name="Alice"; Age=25; City="NYC"}

# Access by key
$user["Name"]           # Alice
$user.Name              # Alice (dot notation)

# Add key-value
$user["Email"] = "[email protected]"
$user.Phone = "555-1234"

# Update value
$user["Age"] = 26
$user.Age = 26

# Remove key
$user.Remove("City")

# Check if key exists
$user.ContainsKey("Name")    # True
$user.ContainsKey("Phone")   # True/False

Hashtable Operations

$hash = @{A=1; B=2; C=3}

# Get keys
$hash.Keys

# Get values
$hash.Values

# Count items
$hash.Count

# Loop through hashtable
foreach ($key in $hash.Keys) {
    Write-Host "$key = $($hash[$key])"
}

# Alternative loop
$hash.GetEnumerator() | ForEach-Object {
    Write-Host "$($_.Key) = $($_.Value)"
}

Custom Objects (PSCustomObject)

Creating Custom Objects

# Method 1: PSCustomObject
$user = [PSCustomObject]@{
    Name = "Alice"
    Age = 25
    City = "NYC"
}

# Method 2: New-Object
$user = New-Object PSObject -Property @{
    Name = "Bob"
    Age = 30
    City = "LA"
}

# Access properties
$user.Name              # Alice
$user.Age               # 25

# Add property
$user | Add-Member -MemberType NoteProperty -Name "Email" -Value "[email protected]"

# Modify property
$user.Age = 26

Working with Objects

# Create multiple objects
$users = @(
    [PSCustomObject]@{Name="Alice"; Age=25; City="NYC"}
    [PSCustomObject]@{Name="Bob"; Age=30; City="LA"}
    [PSCustomObject]@{Name="Charlie"; Age=35; City="Chicago"}
)

# Access objects
$users[0].Name          # Alice
$users[1].Age           # 30

# Filter objects
$users | Where-Object { $_.Age -gt 25 }

# Select properties
$users | Select-Object Name, Age

# Export to CSV
$users | Export-Csv users.csv -NoTypeInformation

# Convert to JSON
$users | ConvertTo-Json

Variable Scope

Scope Levels

Global:     Available everywhere
Script:     Available in script file
Local:      Current scope (default)
Private:    Current scope only (not child scopes)

Scope Examples

# Global variable
$global:name = "Alice"

# Script variable
$script:age = 25

# Local variable (default)
$city = "NYC"

# Private variable
$private:secret = "password"

# Access from different scopes
function Test {
    Write-Host $global:name    # Accessible
    Write-Host $script:age     # Accessible
    Write-Host $city           # May not be accessible
}

Special Variables

Automatic Variables

$_          # Current pipeline object
$?          # Success of last command (True/False)
$^          # First token of last command
$$$         # Last token of last command
$LASTEXITCODE   # Exit code of last program
$Error      # Array of recent errors
$Host       # Host application info
$HOME       # User home directory
$PSVersionTable # PowerShell version info
$PWD        # Current directory
$null       # Null value
$true       # Boolean true
$false      # Boolean false

Examples:

# Check if command succeeded
Remove-Item file.txt
if ($?) {
    Write-Host "Success"
} else {
    Write-Host "Failed"
}

# Get last error
$Error[0]

# Current user home
Write-Host $HOME

# PowerShell version
$PSVersionTable.PSVersion

Environment Variables

Access Environment Variables

# Method 1: $env: prefix
$env:USERNAME
$env:COMPUTERNAME
$env:PATH
$env:TEMP

# Method 2: [Environment] class
[Environment]::GetEnvironmentVariable("USERNAME")

# List all environment variables
Get-ChildItem Env:

# Check if exists
Test-Path Env:\USERNAME

Set Environment Variables

# Current session only
$env:MY_VAR = "value"

# Permanent (user level)
[Environment]::SetEnvironmentVariable("MY_VAR", "value", "User")

# Permanent (system level) - requires admin
[Environment]::SetEnvironmentVariable("MY_VAR", "value", "Machine")

# Remove
[Environment]::SetEnvironmentVariable("MY_VAR", $null, "User")

Practical Examples

Example 1: User Information Object

$user = [PSCustomObject]@{
    Username = $env:USERNAME
    ComputerName = $env:COMPUTERNAME
    Domain = $env:USERDOMAIN
    HomeDir = $env:USERPROFILE
    IsAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}

$user | Format-List

Example 2: Configuration Hashtable

$config = @{
    Target = "192.168.1.100"
    Ports = @(80, 443, 445, 3389)
    Timeout = 5
    Threads = 10
    Output = "results.txt"
}

# Use config
Write-Host "Scanning $($config.Target)..."
foreach ($port in $config.Ports) {
    # Scan port
}

Example 3: Process Collection

$processes = Get-Process | Where-Object { $_.CPU -gt 10 } | ForEach-Object {
    [PSCustomObject]@{
        Name = $_.Name
        PID = $_.Id
        CPU = [math]::Round($_.CPU, 2)
        Memory = [math]::Round($_.WorkingSet / 1MB, 2)
    }
}

$processes | Export-Csv high_cpu.csv -NoTypeInformation

Practical Pentesting Scenarios

Scenario 1: Target List Management

$targets = @(
    [PSCustomObject]@{IP="192.168.1.1"; Name="Router"; Status="Unknown"}
    [PSCustomObject]@{IP="192.168.1.100"; Name="Server1"; Status="Unknown"}
    [PSCustomObject]@{IP="192.168.1.101"; Name="Server2"; Status="Unknown"}
)

foreach ($target in $targets) {
    if (Test-Connection -ComputerName $target.IP -Count 1 -Quiet) {
        $target.Status = "Online"
    } else {
        $target.Status = "Offline"
    }
}

$targets | Format-Table

Scenario 2: Scan Results Storage

$scanResults = @{}

$ports = @(21, 22, 80, 443, 445, 3389)
foreach ($port in $ports) {
    $result = Test-NetConnection -ComputerName $target -Port $port -WarningAction SilentlyContinue
    $scanResults[$port] = $result.TcpTestSucceeded
}

# Display results
$scanResults.GetEnumerator() | ForEach-Object {
    $status = if ($_.Value) { "OPEN" } else { "CLOSED" }
    Write-Host "Port $($_.Key): $status"
}

Scenario 3: Credential Storage

$credentials = @(
    @{Username="admin"; Password="password123"; Status="Untested"}
    @{Username="root"; Password="toor"; Status="Untested"}
    @{Username="admin"; Password="admin"; Status="Untested"}
)

foreach ($cred in $credentials) {
    # Test credential (pseudocode)
    $valid = Test-Credential -User $cred.Username -Pass $cred.Password
    $cred.Status = if ($valid) { "Valid" } else { "Invalid" }
}

$credentials | Format-Table

Quick Reference

Variables:
$name = "value"
$var.GetType()
$var -is [type]

Arrays:
$arr = @(1, 2, 3)
$arr = 1..10
$arr[0]
$arr[-1]
$arr.Count
$arr += 4

Hashtables:
$hash = @{Key="Value"}
$hash["Key"]
$hash.Key
$hash.Keys
$hash.Values
$hash.ContainsKey("Key")

Custom Objects:
$obj = [PSCustomObject]@{Name="Alice"; Age=25}
$obj.Name
$obj | Add-Member -Name "City" -Value "NYC" -MemberType NoteProperty

Scope:
$global:var
$script:var
$local:var
$private:var

Environment:
$env:USERNAME
$env:PATH
[Environment]::GetEnvironmentVariable("VAR")

Resources

Help

Get-Help about_Variables
Get-Help about_Arrays
Get-Help about_Hash_Tables
Get-Help about_Scopes

Next Steps

  1. Practice with arrays - master array operations
  2. Use hashtables for configuration
  3. Create custom objects for structured data
  4. Understand scope - avoid variable conflicts
  5. Master environment variables for system info

Variables are the foundation of every script. Master these concepts and you'll write clean, powerful PowerShell code!


"$variables: Because remembering everything is hard."