#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(",")}
#filter out duplicate device numbers
$devices = $devices | sort-object | Get-Unique
write-host "Getting devices from CORE..."
#GO through the device list and get them all from CORE
$ComputerList = @()
$TotalDevices = ($Devices | Measure-Object).Count
$Currentdevice = 0
ForEach ($DeviceNumber in $Devices){
$Currentdevice++
Write-Progress -Activity "Getting $TotalDevices devices from CORE" `
-PercentComplete (($Currentdevice/$TotalDevices) * 100) -CurrentOperation "Getting CORE information for device $Currentdevice/$TotalDevices`: $DeviceNumber"
$TempDevice = Get-CoreDevice -device $DeviceNumber -RackPW -NoDRAC
$ComputerList += $TempDevice
}
Write-Progress -Completed -Activity "Completed"
#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
$NonWindowsDevices = $Computers | Where-Object {($_.is_windows -ne $true) -OR ($_.Plattype -like "*cluster*")}
if($NonWindowsDevices){
write-host "Removing the following servers as they are not Windows servers:" -ForegroundColor Yellow
foreach ($device in $NonWindowsDevices){
$ServerName = Pad-String -StringTotalLength 17 -String $device.ServerName
write-host "Server: $ServerName - Platform: $($device.PlatName)"
}
}
$FilteredServers = $COmputers | Where-Object {($_.is_windows -eq $true) -AND ($_.Plattype -notlike "*cluster*")}
return $FilteredServers
}
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")
}
$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
DomainUser = $DomainCreds.DomainUser
DomainPass = $DomainCreds.DomainPass
}
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-WHAMOutput{
Param (
[Parameter(mandatory=$true)]$Option
)
Switch ($Option)
{
"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
}
"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
}
"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
}
}
}
Function Get-WHAMUserOutput{
Param (
[Parameter(mandatory=$true)]$computers,
[Parameter()]$ticketnum,
[Parameter()]$SortOrder,
[Parameter()]$FunctionSingleString,
[Parameter()][switch]$Collection,
[Parameter()]$OutputOptions
)
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){
if($OutputOptions){
#this means we have passed the menu options through and we dont need to ask the user
ForEach ($option in $OutputOptions){
Get-WHAMOutput -Option $option
}
}else{
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"
Get-WHAMOutput -Option $MenuAnswer
$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: $($item.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 -Source 3
write-host "BBCode has been updated to $ticketnum..." -ForegroundColor Yellow
}
}
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 = @"
"@
}
$ImportData = $null
if($Global:MenuOutput){
$ImportData = @"
"@
}
$inputXML = @"
$ImportData
$TokenPrompt
"@
<#
#>
#convert the XML to XAML
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^