IP Address Comparison

Working with IP address in the dotted format can become quite challenging. You can use regex in some cases to identify IP addresses but this is limited.

Simple:


^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$

Accurate:


^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$

You can do more, such as deciding is an IP address falls within a given network, by converting the IP addresses into Decimal. This allows you an easy way to compare.


#Define the Networks you wish to check
$Networks = @(
   '192.168.1.0/8'
   '172.16.1.0/16'
)

#Create Objects for each network that define the IP range as a decimal
$PossibleSubnets = $Networks | ForEach-Object {
   $NetworkAddress = [IPAddress]($_ -split '/')[0]
   $MaskLength = [Byte]($_ -split '/')[1]

   $NetworkAddressBytes = $NetworkAddress.GetAddressBytes()
   [Array]::Reverse($NetworkAddressBytes)
   $RangeLower = [BitConverter]::ToUInt32($NetworkAddressBytes, 0)

   $DecimalMask = [Convert]::ToUInt32(("1" * $MaskLength).PadRight(32, "0"), 2)
   $RangeUpper = $RangeLower -bor -bnot $DecimalMask

   [PSCustomObject]@{
      Network = $_
      NetworkAddress = $NetworkAddress
      MaskLength = $MaskLength
      RangeLower = $RangeLower
      RangeUpper = $RangeUpper
   }
}

 

Given this information you can find which interfaces on your machine are part of which networks.


'localhost' | ForEach-Object {
   Write-Host "Checking $_" -ForegroundColor Yellow
   $ComputerName = $_
   $Interfaces = [Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces()
   $Interfaces | ForEach-Object {
      $Interface = $_
      $IpAddresses = $_.GetIPProperties().UnicastAddresses | Where-Object { $_.Address.ToString() -notin '127.0.0.1', '::1' } | Select-Object -ExpandProperty Address
      $IPAddresses | ForEach-Object {
         #Here we convert the IP addresses found to Decimal for comparison
         $IPAddressBytes = $_.GetAddressBytes()
         [Array]::Reverse($IPAddressBytes)
         $DecimalIPAddress = [BitConverter]::ToUInt32($IPAddressBytes, 0)

         $MySubnets = $PossibleSubnets| Where-Object {$DecimalIPAddress -ge $_.RangeLower -and $DecimalIPAddress -le $_.RangeUpper}

         [PSCustomObject]@{
            ComputerName = $ComputerName
            Interface = $Interface.Name
            Network = $MySubnets.Network
         }
      }
   }
}

 

Get-WinEvent

Get-WinEvent should be seen as a more powerful version of the Powershell 1.0 commandlet Get-EventLog. Using this you can query both the classic windows event log and the Event Tracing for Windows (ETW) logs.

To find a list of logs you can query …


Get-WinEvent -ListLog *audio*


LogMode   MaximumSizeInBytes RecordCount LogName
-------   ------------------ ----------- -------
Circular             1052672           0 Microsoft-Windows-Audio/CaptureMonitor
Circular             1052672             Microsoft-Windows-Audio/GlitchDetection
Circular             1052672             Microsoft-Windows-Audio/Informational
Circular             1052672           0 Microsoft-Windows-Audio/Operational
Circular             1052672        2537 Microsoft-Windows-Audio/PlaybackManager

 

…Or a list of providers (note here you can see which provider links to which logs)


Get-WinEvent -ListProvider *audio*

Name     : Microsoft-Windows-XAudio2
LogLinks : {Microsoft-Windows-XAudio2/Debug, Microsoft-Windows-XAudio2/Performance}
Opcodes  : {win:Start, win:Stop, Create, PerformanceWarning}
Tasks    : {Initialize, Shutdown, SourceVoice, SubmixVoice...}

Name     : Microsoft-Windows-Audio
LogLinks : {Application, Microsoft-Windows-Audio/Operational, Microsoft-Windows-Audio/CaptureMonitor, Microsoft-Windows-Audio/Performance...}
Opcodes  : {win:Info, win:Start, win: Stop...}
Tasks    : {AudioPerf_Task_LaunchAudioDG, ...}

 

To get the entries from a particular log (notice here the provider name is listed)


Get-WinEvent -LogName Microsoft-Windows-Audio/PlaybackManager | Select -First 10

   ProviderName: Microsoft-Windows-Audio

TimeCreated                     Id LevelDisplayName Message
-----------                     -- ---------------- -------
04/11/2015 19:23:53             20 Information      Format: 1...
04/11/2015 19:19:03             21 Information      Sound level for application [Microsoft.MicrosoftEdge_8wekyb3d8bbwe!MicrosoftEdge] changed to [2]
04/11/2015 19:19:03             21 Information      Sound level for application [Microsoft.MicrosoftEdge_8wekyb3d8bbwe!MicrosoftEdge] changed to [2]
04/11/2015 19:13:21             21 Information      Sound level for application [Microsoft.MicrosoftEdge_8wekyb3d8bbwe!MicrosoftEdge] changed to [0]

 

To filter further you need to provider a hastable with your filter choices


Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Audio/PlaybackManager';StartTime='04/11/2015 13:00'} | Select -First 10

 

To see the what valid key-pairs you can use help


Get-WinEvent -Parameter filterhashtable

 

As mentioned you can also user this command to see classic event logs however you will see logs split into groups based on the provided. To see classic logs:


Get-WinEvent -ListLog * | Where {$_.IsClassicLog -eq 'True'}


LogMode   MaximumSizeInBytes RecordCount LogName
-------   ------------------ ----------- -------
Circular            20971520        4552 Application
Circular            20971520           0 HardwareEvents
Circular             1052672           0 Internet Explorer
Circular            20971520           0 Key Management Service
Circular             1052672           8 OAlerts
Circular            20971520       11339 Security
Circular            20971520        8318 System
Circular            15728640         862 Windows PowerShell

 

Modulus Operator

You can use the Modulus Operator ‘%’ in a few ways that can be very helpful depending on what you are trying to achive. Modulus can be thought of as a remainder when one number divides another. For instance 5 Mod 3 is 2 and 5 Mod 6 is 5.

 #5 Divided by 3 leaves a remainder of 2
5 % 3
2

#5 Divided by 6 leaves a remainder of 5
5 % 6
5

Find Multiples of x
To find multiples of a given number you can simply find all numbers in an array that have a remainder of 0 when divided by x

 $arr = {Array](1..100)
#Find all multiples of 3
$arr | ?{$_ % 3 -eq 0}

Find every xth item
When not working with numbers you can also use % to find every 2nd, 3rd, … xth item in an array by using $i as a counter. Bare in mind 0 is the first element of an array which can become confusing in this example

 #Get the first value and every xth value thereafter
$arr = 'one','two','three','four','five','six','seven','eight','nine','ten'
$i = 0
$x = 2
While ($i -lt $arr.Length){
    if(($i % $x) -eq 0){
        $arr[$i]
    }
    $i++
}

one
three
five
seven
nine

Base64 Encode/Decode

Using Get-Content you can convert a file such as .pdf or .jpeg into a stream of bytes. Here the ‘-raw’ switch tell Get-Content to read the file as one long string, keeping the line endings exactly as they were. Without this switch the file would be read line by line.

 $doc = Get-Content -Path C:\tmp\file.pdf -Raw -Encoding Byte 

Now you have the file content as Bytes saved as a variable (from the console you will see a decimal representation of a byte on each line). You can convert these bytes to binary or you can convert this entire variable into a Base64 string that can be sent in a file or copy-pasted.

Base64 makes a better transport option than binary (https://en.wikipedia.org/wiki/Base64)

#Just for fun really
$DocAsBin = $a | %{[System.Convert]::ToString($_,2)}

#Converts to a single transportable string
$DocAsStr = [Convert]::ToBase64String($doc)

To reassemble the file you will need to convert back from Base64 string and use the Set-Content cmdlet.

 
[System.Convert]::FromBase64String($DocAsStr) | Set-Content -Path C:\tmp\file.pdf​ 

You can also encode and run a script block in much the same way


$string = {(Get-WindowsFeature).Where{$PSItem.Installed}}.ToString()

$encodedcommand = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($string))

powershell.exe -EncodedCommand $encodedcommand

Playing with Secure Strings

There are three ways you can store a password in Powershell.

  • String: stored as plain text
  • System.Security.SecureString: encrypted in memory, can be reversed only by the principle that encrypted it.
  • System.Management.Automation.PSCredential: a Powershell class composed of username and password
#To prompt for a Secure Password
$SecurePassword = Read-Host -Prompt "Enter password" -AsSecureString

#To convert a string to a Secure Password:
"Pa$$w0rd" | ConvertTo-SecureString -AsPlainText -Force

#To create a PSCredential for use in some cmdlet
$UserName = "Domain\User"
$Credentials = New-Object System.Management.Automation.PSCredential `
-ArgumentList $UserName, $SecurePassword

#Same again but using the New() method.
$PSCred = [System.management.automation.PSCredential]::new('username',$SecurePassword)

 

You can also retrieve the encrypted password as a string again so that it can be used for unattended use. If you save an encrypted password as a string you will need to convert it back to a secure string for use in the unattended script. Its worth noting here that only the user that encrypted the cleartext first can convert the ciphertext back into a secure string correctly.

"password" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString
$password = "01000000d08c9ddf0115d1118c7a00c04fc297eb010b9fe0ca1...."
$SecureString = $pass | Convertto-SecureString

 

You can retrieve the cleartext password from a secure string in one of two ways

#Create a PSCredential and call the GetNetworkCredentials method
$secstr = 'pa$$w0rd' | ConvertTo-SecureString -AsPlainText -Force
$PSCred = [System.management.automation.PSCredential]::new('scottsan',$secstr)
$PSCred.GetNetworkCredential() | fl *

#Convert the SecureString into a BinaryString and then back to cleartext
$BStr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secstr)
[System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BStr)

Parsing Command Line Output

Here is a useful way to parse text based output into an object for powershell to consume using the Switch statement with the -Regex parameter.

Sample Output:

Deployment Image Servicing and Management tool
Version: 10.0.10240.16384

Image Version: 10.0.10240.16384
Obtaining list of 3rd party drivers from the driver store...

Driver packages listing:

Published Name : oem0.inf
Original File Name : msdokub.inf
Inbox : No
Class Name : MEDIA
Provider Name : Microsoft
Date : 24/10/2014
Version : 1.31.35.7

Published Name : oem1.inf
Original File Name : intcdaud.inf
Inbox : No
Class Name : MEDIA
Provider Name : Intel(R) Corporation
Date : 23/02/2015
Version : 6.16.0.3172

...

Parsing with Powershell
Here we use the -Regex switch to test the different regex cases against each line fed into the switch statement. The first case is important as it defines all of the properties you want to pick out. It will also act as a sort of delimiter, creating a NEW object each time the match is valid. Because of this it also outputs $obj BEFORE creating a new one.

#Get the output to be parsed into a variable
$DismOutput = dism /Online /Get-Drivers | %{$_.Trim()}

$obj = $null

Switch -Regex ($DismOutput){
   '^Published Name : (?<pub>.+)' {
      $obj
      $obj = [PSCustomObject]@{
         PublishedName = $Matches.pub
         ProviderName = $null
         Date = $null
         Version = $null
      }
   }
   '^Provider Name : (?<prov>.+)' {$obj.providername = $Matches.prov}
   '^Date : (?<date>.+)' {$obj.date = $Matches.date}
   'Version : (?<ver>.+)' {$obj.version = $Matches.ver}
}
#Last $obj wont be output by the Switch so we output it here.
$obj

 

 

 


 

MDT Part 1: Installing and Configuring

Overview

Creating OS images saves time when deploying new Server and Workstations by cutting out the need for installing updates and software every single time. The key thing with MDT is that by using Tasklists you can ensure the OS is built in the same way every iteration rather  missing things like drivers and config tweaks.

Installing

  1. Install MDT (MicrosoftDeploymentToolkit2013_x64.msi)
  2. Install Windows ADK

Basic Configuration

Open up the Deployment Workbench mmc snap-in (Can be found in Program Files).

  1. Create a New Deployment Share
    • Deployment Workbench > Deployment > Right Click > New Deployment Share
  2. Add OS Files
    • Right click Operating Systems under your new Deployment Share
  3. Add Device Drivers if required
  4. Create Tasklist
    • Select “Standard Client Task Sequence”
    • Select the OS
    • Set an Admin Password

This in theory is all you need to deploy a vanilla OS. Now you can create a bootable image to carry out the deployment. Right click on the Deployment Share on the left hand pane and click Update Deployment Share. This will create a LightTouchPE .wim and .iso file in the Deployment Share \Boot folder.

If you place one of these .wim files in WDS you can PXE boot your test machine into the MDT WinPE where you will be asked a series of installation options before actually installing the OS.

This is the most basic of setup tasks and lets you delpoy a fresh OS with no customisation. In my next post I will cover some of the custom setup steps I have taken for my deployment.