powershell v7 - Function pipeline issue - powershell

I'm trying to write 2 functions:
the first one (Get-Lab) retrieves a [Lab] object
the second one (remove-Lab) remove a [Lab] object
[Lab] is a class defined in my module.
When a run Get-Lab I correctly retrieve my lab instance with the correct type :
When I run Remove-Lab -Lab (Get-Lab -Name Mylab), the operation is correctly performed:
But when I try to pass the [Lab] object through the pipeline it fails.
The function does not receive the object through the pipeline. However I've set the -Lab Parameter as mandatory with ValueFromPipeline=$true.
Function Remove-Lab{
[CmdletBinding(DefaultParameterSetName='Lab')]
param (
[Parameter(ValueFromPipeline=$true,ParameterSetName='Lab',Position=0,Mandatory=$true)]
[Lab]
$Lab,
# Parameter help description
[Parameter(Position=1,Mandatory=$false)]
[switch]
$Force=$false
)
begin {
Write-host ("`tLabName : {0}" -f $Lab.Name) -ForegroundColor Yellow
if ($null -ne $Lab) {
$LabToRemove = $Lab
}
if (-not [string]::IsNullOrEmpty($LabId)) {
$LabToRemove = Get-Lab -Id $LabId
}
if (-not [string]::IsNullOrEmpty($Name)) {
$LabToRemove = Get-Lab -Name $Name
}
if ($null -eq $LabToRemove) {
throw "There is no Lab with specified characteristics. Please check your input"
}
}
process {
$DoRemoval = $true
if ($Force.IsPresent -eq $false) {
while ($null -eq $UserInput -or $UserInput -notin #('Y','N')) {
$UserInput = Read-HostDefault -Prompt "Are you sure want to remove the selected Lab and all its components ? [Y]es, [N]o" -Default 'N'
if ($UserInput -eq 'N') {
$DoRemoval = $false
}
}
Write-Host ("`tUser Input : {0}" -f $UserInput) -ForegroundColor Green
}
if ($DoRemoval -eq $true) {
Write-Host ("`tAbout to Remove the following Lab : {0}" -f $LabToRemove.Name) -ForegroundColor Green
}
}
end {
}
}
As you can see below when a debug this function, the $Lab Parameter is null.
Do you have any idea about this issue ?

Since the function is testing on $LabId or $Name, these variables need to exist in the function and at the moment they do not.
Try changing the parameters to:
[CmdletBinding(DefaultParameterSetName='LabId')]
param (
[Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName = $true, ParameterSetName='LabId',Position=0,Mandatory=$true)]
[string]$LabId,
[Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName = $true, ParameterSetName='LabName',Position=0,Mandatory=$true)]
[string]$Name,
# Parameter help description
[switch]$Force # no need to set a switch to $false because if you don't send that param, the undelying value will be $false by default
)
Then remove
Write-host ("`tLabName : {0}" -f $Lab.Name) -ForegroundColor Yellow
if ($null -ne $Lab) {
$LabToRemove = $Lab
}
Important part here is the ValueFromPipelineByPropertyName = $true declaration

begin runs before anything else, including pipeline parameter binding - so you need to move code that inspects a pipeline-bound parameter (like $Lab) to the process block:
Function Remove-Lab{
[CmdletBinding(DefaultParameterSetName='Lab')]
param (
[Parameter(ValueFromPipeline=$true,ParameterSetName='Lab',Position=0,Mandatory=$true)]
[Lab]
$Lab,
# Parameter help description
[Parameter(Position=1,Mandatory=$false)]
[switch]
$Force=$false
)
process {
Write-host ("`tLabName : {0}" -f $Lab.Name) -ForegroundColor Yellow
if ($null -ne $Lab) {
$LabToRemove = $Lab
}
if (-not [string]::IsNullOrEmpty($LabId)) {
$LabToRemove = Get-Lab -Id $LabId
}
if (-not [string]::IsNullOrEmpty($Name)) {
$LabToRemove = Get-Lab -Name $Name
}
if ($null -eq $LabToRemove) {
throw "There is no Lab with specified characteristics. Please check your input"
}
$DoRemoval = $true
if ($Force.IsPresent -eq $false) {
while ($null -eq $UserInput -or $UserInput -notin #('Y','N')) {
$UserInput = Read-HostDefault -Prompt "Are you sure want to remove the selected Lab and all its components ? [Y]es, [N]o" -Default 'N'
if ($UserInput -eq 'N') {
$DoRemoval = $false
}
}
Write-Host ("`tUser Input : {0}" -f $UserInput) -ForegroundColor Green
}
if ($DoRemoval -eq $true) {
Write-Host ("`tAbout to Remove the following Lab : {0}" -f $LabToRemove.Name) -ForegroundColor Green
}
}

Related

PowerShell: Working with the error from Standard Out (using Nessus Essentials)

Trying to use PowerShell to capture the running status of the "Nessus Essentials" software product. Simply trying to capture product status: running, not running, or other. Getting the below error each time. I've tried changing -like to -match and changing string [warn] [scanner] Not linked to a manager to various other shorter versions, with wildcards and without, to no avail. I still get several lines of an ugly error message when all I want is one line with the string Not linked to a manager returned to console with nothing beneath that.
Pertinent snippet working incorrectly:
} elseif(($agentStatus.stdOut -like "[warn] [scanner] Not linked to a manager")) {
Throw "Not linked to a manager"
The Error:
The Code:
Function Start-ProcessGetStreams {
[CmdLetBinding()]
Param(
[System.IO.FileInfo]$FilePath,
[string[]]$ArgumentList
)
$pInfo = New-Object System.Diagnostics.ProcessStartInfo
$pInfo.FileName = $FilePath
$pInfo.Arguments = $ArgumentList
$pInfo.RedirectStandardError = $true
$pInfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pInfo.CreateNoWindow = $true
$pInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
$proc = New-Object System.Diagnostics.Process
$proc.StartInfo = $pInfo
Write-Verbose "Starting $FilePath"
$proc.Start() | Out-Null
Write-Verbose "Waiting for $($FilePath.BaseName) to complete"
$proc.WaitForExit()
$stdOut = $proc.StandardOutput.ReadToEnd()
$stdErr = $proc.StandardError.ReadToEnd()
$exitCode = $proc.ExitCode
Write-Verbose "Standard Output: $stdOut"
Write-Verbose "Standard Error: $stdErr"
Write-Verbose "Exit Code: $exitCode"
[PSCustomObject]#{
"StdOut" = $stdOut
"Stderr" = $stdErr
"ExitCode" = $exitCode
}
}
Function Get-NessusStatsFromStdOut {
Param(
[string]$stdOut
)
$stats = New-Object System.Collections.Hashtable
$StdOut -split "`r`n" | % {
if($_ -like "*:*") {
$result = $_ -split ":"
$stats.add(($result[0].Trim() -replace "[^A-Za-z0-9]","_").ToLower(),$result[1].Trim())
}
}
Return $stats
}
Function Get-DateFromEpochSeconds {
Param(
[int]$seconds
)
$utcTime = (Get-Date 01.01.1970)+([System.TimeSpan]::fromseconds($seconds))
Return Get-Date $utcTime.ToLocalTime() -Format "yyyy-MM-dd HH:mm:ss"
}
Try {
$nessusExe = Join-Path $env:ProgramFiles -ChildPath "Tenable\Nessus\nessuscli.exe" -ErrorAction Stop
} Catch {
Throw "Cannot find NessusCli.exe"
}
Write-Host "Getting Agent Status..."
$agentStatus = Start-ProcessGetStreams -FilePath $nessusExe -ArgumentList "managed status"
If($agentStatus.stdOut -eq "" -and $agentStatus.StdErr -eq "") {
Throw "No Data Returned from NessusCli"
} elseif($agentStatus.StdOut -eq "" -and $agentStatus.StdErr -ne "") {
Throw "StdErr: $($agentStatus.StdErr)"
} elseif(($agentStatus.stdOut -like "[warn] [scanner] Not linked to a manager")) {
Throw "Not linked to a manager"
} elseif(-not($agentStatus.stdOut -like "*Running: *")) {
Throw "StdOut: $($agentStatus.StdOut)"
} else {
$stats = Get-NessusStatsFromStdOut -stdOut $agentStatus.StdOut
If($stats.last_connection_attempt -as [int]) { $stats.last_connection_attempt = Get-DateFromEpochSeconds $stats.last_connection_attempt }
If($stats.last_connect -as [int]) { $stats.last_connect = Get-DateFromEpochSeconds $stats.last_connect }
If($stats.last_scanned -as [int]) { $stats.last_connect = Get-DateFromEpochSeconds $stats.last_scanned }
}
$stats | Out-Host
Note: Code above is courtesy of here, I've only made a change to the path of Nessus, and I am adding the attempt to capture that it's not connected to a manager.
Modify your code so that it separates standard output from error and so that it handles each line separately.
The following is how to capture standard output (excluding else statements and error handling) of a program (according to your $Proc variable)
if ($proc.Start())
{
while (!$proc.StandardOutput.EndOfStream)
{
$StreamLine = $proc.StandardOutput.ReadLine()
if (![string]::IsNullOrEmpty($StreamLine))
{
# TODO: Duplicate code in this scope as needed or rewrite to use multiline regex
$WantedLine = [regex]::Match($StreamLine, "(?<wanted>.*Not linked to a manager.*)")
$Capture = $WantedLine.Groups["wanted"]
if ($Capture.Success)
{
Write-Output $Capture.Value
}
}
}
}
After that deal with error output separately:
$StandardError = $Proc.StandardError.ReadToEnd()
if (![string]::IsNullOrEmpty($StandardError))
{
# Or use Write-Error
Write-Output $StandardError
}

Read-host not passing variable?

So I writing some code to run some patching on AWS, I have the following script snippet taken out of the whole thing for now.. I seem to be running into an issue with $PSBoundParameters..
(It's Friday & I've had a long week so I may just need to sleep on it) - but I can't seem to pass anything out of read-host...
param (
[Parameter(Mandatory = $true)][ValidateNotNullorEmpty()][string]$Prof,
[Parameter(Mandatory = $false)][ValidateNotNullorEmpty()][string]$Reminder,
[Parameter(Mandatory = $false)][ValidateNotNullorEmpty()][string]$AddToTGs,
[Parameter(Mandatory = $false)][ValidateNotNullorEmpty()][string]$PatchType,
[Parameter(Mandatory = $false)][ValidateNotNullorEmpty()][string]$Instance,
[Parameter(Mandatory = $false)][ValidateNotNullorEmpty()][string]$Environment
)
function PromptInstance {
$Instance = Read-Host -Prompt "Please Specify the Instance"
Write-Host "Using: $Instance" -ForegroundColor Cyan
}
function PromptEnvtoPatch {
$Environment = Read-Host -Prompt "Please Specify the Environment (e.g. dev)"
Write-Host "Using: $Environment" -ForegroundColor Cyan
}
function PromptReminder {
$title = "Calendar"
$message = "Do you want to Add an Outlook Reminder in 24 Hrs?"
$pA = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Adds an Outlook Reminder"
$pB = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Won''t add a reminder"
$options = [System.Management.Automation.Host.ChoiceDescription[]]($pA, $pB)
$CalResult = $host.ui.PromptForChoice($title, $message, $options, 0)
switch ($CalResult)
{
0 {
$global:CalRes = 1
Write-Host "Reminder will be added.." -ForegroundColor Cyan
}
1 {
$global:CalRes = 0
Write-Host "No Reminder will be added.." -ForegroundColor Cyan
}
}
}
function PromptAddToTGs {
$title = "Re-Add to Target Groups"
$message = "Do you want to have this script Automatically add the instances back into the Target Groups after Patching?"
$pA = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Will ADD the instance back into Target Groups"
$pB = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Will NOT add the instance back into Target Groups"
$options = [System.Management.Automation.Host.ChoiceDescription[]]($pA, $pB)
$result = $host.ui.PromptForChoice($title, $message, $options, 1)
switch ($result)
{
0 {
$global:AddTGRes = 1
Write-Host "Instances WILL be added back into Target Groups" -ForegroundColor Cyan
}
1 {
$global:AddTGRes = 0
Write-Host "Instances will NOT be added back into Target Groups" -ForegroundColor Cyan
}
}
}
function PromptPatchType {
$title = "Patching Type"
$message = "Do you want to Patch a Single Instance, or ALL Instances for a specific Team Env?"
$pA = New-Object System.Management.Automation.Host.ChoiceDescription "&Instance", "Patches an Instance"
$pB = New-Object System.Management.Automation.Host.ChoiceDescription "&ALL Instances for an Env", "Patches ALL Instances in a Team Env"
$pQ = New-Object System.Management.Automation.Host.ChoiceDescription "&Quit", "Cancel/Exit"
$options = [System.Management.Automation.Host.ChoiceDescription[]]($pA, $pB, $pQ)
$PatchResult = $host.ui.PromptForChoice($title, $message, $options, 0)
switch ($PatchResult)
{
0 {
$Instance = Read-Host "Please Specify the Instance Id"
}
1 {
$Environment = Read-Host "Please Specify the Team (i.e. dev)"
}
2 {
Write-Host "You Quitter!... :-)"
Exit
}
}
}
function KickOffPatchingEnv {
param ($Prof, $Reg, $Reminder, $AddToTGs, $PatchType, $Environment)
Write-Host "Using the Following Options: (Profile:$Prof) (Region:$Reg) (Reminder:$Reminder) (AddToTGs:$AddToTGs) (PatchType:$PatchType) (Environment:$Environment)"
}
function KickOffPatchingInst {
param ($Prof, $Reg, $Reminder, $AddToTGs, $PatchType, $Instance)
Write-Host "Using the Following Options: (Profile:$Prof) (Region:$Reg) (Reminder:$Reminder) (AddToTGs:$AddToTGs) (PatchType:$PatchType) (Instance:$Instance)"
}
switch -wildcard ($Prof) {
"*dev*" { $Reg = "eu-west-1"; $Bucket = "s3-dev-bucket" }
"*admin*" { $Reg = "eu-west-1"; $Bucket = "s3-admin-bucket" }
"*prod*" { $Reg = "eu-west-1"; $Bucket = "s3-prod-bucket" }
"*staging*" { $Reg = "eu-west-1"; $Bucket = "s3-staging-bucket" }
}
if (!$PSBoundParameters['Reminder']) { PromptReminder }
if (!$PSBoundParameters['AddToTGs']) { PromptAddToTGs }
if ($PSBoundParameters['PatchType']) {
if (($PatchType -eq "i") -or ($PatchType -eq "instance") -or ($PatchType -eq "I") -or ($PatchType -eq "Instance")) {
if (!$PSBoundParameters['Instance']) { PromptInstance }
KickOffPatchingInst $Prof $Reg $Reminder $AddToTGs $PatchType $Instance
}
if (($PatchType -eq "a") -or ($PatchType -eq "all") -or ($PatchType -eq "A") -or ($PatchType -eq "All")) {
if (!$PSBoundParameters['Environment']) { PromptEnvtoPatch }
KickOffPatchingEnv $Prof $Reg $Reminder $AddToTGs $PatchType $Environment
}
} else { PromptPatchType }
If I use the parameters on the command line, it works fine..
PS C:\Users\myself\Desktop> .\test.ps1 -Prof dev -Reminder y -AddToTGs y -PatchType a -Environment dev
Using the Following Options: (Profile:dev) (Region:eu-west-1) (Reminder:y) (AddToTGs:y) (PatchType:a) (Environment:dev)
But if I omit an option, say for instance the Environment, I'm prompted for it, but the value is not displayed..
PS C:\Users\myself\Desktop> .\test.ps1 -Prof dev -Reminder y -AddToTGs y -PatchType a
Please Specify the Environment (e.g. dev): dev
Using: dev
Using the Following Options: (Profile:dev) (Region:eu-west-1) (Reminder:y) (AddToTGs:y) (PatchType:a) (Environment:)
Environment is empty....
I've tried loads of different things such as setting global:Environment etc, but I always seem to be missing out whichever variable isn't specified in the command?
Maybe this isn't the best way to write this, but i've never used $PSBoundParameters before so this is my first time trying it out..
Can anyone see my glaring error here at all?
TIA :)
#Mathias R. Jessen answered this for me.
I put this in the function
function PromptProfile {
$Prof = Read-Host -Prompt "Please Specify the Profile"
$global:UserProf = $Prof
}
then changed code later on with the global variable, so I can use them

Function not outputting value?

I'm working on a PowerShell script for some work use, and am having trouble getting a function it's data directly to a variable. Below is the function and my test:
function validateInput {
Param(
[string]$Text = 'Put text here'
)
do {
try {
$numOk = $true
$GetMyANumber = Read-Host "$text"
if ($GetMyANumber -gt 3){
$numOK = $false
}
} catch {
$numOK = $false
}
if ($numOK -ne $true) {
cls
Write-Host "Please enter a valid number" -Foreground Red
}
} until (($GetMyANumber -ge 0 -and $GetMyANumber -le 3) -and $numOK)
}
$firstName = validateInput($firstNamePrompt)
$lastName = validateInput ($lastNamePrompt)
Write-Host "First name length is $firstname"
Write-Host "Last name length is $lastname"
My understanding is that the in the last few lines, the function SHOULD assign it's output to the variables $firstName and $lastName, but I output that, they are blank. I'm sure I'm missing something obvious here, but can anyone let me know what I'm screwing up?

Powershell v3-5: Out-Grid View Truncated Data

I'm working on a PS script (v5 on the machine I'm using) that uses Invoke-WebRequest to grab information from a web address and returns the results.
When attempting to pipe my output to Out-GridView with more than 9 results, the column containing data lists "..." on the 10th line.
I've tried doing several types of joins, and am just wondering if I need to have my result in a specific type to avoid having this effect (hashtable maybe?)
Checking MS forums has only yielded results about joining on line-end's, which doesn't seem to help in this case.
The pages I'm querying are simple HTML showing the output of .txt files, however the .Content property of my Invoke-WebRequest query seems to be one long string.
Here's my code thus far:
[cmdletBinding(
DefaultParameterSetName='FileWithURIs'
)]
Param(
[Parameter(ParameterSetName='FileWithURIs',
Mandatory=$true
)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateScript({
if(-Not ($_ | Test-Path) ){
throw "File or folder does not exist"
}
if(-Not ($_ | Test-Path -PathType Leaf) ){
throw "The Path argument must be a file. Folder paths are not allowed."
}
if($_ -notmatch "(\.txt)"){
throw "The file specified in the path argument must be of type txt"
}
return $true
})]
[String]$FileWithURIs,
[Parameter(ParameterSetName='SingleURI',
Mandatory=$True)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateScript({
if($_.StartsWith("http://") -eq $false -and $_.StartsWith("https://" -eq $false))
{
throw "User specified URI must start with http:// or https://"
}
else
{
return $true
}
})]
[String]$URI,
[Switch]$ViewAsGrid
)
BEGIN
{
Function Check-CustomType()
{
if("TrustAllCertsPolicy" -as [type])
{
Out-Null
}
else
{
Set-CustomType
}
}
Function Set-CustomType()
{
add-type #"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"#
$script:newCertPolicy = [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
}
Function Evaluate-URIs()
{
if($URI)
{
Get-Blocklist -ListURI $URI
}
elseif($FileWithURIs)
{
$lines = Get-Content $FileWithURIs
foreach($line in $lines)
{
Get-Blocklist -ListURI $line
}
}
}
Function Create-Table()
{
$script:tabName = "ResultTable"
# Create Table object
$script:table = New-Object System.Data.DataTable “$script:tabName”
# Create first column
$script:col1 = New-Object System.Data.DataColumn SiteName,([string])
$script:table.columns.add($script:col1)
}
Function Add-RowToTable($Value)
{
# Create new row
$newRow = $script:table.NewRow()
# Add value to row
$newRow.SiteName = $Value
# Add row to table
$script:table.Rows.Add($newRow)
}
Function Get-Blocklist($ListURI)
{
try
{
$query = Invoke-WebRequest -Uri "$ListURI"
if($ViewAsGrid)
{
Create-Table
$content = #($query.Content.Split("`r`n"))
foreach($entry in $content)
{
Add-RowToTable -Value $entry
}
$script:table | Out-GridView -Title "Blocklist for $ListURI"
}
else
{
Write-Host "`nBlocklist for $ListURI " -ForegroundColor Yellow -NoNewline
Write-Host "`n`n$($query.Content | Sort -Descending)"
}
}
catch [Exception]
{
Write-Host "`nUnable to connect to resource " -ForegroundColor Yellow -NoNewline
Write-Host "$ListURI" -ForegroundColor Red
Write-Host "`nERROR: $($Error[0].Exception)"
}
}
Function Run-Stack()
{
Check-CustomType
Evaluate-URIs
}
}
PROCESS
{
Run-Stack
}
END { Write-Host "`nEnd of script" }
The idea is to only use Out-GridView if a single, user-entered address is the input. Otherwise it just becomes console output.
Any help would be appreciated!

Is there a way to pre-fill out Read-Host in Powershell?

I have a script that helps a user find if a file hash exists in a folder. After the user has entered the hash I determine what type of hash it is and if it is not supported or if the user missed a letter it will return to asking for a hash. For ease of use I want to be able to pre-fill out what the user had type in the previously so they do not need to start over.
while (1)
{
$hashToFind = Read-Host -Prompt "Enter hash to find or type 'file' for multiple hashes"
# Check if user wants to use text file
if ($hashToFind -eq "file" )
{
Write-Host "Be aware program will only support one has type at a time. Type is determined by the first hash in the file." -ForegroundColor Yellow
Start-Sleep -Seconds 3
$hashPath = New-Object system.windows.forms.openfiledialog
$hashPath.InitialDirectory = “c:\”
$hashPath.MultiSelect = $false
if($hashPath.showdialog() -ne "OK")
{
echo "No file was selected. Exiting program."
Return
}
$hashToFind = Get-Content $hashPath.filename
}
# Changes string to array
if ( $hashToFind.GetTypeCode() -eq "String")
{
$hashToFind+= " a"
$hashToFind = $hashToFind.Split(" ")
}
if ($hashToFind[0].Length -eq 40){$hashType = "SHA1"; break}
elseif ($hashToFind[0].Length -eq 64){$hashType = "SHA256"; break}
elseif ($hashToFind[0].Length -eq 96){$hashType = "SHA384"; break}
elseif ($hashToFind[0].Length -eq 128){$hashType = "SHA512"; break}
elseif ($hashToFind[0].Length -eq 32){$hashType = "MD5"; break}
else {echo "Hash length is not of supported hash type."}
}
I am newer to PowerShell so if there are any other comments they are welcome!
From Super User:
[System.Windows.Forms.SendKeys]::SendWait("yes")
Read-Host "Your answer"
I have came up with solution like this:
while (1)
{
$hashToFind = Read-Host -Prompt "Enter hash to find or type 'file' for multiple hashes. Enter 'R' for reply input"
if ($hashToFind -eq 'R' -and $PreviousInput)
{
$handle = (Get-Process -Id $PID).MainWindowHandle
$code = {
param($handle,$PreviousInput)
Add-Type #"
using System;
using System.Runtime.InteropServices;
public class Tricks {
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
}
"#
[void][Tricks]::SetForegroundWindow($handle)
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait($PreviousInput)
}
$ps = [PowerShell]::Create()
[void]$ps.AddScript($code).AddArgument($handle).AddArgument($PreviousInput)
[void]$ps.BeginInvoke()
}
$PreviousInput = $hashToFind
# Check if user wants to use text file
if ($hashToFind -eq "file" )
{
$PreviousInput = $null
Write-Host "Be aware program will only support one has type at a time. Type is determined by the first hash in the file." -ForegroundColor Yellow
Start-Sleep -Seconds 3
$hashPath = New-Object system.windows.forms.openfiledialog
$hashPath.InitialDirectory = “c:\”
$hashPath.MultiSelect = $false
if($hashPath.showdialog() -ne "OK")
{
echo "No file was selected. Exiting program."
Return
}
$hashToFind = Get-Content $hashPath.filename
}
# Changes string to array
if ( $hashToFind.GetTypeCode() -eq "String")
{
$hashToFind+= " a"
$hashToFind = $hashToFind.Split(" ")
}
if ($hashToFind[0].Length -eq 40){$hashType = "SHA1"; break}
elseif ($hashToFind[0].Length -eq 64){$hashType = "SHA256"; break}
elseif ($hashToFind[0].Length -eq 96){$hashType = "SHA384"; break}
elseif ($hashToFind[0].Length -eq 128){$hashType = "SHA512"; break}
elseif ($hashToFind[0].Length -eq 32){$hashType = "MD5"; break}
else {echo "Hash length is not of supported hash type."}
}