Powershell V2 CTP 2 - Where did that object come from ?

One of the biggest features of PS V2 is the ability to do remoting. It’s great to be able to run a ScriptBlock against a bunch of computers.

In the first version of the CTP, one issue I had was that I couldn’t tell which object came from which computer.

It looks like CTP2 has a resolution to this. They tack on a ComputerName property to objects that are returned from a remote connection.

Notice that if I do a get-process on a local computer there is no ComputerName attribute.

image

However, if I get an object back from a remote system I can get a ComputerName attribute.

image

I am using the icm alias for the invoke-command cmdlet against a runspace that has 3 remote computers, Powershell-dev1, Powershell-dev2, and Powershell-dev3.

What I think would be nice is to have ComputerName be a default property that gets displayed automatically when you list a remote object. That way you wouldn’t have to explicitly use select object or format-table/format-list to see the attribute.

The intern gets it !

A couple weeks ago we hired an intern. I recently gave him a task to create a bunch of DSN’s on some servers. I told him to take some extra time and maybe figure out how to script it, and also let him know that I thought all the info was stored in the registry.

He came back into my office  later on in the day with a big smile exclaiming “This is awesome! Did you know you can navigate the registry just like a file system in Powershell ?”

I just love being there in the moment when the light bulb goes on and someone new “gets” Powershell. Its a beautiful thing!

ScriptBlock Parameters

This was another one of those “Ah Hah!” moments with Powershell. Yesterday Jeffrey Snover wrote up a great post showing how to use ScriptBlocks as parameters in a pipeline.

This reminded me of a great podcast where Scott Hanselman interviewed Bruce Payette. Back when I first listened to it, they were talking about ScriptBlock parameters and it went completely over my head.

Now if you couple their discussion on the podcast with Jeffrey’s post, you can come out with some incredibly powerful knowledge and understanding of Powershell.

Also, if you are not listening to Scott’s podcast on a regular basis, I have one question for you. Why the heck not? He covers all kinds of great topics from HDTV to web development.

Open Source Powershell on Mac and Linux

I am really looking forward to coming home and trying this out on my Mac. Igor  Moochnick has built Pash (Posh + Bash).  I can’t wait to check it out. Perhaps I was wrong when I was discussing Text vs Objects.

I am really looking forward to seeing where this goes. Good stuff!

The Powershell Function Directory

Anybody that has been using Powershell has created a function at some point. A function is simply a named ScriptBlock that can accept parameters. Also, you very likely know that there is a Function provider in Powershell. But I am willing to bet a lot of folks haven’t used the provider all that much. If I am wrong, I would love to hear some feedback on the topic.

So here I am going to create a basic function and then we will look at what we can do with the function provider to manipulate it.

   1: 6 >  function foo {“Hello $args”}
   2: 7 >  foo world
   3: Hello world

So now that we have a new function we can cd into the function drive as follows. Notice that now we can get some cool info about the function we just created.

   1: 11 >  cd function:
   2: 12 >  ls foo | fl
   3:  
   4:  
   5: Name        : foo
   6: CommandType : Function
   7: Definition  : “Hello $args”

Since we are in the function directory, we can rename a function quite easily.

   1: 15 >  rename-item foo bar
   2: 16 >  bar world
   3: Hello world
   4:  
   5: 17 >  ls foo
   6: Get-ChildItem : Cannot find path ‘foo’ because it does not exist.
   7: At line:1 char:3
   8: + ls <<<<  foo
   9: 18 >  ls bar | fl
  10:  
  11:  
  12: Name        : bar
  13: CommandType : Function
  14: Definition  : “Hello $args”

There is no longer a function called foo but we do have a function named bar with the exact same definition that foo had originally.

Armed with this information, occasionally I like to take a look at what one of my functions looks like.

To do this all I need is to run get-content on a function, or use the alias cat.

   1: 28 >  cat Function:\Add-Assembly
   2: param($name) return [System.Reflection.Assembly]::LoadWithPartialName($name)

WS-Man, Powershell, and MRTG

There’s a cool site over at http://wsman.msft.net/ that has some examples of using MRTG (Multi Router Traffic Grapher) coupled with Powershell and Win RM.

There are some Powershell scripts up there that show how to access data with WinRM in Powershell and cast it from XML to a rich PS based object.

Take a look!

Enabling WinRM with Powershell

In the v2 CTP drop, there is a script called Configure-WSman.ps1 that does a few things including opening up firewall rules and setting up Windows Remote Management. This script was written by MS to configure all the requirements for remoting with Powershell V2.

I have been using it on a my dev boxes to enable and configure WinRM without PS v2 so I can use WinRS to manage them.

I have been running the script my 2008 server machines and then I use my Vista desktop to manage them.

In order to run a command on a remote server I just need to run the following command

winrs -r:server command

Pretty darn slick.

Also, here is the code for Configure-WSMan.ps1

 

   1: ################################################################
   2: #
   3: # Name: Configure-WSMan
   4: #
   5: # Desc: Configures WSMan to enable PowerShell remoting
   6: #
   7: ################################################################
   8:  
   9: param($action, [switch]$force, [switch]$help)
  10: trap { Write-Host -ForeGroundColor Red “Error: $_”; exit 1 }
  11:  
  12: if ($args -contains “-?” -or $args -contains “/?” -or $help)
  13: {
  14: @”
  15: NAME
  16:     Configure-WSMan
  17:  
  18: SYNOPSIS
  19:     Configures WSMan to enable PowerShell remoting
  20:  
  21: SYNTAX
  22:     Configure-WSMan [-action <actionparam>] [-force] [-help]
  23:  
  24: DETAILED DESCRIPTION
  25:     This script configures WSMan service to enable PowerShell
  26:     remoting. The settings are set to a value with which 
  27:     PowerShell remoting can work without any glitches. Its
  28:     generally recommended not to modify these values that this
  29:     script sets by default. 
  30:     The script makes the following modifications in your system:
  31:           - Maximum shells a remote user can launch on this 
  32:             machine = 16
  33:           - Maximum number of users who can connect concurrently 
  34:             to this machine = 5
  35:           - Maximum memory that can be allocated to a remotely 
  36:             lauched shell (any remotely launched PowerShell) = 150 MB
  37:           - Timeout for client to wait for server response = 3 mins
  38:           - Maximum provider requests that a lauched shell in the
  39:             server will support (any remote PowerShell instance
  40:             launched typically has 6 requests) = 400          
  41:           - Maximum connections on the service = 50
  42:           - Creates a HTTP listener that can bind to incoming requests
  43:             from any IP address
  44:           - Opens port 80 and 443 on your system
  45:           - Creates a default WSMan shell for launching PowerShell
  46:             remotely
  47:  
  48: PARAMETERS
  49:     -action
  50:         Allows the user to specify advanced configuration actions
  51:         CreateShell - creates a new WSMan shell
  52:         SetWSManParameter - sets a WSMan parameter
  53:         GetWSManConfig - gets the current WSMan configuration
  54:     -force
  55:         Do not prompt the user, force configure WSMan
  56:     -help
  57:         Show this help information
  58:  
  59: REMARKS
  60:     Do not modify any of the above settings unless you are
  61:     absolutely sure on what those WSMan settings mean.
  62:     Ensure that on Vista/Longhorn you run this script from an 
  63:     elevated PowerShell prompt
  64: “@
  65: exit 1
  66: }
  67:  
  68: #Globals
  69: $Downlevel = “Downlevel”
  70: $Vista = “Vista”
  71:  
  72: #Error Messages
  73: $ErrorMsgs = @{}
  74: $ErrorMsgs += @{ErrorPort80 =“Error opening port 80 on system. `nCheck if the ‘Windows Firewall/Internet Connection Sharing (ICS)’ service is running using the following command:`n`n`t get-service SharedAccess`n`nIf the service is not running, use the following to start the service:`n`n`t netsh firewall set opmode enable`n “}
  75: $ErrorMsgs += @{ErrorPort443 = “Error opening port 443 on system.`nCheck if the ‘Windows Firewall/Internet Connection Sharing (ICS)’ service is running using the following command:`n`n`t get-service SharedAccess`n`nIf the service is not running, use the following to start the service:`n`n`t netsh firewall set opmode enable`n “}
  76: $ErrorMsgs += @{
  77:     ErrorRestore = “Error restoring default WSMan configuration. Exiting”
  78:     ErrorMaxShellsPerUser = “Error Setting MaxShellsPerUser”
  79:     ErrorMaxConcurrentUsers = “Error setting MaxConcurrentUsers”
  80:     ErrorMaxMemoryPerShellMB = “Error setting MaxMemoryPerShellMB”
  81:     ErrorMaxTimeoutms = “Error setting MaxTimeoutms”
  82:     ErrorMaxProviderRequests = “Error setting MaxProviderRequests”
  83:     ErrorMaxConnections = “Error setting MaxConnections”
  84:     ErrorCreateListener = “Error creating HTTP listener”
  85:     ErrorCreateCustomShell = “Error creating PowerShell custom shell on WSMan”
  86: }
  87:  
  88: $commands =@(
  89:     “winrm set winrm/config/winrs `’@{MaxShellsPerUser=`”16`“}`’”, 
  90:     “winrm set winrm/config/winrs `’@{MaxConcurrentUsers=`”5`“}`’”,
  91:     “winrm set winrm/config/winrs `’@{MaxMemoryPerShellMB=`”150`“}`’”,
  92:     “winrm set winrm/config `’@{MaxTimeoutms=`”180000`“}`’” ,
  93:     “winrm set winrm/config `’@{MaxProviderRequests=`”400`“}`’”, 
  94:     “winrm set winrm/config/Service `’@{MaxConnections=`”50`“}`’”, 
  95:     “winrm create winrm/config/Listener?Address=*+Transport=HTTP” 
  96: )
  97:  
  98: $commanderrors = @(
  99:     $ErrorMsgs["ErrorMaxShellsPerUser"],
 100:     $ErrorMsgs["ErrorConcurrnetUsers"],
 101:     $ErrorMsgs["ErrorMaxMemoryPerShellMB"],
 102:     $ErrorMsgs["ErrorMaxTimeoutms"],
 103:     $ErrorMsgs["ErrorMaxProviderRequests"],
 104:     $ErrorMsgs["ErrorMaxConnections"],
 105:     $ErrorMsgs["ErrorCreateListener"]
 106: )
 107:  
 108:  
 109: # Write-Message
 110: function Write-Message($msg)
 111: {
 112:     Write-Host -ForegroundColor Yellow $msg
 113: }
 114:  
 115: #write-error
 116: function Write-Error($msg)
 117: {
 118:     Write-Host -ForegroundColor Red $msg
 119: }
 120:  
 121: #function fixhash
 122: function fixhash ($h)
 123: {
 124:     $c=0
 125:    @(‘@{’) +
 126:         $(foreach ($k in $h.get_keys())
 127:         { 
 128:             if ($c++) {‘;’}
 129:             “$k=”
 130:            “`”$($h[$k])`“”
 131:         }
 132:     ) +
 133:     “}”
 134: }
 135:  
 136: #Check-Error
 137: function Check-Error ($msg)
 138: {
 139:     if ($lastExitCode -ne 0){
 140:         Write-Error $msg
 141:     }
 142: }
 143:  
 144: #Create-Shell
 145: function Create-Shell
 146: {
 147:     Write-Host -noNewLine “Enter name for Shell (ex: Microsoft.PowerShell) : “
 148:     $name = Read-Host
 149:     Write-Host -noNewLine “Enter full path to server executable`n(ex: c:\windows\system32\WindowsPowerShell\v1.0\PowerShell.exe): “
 150:     $path = Read-Host
 151:     Write-Host -noNewLine “Enter arguments to server executable (optional)`n(ex: -s -nologo): “
 152:     $arguments = Read-Host
 153:     $fixhashparam = @(‘@{’) + $(“Shell=” 
 154:                                 “`”$path`“” 
 155:                                 ‘;’ 
 156:                                 “Arguments=” 
 157:                                 “`”$arguments`“” 
 158:                                 ) + “}”
 159:     winrm create winrm/config/winrs/customremoteshell?uri=http://schemas.microsoft.com/wbem/winrm/1/windows/shell/$name $fixhashparam | out-null
 160:     Check-Error $ErrorMsgs["ErrorCreateCustomShell"]
 161: }
 162:  
 163: #Set winrm parameters
 164: function Set-winrmParameter
 165: {
 166:     $paramname = $args[0]
 167:     $value = $args[1]
 168:  
 169:     $i = -1
 170:     if ($paramname -ieq “maxshellsperuser”){$i = 0}
 171:     if ($paramname -ieq “maxconcurrentusers”){$i = 1}
 172:     if ($paramname -ieq “maxmemorypershellmb”){$i = 2}
 173:     if ($paramname -ieq “maxtimeoutms”){$i = 3}
 174:     if ($paramname -ieq “maxproviderrequests”){$i = 4}
 175:     if ($paramname -ieq “maxconnections”){$i = 5}
 176:     
 177:     if ($i -eq -1){
 178:         Write-Error “Specified parameter:$paramname is either not valid or cannot be modified”
 179:         exit 1
 180:     }
 181:  
 182:     $command = $commands[$i] -replace “[0-9]+”, $value
 183:     Invoke-Expression $command > $null
 184:     Check-Error $commanderrors[$i]    
 185: }
 186:  
 187: #Detect the OS version. Installable varies as per the OS version
 188: $version = [system.environment]::OSVersion.Version
 189: $majorversion = $version.Major
 190: if ($majorversion -lt 6){
 191:    $os = $Downlevel
 192: }
 193: else{
 194:    $os = $vista
 195: }
 196:  
 197: #Check if elevated in case of Vista
 198: if ($os -eq $vista -and !$force){
 199:     #check if user is running elevated
 200:     $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
 201:     $p = New-Object System.Security.Principal.WindowsPrincipal($id)
 202:  
 203:     if (!$p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)){
 204:  
 205:         Write-Error “Configuration on Vista requires that user is elevated. Try Configuration from an elevated prompt”
 206:         exit 1
 207:     }
 208: }
 209:  
 210: #check if winrm service is installed before configuring
 211: if (!(Test-Path HKLM:\SYSTEM\CurrentControlSet\Services\winrm)){
 212:     Write-Error “winrm service is not installed”
 213:     exit 1
 214: }
 215:  
 216: if ($action -match “CreateShell”){
 217:     Create-Shell
 218:     exit 1
 219: }
 220: elseif ($action -match “SetWSManParameter”){
 221:     $WSManparam = Read-Host -prompt “Enter parameter to set”
 222:     $WSManparamvalue = Read-Host -prompt “Enter value”
 223:     Set-WinRMParameter $WSManparam $WSManparamvalue
 224: }
 225: elseif ($action -match “GetWSManConfig”){
 226:     winrm get winrm/config
 227: }
 228: elseif ($action -eq $null){
 229:  
 230:     Write-Message “Configuring WSMan”
 231:     winrm invoke restore winrm/config | out-null
 232:  
 233:     if ($lastexitcode -ne 0){
 234:         Write-Error $ErrorMsgs["ErrorRestore"]
 235:         exit 1
 236:     }
 237:  
 238:     for($i=0; $i -lt $commands.length; $i++){
 239:         Invoke-Expression $commands[$i] | out-null
 240:         Check-Error $commanderrors[$i]
 241:     }
 242:  
 243:     winrm create winrm/config/winrs/customremoteshell?uri=http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Microsoft.PowerShell (fixhash @{Shell=“$env:windir\system32\windowspowershell\v1.0\powershell.exe”;Arguments=“-s -nologo”}) | out-null
 244:     Check-Error $ErrorMsgs["ErrorCreateCustomShell"]
 245:     Write-Message “Configuring WSMan Complete”    
 246:  
 247:     #open network ports as necessary
 248:     Write-Message “Opening port 80 and port 443″
 249:     netsh firewall set portopening TCP 80 HTTP |  out-null 
 250:     Check-Error $ErrorMsgs["ErrorPort80"]
 251:     netsh firewall set portopening TCP 443 HTTPS | out-null
 252:     Check-Error $ErrorMsgs["ErrorPort443"]
 253: }
 254: else{
 255:     Write-Error “Argument $action is not supported”
 256: }
 257:  
 258:  
 259: # SIG # Begin signature block
 260: # MIIkOQYJKoZIhvcNAQcCoIIkKjCCJCYCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
 261: # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
 262: # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUYKlqhAlmtu+VBY1YW8CPVuGX
 263: # 9J+ggh7hMIIEEjCCAvqgAwIBAgIPAMEAizw8iBHRPvZj7N9AMA0GCSqGSIb3DQEB
 264: # BAUAMHAxKzApBgNVBAsTIkNvcHlyaWdodCAoYykgMTk5NyBNaWNyb3NvZnQgQ29y
 265: # cC4xHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEhMB8GA1UEAxMYTWlj
 266: # cm9zb2Z0IFJvb3QgQXV0aG9yaXR5MB4XDTk3MDExMDA3MDAwMFoXDTIwMTIzMTA3
 267: # MDAwMFowcDErMCkGA1UECxMiQ29weXJpZ2h0IChjKSAxOTk3IE1pY3Jvc29mdCBD
 268: # b3JwLjEeMBwGA1UECxMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEwHwYDVQQDExhN
 269: # aWNyb3NvZnQgUm9vdCBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
 270: # ggEKAoIBAQCpAr3BcOY78k4bKJ+XeF4w6qKpjSVf+P6VTKO3/p2iID58UaKboo9g
 271: # MmvRQmR57qx2yVTa8uuchhyPn4Rms8VremIj1h083g8BkuiWxL8tZpqaaCaZ0Dos
 272: # vwy1WCbBRucKPjiWLKkoOajsSYNC44QPu5psVWGsgnyhYC13TOmZtGQ7mlAcMQgk
 273: # FJ+p55ErGOY9mGMUYFgFZZ8dN1KH96fvlALGG9O/VUWziYC/OuxUlE6u/ad6bXRO
 274: # rxjMlgkoIQBXkGBpN7tLEgc8Vv9b+6RmCgim0oFWV++2O14WgXcE2va+roCV/rDN
 275: # f9anGnJcPMq88AijIjCzBoXJsyB3E4XfAgMBAAGjgagwgaUwgaIGA1UdAQSBmjCB
 276: # l4AQW9Bw72lyniNRfhSyTY7/y6FyMHAxKzApBgNVBAsTIkNvcHlyaWdodCAoYykg
 277: # MTk5NyBNaWNyb3NvZnQgQ29ycC4xHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3Jh
 278: # dGlvbjEhMB8GA1UEAxMYTWljcm9zb2Z0IFJvb3QgQXV0aG9yaXR5gg8AwQCLPDyI
 279: # EdE+9mPs30AwDQYJKoZIhvcNAQEEBQADggEBAJXoC8CN85cYNe24ASTYdxHzXGAy
 280: # n54Lyz4FkYiPyTrmIfLwV5MstaBHyGLv/NfMOztaqTZUaf4kbT/JzKreBXzdMY09
 281: # nxBwarv+Ek8YacD80EPjEVogT+pie6+qGcgrNyUtvmWhEoolD2Oj91Qc+SHJ1hXz
 282: # UqxuQzIH/YIX+OVnbA1R9r3xUse958Qw/CAxCYgdlSkaTdUdAqXxgOADtFv0sd3I
 283: # V+5lScdSVLa0AygS/5DW8AiPfriXxas3LOR65Kh343agANBqP8HSNorgQRKoNWob
 284: # ats14dQcBOSoRQTIWjM4bk0cDWK3CqKM09VUP0bNHFWmcNsSOoeTdZ+n0qAwggQS
 285: # MIIC+qADAgECAg8AwQCLPDyIEdE+9mPs30AwDQYJKoZIhvcNAQEEBQAwcDErMCkG
 286: # A1UECxMiQ29weXJpZ2h0IChjKSAxOTk3IE1pY3Jvc29mdCBDb3JwLjEeMBwGA1UE
 287: # CxMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEwHwYDVQQDExhNaWNyb3NvZnQgUm9v
 288: # dCBBdXRob3JpdHkwHhcNOTcwMTEwMDcwMDAwWhcNMjAxMjMxMDcwMDAwWjBwMSsw
 289: # KQYDVQQLEyJDb3B5cmlnaHQgKGMpIDE5OTcgTWljcm9zb2Z0IENvcnAuMR4wHAYD
 290: # VQQLExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xITAfBgNVBAMTGE1pY3Jvc29mdCBS
 291: # b290IEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkC
 292: # vcFw5jvyThson5d4XjDqoqmNJV/4/pVMo7f+naIgPnxRopuij2Aya9FCZHnurHbJ
 293: # VNry65yGHI+fhGazxWt6YiPWHTzeDwGS6JbEvy1mmppoJpnQOiy/DLVYJsFG5wo+
 294: # OJYsqSg5qOxJg0LjhA+7mmxVYayCfKFgLXdM6Zm0ZDuaUBwxCCQUn6nnkSsY5j2Y
 295: # YxRgWAVlnx03Uof3p++UAsYb079VRbOJgL867FSUTq79p3ptdE6vGMyWCSghAFeQ
 296: # YGk3u0sSBzxW/1v7pGYKCKbSgVZX77Y7XhaBdwTa9r6ugJX+sM1/1qcaclw8yrzw
 297: # CKMiMLMGhcmzIHcThd8CAwEAAaOBqDCBpTCBogYDVR0BBIGaMIGXgBBb0HDvaXKe
 298: # I1F+FLJNjv/LoXIwcDErMCkGA1UECxMiQ29weXJpZ2h0IChjKSAxOTk3IE1pY3Jv
 299: # c29mdCBDb3JwLjEeMBwGA1UECxMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEwHwYD
 300: # VQQDExhNaWNyb3NvZnQgUm9vdCBBdXRob3JpdHmCDwDBAIs8PIgR0T72Y+zfQDAN
 301: # BgkqhkiG9w0BAQQFAAOCAQEAlegLwI3zlxg17bgBJNh3EfNcYDKfngvLPgWRiI/J
 302: # OuYh8vBXkyy1oEfIYu/818w7O1qpNlRp/iRtP8nMqt4FfN0xjT2fEHBqu/4STxhp
 303: # wPzQQ+MRWiBP6mJ7r6oZyCs3JS2+ZaESiiUPY6P3VBz5IcnWFfNSrG5DMgf9ghf4
 304: # 5WdsDVH2vfFSx73nxDD8IDEJiB2VKRpN1R0CpfGA4AO0W/Sx3chX7mVJx1JUtrQD
 305: # KBL/kNbwCI9+uJfFqzcs5HrkqHfjdqAA0Go/wdI2iuBBEqg1ahtq2zXh1BwE5KhF
 306: # BMhaMzhuTRwNYrcKoozT1VQ/Rs0cVaZw2xI6h5N1n6fSoDCCBGAwggNMoAMCAQIC
 307: # Ci6rEdxQ/1ydy8AwCQYFKw4DAh0FADBwMSswKQYDVQQLEyJDb3B5cmlnaHQgKGMp
 308: # IDE5OTcgTWljcm9zb2Z0IENvcnAuMR4wHAYDVQQLExVNaWNyb3NvZnQgQ29ycG9y
 309: # YXRpb24xITAfBgNVBAMTGE1pY3Jvc29mdCBSb290IEF1dGhvcml0eTAeFw0wNzA4
 310: # MjIyMjMxMDJaFw0xMjA4MjUwNzAwMDBaMHkxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
 311: # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
 312: # ZnQgQ29ycG9yYXRpb24xIzAhBgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNpZ25pbmcg
 313: # UENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt3l91l2zRTmoNKwx
 314: # 2vklNUl3wPsfnsdFce/RRujUjMNrTFJi9JkCw03YSWwvJD5lv84jtwtIt3913UW9
 315:</