Willy’s Cave Dwelling Notes #8 – PowerShell

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, https://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:https://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:https://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:https://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:'https://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, https://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 = 'https://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 https://www.newdrp.com.

image


Other Cave Dwelling Notes: