Monthly Archive

Categories

Location, location

Just recently I’ve found my self repeatedly working through a location, location pattern.

cd C:\test\ 
.\hello.ps1 
cd C:\Scripts\

The pattern consists of changing to another folder. Running some code and then changing back to the original folder – assuming you can remember it.

I then remembered the location cmdlets

PS> Get-Command *-Location | ft -a

CommandType Name          Version Source 
----------- ----          ------- ------ 
Cmdlet      Get-Location  3.1.0.0 Microsoft.PowerShell.Management 
Cmdlet      Pop-Location  3.1.0.0 Microsoft.PowerShell.Management 
Cmdlet      Push-Location 3.1.0.0 Microsoft.PowerShell.Management 
Cmdlet      Set-Location  3.1.0.0 Microsoft.PowerShell.Management

The 2 useful ones in this working pattern are Push-Location and Pop-Location

Push-Location adds the current location to the location stack and moves you to a new location if you supply a path

Pop-Location pulls the topmost location from the location stack and moves you to that location

The pattern then becomes

PS> Get-Location

Path 
---- 
C:\Scripts

PS> Push-Location -Path C:\test\ 
PS> Get-Location

Path 
---- 
C:\test

PS> .\hello.ps1 
Hello world! 
PS> Pop-Location

PS> Get-Location

Path 
---- 
C:\Scripts

Saves all that remembering stuff

$using

A comment on yesterday’s post about passing parameters into a script block asked why I hadn’t mention $using

$using allows you to access a local variable in a scriptblock

BUT you need to be careful

PS> $proc = "power*"

Invoke-Command -ScriptBlock { 
   Get-Process -Name $using:proc 
 } 
 A Using variable cannot be retrieved. A Using variable can be used only with 
 Invoke-Command, Start-Job, or InlineScript in the script workflow. When it is 
 used with Invoke-Command, the Using variable is valid only if the script block 
 is invoked on a remote computer. 
 At line:4 char:4 
 +    Get-Process -Name $using:proc 
 +    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException 
    + FullyQualifiedErrorId : UsingWithoutInvokeCommand

What about the call operator?

PS> $proc = "power*"

& { 
   Get-Process -Name $using:proc 
 } 
 A Using variable cannot be retrieved. A Using variable can be used only with 
 Invoke-Command, Start-Job, or InlineScript in the script workflow. When it is 
 used with Invoke-Command, the Using variable is valid only if the script block 
 is invoked on a remote computer. 
 At line:4 char:4 
 +    Get-Process -Name $using:proc 
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException 
    + FullyQualifiedErrorId : UsingWithoutInvokeCommand

And in a job?

PS> $proc = "power*"

Start-Job -ScriptBlock { 
   Get-Process -Name $using:proc 
 }

Id     Name            PSJobTypeName   State         HasMoreData     Location  
 --     ----            -------------   -----         -----------     --------  
 3      Job3            BackgroundJob   Running       True            localhost

PS> Receive-Job -Id 3

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName          
 -------  ------    -----      -----     ------     --  -- -----------          
    510      26    42192      58004       0.55   9172   9 powershell           
    634      30    73112      90028       1.53  14680   9 powershell           
    955      68   161040     159956      12.23   4012   9 powershell_ise

You can’t use $using with the call operator and if you use it with Invoke-Command you have to be accessing a remote computer.

$using with InlineScript is for accessing variables defined elsewhere in the workflow

PowerShell on Linux

An introduction to PowerShell v6 on Windows, mac and Linux is available here

https://blogs.msdn.microsoft.com/powershell/2017/06/09/getting-started-with-powershell-core-on-windows-mac-and-linux/

Well worth a read if you haven’t looked at PowerShell v6 yet

Passing parameters to a script block

Passing parameters to a scriptblock seems to be an issue at the moment.

Consider a simple scriptblock

Invoke-Command -ScriptBlock {Get-Process}

How can you modify that to parameterise the processes that are returned.

Its a two step process. Add a parameter block to your script block and secondly pass the correct values to the scriptblock

Invoke-Command -ScriptBlock {
  param ($proc )
  Get-Process -Name $proc
} -ArgumentList "power*"

If you want to pass an array of values to the scriptblock you have 2 options. First abandon the named parameter

$p = ("power*", "win*")

Invoke-Command -ScriptBlock {
  
  Get-Process -Name $args
} -ArgumentList $p

if you use $args then all arguments are available through the array

Scriptblocks unravel the array of arguments so if you want named parameters then you need to force the scriptblock to accept an array by using the unary comma operator

$p = ("power*", "win*")

Invoke-Command -ScriptBlock {
  param ($proc )
   
  Get-Process -Name $proc
} -ArgumentList (,$p)

Generating passwords

Generating new passwords can be a painful business. There are many ways of accomplishing password generation – depending on your needs.  One suggestion for generating passwords is to use a GUID as the basis of the password

PS> New-Guid

Guid 
---- 
269f328d-b80d-446a-a14c-6197ff1bcc40

You could then remove the hyphens and extract part of the guid

PS> (New-Guid).Tostring() -replace '-' 
c5023096aee24b3ba1d9988ff1c774e4

You need to decide the length of the password.  Guids are 32 characters so make sure you start your extraction in a position that gives room for the length you need

$length = 8

((New-Guid).Tostring() -replace '-').Substring((Get-Random -Minimum 0 -Maximum (31-$length)), $length)

Your results will look like these examples

fbe8e66e 
980d4032 
0341d71f 
6f6478fd 
fbfea1ce 
34694bc6 
62666733 
b1419ac0 
3cf8aa7d

The drawback is that you only have numbers and lower case characters

If you use the Membership class from System.Web you can do this

PS> Add-Type -AssemblyName System.Web 
PS> [System.Web.Security.Membership]::GeneratePassword(8,2) 
1L*q381)

The GeneratePassword method takes 2 arguments – the first is the length of the password and the second is the number of non-alphanumeric characters

If you’re running PowerShell v5 you can utilise the using keyword instead of Add-Type

PS> using assembly System.Web 
PS> [System.Web.Security.Membership]::GeneratePassword(8,1) 
f>jg84XR

Nano server changes

Nano server is the small, really small, footprint install version of Windows Server that was introduced with Server 2016.

It has a limited number of roles available to to install – much like the original version of  Server core.

Recent announcements - https://blogs.technet.microsoft.com/hybridcloud/2017/06/15/delivering-continuous-innovation-with-windows-server/

https://docs.microsoft.com/en-us/windows-server/get-started/nano-in-semi-annual-channel

indicates that Nano server is going to become even smaller (by more than 50%) and dedicated to delivering Containers. The infrastructure related roles will be removed. Nano Server will ONLY be available as a container base OS image

In addition, starting this Autumn, Nano server and  Server Core  will getting 2 feature updates per year.

Deal of the Day – 15 June 2017

My book is Manning’s Deal of the Day  - 15 June 2017:

Half off Learn Hyper-V in a Month of Lunches. Use code dotd061517au at http://bit.ly/2rZXI9x

Sign up for DoD notifications at https://www.manning.com/dotd

Find the logged on user

One method of finding the logged on users is to use CIM

$ComputerName = $env:COMPUTERNAME

Get-CimInstance -ClassName Win32_Process -ComputerName $ComputerName -Filter "Name = 'explorer.exe'" | 
foreach { 
 
 $lguser = Invoke-CimMethod -InputObject $psitem -MethodName GetOwner 
 
 $Properties = @{ 
 ComputerName = $ComputerName 
 User = $lguser.User 
 Domain = $lguser.Domain 
 Time = $User.CreationDate 
 } 
 
 New-Object -TypeName PSObject -Property $Properties 
 }

Get the Win32_Process instances for explorer.exe and foreach of them use the GetOwner method to get the owners names and domain. Create an object and ouput

 

Get-PhysicalDisk options

These are the Get-PhysicalDisk options for identifying the disk you want

-UniqueId <string>

-ObjectId <string>

-FriendlyName <string>

-InputObject <CimInstance#MSFT_PhysicalDisk>

-StorageSubsystem <CimInstance#MSFT_StorageSubsystem>

-StorageEnclosure <CimInstance#MSFT_StorageEnclosure>

-StorageNode <CimInstance#MSFT_StorageNode>

-StoragePool <CimInstance#MSFT_StoragePool>

-VirtualDisk <CimInstance#MSFT_VirtualDisk>

When dealing with disks installed in the machine then the friendly names is the easiest to use

PS> Get-PhysicalDisk | Format-List UniqueId, ObjectId, FriendlyName

UniqueId     : 60022480233DF060FE631B8A4EDD93A0
ObjectId     : {1}\\W510W16\root/Microsoft/Windows/Storage/Providers_v2\SPACES_PhysicalDisk.ObjectId="{1dab9cf6-a1b4-11e6-a890-806e6f6e6963}:PD:{12e941a8-6125-c008-8806-8868642331ef}"
FriendlyName : Msft Virtual Disk

UniqueId     : {d8e80f34-22bc-0a36-b302-d96abe30a6cc}
ObjectId     : {1}\\W510W16\root/Microsoft/Windows/Storage/Providers_v2\SPACES_PhysicalDisk.ObjectId="{1dab9cf6-a1b4-11e6-a890-806e6f6e6963}:PD:{d8e80f34-22bc-0a36-b302-d96abe30a6cc}"
FriendlyName : Samsung SSD 840 PRO Series

String casing

There are times when you may want to change string casing. You have a couple of options.

There are a couple of methods on the string class that you can use to modify the case of a string.

PS> 'aaa'.ToUpper() 
AAA

PS> 'AAA'.ToLower() 
aaa

Alternatively you can use the culture information

PS> (Get-Culture).TextInfo.ToLower('AAA') 
aaa 
 PS> (Get-Culture).TextInfo.ToUpper('aaa') 
 AAA 
 PS> (Get-Culture).TextInfo.ToTitleCase('aaa') 
Aaa

The interesting one is To Titlecase which will capitalise the first letter and make the rest lower case

PS> (Get-Culture).TextInfo.ToTitleCase('aaBaaC') 
Aabaac

At least it does for my culture settings

PS> Get-Culture

LCID             Name             DisplayName 
 ----             ----             ----------- 
 2057             en-GB            English (United Kingdom)

You’ll need to test what it does if you have a different culture setting