#v1.0 - full release #v0.0 - Initial build # Written by: Martin Howlett martin.howlett@rackspace.co.uk Function Get-WHAMTicketDevices{ Param( [Parameter()]$ticketnum ) if (-NOT $ticketnum){ $ticketnum = Read-Host "Enter ticket number..." } $COREComputers = Get-CoreDevicesTicket -ticket $ticketnum -RackPW -NoDRAC #Filter non windows and virtual cluster devices $COmputers = Remove-NonWindowDevices -Computers $COREComputers $Output = New-Object PSObject -Property @{ Computers = $COmputers Ticketnum = $ticketnum } Return $Output } Function Get-WHAMAccountDevices{ Param( [Parameter()]$Account ) #if we dont have the Account var, we prompt the user for it if (-NOT $account){ $Account = Read-Host "Enter Account number..." } write-host "Getting devices from CORE..." $COREComputers = Get-CoreDevicesAccount -Account $Account -RackPW -NoDRAC #Remove non window devices $COmputers = Remove-NonWindowDevices -Computers $COREComputers $Output = New-Object PSObject -Property @{ Computers = $COmputers } Return $Output } Function Get-WHAMListDevices{ Param( [Parameter()]$Devices ) #if we dont have the device var, we prompt the user for it if (-NOT $devices){ $devices = Read-Host "Enter devices, 6 digit numbers only seperated by comma, example: 658412,659854,652147" } #if we have passed a string of comma seperated devices, convert to array if ($devices -like "*,*"){$devices = $devices.split(",")} write-host "Getting devices from CORE..." #GO through the device list and get them all from CORE $ComputerList = @() ForEach ($DeviceNumber in $Devices){ $TempDevice = Get-CoreDevice -device $DeviceNumber -RackPW -NoDRAC $ComputerList += $TempDevice } #Filter non windows and virtual cluster devices $CoreComputers = Remove-NonWindowDevices -Computers $ComputerList $Output = New-Object PSObject -Property @{ Computers = $CoreComputers Ticketnum = $ticketnum } Return $Output } Function Remove-NonWindowDevices{ Param( [Parameter(Mandatory=$true)]$Computers ) #Filter non windows and virtual cluster devices $COmputersthatnolongerhavenonwindowsdevicesinthem = $COmputers | Where-Object {($_.is_windows -eq $true) -AND ($_.Plattype -notlike "*cluster*")} return $COmputersthatnolongerhavenonwindowsdevicesinthem } Function Write-SelectedDevices{ Param ( [Parameter(Mandatory=$true)]$COmputers ) write-host "`nYou have selected the following servers: " write-host " " ForEach ($Computer in $Computers){ [string]$Servername = Pad-String -String $($Computer.Servername) -StringTotalLength 16 [string]$Account = Pad-String -String $($Computer.Account) -StringTotalLength 8 write-host "Account: " -NoNewline write-host "$Account" -ForegroundColor Cyan -NoNewline write-host " - " -NoNewline write-host "$Servername " -ForegroundColor Green } } Function Get-WHAMDevicesNI{ param( [Parameter()]$ticketnum, [Parameter()]$devices, [Parameter()]$account ) if ($ticketnum){ $Computers = Get-WHAMTicketDevices -ticketnum $ticketnum } if ($devices){ $Computers = Get-WHAMListDevices -devices $devices } if ($account){ $Computers = Get-WHAMAccountDevices -Account $account } if ($($Computers | Measure-Object).Count -lt 1){ throw ("ERROR: Unable to get devices from CORE. Script quitting") } <# ForEach ($Computer in $Computers) { $ComputerTemp = $Computer $ComputerTemp | Add-Member -type NoteProperty -Name "DomainUser" -Value $DomainCreds.DomainUser -Force $ComputerTemp | Add-Member -type NoteProperty -Name "DomainPass" -Value $DomainCreds.DomainPass -Force $NewComputers += $ComputerTemp } #> $Output = New-Object PSObject -Property @{ Computers = $Computers.computers Ticketnum = $ticketnum.Ticketnum } return $output } Function Display-Header{ $ASCII = @" __ __ _ / / /\ \ \ /\ /\ /_\ /\/\ \ \/ \/ // /_/ ///_\\ / \ \ /\ // __ // _ \/ /\/\ \ \/ \/ \/ /_/ \_/ \_/\/ \/ v$Global:version "@ $Roary = @" ,%%%%%%%, ,%%/\%%%%/\%, ,%%%\c "" J/%%, %%%%/ d b \%%% %%%% _ |%%% %%%%(=_Y_=)%%' %%%%`\7/%%%' `%%%%%%%%' "@ if($Global:Dev){ Write-Host "WARNING: YOU ARE RUNNING IN DEV MODE! PROCCED WITH CAUTION, HERE BE LIONS" -ForegroundColor Cyan Write-Host "WARNING: YOU ARE RUNNING IN DEV MODE! PROCCED WITH CAUTION, HERE BE LIONS" -ForegroundColor Red write-host "$Roary" -ForegroundColor Yellow Write-Host "WARNING: YOU ARE RUNNING IN DEV MODE! PROCCED WITH CAUTION, HERE BE LIONS" -ForegroundColor Red Write-Host "WARNING: YOU ARE RUNNING IN DEV MODE! PROCCED WITH CAUTION, HERE BE LIONS" -ForegroundColor Cyan } write-host "$ASCII" -ForegroundColor Cyan Write-Host "Windows Hammertime Alternative Module" -ForegroundColor Gray Write-Host "" Write-Host "wiki: rax.io/wham" -ForegroundColor Gray Write-Host "" } Function Get-WHAMDevices{ Param( [Parameter()]$ticketnum ) if (!$MenuAnswer){ write-host "Welcome to WHAM, please select what devices to use:" -ForegroundColor Cyan Write-Host "" write-host "1. All windows devices from ticket" write-host "2. All windows devices from an account" write-host "3. Enter list of devices" write-host "4. Quit" Write-Host "" #Get response from the menu above $MenuAnswer = $null $MenuAnswer = Ask-Question -Question "Please enter menu number..." -ValidationArray "1","2","3","4" Write-Verbose "Menuanswer: $MenuAnswer" } Switch ($MenuAnswer){ "1" { #Ticket $Output = Get-WHAMTicketDevices } "2" { #Account $Output = Get-WHAMAccountDevices } "3" { #Device list $Output = Get-WHAMListDevices } "4" {exit} Default {write-host "ERROR: Unknown menu option selected. Script quitting" ; break} } $Computers = $output.Computers $Ticketnum = $output.Ticketnum if($($Computers | Measure-Object).Count -lt 1){ throw ("ERROR: Unable to get devices from CORE. Script quitting") } #Get our customer domain credentials $DomainCreds = Get-DomainCredentials #Get Intensvie credentials $IntensiveCreds = Get-IntensiveCredentials $NewComputers = @() ForEach ($Computer in $Computers){ $ComputerTemp = $Computer $ComputerTemp | Add-Member -type NoteProperty -Name "DomainUser" -Value $DomainCreds.DomainUser -Force $ComputerTemp | Add-Member -type NoteProperty -Name "DomainPass" -Value $DomainCreds.DomainPass -Force $NewComputers += $ComputerTemp } $Output = New-Object PSObject -Property @{ Computers = $COmputers Ticketnum = $ticketnum IntensiveUser = $IntensiveCreds.IntensiveUser IntensivePass = $IntensiveCreds.IntensivePass } Return $Output } Function Get-DomainCredentials{ $DomainUser = Read-Host "Please enter the customer Domain username (Press enter to skip)" if ($DomainUser){ #This will obscure the password entry but save it back as a nonsecurestring $DomainPass = $([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($(Read-Host -Prompt "Please enter the customer Domain password" -AsSecureString)))) }else{ $DomainPass = $null } $Output = New-Object PSObject -Property @{ DomainUser = $DomainUser DomainPass = $DomainPass } return $output } Function Get-IntensiveCredentials{ $IntensiveUser = Read-Host "Please enter your Intensive username (Press enter to skip)" if ($IntensiveUser) { #This will obscure the password entry but save it back as a nonsecurestring $IntensivePass = $([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($(Read-Host -Prompt "Please enter your Intensive username password" -AsSecureString)))) }else{ $IntensivePass = $null } $Output = New-Object PSObject -Property @{ IntensiveUser = $IntensiveUser IntensivePass = $IntensivePass } return $output } Function Write-WHAMConnectionSummary{ Param( [Parameter(Mandatory=$true)]$Computers ) $ConnectionSummary=@() ForEach ($COmputer in $Computers){ $Connection = $null $Script = $null $success = $null $ErrorMessage = $null #check if both the connection and scipt where successful if (($Computer.Connection.ConnectionFailure -ne $true) -AND ($Computer.WHAMScript.ScriptFailure -ne $true)) { $success = $true }else{ $success = $false } #Check if we have a connection failure if ($Computer.Connection.ConnectionFailure){ $Connection = $false $Script = $false $ErrorMessage = $Computer.Connection.ConnectionFailureReason }else{ $Connection = $true } #if the script and connection are successful, our script section was successful if (($Computer.WHAMSCript.ScriptFailure -ne $true) -AND ($Computer.Connection.ConnectionFailure -ne $True)){ $Script = $true }else{ $script = $false #only update the error message if it is blank if ($ErrorMessage.Length -lt 1){ $ErrorMessage = $Computer.WHAMSCript.ScriptFailureReason } } $Summary = New-Object PSObject -Property @{ ServerName = $Computer.ServerName Connection = $COnnection ConnectionFailureReason = $Computer.Connection.ConnectionFailureReason SCript = $Script SCriptFailureReason = $Computer.WHAMSCript.ScriptFailureReason Success = $success ErrorMessage = $ErrorMessage } $ConnectionSummary += $Summary } $ConnectionSummary = $ConnectionSummary | Sort-Object Connection,Script,ServerName write-host "Final execution summary:" -ForegroundColor Yellow ForEach ($server in $ConnectionSummary){ [string]$Servername = Pad-String -String $($server.Servername) -StringTotalLength 16 [string]$Connection = Pad-String -String $($server.Connection) -StringTotalLength 6 [string]$Script = Pad-String -String $($server.Script) -StringTotalLength 6 if ($server.success){ write-host "Success:" -NoNewline -ForegroundColor Green }else{ write-host "Failure:" -NoNewline -ForegroundColor Red } write-host " $Servername Connection:" -NoNewline if ($server.Connection){ write-host "$Connection" -NoNewline -ForegroundColor Green }else{ write-host "$Connection" -NoNewline -ForegroundColor Red } write-host "Script: " -NoNewline if ($server.Script){ write-host "$Script" -ForegroundColor Green }else{ write-host "$Script" -ForegroundColor Red -NoNewline write-host " - $($server.ErrorMessage)" -ForegroundColor Red } } } Function Output-textfile{ Param( [Parameter(mandatory=$true)]$data ) #Create new text file $tempFile = [io.path]::GetTempFileName() New-Item $tempFile -Type File -Force | Out-Null #Save bbcode string to txt file Set-Content -Path $tempFile -Value $data #Open txt file notepad $tempFile } Function Sort-WhamData{ Param( [Parameter(mandatory=$true)]$computers, [Parameter()]$SortOrder ) #sort data if an order is specified if(($SortOrder | measure-object).count -gt 1){ #Add server as the first field $Order=@("Server") ForEach($FunctionName in $SortOrder){ #this function is from WHAM_template_module.psm1 #convert the function names to proper headings $Order += $(Get-NameWithSpaces -Functioname $FunctionName) } #sort by this $Data = $Success.WHAMScript.ReturnedData | Select-Object $Order }else{ $data = $Success.WHAMScript.ReturnedData } Return $data } Function Get-WHAMUserOutput{ Param ( [Parameter(mandatory=$true)]$computers, [Parameter()]$ticketnum, [Parameter()]$SortOrder, [Parameter()]$FunctionSingleString, [Parameter()][switch]$Collection, [Parameter()]$MenuOptions ) Write-WHAMConnectionSummary -Computers $computers #Filter results to just servers with returned data $Success = $computers | where-object {$_.whamscript.scriptfailure -ne $true} if ($collection){ #expand collection $SortedData = Expand-PSobjectData -data $Success.whamscript.returneddata -collection }else{ #expand any psobjects that have been returned $SortedData = Expand-PSobjectData -data $Success.whamscript.returneddata } #Sort our data if we specified a sort order if ((($SortOrder | Measure-Object).Count -gt 2) -AND ($SortedData)){ $SortedData = $SortedData | Select-Object $SortOrder } if ($SortedData){ Do{ write-host "Please select data output:" -ForegroundColor Cyan Write-Host "" write-host "1. BBCode table to clipboard" write-host "2. BBCode list (seperate BBCode per device) to clipboard" write-host "3. BBCode table to ticket" write-host "4. BBCode list (seperate BBCode per device) to ticket" write-host "5. Export to CSV" write-host "6. Export to HTML" write-host "7. Export to XML" write-host "8. Write to screen" $Options = ("1","2","3","4","5","6","7","8") $Question = "Please select data output:" $MenuAnswer = Ask-Question -Question $Question -ValidationArray $Options -Default "1" Switch ($MenuAnswer) { "1" { #BBCode table to clipboard $output = Get-WHAMDatainBBCode -data $SortedData $output | clip Output-textfile -data $output write-host "BBCode is now in your clipboard and opened int notepad..." -ForegroundColor Yellow } "2" { #BBCode list (seperate BBCode per device) to clipboard $output = Get-WHAMDatainBBCode -data $SortedData -List $output | clip Output-textfile -data $output write-host "BBCode is now in your clipboard and opened int notepad..." -ForegroundColor Yellow } "3" { if ($ticketnum.length -eq 0){ $ticketnum = Read-Host "Enter ticket number..." } #BBCode table to ticket Set-BBCodeToTicket -data $SortedData -ticketnum $ticketnum write-host "BBCode has been updated to $ticketnum..." -ForegroundColor Yellow } "4" { if ($ticketnum.length -eq 0){ $ticketnum = Read-Host "Enter ticket number..." } #BBCode list (seperate BBCode per device) to ticket Set-BBCodeToTicket -data $SortedData -ticketnum $ticketnum -List write-host "BBCode has been updated to $ticketnum..." -ForegroundColor Yellow } "5" { #Export to CSV Set-WHAMDataExport -data $SortedData -CSV } "6" { #Export to HTML Set-WHAMDataExport -data $SortedData -HTML } "7" { #Export to XML Set-WHAMDataExport -data $SortedData -XML } "8" { #Write to script write-host ($SortedData | Out-String) } Default { #Do nothing } } $Question = "Would you like to export another format? (Y/N)" $ExitMenu = Ask-Question -Question $Question -ValidationArray "Y","N" -Default "N" }While($ExitMenu -ne $false) }else{ Write-host "All servers failed - nothing to output" -ForegroundColor Red } } Function Get-WHAMDatainBBCode { Param ( [Parameter(mandatory=$true)]$data, [Parameter()][switch]$List ) $Output = @() $Output += "WHAM Output generated by $([Environment]::UserName) at $(Get-Date): " if ($List){ ForEach ($item in $data){ $Params = @{ data = $item list = $true } $Output += " " $Output += "------------------------------------------------------" $Output += "[size=18][b]Server: $($data.Server)[/b][/size]" $Output += ConvertTo-BBCode @Params -clip } }else{ $Output += ConvertTo-BBCode -data $data -clip } Return $Output } Function Set-BBCodeToTicket{ Param ( [Parameter(Mandatory=$true)]$data, [Parameter(Mandatory=$true)]$Ticketnum, [Parameter()][switch]$List ) $Params = @{ data = $data } if ($List){ $Params.Add("List", $true) } $output = Get-WHAMDatainBBCode @Params $Question = "Update ticket $ticketnum with BBCode output? (Y/N)" $MenuAnswer = Ask-Question -Question $Question -ValidationArray "Y","N" -Default "N" if ($MenuAnswer){ Update-CoreTicket -Ticket $Ticketnum -Message $($output | Out-String) -Private 1 -has_bbcode 1 } } Function Get-TidyFilepath{ Param ( [Parameter(Mandatory=$true)]$Path ) [string]$path = $Path $Path = $Path.replace("Microsoft.PowerShell.Core\FileSystem::","") Return $Path } Function Set-WHAMDataExport{ Param ( [Parameter(mandatory=$true)]$data, [Parameter()][switch]$CSV, [Parameter()][switch]$HTML, [Parameter()][switch]$XML ) if($CSV){ $Type = "CSV" $Path = Get-TidyFilepath -Path "$Global:WHAMExportPath\$Type`_output.$Type" $Data | Export-Csv $Path -NoTypeInformation -Force } if($HTML){ $Type = "HTML" $Path = Get-TidyFilepath -Path "$Global:WHAMExportPath\$Type`_output.$Type" #Thanks to Mark W whose code I borrowed below from the BBCode function :) $HTMLtmp = $Data | ConvertTo-HTML out-file -filePath $Path -inputobject $HTMLtmp -Force | Out-Null Invoke-Item $Path } if($XML){ $Type = "XML" $Path = Get-TidyFilepath -Path "$Global:WHAMExportPath\$Type`_output.$Type" $Computers.whamscript.returneddata | Export-Clixml $Path -Force } write-host "$Type exported to $Path" -ForegroundColor Yellow Invoke-Item $Path } Function Pad-String{ Param( [Parameter(mandatory=$true)]$StringTotalLength, [Parameter(mandatory=$true)][string]$String ) if (($String | Measure-Object -Character).Characters -ge $StringTotalLength){ $String = $String.substring(0, $StringTotalLength) }else{ $Pad = $StringTotalLength - ($String | Measure-Object -Character).Characters $arr = 0 do{ [string]$String = "$String " $arr++ } until ($arr -eq $Pad) } Return $String } Function Get-TimeSpan{ Param ( [Parameter(Mandatory = $true)]$Start, [Parameter(Mandatory = $true)]$Finish ) $TimeSpan = new-timespan -seconds $(($Finish-$Start).totalseconds) $Time = '{0:00}m:{1:00}s' -f $TimeSpan.Minutes,$TimeSpan.Seconds Return $Time } Function Get-ScriptTimes{ Param ( [Parameter(Mandatory = $true)]$Time ) #Calculate execution times $Time.TotalScript = Get-TimeSpan -Start $Time.ScriptStart -Finish $Time.ScriptFinish if ($time.DataGatheringStart){ $Time.TotalDataGathering = Get-TimeSpan -Start $Time.DataGatheringStart -Finish $Time.DataGatheringFinish } if ($time.TemplateStart){ $Time.TotalTemplate = Get-TimeSpan -Start $Time.TemplateStart -Finish $Time.TemplateFinish } if ($time.ConnectionTestJobStart){ $Time.TotalConnectionTestJob = Get-TimeSpan -Start $Time.ConnectionTestJobStart -Finish $Time.ConnectionTestJobFinish } if ($time.ScriptJobStart){ $Time.TotalScriptJob = Get-TimeSpan -Start $Time.ScriptJobStart -Finish $Time.ScriptJobFinish } write-host "Script Timing Summary:" -ForegroundColor Yellow if ($time.TotalDataGathering){ write-host "Data gathering: $($Time.TotalDataGathering)" } if ($time.TotalTemplate){ write-host "Template creation: $($Time.TotalTemplate)" } if ($time.TotalConnectionTestJob){ write-host "Connection test jobs: $($Time.TotalConnectionTestJob)" } if ($time.TotalScriptJob){ write-host "Script jobs: $($Time.TotalScriptJob)" } write-host "Total script time: $($Time.TotalScript)" } Function Expand-PSobjectData{ Param ( [Parameter()]$Data, [Parameter()][switch]$Collection ) if (-NOT $data){ Return $false }else{ $output = @() if ($Collection){ #Get the datatypes from the retuened data object from the server $datatypes = $data | Get-Member -MemberType NoteProperty | Where-Object {$_.Name -notlike "Server"} #check we have not returned other objects apart from the collection if ($datatypes.count -gt 1){ Write-host "Error: Could not parse output, collection selected along with another output, you will need to manually parse output by exporting to XML" -ForegroundColor Red Return $data break } #return just the object $Output = $data."$($datatypes.name)" }else{ #Loop through each returned psobject from each server ForEach ($ReturnedData in $Data){ #start our new psobject where we will merge the data $TempOutput = New-Object PSObject -Property @{ Server = $ReturnedData.Server } #Get the datatypes from the retuened data object from the server $datatypes = $ReturnedData | Get-Member -MemberType NoteProperty | Where-Object {$_.Name -notlike "Server"} #go through every datatype returned from the psojbect ForEach ($type in $datatypes){ #Look for this object: Deserialized.System.Management.Automation.PSCustomObject if ($type.Definition -like "Deserialized.System.Management.Automation.PSCustomObject*"){ #if we find it, get all the properties of it $TempPsobject = $ReturnedData."$($type.name)" $TempDatatypes = $TempPsobject | Get-Member -MemberType NoteProperty #merge these into the new psobject ForEach ($Name in $TempDatatypes.Name){ $Value = $($ReturnedData."$($type.name)"."$($name)") #Check if the value is blank, if it is update it (if we dont do this it breaks our BBCode) if ($Value.length -eq 0){ $Value = "Blank value returned from script" } $TempOutput | Add-Member -type NoteProperty -Name $Name -Value $Value -Force } }else{ #Not PSobject so just add it to the output $Value = $($ReturnedData."$($type.name)") #Check if the value is blank, if it is update it (if we dont do this it breaks our BBCode) if ($Value.length -eq 0){ $Value = "Blank value returned from script" } $TempOutput | Add-Member -type NoteProperty -Name $type.name -Value $Value -Force } } #add to our return array $output += $TempOutput } } return $output } } Function Show-Menu{ $TokenPrompt = $null if(-NOT (Validate-CoreToken -ReturnBooleen)){ $TokenPrompt = @"