Resizing the PowerShell Console
Windows 10's support for high DPI displays is much better than previous iterations of Windows, but there are still some times it gets a bit confused. One such problem occurs when you have multiple high DPI displays or two displays of different sizes. If you move PowerShell console windows between displays or log back in after being logged out for a while, you can end up with a scrunched up PowerShell window. Nothing I had to deal with when all I had was a pair of standard FullHD monitors, but ever since I got my Surface Book, and connected it to a 28 inch 4k monitor, I've had periodic problems. Very annoying when your PowerShell window changes to 37 characters wide and 7 lines long!
The fix is to reset the window size. Now I can do this graphically (right click on the title bar, select Properties, and then the Layout tab), but that's a nuisance at best, and besides, the whole idea of using the GUI to fix a console just isn't right. The answer is to leverage the built-in $host variable:
$host | Get-Member TypeName: System.Management.Automation.Internal.Host.InternalHost Name MemberType Definition ---- ---------- ---------- EnterNestedPrompt Method void EnterNestedPrompt() Equals Method bool Equals(System.Object obj) ExitNestedPrompt Method void ExitNestedPrompt() GetHashCode Method int GetHashCode() GetType Method type GetType() NotifyBeginApplication Method void NotifyBeginApplication() NotifyEndApplication Method void NotifyEndApplication() PopRunspace Method void PopRunspace(), void IHostSupportsInteractiveSession.PopRunspace() PushRunspace Method void PushRunspace(runspace runspace), void IHostSupportsInteractiveSession.PushRunspace(runspace runspace) SetShouldExit Method void SetShouldExit(int exitCode) ToString Method string ToString() CurrentCulture Property cultureinfo CurrentCulture {get;} CurrentUICulture Property cultureinfo CurrentUICulture {get;} DebuggerEnabled Property bool DebuggerEnabled {get;set;} InstanceId Property guid InstanceId {get;} IsRunspacePushed Property bool IsRunspacePushed {get;} Name Property string Name {get;} PrivateData Property psobject PrivateData {get;} Runspace Property runspace Runspace {get;} UI Property System.Management.Automation.Host.PSHostUserInterface UI {get;} Version Property version Version {get;}
OK, there's some interesting bits there, but the one that looks most promising is UI. So:
$host.UI | Get-Member TypeName: System.Management.Automation.Internal.Host.InternalHostUserInterface Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() Prompt Method System.Collections.Generic.Dictionary[string,psobject] Prompt(string caption, string message, System.Collection... PromptForChoice Method int PromptForChoice(string caption, string message, System.Collections.ObjectModel.Collection[System.Management... PromptForCredential Method pscredential PromptForCredential(string caption, string message, string userName, string targetName), pscredent... ReadLine Method string ReadLine() ReadLineAsSecureString Method securestring ReadLineAsSecureString() ToString Method string ToString() Write Method void Write(string value), void Write(System.ConsoleColor foregroundColor, System.ConsoleColor backgroundColor, ... WriteDebugLine Method void WriteDebugLine(string message) WriteErrorLine Method void WriteErrorLine(string value) WriteInformation Method void WriteInformation(System.Management.Automation.InformationRecord record) WriteLine Method void WriteLine(), void WriteLine(string value), void WriteLine(System.ConsoleColor foregroundColor, System.Cons... WriteProgress Method void WriteProgress(long sourceId, System.Management.Automation.ProgressRecord record) WriteVerboseLine Method void WriteVerboseLine(string message) WriteWarningLine Method void WriteWarningLine(string message) RawUI Property System.Management.Automation.Host.PSHostRawUserInterface RawUI {get;} SupportsVirtualTerminal Property bool SupportsVirtualTerminal {get;}
Hmmm. Even more interesting stuff. I can tell I'm going to be doing some poking around in here! But, for our purposes, let's take a look at RawUI.
That looks the most promising:
$host.UI.RawUI | Get-Member TypeName: System.Management.Automation.Internal.Host.InternalHostRawUserInterface Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) FlushInputBuffer Method void FlushInputBuffer() GetBufferContents Method System.Management.Automation.Host.BufferCell[,] GetBufferContents(System.Management.Automation.Host.Rectangle r) GetHashCode Method int GetHashCode() GetType Method type GetType() LengthInBufferCells Method int LengthInBufferCells(string str), int LengthInBufferCells(string str, int offset), int LengthInBufferCells(cha... NewBufferCellArray Method System.Management.Automation.Host.BufferCell[,] NewBufferCellArray(string[] contents, System.ConsoleColor foregro... ReadKey Method System.Management.Automation.Host.KeyInfo ReadKey(System.Management.Automation.Host.ReadKeyOptions options), Syst... ScrollBufferContents Method void ScrollBufferContents(System.Management.Automation.Host.Rectangle source, System.Management.Automation.Host.C... SetBufferContents Method void SetBufferContents(System.Management.Automation.Host.Coordinates origin, System.Management.Automation.Host.Bu... ToString Method string ToString() BackgroundColor Property System.ConsoleColor BackgroundColor {get;set;} BufferSize Property System.Management.Automation.Host.Size BufferSize {get;set;} CursorPosition Property System.Management.Automation.Host.Coordinates CursorPosition {get;set;} CursorSize Property int CursorSize {get;set;} ForegroundColor Property System.ConsoleColor ForegroundColor {get;set;} KeyAvailable Property bool KeyAvailable {get;} MaxPhysicalWindowSize Property System.Management.Automation.Host.Size MaxPhysicalWindowSize {get;} MaxWindowSize Property System.Management.Automation.Host.Size MaxWindowSize {get;} WindowPosition Property System.Management.Automation.Host.Coordinates WindowPosition {get;set;} WindowSize Property System.Management.Automation.Host.Size WindowSize {get;set;} WindowTitle Property string WindowTitle {get;set;}
BINGO! I see BufferSize and WindowSize, and I know from the GUI Properties page that those are the relevant settings, but just to verify:
$host.UI.RawUI.BufferSize | Get-Member TypeName: System.Management.Automation.Host.Size Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() Height Property int Height {get;set;} Width Property int Width {get;set;} $host.UI.RawUI.WindowSize | Get-Member TypeName: System.Management.Automation.Host.Size Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() Height Property int Height {get;set;} Width Property int Width {get;set;}
And there we have it. Both of them can be retrieved and set. So, I came up with a little script, Set-myConSize, that lets me restore the window to its default size, or set it to a new size if I'm doing something that needs a bit of window size tweaking.
<# .Synopsis Resets the size of the current console window .Description Set-myConSize resets the size of the current console window. By default, it sets the windows to a height of 40 lines, with a 3000 line buffer, and sets the the width and width buffer to 120 characters. .Example Set-myConSize Restores the console window to 120x40 .Example Set-myConSize -Height 30 -Width 180 Changes the current console to a height of 30 lines and a width of 180 characters. .Parameter Height The number of lines to which to set the current console. The default is 40 lines. .Parameter Width The number of characters to which to set the current console. Default is 120. Also sets the buffer to the same value .Inputs [int] [int] .Notes Author: Charlie Russel Copyright: 2017 by Charlie Russel : Permission to use is granted but attribution is appreciated Initial: 28 April, 2017 (cpr) ModHist: : #> [CmdletBinding()] Param( [Parameter(Mandatory=$False,Position=0)] [int] $Height = 40, [Parameter(Mandatory=$False,Position=1)] [int] $Width = 120 ) $Console = $host.ui.rawui $Buffer = $Console.BufferSize $ConSize = $Console.WindowSize # If the Buffer is wider than the new console setting, first reduce the buffer, then do the resize If ($Buffer.Width -gt $Width ) { $ConSize.Width = $Width $Console.WindowSize = $ConSize } $Buffer.Width = $Width $ConSize.Width = $Width $Buffer.Height = 3000 $Console.BufferSize = $Buffer $ConSize = $Console.WindowSize $ConSize.Width = $Width $ConSize.Height = $Height $Console.WindowSize = $ConSize
One quick comment on this script -- you can't set the BufferSize to smaller than the current WindowSize. With a Height buffer set to 3,000, that's not likely to be a problem, but if you don't want scroll bars on the bottom of your console windows (and you do NOT, trust me!), then you need the console WindowSize.Width to be the same as the BufferSize.Width. So if your reducing, you need to change the WindowSize first, then you can reduce the BufferSize. If you're increasing width, you need to do the buffer first.
Finally, I set an alias in my $Profile:
Set-Alias -Name Resize -Value Set-myConSize
Configuring Windows Server 2016 Core with and for PowerShell
I know I owe you more on creating a lab with PowerShell, and I'll get to that in a few days. But having just set up a new domain controller running Server 2016 Core, I thought I'd include a couple of tricks I worked through to make your life a little easier if you choose to go with core.
First: Display Resolution -- the default window for a virtual machine connected with a Basic Session in VMConnect is 1024x768. Which is just too small. So, we need to change that. Now in the full Desktop Experience, you'd right click and select Display Resolution, but that won't work in Server Core, obviously. Instead we have PowerShell. Of course. The command to set the display resolution to 1600x900 is:
Set-DisplayResolution -Width 1600 -Height 900
This will accept a -Force parameter if you don't like being prompted. A key point, however, is that it ONLY accepts supported resolutions. For a Hyper-V VM, that means one of the following resolutions:
1920x1080 1600x1050 1600x1200 1600x900 1440x900 1366x768 1280x1024 1280x800 1280x720 1152x864 1024x768 800x600
Now that we have a large enough window to get something done, start PowerShell with the Start PowerShell (that space is on purpose, remember we're still in a cmd window.) But don't worry, we'll get rid of that cmd window shortly.
Now that we have a PowerShell window, you can set various properties of that window by using any of the tricks I've shown before, such as Starting PowerShell Automatically which sets the Run key to start PowerShell for the current user on Login with:
New-ItemProperty HKCU:\Software\Microsoft\Windows\CurrentVersion\Run ` -Name "Windows PowerShell" ` -Value "C:\Windows\system32\WindowsPowerShell\v1.0\PowerShell.exe"
I also showed you how to set the PowerShell window size, font, etc in Starting PowerShell Automatically Revisited. And, of course, you can set the PowerShell window colour and syntax highlighting colours as described in Setting Console Colours. Of course, all my $Profile tricks work as well, so check those out.
So, now that we've configured the basics of our PowerShell windows, let's set PowerShell to replace cmd as the default console window. To do that, use the Set-ItemProperty cmdlet to change the WinLogon registry key:
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' ` -Name Shell ` -Value 'PowerShell.exe -NoExit'
Viola! Now, when we log on to our Server Core machine, it will automatically open a pair of PowerShell windows, one from WinLogon registry key and one from the Run registry key.
Starting PowerShell Automatically – Revisited
A few months ago, I posted a quick blog on how I set PowerShell to start automatically when I log in. Well, it occurred to me that I should extend that to show you how I set the console window parameters so that my PowerShell windows are the right size and font, since on all my systems I use the same basic settings (except for my Surface Book with the 4K addon monitor -- my old eyes need a bit bigger on that, so I adjusted the values accordingly.)
The function I use to set the console size, font and font weight is: (FontSize here translates to 18 point on my FullHD systems)
Function Set-myDefaultFont () { $64bit = "HKCU:\Console\%SystemRoot%_System32_WindowsPowerShell_v1.0_powershell.exe" $Default = "HKCU:\Console" Set-ItemProperty -Path $Default -Name FaceName -Value "Consolas" Set-ItemProperty -Path $Default -Name FontSize -Type DWord -Value 0x00120000 Set-ItemProperty -Path $Default -Name FontWeight -Type DWord -Value 0x000002bc Set-ItemProperty -Path $Default -Name FontFamily -Type DWord -Value 0x00000036 Set-ItemProperty -Path $Default -Name QuickEdit -Type DWord -Value 0x00000001 Set-ItemProperty -Path $64bit -Name FaceName -Value "Consolas" Set-ItemProperty -Path $64bit -Name FontSize -Type DWord -Value 0x00120000 Set-ItemProperty -Path $64bit -Name FontWeight -Type DWord -Value 0x000002bc Set-ItemProperty -Path $64bit -Name FontFamily -Type DWord -Value 0x00000036 Set-ItemProperty -Path $64bit -Name QuickEdit -Type DWord -Value 0x00000001 }
That's all there is to it. I've added this function to the Set-myPowerShellStart.ps1 script, and I call that whenever I create or log onto a new system. If you prefer different sizes or a different font, simply change the values to match what you need. For example, if you want a 16 point font, try a value of 0x00100000.
ETA: Fixed value of FontSize.
Testing for Location on a Laptop
On my laptops, I have a different set of drive maps when I'm at home, or on the road. At home, I map to various local domain resources, but when I'm on the road, those resources aren't available and I need to create "local" maps to versions that I can sync up to those domain resources when I get back home. But how to know where I am? Use PowerShell's Test-Connection, of course, the PowerShell version of "ping".
The problem is further complicated by the dual nature of my home network - I have two different subnets, and the resources could be reachable on either, depending on the wireless network to which I'm connected.
So, how to handle? I've chosen to use the logical OR operator for this test. I could have used an if {} elseif {} construction, but that seemed clunky, so I went with a simple test:
$AtHome= ((test-connection 192.168.16.2 ` -Count 1 ` -TimeToLive 1 ` -quiet) ` -OR (Test-Connection 192.168.17.2 ` -count 1 ` -TimeToLive 1 ` -quiet) )
The test tells me if my domain controller is reachable on either of the subnets used. Because PowerShell uses shortcut processing for logical operators, if the test succeeds at 192.168.16.2, it immediately returns $True to the $AtHome variable and doesn't bother processing the second test. But if it fails the first test, it then tries the second subnet. If it can reach 192.168.17.2, it sets $AtHome to $True. If not, it sets $AtHome to $False.
Now, when I call my drive mapping script, and I pass it the result of $AtHome. That script knows to modify the behaviour based on the Boolean value of $AtHome...
Map-myDrive -SMB -AtHome $AtHome -Force
Mapping Drives Revisited
In my old drive mapping post, I was forced to do some fairly ugly stuff because I had to call the old net use command. Yech. Eventually, we got New-PSDrive, and that helped, but in PowerShell v5 (Windows 10's version), we get New-SmbMapping and it actually works. (New-SmbMapping was added earlier, but there were issues. Those appear to be resolved in the final version of v5.)
When New-PSDrive finally had persistent drive mappings, I replaced my my old MapDrives.cmd file with a new MapDrives.ps1 that used the New-PSDrive syntax:
New-PSDrive -Name I -root \\srv2\Install -Scope Global -PSProv FileSystem -Persist
A bit awkward, but it works. However, it sometimes ran into problems with drives that were mapped with net use, so I was glad when we finally got a useful version of New-SmbMapping. Now the syntax for mapping my I: drive to \\srv1\install is:
New-SmbMapping -LocalPath I: -RemotePath \\srv2\Install -Persistent $True
Great. But it doesn't have a -Force parameter, so I can't tell it to override any maps that already exist. That requires cleaning up the old maps before I make new ones. For that, we have Remove-SmbMapping and Remove-PsDrive.
function Remove-myMaps () { $DriveList = (Get-SmbMapping).LocalPath write-verbose "Removing drives in DriveList: $drivelist" Foreach ($drive in $DriveList) { $psDrive = $drive -replace ":" #remove unwanted colon from PSDrive name Write-Verbose "Running Remove-SmbMapping -LocalPath $Drive -Force -UpdateProfile" Remove-SmbMapping -LocalPath $Drive -Force -UpdateProfile If ( (Get-PSDrive -Name $psDrive) 2>$Null ) { Write-Verbose "Running Remove-PsDrive -Name $psDrive -Force " Remove-PSDrive -Name $psDrive -Force } } write-host " " $DriveList = (Get-SMBMapping).LocalPath Write-Verbose "The drive list is now: $DriveList" }
As you might have noticed, PsDrives don't have a colon, and SmbMapping drives do. But PowerShell gives us the useful -replace operator, allowing us to simply remove the stray colon from the drive letter that SmbMapping has.
So, here's the entire script. Feel free to use it as the basis for your own mappings, but please, respect the copyright and don't republish it but link to here instead. Thanks. :)
Charlie.
<# .Synopsis Maps network drives to drive letters .Description Map-myDrive is used to map network resources to local drive letters. It can use SmbMapping or PsDrive to do the mapping, but defaults to simple PsDrives for historical reasons. (SmbMapping was buggy when it was first introduced!) .Example Map-myDrive Performs a standard drive mapping on the local machine using New-PsDrive syntax. .Example Map-MyDrive -SMB Performs a standard drive mapping on the local machine using New-SmbMapping syntax. .Example Map-MyDrive -SMB -Force Performs a standard drive mapping on the local machine using New-SmbMapping syntax, and forces an unmapping of any existing drive maps before remapping. .Example Map-MyDrives -SMB -Force -AtHome $False Performs a standard drive mapping on the local machine using New-SmbMapping syntax, and forces an unmapping of any existing drive maps before remapping. The script assumes that it is NOT being run in my home domain, and therefore does only local mappings. .Parameter SMB When set, Map-myDrive uses SMB syntax to do the drive mappings .Parameter Force When set, Map-myDrive completely unmaps any existing drive mappings, and then remaps them. .Parameter AtHome When True, Map-myDrive assumes that it has connectivity to the home domain resources. When False, it assumes no home domain resources are available and maps to local shares. .Inputs [switch] [switch] [switch] [Boolean] .Notes Author: Charlie Russel Copyright: 2016 by Charlie Russel : Permission to use is granted but attribution is appreciated Initial: 10 June, 2012 ModHist: 26 June, 2012 A single, cleaner, call to GWMI : 26 April,2015 Cleaned up smb unmapping failures, and added Verbose : 27 June, 2015 New unmapping function : 04 Sept, 2016 Added AtHome Boolean to override environment, Force switch to force unmapping/remapping : #> [CmdletBinding(SupportsShouldProcess=$True)] Param ([Parameter(Mandatory=$false)][Switch]$SMB, [Parameter(Mandatory=$false)][Switch]$Force, [Parameter(Mandatory=$false)][Boolean]$AtHome=$True) # Write-Verbose "Running mapdrives.ps1 with SMB set to $SMB" Write-Verbose "Athome is set to $AtHome" Write-Verbose "Force is $Force" $Psh = Get-Process PowerShell # Start by checking for mapped drives $DriveList = $Null $PSDriveList = $Null #$DriveList = Get-WMIObject Win32_LogicalDisk | Where-Object { $_.DriveType -eq 4 } $DriveList = (Get-SMBMapping).LocalPath write-verbose "The DriveList is: $DriveList" if ($DriveList) { $PSDriveList = $DriveList -replace ":" write-verbose "The PSDrivelist is: $PSDriveList" } # Unmap any lingering ones Function Remove-myMaps () { write-verbose "Removing drives in DriveList: $drivelist" Foreach ($drive in $DriveList) { $psDrive = $drive -replace ":" #remove unwanted colon from PSDrive name Write-Verbose "Running Remove-SmbMapping -LocalPath $Drive -Force -UpdateProfile" Remove-SmbMapping -LocalPath $Drive -Force -UpdateProfile If ( (Get-PSDrive -Name $psDrive) 2>$Null ) { Write-Verbose "Running Remove-PsDrive -Name $psDrive -Force " Remove-PSDrive -Name $psDrive -Force } } write-host " " $DriveList = (Get-SMBMapping).LocalPath Write-Verbose "The drive list is now: $DriveList" } Function Map-myPSDrive () { New-PSDrive -Name I -root \\server\Install -Scope Global -PSProv FileSystem -Persist New-PSDrive -Name J -root \\server\Download -Scope Global -PSProv FileSystem -Persist New-PSDrive -Name S -root \\server\SharedDocs -Scope Global -PSProv FileSystem -Persist New-PSDrive -Name W -root \\server\Working -Scope Global -PSProv FileSystem -Persist New-PSDrive -Name H -root \\server2\Download -Scope Global -PSProv Filesystem -Persist New-PSDrive -Name K -root \\server2\Kindle -Scope Global -PSProv FileSystem -Persist New-PSDrive -Name M -root \\server2\Music -Scope Global -PSProv FileSystem -Persist New-PSDrive -Name N -root \\server2\Audible -Scope Global -PSProv FileSystem -Persist New-PSDrive -Name P -root \\server2\Pictures -Scope Global -PSProv FileSystem -Persist New-PSDrive -Name T -root \\labserver\Captures\70-742 -Scope Global -PSProv FileSystem -Persist } # Map using SMBMapping. It's more robust Function New-mySMBMaps () { New-SmbMapping -LocalPath I: -RemotePath \\server\Install -Persistent $True New-SmbMapping -LocalPath J: -RemotePath \\server\Download -Persistent $True New-SmbMapping -LocalPath S: -RemotePath \\server\SharedDocs -Persistent $True New-SmbMapping -LocalPath W: -RemotePath \\server\Working -Persistent $True New-SmbMapping -LocalPath H: -RemotePath \\server2\Downloads -Persistent $True New-SmbMapping -LocalPath K: -RemotePath \\server2\Kindle -Persistent $True New-SmbMapping -LocalPath M: -RemotePath \\server2\Music -Persistent $True New-SmbMapping -LocalPath N: -RemotePath \\server2\Audible -Persistent $True New-SmbMapping -LocalPath P: -RemotePath \\server2\Pictures -Persistent $True New-SmbMapping -LocalPath T: -RemotePath \\labserver\Captures\70-742 -Persistent $True } Function Map-myLocal () { New-SmbMapping -LocalPath K: -RemotePath \\localhost\Kindle -Persistent $False New-SmbMapping -LocalPath T: -RemotePath \\localhost\Captures\70-742 -Persistent $False } if(! $PSDriveList ) { if ($Force) { Remove-myMaps } Write-Verbose "SMB is set to $SMB" if ($SMB) { If ($AtHome) { New-mySMBMaps } else { Map-myLocal } } else { If ($AtHome) { Map-myPSDrive } else { Map-myLocal } } } else { # $Psh.count is the number of open PowerShell windows. I know that if it's less than or equal # to 2, then I haven't yet mapped the drives on both limited user and administrative windows. # Therefore, we need to mapdrives here. Or, if I have run this with a -Force command, obviously. if (($Psh.count -le 2) -OR ($Force)) { Remove-myMaps Write-Verbose "SMB is set to $SMB" if ($SMB) { If ($AtHome) { New-mySMBMaps } else { Map-myLocal } } else { If ($AtHome) { Map-myPSDrive } else { Map-myLocal } } } }
Setting Console Colours
As I described in my previous post, I always open both an admin and non-admin PowerShell window when I log on to a computer. To tell the two apart, I set the background colour of the Admin window to dark red, with white text, and the non-admin window to a white background with dark blue text. The result is clearly and immediately different, warning me when I'm running as an administrator. To do that, I use the following:
$id = [System.Security.Principal.WindowsIdentity]::GetCurrent() $p = New-Object system.security.principal.windowsprincipal($id) # Find out if we're running as admin (IsInRole). # If we are, set $Admin = $True. if ($p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)){ $Admin = $True } else { $Admin = $False } if ($Admin) { $effectivename = "Administrator" $host.UI.RawUI.Backgroundcolor="DarkRed" $host.UI.RawUI.Foregroundcolor="White" clear-host } else { $effectivename = $id.name $host.UI.RawUI.Backgroundcolor="White" $host.UI.RawUI.Foregroundcolor="DarkBlue" clear-host }
That works great for older versions of the Windows PowerShell console, but starting with PowerShell v5, that can have an unintended side effect. In PowerShell v5, we now have PSReadLine that does context sensitive colouration of the command line. But the default PowerShell console has a dark blue background, with white text. And when I changed the background colour of my non-admin PowerShell window to white, it gets a little hard to read!! So, to fix that, I use Set-PSReadLineOption to change the various kinds of context sensitive colour changes to something that works with a light background. We don't want to do that for the dark red background of the Admin window, so we'll need to check which colour we are and adjust accordingly.
First, get the current colour:
$pData = (Get-Host).PrivateData $curForeground = [console]::ForegroundColor $curBackground = [console]::BackgroundColor
You'll only want to configure the context sensitive colouring options if you're running on Windows 10 or Server 2016. Prior versions of Windows didn't have the new system console that comes with Windows 10. So you'll want to check that the build number is > 10240
$Build = (Get-WmiObject Win32_OperatingSystem).BuildNumber
If $Build -ge 10240, then set the various context sensitive tokens to work with the colour we have.
# PowerShell v5 uses PSReadLineOptions to do syntax highlighting. # Base the color scheme on the background color If ( $curBackground -eq "White" ) { Set-PSReadLineOption -TokenKind None -ForegroundColor DarkBlue -BackgroundColor White Set-PSReadLineOption -TokenKind Comment -ForegroundColor DarkGray -BackgroundColor White Set-PSReadLineOption -TokenKind Keyword -ForegroundColor DarkGreen -BackgroundColor White Set-PSReadLineOption -TokenKind String -ForegroundColor Blue -BackgroundColor White Set-PSReadLineOption -TokenKind Operator -ForegroundColor Black -BackgroundColor White Set-PSReadLineOption -TokenKind Variable -ForegroundColor DarkCyan -BackgroundColor White Set-PSReadLineOption -TokenKind Command -ForegroundColor DarkRed -BackgroundColor White Set-PSReadLineOption -TokenKind Parameter -ForegroundColor DarkGray -BackgroundColor White Set-PSReadLineOption -TokenKind Type -ForegroundColor DarkGray -BackgroundColor White Set-PSReadLineOption -TokenKind Number -ForegroundColor Red -BackgroundColor White Set-PSReadLineOption -TokenKind Member -ForegroundColor DarkBlue -BackgroundColor White $pData.ErrorForegroundColor = "Red" $pData.ErrorBackgroundColor = "Gray" $pData.WarningForegroundColor = "DarkMagenta" $pData.WarningBackgroundColor = "White" $pData.VerboseForegroundColor = "DarkYellow" $pData.VerboseBackgroundColor = "DarkCyan" } elseif ($curBackground -eq "DarkRed") { Set-PSReadLineOption -TokenKind None -ForegroundColor White -BackgroundColor DarkRed Set-PSReadLineOption -TokenKind Comment -ForegroundColor Gray -BackgroundColor DarkRed Set-PSReadLineOption -TokenKind Keyword -ForegroundColor Yellow -BackgroundColor DarkRed Set-PSReadLineOption -TokenKind String -ForegroundColor Cyan -BackgroundColor DarkRed Set-PSReadLineOption -TokenKind Operator -ForegroundColor White -BackgroundColor DarkRed Set-PSReadLineOption -TokenKind Variable -ForegroundColor Green -BackgroundColor DarkRed Set-PSReadLineOption -TokenKind Command -ForegroundColor White -BackgroundColor DarkRed Set-PSReadLineOption -TokenKind Parameter -ForegroundColor Gray -BackgroundColor DarkRed Set-PSReadLineOption -TokenKind Type -ForegroundColor Magenta -BackgroundColor DarkRed Set-PSReadLineOption -TokenKind Number -ForegroundColor Yellow -BackgroundColor DarkRed Set-PSReadLineOption -TokenKind Member -ForegroundColor White -BackgroundColor DarkRed $pData.ErrorForegroundColor = "Yellow" $pData.ErrorBackgroundColor = "DarkRed" $pData.WarningForegroundColor = "Magenta" $pData.WarningBackgroundColor = "DarkRed" $pData.VerboseForegroundColor = "Cyan" $pData.VerboseBackgroundColor = "DarkRed" } }
Finally, let's make sure that console window is the right size, and while we're at it, set the window title. (This is a workaround for a PITA bug in recent builds of Windows 10/Server 2016 that seems to have problems setting the console window size and keeping it!)
$host.ui.rawui.WindowTitle = $effectivename + "@" + $HostName +" >" $Host.UI.RawUI.WindowSize = New-Object System.Management.Automation.Host.Size(120,40)
Now, with all of this, you have effective, context-sensitive, command-line colouring of your PowerShell windows.
PowerShell v3 – Using PSDrive to Replace Net Use
I routinely have to map drives across domain boundaries, or to/from non-domain and domain machines. In the old days, I used NET USE commands, which were OK, but there were some issues. Besides, it’s time to move to away from legacy commands such as NET. PowerShell v3 includes an updated set of PSDrive cmdlets (Get, New, Remove) that have added the ability to create persistent mappings to a drive letter. Plus, unlike NET USE commands, I can pass a single credential to connect to multiple machines, and prompt for the password. Ah, HA. Now that’s useful. Here’s my drive mapping script for connecting to three different machines with my domain credentials, even though I’m actually connecting from a non-domain joined machine.
# PowerShell script to map drives using New-PSDrive command. # Prompts once for credentials, then uses them. Or so we hope. # # Initial: 10 June, 2012 # # Start by checking for already mapped drives. We’ll use Get-WMIObject to query Win32_LogicalDisk. # A drivetype of 4 means that the drive is a network drive. $NetDrives = Get-WMIObject Win32_LogicalDisk | Where-Object { $_.DriveType -eq 4 } # Check which servers have drives mapped to them. $Srv1Mapped = $NetDrives | Where-Object {$_.ProviderName -match "srv1" } $wssMapped = $NetDrives | Where-Object { $_.ProviderName -match "wss-100" } # Prompt for credentials and store in a variable. $Contoso = Get-Credential -Cred "CONTOSO\Charlie" # Now, map drives based on that credential # First, drives on SRV1. These are general Contoso resources if ($Srv1Mapped ) { Echo "Skipping core maps on SRV1" } else { New-PSDrive -Name I –root \\srv1\install -scope Global -PSProv FileSystem -Cred $Contoso –Persist New-PSDrive -Name J -root \\srv1\Download -scope Global -PSProv FileSystem -Cred $Contoso -Persist } # Now, shared drives for the home resources if ($wssMapped ) { Echo "Skipping Home maps on Windows Storage Server WSS-100" } else { New-PSDrive -Name M -root \\wss-100\Music -scope Global -PSProv FileSystem -Cred $Contoso -Persist New-PSDrive -Name P -root \\wss-100\Pictures -scope Global -PSProv FileSystem -Cred $Contoso -Persist New-PSDrive -Name V -root \\wss-100\Videos -scope Global -PSProv FileSystem -Cred $Contoso -Persist } # Finally, some specialized resources New-PSDrive -Name W -root \\srv1\Working -scope Global -PSProv FileSystem -Cred $Contoso -Persist New-PSDrive -Name U -root \\srv1\Charlie -scope Global -PSProv FileSystem -Cred $Contoso -Persist New-PSDrive -Name Y -root \\hp180-ts-17\RemoteApps -scope Global -PSProv FileSystem -Cred $Contoso -Persist }
There we go, and I can run this from both elevated and standard user PowerShell windows. The best part is, these mapped drives are visible in that PowerShell window, but also in Windows Explorer, and anywhere else I need a mapped drive.
Charlie.
ETA: We've come a long way in Windows PowerShell v5, and there's a better way to do this. See Mapping Drives Revisited.