PowerShell: Move Windows Server 2008 Cluster Group

Where I work, we were a little behind finally getting to Windows Server 2008 R2.  Our Exchange Servers were running on Server 2003 R2 until recently.  Windows clustering is quite different on 2k8 vs. 2k3.  I wrote this function to move the Windows cluster group “Cluster Group” (quorum) to the other node of the cluster.  I know it’s not that difficult to do this, but it saves a few extra keystrokes.

Function Invoke-WindowsClusterFailover
{
	#Requires -version 2
	[CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact="High")]
	param()
	Write-Verbose "Checking to see if $($env:computername) is a Clustered Exchange Server"
	Get-ClusteredMailboxServerStatus -ErrorAction SilentlyContinue | Out-Null
	If ($? -eq $False)
	{
		Write-Verbose "$($env:computername) is not a clustered Exchange Server - Exiting"
		Throw "$($env:computername) is not a clustered Exchange Server"
	}
	Else
	{
		Write-Verbose "$($env:computername) is a Clustered Exchange Server"
		If (((Get-WmiObject Win32_OperatingSystem).Caption).ToString() -like "*2008*")
		{
			Write-Verbose "$($env:computername) is a Windows Server 2008 or later"
			Write-Verbose "Loading Windows Failover Cluster PowerShell Module"
			Import-Module FailoverClusters
		}
		Else
		{
			Write-Verbose "$($env:computername) is not Windows Server 2008 or later"
			Write-Verbose "$((Get-WmiObject Win32_OperatingSystem).Caption) does not support the Powershell FailoverCluster Module - Exiting"
			Throw "$((Get-WmiObject Win32_OperatingSystem).Caption) does not support the Powershell FailoverCluster Module"
		}
		Write-Verbose "Getting Windows Cluster Name"
		$ClusterName = (Get-Cluster).Name
		Write-Verbose "Checking ShouldContinue if OK to proceed"
		if($PSCmdlet.ShouldProcess("$($ClusterName)", "Failover Windows Cluster"))
		{
			Write-Verbose "Moving Windows Cluster Group"
			Get-ClusterGroup "Cluster Group" | Move-ClusterGroup
		}
		Write-Verbose "End"
	}
				.SYNOPSIS
		Moves the Windows cluster "Cluster Group" to the other node of the cluster

		.DESCRIPTION
		Moves the Windows cluster "Cluster Group" to the other node of the cluster.  This requires
		Windows Server 2008 or later.  This function supports "Should Continue" so it prompts the
		user to continue at the "dangerous" portion of the function (Move-ClusterGroup).  If you
		don't want the should continue prompt, you can use the  param.

		.NOTES
		Function Name: Invoke-WindowsClusterFailover
		Author: Dan Burgess
		Email: nerd@everydaynerd.com
		Script Requires:  Powershell 2.0 or higher; Windows Server 2008 or later
	#>
}

I really enjoyed using the “SupportsShouldProcess” feature of CmdletBinding!  Expand the code above, and double click to select all.

Posted in PowerShell | Leave a comment

PowerShell: Get-Uptime for Computer(s)

I needed to check the server uptime for multiple servers, and well, I wanted to do it in PowerShell. :)  I found a sample from MSDN, and modified it, and threw it into a function.

Function Get-Uptime
{
	#Requires -version 2
	[CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact="High")]
	Param(
	[parameter(Mandatory=$false,HelpMessage="Computer Name(s)",ValueFromPipeline=$true)]
	[object[]]$ComputerName = "."
	)
	PROCESS
	{
		$WmiOS = Get-WMIObject -class Win32_OperatingSystem -computer $ComputerName
		Function ConvertWMIDateToDateTime($BootTime)
		{
			[System.Management.ManagementDateTimeconverter]::ToDateTime($BootTime)
		}
		Function GetUptime($ComputerName)
		{
			$BootTime = $WmiOS.LastBootUpTime
			$LastBootUpTime = ConvertWMIDateToDateTime($BootTime)
			$Uptime = (Get-Date) - $lastBootUpTime
			$days = $Uptime.Days
			$hours = $Uptime.Hours
			$min = $uptime.Minutes
			$sec = $uptime.Seconds
			[console]::ForegroundColor = "Green"
			"$($ComputerName) has been up for: {0} days, {1} hours, {2} minutes and {3} seconds" -f $days,$hours,$min,$sec
			[console]::ResetColor()
		}
		GetUptime $ComputerName
	}
	<#
		.SYNOPSIS
		Gets uptime of a system via WMI

		.DESCRIPTION
		Get-Uptime uses WMI (specificaly Win32_ComputerSystem) to get the uptime of a system.
		The core component of the script was found at http://msdn.microsoft.com/en-us/library/aa394591(VS.85).aspx
		and addapted with additional functionality.

		.EXAMPLE
		PS c:\> Get-UpTime SERVER01
		SERVER01 has been up for: 355 days, 23 hours, 59 minutes and 59 seconds

		.EXAMPLE
		PS c:\> Get-ExchangeServer | Get-Uptime
		SERVER01 has been up for: 355 days, 23 hours, 59 minutes and 59 seconds
		SERVER02 has been up for: 355 days, 23 hours, 59 minutes and 59 seconds
		SERVER03 has been up for: 355 days, 23 hours, 59 minutes and 59 seconds
		SERVER04 has been up for: 355 days, 23 hours, 59 minutes and 59 seconds

		.LINK
		Script Posted to:

http://everydaynerd.com

		Adapted from sample posted at:

http://msdn.microsoft.com/en-us/library/aa394591(VS.85).aspx

		.NOTES
		Function Name : Get-UpTime
		Author: Dan Burgess
		Email: nerd@everydaynerd.com
		Script Requires:  Powershell 2.x or higher
	#>
}

Here’s an alternative to this function that outputs to a powershell object – rather than just writing to the screen. (Thanks for the encouragement Jeffery)

Function Get-Uptime
{
	#Requires -version 2
	[CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact="High")]
	Param(
	[parameter(Mandatory=$true,HelpMessage="Computer Name(s)",ValueFromPipeline=$true)]
	[object[]]$ComputerName
	)
	PROCESS
	{
		$System = Get-WMIObject -class Win32_OperatingSystem -computer $ComputerName
		Function GetUptime($ComputerName)
		{
			$Bootup = $System.LastBootUpTime
			$LastBootUpTime = $System.ConvertToDateTime($System.LastBootUpTime)
			$now = Get-Date
			$Uptime = $now - $lastBootUpTime
			$NewObjectProperties = @{
				ComputerName=$ComputerName
				Days=$Uptime.Days;`
				Hours=$Uptime.Hours;`
				Minutes=$Uptime.Minutes;`
				Seconds=$Uptime.Seconds;`
				}
			New-Object psobject -Property $NewObjectProperties
		}
		$Results = GetUptime $ComputerName
		$Results | Select ComputerName, Days, Hours, Minutes, Seconds
	}
	<#
		.SYNOPSIS
		Gets uptime of a system via WMI

		.DESCRIPTION
		Get-Uptime uses WMI (specificaly Win32_ComputerSystem) to get the uptime of a system.
		The core component of the script was found at http://msdn.microsoft.com/en-us/library/aa394591(VS.85).aspx
		and addapted with additional functionality.

		.EXAMPLE
		PS c:\> Get-UpTime server01
		ComputerName : {server01}
		Days         : 27
		Hours        : 2
		Minutes      : 21
		Seconds      : 26

		.EXAMPLE
		PS c:\> Get-ExchangeServer | Get-Uptime | ft -AutoSize
		ComputerName Days Hours Minutes Seconds
		------------ ---- ----- ------- -------
		{server01}    27     2      15      49
		{server02}   130     6      40      23

		.LINK
		Script Posted to:

http://everydaynerd.com

		Adapted from sample posted at:

http://msdn.microsoft.com/en-us/library/aa394591(VS.85).aspx

		.NOTES
		Function Name : Get-UpTime
		Author: Dan Burgess
		Email: nerd@everydaynerd.com
		Script Requires:  Powershell 2.x or higher
	#>
}

Happy PowerShelling!

Posted in PowerShell | 2 Comments

Notepad ++ Powershell Language Syntax Coloring

Notepad++ is my editor of choice for writing PowerShell code.  There is a built in language syntax for PowerShell, but it doesn’t include Exchange cmdlets in the syntax coloring.  So, I’ve created a custom user-defined language file for PowerShell, that includes the Exchange, and Active Directory cmdlets, as well as a few other modules that I use regularly.  I also included the new PowerShell 2 comment block coloring, as the built-in didn’t recognize them.

This is my first attempt at creating a user-defined language for Notepad++, but I thought I’d share it, mainly because I didn’t see anyone else that had one available on the internet.

Here’s an example of the coloring:

Extract the zip, and place the xml file in %AppData%\Notepad++\ directory.  If you already have a userDefineLang.xml, you will have to edit your original, and append the data from this file to it.  More info can be found on Notepad++ website if you need help.

[Download]

Posted in General | Leave a comment

R.I.P. Steve Jobs

(image credit)
Posted in General | Leave a comment

PowerShell: Drive Space Info from multiple systems

With PowerShell 2 came Powershell remoting and jobs, both of which are REALLY cool!  I’m going to show you two ways to get drive space info from multiple systems, first with a normal foreach loop, and then with remoting (which is basically a remote job).  Both methods use WMI class Win32_LogicalDisk to query the drives for information.

First, the old fashion foreach loop.  Notice the “Process” section – this in essence does a foreach of the $Identity – notice that in the parameter that the $Identity has  [object[]] in front of it.  Two things about this:  One, [] denotes an array, versus a single object.  Two, it’s object, instead of string, int or other data type.  Object allows the parameter to be just a string – a computer name (“SERVER01″), a string array (“SERVER01″, “SERVER02″), or even the identity from other objects (Get-ExchangeServer | GetDriveSpace).  I’ll be posting more in the future about parameters, and advanced functions (cmdletbinding).

Function GetDriveSpace
{
	[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Low")]
	param (
	[parameter(Mandatory=$true,HelpMessage="ComputerName(s)",ValueFromPipeline=$true)]
	[Object[]]$Identity
	)
	Process
	{
		$Name = (Get-WmiObject -ComputerName $Identity win32_computersystem  | Select Name).Name.ToString()
		$drives = Get-WmiObject -ComputerName $Identity Win32_LogicalDisk | Where-Object {$_.DriveType -eq 3}

		foreach($drive in $drives)
		{
			$NewObjectProperties = @{
				ComputerName=$Name;`
				DriveLetter=$drive.DeviceID;`
				Label=($drive.VolumeName);`
				DriveSize=($drive.Size/1gb).ToString("0.00");`
				FreeSpaceMB=($drive.freespace/1GB).tostring("0.00");`
				PercentFree=((($drive.freespace/1GB)/($drive.size/1GB))*100).tostring("0.00")`
			}
			New-Object psobject -Property $NewObjectProperties
		}
	}

	GetDriveSpace -Identity SERVER01

		Returns Drive Space Info for SERVER01

		.EXAMPLE
		PS] C:\>Get-MailboxServer | GetDriveSpace | FT

		Returns Drive Space Info for Exchange Mailbox Servers

		.EXAMPLE
		PS] C:\>GetDriveSpace -Identity SERVER01, SERVER02 | FT -AutoSize

		Returns Drive Space Info for SERVER01 & SERVER02

		.NOTES
		Function Name : GetDriveSpace
		Author : Dan Burgess
		Email: nerd@everydaynerd.com
		Script Requires:  Powershell 2.0 or higher
	#>
}

Now, lets do the same thing, but this time with PowerShell remoting.  As before, the function is utilizing  CmdletBinding, Parameter, with a single param [object[]]$Identity, and the Process block, but an End block as well.  In the Process block, each server passed in the parameter has a remote job started with the Invoke-Command cmdlet, passing the script block with the code to query WMI to return the disk space information.  Note the -AsJob switch – this allows the invoke-command to work in the background, instead of waiting on the job to finish.  The End block then checks all the jobs, and waits till none of the jobs have a status of “Running” before issuing a “Receive-Job”.  This gathers all the returned values from each job that ran.  The -Keep leaves a copy of the results in the job queue – if receive-job is run with out this, the results are removed from the job status.

Function GetDriveSpace-Remoting
{
	[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Low")]
	param (
	[parameter(Mandatory=$true,HelpMessage="ComputerName(s)",ValueFromPipeline=$true)]
	[Object[]]$Identity
	)
	Process
	{
		Invoke-Command -ComputerName $Identity -ScriptBlock {$drives = Get-WmiObject Win32_LogicalDisk | Where-Object {$_.DriveType -eq 3}
			foreach($drive in $drives)
			{
				$NewObjectProperties = @{
					DriveLetter=$drive.DeviceID;`
					Label=($drive.VolumeName);`
					DriveSizeMB=($drive.Size/1gb).ToString("0.00");`
					FreeSpaceMB=($drive.freespace/1GB).tostring("0.00");`
					PercentFree=((($drive.freespace/1GB)/($drive.size/1GB))*100).tostring("0.00")`
				}
				New-Object psobject -Property $NewObjectProperties
			}
		} -SessionOption (New-PSSessionOption -NoMachineProfile) -AsJob

	}
	End
	{
		While (Get-Job -State "Running")
		{
			$i = ((Get-Job).Count) - ((Get-Job -State "Running").count)
			$Progress = [int][Math]::Ceiling(($i / ((Get-Job).Count) * 100))
			Write-Progress -Activity "Waiting on $(((Get-Job -State "Running").count)) Jobs to finish" -PercentComplete $progress -Status "$($progress)% Complete" -Id 1;
		}
		$Results = Get-Job | % {Receive-Job $_ }
		$Results
	}

	GetDriveSpace-Remoting -Identity SERVER01

		Returns Drive Space Info for SERVER01

		.EXAMPLE
		PS] C:\>Get-MailboxServer | GetDriveSpace-Remoting | FT

		Returns Drive Space Info for Exchange Mailbox Servers

		.EXAMPLE
		PS] C:\>GetDriveSpace-Remoting -Identity SERVER01, SERVER02 | FT -AutoSize

		Returns Drive Space Info for SERVER01 & SERVER02

		.NOTES
		Function Name : GetDriveSpace-Remoting
		Author : Dan Burgess
		Email: nerd@everydaynerd.com
		Script Requires:  Powershell 2.0 or higher and WinRM enabled on remote hosts
	#>
}

Both methods work, but in my tests, the remote function was MUCH faster when pulling from multiple systems.  For a list of 48 systems, the normal foreach loop took 30 seconds, while the remote function only took 5 seconds!!!

Posted in Exchange, PowerShell | Leave a comment

PowerShell: Change PS Window Size on the fly

First, my apologies for not posting ANYTHING to EverydayNerd since April 25, but hey, this is just for fun, and I DO have a day job :)

I am going to be posting a LOT of PowerShell stuff here, as it has, over the last 6 months, become my best friend at the office! Thanks to Gary Siepser, I have gone from writing super simple scripts, to now, well trust me, it’s a day/night difference.

So I work on a laptop, but also dock, and when I un-dock my resolution changes – enough that I have scroll bars in my PowerShell window. This was really annoying, so I wrote a quick function that will change the window size on the fly.

I put these functions into my PowerShell profile, so I can call them whenever I need to change my PS window size. Works like a charm!  If you don’t know how to edit our PowerShell profile, just enter this in your shell:  notepad $Profile

Hope this helps, and stay tuned for more PowerShell goodies!

Source Code:

Function Set-Wide
{
	$aff = (Get-Host).UI.RawUI
	$bff = $aff.BufferSize
	$bff.Width = 170
	$bff.Height = 9000
	$aff.BufferSize = $bff
	$wff = $aff.WindowSize
	$wff.Width = 170
	$wff.Height = 60
	$aff.WindowSize = $wff
}

Function Set-Small
{
	$aff = (Get-Host).UI.RawUI
	$wff = $aff.WindowSize
	$wff.Width = 90
	$wff.Height = 45
	$aff.WindowSize = $wff
	$bff = $aff.BufferSize
	$bff.Width = 90
	$bff.Height = 9000
	$aff.BufferSize = $bff
}
Posted in PowerShell | Leave a comment

THIS is why you don’t leave your Wi-Fi open

I’ve said this for years.  It finally happened.  Man leaves Wi-Fi open, allowing a pedophile to use his internet to download images.  This prompted an FBI raid on the man’s house, and leaving the man a whole lot of explaining to do…

Please, please PLEASE – secure your Wi-Fi connection!!!

[ How to secure your wireless ]

[ source ]

Posted in Security | Leave a comment