As part of the ALM Ranger Hands-on lab and associated environment setup, we have started using PowerShell as our setup automation framework. PowerShell consists of a CMD-like command-line shell and a scripting language, which seamlessly integrates with environments such as COM, .NET and WMI. In this post I will create a few “getting started notes”, include one of our setup scripts as an example and reference invaluable PowerShell focused material.


Getting Started

To get started you can use get-command to get a list of all the cmdlets and other elements of Windows PowerShell commands. To get additional help, type get-help and the command, followed by an optional –detailed for even more detail and samples.

Let’s peek at get-help, get-help get-command and get/set-alias commands.

  • get-help command [–detailed]
    image
  • get-command
    image
  • set-alias
    Creates or changes an alias for a cmdlet or other command in the current session. To save aliases use export-alias and to import the saved alias use import-alias.
  • get-/set execution policy
    image
    image


Piping

One of the brain twisters (at least for me) and a powerful feature is “piping”, which allows you to build up powerful tasks. As shown below we start with the command, followed by pipeline characters and commands, such as sort and where-object. The shown example gets all child items in the folder c:\Users\willys, sorts the list alphabetically, where the size of the object (file) is greater than 50 kilobytes.

image


Formatting

The pipeline and the powerful features of PowerShell allow us to do some creative formatting of the data we process.

Basic formatting, i.e. Numeric

Basic formatting, i.e. DateTime

Formatting and the Pipeline

Using a Format-Table

Select the relevant columns:

image

By default PowerShell shows the first four values of a property. Use $FormatEnumerationLimit to change the default.

image

Use –Wrap to avoid truncation:

image

Using a Format-List

To switch to a list view, use the Format-List:

image


Magic … Providers

If we call get-psprovider we find more magic in the form of Windows PowerShell providers. These present themselves as drives, allowing us to literally access environments such as WMI, environment variables and the registry like the file system, … magic.

Wish to list all hives in the registry? Simple!

image


Example

The following PowerShell script is one of the Hands-on lab setup scripts. It comes from the TFS Integration Tools (Platform) environment and demonstrates a number of interesting setup automation tasks:

   1: #
   2: # This is sample code only, do not use in production environments
   3: #
   4: # Copyright © Microsoft Corporation.  All Rights Reserved.
   5: # This code released under the terms of the 
   6: # Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)
   7: # This is sample code only, do not use in production environments
   8: #
   9:  
  10: function SetupHOL
  11: {
  12:  $currentLocation = Get-Location
  13:  $tempLocation    = Get-content env:tfspowertooldir
  14:  
  15:  if(!("C:\HOL\TFS Integration Platform\HOLSetup" -eq $currentLocation))
  16:  {
  17:    "Please run script from C:\HOL\TFS Integration Platform\HOLSetup"
  18:    return
  19:  }
  20:   
  21:  # Create logs directory if does not exist
  22:  if ((Test-Path -path c:\HOL\Logs\) -ne $True)
  23:  {
  24:    New-Item c:\HOL\Logs\ -type directory
  25:  }
  26:   
  27:  Set-Location $tempLocation
  28:  
  29:  #Create team project with tfpt
  30:  .\TFPT.EXE createteamproject /settingsfile:'C:\HOL\TFS Integration Platform\HOLSetup\Project_TP-A.XML'
  31:  .\TFPT.EXE createteamproject /settingsfile:'C:\HOL\TFS Integration Platform\HOLSetup\Project_TP-B.XML'
  32:  .\TFPT.EXE createteamproject /settingsfile:'C:\HOL\TFS Integration Platform\HOLSetup\Project_TP-C.XML'
  33:  .\TFPT.EXE createteamproject /settingsfile:'C:\HOL\TFS Integration Platform\HOLSetup\Project_RationalDemo.XML'
  34:  
  35:  $tempLocation    = Get-content env:vs100comntools
  36:  Set-Location $tempLocation
  37:  
  38:  #Create Workspace
  39:  Write-Host -Object:"Create Workspace TP-A"
  40:  ..\IDE\TF.EXE workspace /new TP-A /noprompt /collection:http://localhost:8080/tfs/DefaultCollection
  41:  ..\IDE\TF.EXE workfold  /map $/TP-A 'C:\HOL\TFS Integration Platform\Source Code\Demo\Sandbox-A'     /workspace:TP-A
  42:  ..\IDE\TF.EXE workfold  /unmap $/
  43:  
  44:  Write-Host -Object:"Create Workspace TP-B"
  45:  ..\IDE\TF.EXE workspace /new TP-B /noprompt /collection:http://localhost:8080/tfs/DefaultCollection
  46:  ..\IDE\TF.EXE workfold  /map $/TP-B 'C:\HOL\TFS Integration Platform\Source Code\Demo\Sandbox-B'     /workspace:TP-B
  47:  ..\IDE\TF.EXE workfold  /unmap $/
  48:  
  49:  Write-Host -Object:"Create Workspace TP-C"
  50:  ..\IDE\TF.EXE workspace /new TP-C /noprompt /collection:http://localhost:8080/tfs/DefaultCollection
  51:  ..\IDE\TF.EXE workfold  /map $/TP-C 'C:\HOL\TFS Integration Platform\Source Code\Demo\Sandbox-C'     /workspace:TP-C
  52:  ..\IDE\TF.EXE workfold  /unmap $/
  53:  
  54:  #Create the VC space as per HOL
  55:  Write-Host -Object:"Checkin code and branches"
  56:  ..\IDE\TF.EXE add       'C:\HOL\TFS Integration Platform\Source Code\Demo\Sandbox-A\Main'         /recursive
  57:  ..\IDE\TF.EXE checkin   'C:\HOL\TFS Integration Platform\Source Code\Demo\Sandbox-A\Main'         /comment:'HOL Automated Checkin' /recursive /noprompt
  58:  ..\IDE\TF.EXE branch    'C:\HOL\TFS Integration Platform\Source Code\Demo\Sandbox-A\Main’      ‘$/TP-A/Dev’
  59:  ..\IDE\TF.EXE checkin   'C:\HOL\TFS Integration Platform\Source Code\Demo\Sandbox-A\Dev'         /comment:'HOL Automated Branch' /noprompt
  60:  copy 'C:\HOL\TFS Integration Platform\HOLSetup\Raw\HelloWorldDemo' 'C:\HOL\TFS Integration Platform\Source Code\Demo\Sandbox-A\Dev' -recurse
  61:  ..\IDE\TF.EXE add       'C:\HOL\TFS Integration Platform\Source Code\Demo\Sandbox-A\Dev\HelloWorldDemo'     /recursive
  62:  ..\IDE\TF.EXE checkin   'C:\HOL\TFS Integration Platform\Source Code\Demo\Sandbox-A\Dev'         /comment:'HOL Automated Checkin' /recursive /noprompt
  63:  
  64:  $tempLocation    = Get-content env:tfspowertooldir
  65:  Set-Location $tempLocation
  66:  
  67:  #create workitem
  68:  .\TFPT.EXE workitem /new TP-A\Bug /Fields:"Title=Change the string goodbye world to hello world;Assigned To= Administrator" /collection:'http://localhost:8080/tfs/defaultcollection'
  69:  Set-Location $currentLocation
  70: }
  71:  
  72: #displayWelcome    
  73: SetupHOL

The next sample comes from the Branching Guide and shows a few different ways of dealing with some of the administrative tasks:

   1: #
   2: # This is sample code only, do not use in production environments
   3: #
   4: # Copyright © Microsoft Corporation.  All Rights Reserved.
   5: # This code released under the terms of the 
   6: # Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)
   7: # This is sample code only, do not use in production environments
   8: #
   9:  
  10: function createUser([string]$accountName, [string]$password, [string]$name) 
  11: {  
  12:     # create user
  13:     $computer = [adsi] 'WinNT://localhost'
  14:     $user = $computer.Create('User', $accountName)      
  15:     $user.SetPassword($password)
  16:     $user.SetInfo()         
  17:     $user.FullName = $name
  18:     $user.put('UserFlags',65536 -bor 64)
  19:     $user.SetInfo()         
  20:  
  21:     # add user to Remote desktop group to allow RD access
  22:     net localgroup 'Remote Desktop Users' $accountName /add
  23:     
  24:     # add user to Power Users group to allow them to logon locally
  25:     net localgroup 'Power Users' $accountName /add
  26: }
  27:  
  28: function createSampleAccounts
  29: {
  30:     createUser 'michaf' 'P@ssw0rd' 'Michael Affronti (PM)'
  31:     createUser 'gprist' 'P@ssw0rd' 'Gary Stewart (Dev Lead)'
  32:     createUser 'dorikr' 'P@ssw0rd' 'Doris Krieger (Dev)'
  33:     createUser 'abuobe' 'P@ssw0rd' 'Abu Obeida Bakhach (Build Master)'
  34:     createUser 'chriko' 'P@ssw0rd' 'Christine Koch (Tester)'
  35:     createUser 'chriba' 'P@ssw0rd' 'Chris Barry (Business Stakeholder)'
  36:     createUser 'robiwo' 'P@ssw0rd' 'Robin Wood (End User)'
  37: }
  38:  
  39: function addToTFSGlobal([string]$accountName, [string]$groupname ) 
  40: {  
  41:     $currentLocation = Get-Location
  42:     $tempLocation    = Get-Content env:vs100comntools
  43:     Set-Location $tempLocation
  44:  
  45:     ..\IDE\tfssecurity /g+ $groupname n:$accountName /server:localhost
  46:  
  47:     Set-Location $currentLocation
  48: }
  49:  
  50: function addToTFSProject([string]$accountName, [string]$groupname, [string]$collection = '', [string]$targetProjectName = '') 
  51: {  
  52:     $target = ''
  53:     if ($targetProjectName -ne '')
  54:     {
  55:         $target = '[' +$targetProjectName + ']\' +$groupname
  56:     }
  57:     else
  58:     {
  59:         $target = $groupname
  60:     }
  61:     
  62:     if (!$accountName.Contains('\'))
  63:     {
  64:         $accountName = $hostname + '\' + $accountName
  65:     }
  66:     
  67:     $currentLocation = Get-Location
  68:         $tempLocation    = Get-Content env:vs100comntools
  69:         Set-Location $tempLocation
  70:  
  71:     $argument = 'http://localhost:8080/tfs/' + $collection
  72:     ..\IDE\tfssecurity /g+ $target n:$accountName /collection:$argument /server:$hostname
  73:     
  74:     Set-Location $currentLocation
  75: }
  76:  
  77: function addSampleAccountsToGroups
  78: {    
  79:     addToTFSGlobal  'michaf' 'Team Foundation Administrators'
  80:     addToTFSGlobal  'gprist' 'Team Foundation Administrators'
  81:     # BuildAndDeploy
  82:     addToTFSProject 'michaf' 'Project Administrators'             '/defaultcollection' 'BuildAndDeploy'
  83:     addToTFSProject 'gprist' 'Project Administrators'             '/defaultcollection' 'BuildAndDeploy'
  84:     addToTFSProject 'dorikr' 'Contributors'                     '/defaultcollection' 'BuildAndDeploy'
  85:     addToTFSProject 'abuobe' 'Contributors'                     '/defaultcollection' 'BuildAndDeploy'
  86:     addToTFSProject 'abuobe' 'Builders'                         '/defaultcollection' 'BuildAndDeploy'
  87:     addToTFSProject 'chriko' 'Contributors'                     '/defaultcollection' 'BuildAndDeploy'
  88:     addToTFSProject 'chriba' 'Contributors'                     '/defaultcollection' 'BuildAndDeploy'
  89:     addToTFSProject 'robiwo' 'Readers'                          '/defaultcollection' 'BuildAndDeploy'
  90:     # CustomActivity
  91:     addToTFSProject 'michaf' 'Project Administrators'             '/defaultcollection' 'CustomActivity'
  92:     addToTFSProject 'gprist' 'Project Administrators'             '/defaultcollection' 'CustomActivity'
  93:     addToTFSProject 'dorikr' 'Contributors'                     '/defaultcollection' 'CustomActivity'
  94:     addToTFSProject 'abuobe' 'Contributors'                     '/defaultcollection' 'CustomActivity'
  95:     addToTFSProject 'abuobe' 'Builders'                         '/defaultcollection' 'CustomActivity'
  96:     addToTFSProject 'chriko' 'Contributors'                     '/defaultcollection' 'CustomActivity'
  97:     addToTFSProject 'chriba' 'Contributors'                     '/defaultcollection' 'CustomActivity'
  98:     addToTFSProject 'robiwo' 'Readers'                          '/defaultcollection' 'CustomActivity'
  99: }
 100:  
 101: function setupwebworld
 102: {
 103:     Add-PSSnapin WebAdministration
 104:  
 105:     # Create the needed folder
 106:     New-Item -type directory -path "$env:systemdrive\inetpub\MVCWebs\Dev"
 107:     New-Item -type directory -path "$env:systemdrive\inetpub\MVCWebs\Test"
 108:     New-Item -type directory -path "$env:systemdrive\inetpub\MVCWebs\QA"
 109:  
 110:     # Create the needed web sites
 111:     New-WebSite -Name dev.contoso.com -Port 80 -HostHeader dev.contoso.com -PhysicalPath "$env:systemdrive\inetpub\MVCWebs\Dev" -ApplicationPool "ASP.NET v4.0"
 112:     New-WebSite -Name test.contoso.com -Port 80 -HostHeader test.contoso.com -PhysicalPath "$env:systemdrive\inetpub\MVCWebs\Test" -ApplicationPool "ASP.NET v4.0"
 113:     New-WebSite -Name qa.contoso.com -Port 80 -HostHeader qa.contoso.com -PhysicalPath "$env:systemdrive\inetpub\MVCWebs\QA" -ApplicationPool "ASP.NET v4.0"
 114:  
 115:     # Add host header information to the hosts file
 116:     Add-Content -value "127.0.0.1       dev.contoso.com" -path "C:\Windows\System32\drivers\etc\hosts"
 117:     Add-Content -value "127.0.0.1       test.contoso.com" -path "C:\Windows\System32\drivers\etc\hosts"
 118:     Add-Content -value "127.0.0.1       qa.contoso.com" -path "C:\Windows\System32\drivers\etc\hosts"
 119:  
 120:     # Disable IE phishing settings
 121:     # Set-Itemproperty "HKCU:\Software\Microsoft\Internet Explorer\PhishingFilter" -name Enabled -value 1
 122:  
 123:     # Disable IE Proxy properties (delete AutoConfigProxy value wininet.dll
 124:     Set-Itemproperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -name ProxyEnable -value 0
 125:     Set-Itemproperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -name AutoConfigProxy -value ""
 126:     Set-Itemproperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections" -name DefaultConnectionSettings -value ""
 127:  
 128:     # Add URL's to the local intranet zone
 129:     New-Item -type directory -path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\contoso.com"
 130:     New-Item -type directory -path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\contoso.com\dev"
 131:     New-Item -type directory -path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\contoso.com\test"
 132:     New-Item -type directory -path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\contoso.com\qa"
 133:     New-Itemproperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\contoso.com\dev" -name "http" -value 1 -propertytype DWord
 134:     New-Itemproperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\contoso.com\test" -name "http" -value 1 -propertytype DWord
 135:     New-Itemproperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\contoso.com\qa" -name "http" -value 1 -propertytype DWord
 136: }
 137:  
 138: function SetupHOL
 139: {
 140:  $currentLocation = Get-Location
 141:  $tempLocation    = Get-Content env:tfspowertooldir
 142:  
 143:  if(!("C:\HOL\BuildCustomization_ATE\HOLSetup" -eq $currentLocation))
 144:  {
 145:    "Please run script from C:\HOL\BuildCustomization_ATE\HOLSetup"
 146:    return
 147:  }
 148:  
 149:  # Create logs directory if does not exist
 150:  if ((Test-Path -path c:\HOL\Logs\) -ne $True)
 151:  {
 152:    New-Item c:\HOL\Logs\ -type directory
 153:  }
 154:  
 155:  Set-Location $tempLocation
 156:  
 157:  #Create team project with tfpt
 158:  .\TFPT.EXE createteamproject /settingsfile:'c:\HOL\BuildCustomization_ATE\HOLSetup\MyHOLs.XML'
 159:  
 160:  #Create team project with tfpt
 161:  .\TFPT.EXE createteamproject /settingsfile:'c:\HOL\BuildCustomization_ATE\HOLSetup\BuildAndDeploy.XML'
 162:  
 163:  #Create team project with tfpt
 164:  .\TFPT.EXE createteamproject /settingsfile:'c:\HOL\BuildCustomization_ATE\HOLSetup\CustomActivity.XML' 
 165:  
 166:  #Create team project with tfpt
 167:  .\TFPT.EXE createteamproject /settingsfile:'C:\HOL\BuildCustomization_ATE\HOLSetup\BRDLite.XML'
 168:  
 169:  Set-Location $currentLocation
 170:  
 171:  createSampleAccounts
 172:  addSampleAccountsToGroups
 173:  setupwebworld
 174: }
 175:  
 176: SetupHOL

Upcoming fix

Mike Douglas has recently helped us troubleshoot our scripts against Windows Server 2012 and has suggested the use of the computer name instead of localhost.  It seems low risk.

Existing Statement:

$computer = [adsi] 'WinNT://localhost'

Updated Statement:

$ComputerName = $env:ComputerName

$computer = [ADSI]"WinNT://$ComputerName"

Anything else we should be penciling onto our backlog?


Quick Reference Posters

Finally the posters and references.

Two posters we created a long time ago for the South-African community are hosted by the D*RP http://www.newdrp.com.

image


Other Cave Dwelling Notes: