Monthly Archives: February 2011

Booleans in PowerShell

Booleans seem like just about the simplest things. They are, after all, binary. Either $True or $False. But Windows PowerShell’s assumption that the left side of an expression should control how the right side is interpreted can sometimes confuse you. Or at least me. For example:

PS> $True –eq 2

True

PS> 2 –eq $True

False

Yup. That makes perfect sense. If you realize that in the first case, what is happening is:

[Bool]$True –eq [Bool]2

but in the second case:

[int]2 –eq [int]$True

In the first case, the number two is evaluated as a Boolean, and any non-zero value is considered true. In the second case, the integer value of 2 causes $true to be evaluated as an integer, and the integer value of $true is 1. So the two integers aren’t equal.

I put up an article on the TechNet Wiki here. Add to it, show some more examples, make it better. That’s the Wiki way. Smile

New Theme

My personal thanks to Barb Bowman for the new theme I'm using now. Very much appreciated! What I especially like is that it gives me a reasonable font (Consolas) for code blocks.

Changing the Maximum Message Size in Exchange 2010

This little script will change the maximum message size for an Exchange 2010 server. It’s not  tested and designed for use in very large Exchange organizations, but has been tested and works on single Exchange 2010 server environments such as Windows Small Business Server 2011 Standard.

edited: 19/2/2011. reminded by Brian Desmond that I really should use param($MaxSize). Thanks!
# Change-ExchSize.ps1 # Script to change the size of the maximum send and receive for # a Windows SBS 2011 Standard installation with Exchange 2010 # # Expects: maximum size parameter in MB or prompts # # Created: 19/2/2011 # ModHist: 19/2/2011 (changed to use param(). Thanks Brian param($MaxSize) if (! $MaxSize ) { $MaxSize = Read-Host "What's the max size(in MB) you want for all mailboxes? " } $stMaxSize = "$MaxSize" + "MB" "Setting Maximum Send and Receive Transport Size to: $stMaxSize" Set-TransportConfig -MaxSendSize $stMaxSize -MaxReceiveSize $stMaxSize Get-TransportConfig | ft -maxsendsize,maxreceivesize "Setting Maximum Send and Receive Connectors to: $stMaxSize" $ReceiveConnectors = Get-ReceiveConnector $SendConnectors = Get-SendConnector ForEach ($Connector in $ReceiveConnectors ) { Set-ReceiveConnector -Identity $Connector.name -MaxMessageSize $stMaxSize } ForEach ($Connector in $SendConnectors ) { Set-SendConnector -Identity $Connector.name -MaxMessageSize $stMaxSize } "The Maximum Receive Connector size has been set to: " Get-ReceiveConnector | ft Name, MaxMessageSize "The Maximum Send Connector size has been set to: " Get-SendConnector | ft Name, MaxMessageSize



Uninstalling Windows Server 2008 R2 SP1 Beta and RC

Before you can install Windows Server 2008 R2 SP1 (or Windows 7 SP1), you need to uninstall the beta or RC version that might be installed. On a GUI install of Windows Server, no problem, run AppWiz.cpl and click on Updates. But on Hyper-V Server 2008 R2 (or Server Core) there is no obvious way to uninstall the beta or RC versions of the Service Pack. You need to use the command line version of the Windows Update Installer: wusa.exe.

wusa.exe /uninstall /kb:976932

It’ll take a couple of reboots before it’s completely uninstalled, and then you can install the SP1 you just downloaded.

Update: Note, if it wasn't clear. This command line works equally well from GUI installs, including Windows 7.

PowerShell, Hyper-V and WMI

I’ve started an article over on the PowerShell Survival Guide Wiki to drop in quick hits how to do “stuff” with Hyper-V, using PowerShell and the native WMI interface of Hyper-V. The WMI namespace for Hyper-V is  “root\virtualization”. Turns out managing Hyper-V isn’t as hard as I thought, at least in no small part because working with WMI in PowerShell is actually pretty straightforward. I’m still learning and poking around, but this stuff is all over the net if your bingle skills are good. Today I added the simple steps needed to create a VHD, either dynamic or fixed, to the Wiki page. Any one who wants to join in is more than welcome – that’s what a Wiki is all about after all.

Charlie.

QuickStart PowerShell Remoting

Just created a new Wiki page on the PowerShell Survival Guide that is a quick guide to getting started with PowerShell remoting. I focused strictly on PSSession, since I find that the simplest way to get started. Let me know what you think, or better yet, update the content to improve it. That, after all, is what a Wiki is all about!

Customizing PowerShell–Using $Profile

A copy of the profile described here is at: Charlie_Profile

Probably one of the first scripts any new Windows PowerShell user writes is a custom profile. Your PowerShell profile ($profile in PowerShell speak), is run every time you open a PowerShell window, and it allows you to do a lot of different things to set up your environment the way you want it. Actually, though, there are four profiles that affect your PowerShell window, as described in this MSDN article. There are arguments for which profile you should be editing, but my personal preference is to use the most specific one:
%UserProfile%\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1

The only problem is, that’s not really were “My Documents” is for me. So, how to be sure you always work on the right one, wherever it’s located? Easy – let PowerShell tell you!

First, let’s see if we have a $profile yet:
PS1> Test-Path $profile False

Nope. So, how to make one. First,let’s create the path:
PS1> mkdir (Split-Path $profile)     Directory: C:UserscprDocuments Mode                LastWriteTime     Length Name ----                -------------     ------ ---- d----        12/22/2010  12:05 PM            WindowsPowerShell

Now,use your favourite plain text editor to create the file. In my case that would be:
PS1> gvim $profile

And here you can start to build your customizations for PowerShell. There are lots of possibilities, including changing the colouring, updating the title bar, creating aliases and functions that are always available. Really the possibilities are nearly endless.

In my own case, I like to distinguish between a regular window and an elevated one, so I change the colour of all my regular PowerShell windows to white with dark blue text (works better for screen shots), and my elevated windows to dark red with white text. I also use a multiline prompt, and do some other customizations.

Here’s my full $profile:
# This is Charlie”s custom PS Profile… # # ModHist: 26/05/2007 – Initial #        : 08/06/2007 – Added clear-host and winzip alias #        : 10/06/2007 – Changed OS detection #        : 22/07/2007 – support for running from local home #        : 14/03/2008 – changed to Microsoft.PowerShell_profile.ps1 #        : 16/03/2008 – added productID test for Vista #        : 24/03/2009 – added mapdrives for admin shell #        : 17/10/2010 – minor cleanup before sending to VanTUG #        : 26/02/2011 - modified to post cleaner on WordPress # #*************************************************************************** # # Get the name of this version... $CallingProfile = $myInvocation.mycommand.path $FileRegEx = "(?<name>.*)(?<dot>\.)(?<extension>[pP][sS]1)" $CallingProfile -match $FileRegEx # Find out who we are... $id = [System.Security.Principal.WindowsIdentity]::GetCurrent() $p = New-Object system.security.principal.windowsprincipal($id) $admin=$p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) # Get System WMI info -- Get it once, use it multiple $SysWMI =  Get-WmiObject Win32_OperatingSystem $hostname = $(hostname).tolower() $otherway = ($ENV:computername).tolower() $ProdID=(gi ''HKLM:/Software/Microsoft/Windows NT/CurrentVersion'').getvalue(''ProductID'') # Build a hashtable with information about versions, etc. $SystemHash = @{}  # Initialize the variable as a hashtable $SystemHash["Build"] = $SysWMI.BuildNumber $SystemHash["SPNumber"] = $SysWMI.CSDVersion $SystemHash["Caption"] = $SysWMI.Caption $SystemHash["SKU"] = $SysWMI.OperatingSystemSKU $SystemHash["Architecture"] = $SysWMI.OSArchitecture $SystemHash["Hostname"] = $SysWMI.CSName.ToLower() $SystemHash["Arch"] = $ENV:Processor_Architecture $SystemHash["ProductID"] = $ProdID # We’ll need the build number to persist in the environment $Build = $SystemHash["Build"] switch -regex ($SystemHash["Build"]) { 2600 { $ver="XP" } 3790 { if ($SystemHash["Caption"] -match "XP") { $ver = "XPx64" } else { $ver = "Server 2003" } } 6000 { $ver="Vista" } 6001 { if ($SystemHash["Caption"] -match "Vista" ) { $ver="Vista" } else { $ver="Server 2008" } } 7600 { if ($SystemHash["Caption"] -match "Windows 7" ) { $ver="Windows 7" } else { $ver="Server 2008 R2" } } 7601 { if ($SystemHash["Caption"] -match "Windows 7" ) { $ver="Windows 7 SP1" } else { $ver="Server 2008 R2 SP1" } } } if ($SystemHash["Arch"] -eq "AMD64" ) { $64bit=$true $32bit=$false } else{ $64bit=$false $32bit=$true } # Find out if we''re running as admin (IsInRole) # if we are, change the window colour and set effectivename variable # But only on Windows Vista or Later if ($admin -and ($build -ge 6000 )) { $effectivename = "Administrator" $host.UI.RawUI.Backgroundcolor="DarkRed" $host.UI.RawUI.Foregroundcolor="White" clear-host $maps=''C:\Windows\system32\mapdrives.cmd''  #cmd file to map standard drives if (Test-Path $maps) {& $maps } } else { $effectivename = $id.name $host.UI.RawUI.Backgroundcolor="White" $host.UI.RawUI.Foregroundcolor="DarkBlue" clear-host } $host.ui.rawui.WindowTitle = $effectivename + "@" + $HostName +" >" function global:prompt { if ( $effectivename -eq "Administrator" ) { write-host ("[") -nonewline -foregroundcolor red write-host ($Hostname) -nonewline -foregroundcolor Magenta write-host ("]") -nonewline -foregroundcolor Red Write-Host ([string]$(get-location) +":") -foregroundcolor Green write-host ("PSH>" ) -nonewline -foregroundcolor White } else { write-host ("[") -nonewline -foregroundcolor red write-host ($Hostname) -nonewline -ForegroundColor Magenta write-host ("]") -nonewline -foregroundcolor Red Write-Host ([string]$(get-location) + ''> '') -ForegroundColor DarkGreen write-host ("PSH>" ) -nonewline -foregroundcolor DarkBlue } return " " } # function to elevate a command. Finally. function sudo ([string]$file, [string]$arguments) { $psi = new-object System.Diagnostics.ProcessStartInfo $file; $psi.Arguments = $arguments; $psi.Verb = "runas"; $psi.WorkingDirectory = get-location; [System.Diagnostics.Process]::Start($psi); } # Now, a function to edit the executable script without knowing where it is. function fvi { param($script ) $s = (get-command -ea silentlycontinue $script).definition if ( $s ) { gvim $s } else { "$script not found" } } # Update the path to make life easier, and add a function while we''re at it... $ENV:Path="$ENV:PATH;" + "U:\psbin;" + "$ENV:HOMEDRIVE" + "$ENV:HOMEPATH" + "\psbin;" function path { write-host $env:path } # Now, start to build some aliases. These may be machine specific, so be careful... if ($64bit) { set-alias winzip -value "C:\Program Files (x86)\WinZip\WinZip32.exe" set-alias word -value "C:\Program Files (x86)\Microsoft Office\Office14\WinWord.exe" set-alias excel -value "C:\Program Files (x86)\Microsoft Office\Office14\Excel.exe" set-alias OneNote -value "C:\Program Files (x86)\Microsoft Office\Office14\OneNote.exe" set-alias Visio -value "& mstsc C:\Program Files (x86)\RemotePackages\Visio_2010.rdp" set-alias hyperv -value "C:\Program Files\Hyper-V\virtmgmt.msc" set-alias vm -value "C:\Program Files\Hyper-V\vmconnect.exe" } else { set-alias winzip -value "C:\Program Files\WinZip\WinZip32.exe" set-alias word -value "C:\Program Files\Microsoft Office\Office14\WinWord.exe" set-alias excel -value "C:\Program Files\\Microsoft Office\Office14\Excel.exe" set-alias OneNote -value "C:\Program Files\Microsoft Office\Office14\OneNote.exe" set-alias Visio -value "& mstsc C:\Program Files\RemotePackages\Visio_2010.rdp" } set-alias edit -value "C:Windows\gvim.bat" set-alias vi -value "C:windows\gvim.bat" # set-alias ping test-connection # disabled. More annoying than useful # The following is useful if you have automatic startup PowerShell windows... if ( ! $Admin ) { cd $home }

There, that’s complicated enough for most uses. :-)
But hopefully you’ll find some useful stuff in this that you can use for your own $profile.

Charlie.

Windows Server Backup with PowerShell

Windows Server 2008 R2 added some useful enhancements to the PowerShell snapin that was in Windows Server 2008. But it’s still a pain to use, and a bit hard to get your head around. Here’s a basic PowerShell script to do a backup. There are enough comments in there that it should help you get started.

# -----------------------------------------------------------------
# Script to set a Windows Server Backup policy for a server
#
# Assumes: Volumes C: and D: to backup.
#        : Target - \storageserverserverbackup
#        : Exclusions - D:temp, D:Users*.MP3, D:Users*.WMA
#        : VSS Mode - Full Backup
#        : System State - True
#        : Bare Metal Recovery - True
#        : Schedule - 12:30 PM, 11:00 PM
# Requires: Elevated PowerShell
#         :   (Must be run As Administrator)
#
# Planned Improvements: [minor] Check for elevated priv, error politely if not
#                     : [minor] Check for Server Backup PSSnapin before trying to load
#                     : [major] Accept cmdline parameters
#
# ModHist: 1/31/11 - initial (Charlie Russel)
#        :
#
# With profound thanks to Richard Siddaway, Windows PowerShell MVP
# -----------------------------------------------------------------
# By default, the PSSnapin isn''t loaded automatically, so first load it.
Add-PSSnapin Windows.ServerBackup # will error if already loaded, but continue

# First, create a new empty policy
# Alternately, you can open the existing policy in Edit mode with
#   $BackupPolicy = Get-WBPolicy -edit
$BackupPolicy = New-WBPolicy

# Now,define the parts of it.
# First,let''s do the "source" volumes that will be part of the backup.
# This requires us to first get a list of them, and then parse that list
# to add the ones we want (C: and D:)
# We don''t actually need C:, since we''ll get that as part of Bare Metal Restore,
# but we include it anyway for completeness

$volC = Get-WBVolume -AllVolumes | Where {$_.MountPath -eq "C:"}
$volD = Get-WBVolume -AllVolumes | Where {$_.MountPath -eq "D:"}
$Volumes = $volC,$volD

# Now, add those volumes to the blank policy.
Add-WBVolume -policy $BackupPolicy -volume $Volumes

# If you want to do the entire volume, you don''t need to define any exclusions,
# but if you want to exclude any files or folders, you need to define that exclusion
# and add that to the backup policy you''re building. First, define it. 
$excTEMP = New-WBFileSpec -Filespec D:Temp -exclude
$excWMA = New-WBFileSpec -Filespec "D:Users*.wma" -exclude  # Recursive unless
$excMP3 = New-WBFileSpec -Filespec "D:Users*.mp3" -exclude  # -NonRecursive specified
$FileExclusions = $excTEMP,$excWMA,$excMP3

# and then add that to the policy we''re building
Add-WBFileSpec -policy $BackupPolicy -filespec $FileExclusions

# Define the backup target, this time as a network share.
# For backup to share, you need to create a credential to connect to the remote share.
# This wouldn''t be required for specifying a removable disk.
# You can specify the username here (DOMAINUser) but will be prompted for password
$Cred = Get-Credential exampleuser

# Now, define the target
$Target = New-WBBackupTarget -NetworkPath \StorageServerServerBackup -Credential $Cred

# Add the target to the policy
Add-WBBackupTarget -policy $BackupPolicy -target $Target

# Define the schedule
$sch1 = [datetime]"01/31/2011 12:30:00"
$sch2 = [datetime]"01/31/2011 21:00:00"
Set-WBSchedule -policy $BackupPolicy -schedule $sch1,$sch2

# Set for system state and for bare metal recovery
Add-WBSystemState -policy $BackupPolicy
Add-WBBareMetalRecovery -policy $BackupPolicy

# Finally, set for full VSS Backup
Set-WBVssBackupOptions -policy $BackupPolicy -VssFullBackup

# Finally, we need to SET the policy before it actually takes control
Set-WBPolicy -force -policy $BackupPolicy

# This completes the configuration of the SBS server backup policy
$Server = (hostname).tolower()
" The server $Server now has the following backup configuration: "
""
Get-WBPolicy