PowerShell for Windows Admins


June 12, 2016  4:22 AM

Months

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

It would be nice to be able to do this:

PS>  Get-Date -Day 25 -Month December -Year 2016
Get-Date : Cannot bind parameter ‘Month’. Cannot convert value “December” to type “System.Int32”.
Error: “Input string was not in a correct format.”
At line:1 char:25
+ Get-Date -Day 25 -Month December -Year 2016
+                         ~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [Get-Date], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.GetDateCo
mmand

You have to do this:

PS>  Get-Date -Day 25 -Month 12 -Year 2016

25 December 2016 11:00:56

The time is set to current time automatically.

One of the gaps in .NET is an enum for the months of the year. You get an enum for days of the week:

PS>  [System.DayOfWeek]::Friday
Friday

PS>  0..6 | foreach {[System.DayOfWeek]$psitem}
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday

PowerShell v5 gives you the capability to make your own enums:

enum MonthsOfYear {
January = 1
February = 2
March = 3
April = 4
May = 5
June = 6
July = 7
August = 8
September = 9
October = 10
November = 11
December = 12
}

PS>  [MonthsOfYear]10
October
PS>  [MonthsOfYear]::April
April

PS>  1..12 | foreach {[MonthsoFYear]$PSItem}
January
February
March
April
May
June
July
August
September
October
November
December

PS>  [System.Enum]::GetNames([MonthsOfYear])
January
February
March
April
May
June
July
August
September
October
November
December

Now you can use the name of the month

PS>  Get-Date -Day 25 -Month ([MonthsOfYear]::December) -Year 2016

25 December 2016 11:19:38

Enums are one of those pieces of functionality that you don’t necessarily think about but are very useful. The ability to create your own in PowerShell is very useful and will become more widespread as people realise its available.

June 11, 2016  4:28 AM

WMI classes and Storage cmdlets

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
CIM, Powershell, WMI

There is a hierarchy of objects to work through when dealing with disks

First you have the physical disk

PS>  Get-CimInstance -ClassName Win32_DiskDrive | fl

Partitions : 5
DeviceID   : \\.\PHYSICALDRIVE0
Model      : HFS256G3AMNB-2200A
Size       : 256052966400
Caption    : HFS256G3AMNB-2200A

A physical disk can have 1 or more partitions:

PS>  Get-CimInstance -ClassName Win32_DiskPartition | fl
NumberOfBlocks   : 716800
BootPartition    : False
Name             : Disk #0, Partition #0
PrimaryPartition : False
Size             : 367001600
Index            : 0

NumberOfBlocks   : 409600
BootPartition    : True
Name             : Disk #0, Partition #1
PrimaryPartition : True
Size             : 209715200
Index            : 1

NumberOfBlocks   : 485312512
BootPartition    : False
Name             : Disk #0, Partition #2
PrimaryPartition : True
Size             : 248480006144
Index            : 2

NumberOfBlocks   : 921600
BootPartition    : False
Name             : Disk #0, Partition #3
PrimaryPartition : False
Size             : 471859200
Index            : 3

NumberOfBlocks   : 12492800
BootPartition    : False
Name             : Disk #0, Partition #4
PrimaryPartition : False
Size             : 6396313600
Index            : 4

next step down is logical disks

PS>  Get-CimInstance -ClassName Win32_LogicalDisk | fl
DeviceID     : C:
DriveType    : 3
ProviderName :
FreeSpace    : 108900372480
Size         : 248480002048
VolumeName   : Windows

 

The classes Win32_DiskDriveToDiskPartition and Win32_LogicalDiskToPartition  link physical disks to partitions and partitions to logical disks respectively.

Then you’ve got volumes – which is where you actually work with disks for the most part

PS>  Get-CimInstance -ClassName Win32_Volume | fl Caption, Label
Caption : C:\
Label   : Windows

Caption : \\?\Volume{524b798f-a072-4ecc-8cfe-fb823e10a5e7}\
Label   : Windows RE tools

Caption : \\?\Volume{4ea44e2e-dd30-4cd9-bfd1-c991be836d97}\
Label   :

Caption : \\?\Volume{c671d23c-f5e5-473d-b6c4-fecb4a99e5b3}\
Label   : Recovery image

 

The Storage module introduced with Windows 8 has cmdlets for some of these tasks:

PS>  Get-PhysicalDisk | fl FriendlyName, SerialNumber, CanPool, OperationalStatus, HealthStatus, Usage
, Size
FriendlyName      : HFS256G3AMNB-2200A
SerialNumber      : EI3AN118813AM3740
CanPool           : False
OperationalStatus : OK
HealthStatus      : Healthy
Usage             : Auto-Select
Size              : 256060514304

 

Partitions:

PS>  Get-Partition | fl PartitionNumber, DriveLetter, Offset, Size, Type
PartitionNumber : 1
DriveLetter     :
Offset          : 1048576
Size            : 367001600
Type            : Recovery

PartitionNumber : 2
DriveLetter     :
Offset          : 368050176
Size            : 209715200
Type            : System

PartitionNumber : 3
DriveLetter     :
Offset          : 577765376
Size            : 134217728
Type            : Reserved

PartitionNumber : 4
DriveLetter     : C
Offset          : 711983104
Size            : 248480006144
Type            : Basic

PartitionNumber : 5
DriveLetter     :
Offset          : 249191989248
Size            : 471859200
Type            : Recovery

PartitionNumber : 6
DriveLetter     :
Offset          : 249663848448
Size            : 6396313600
Type            : Recovery

 

Get-Disk returns similar, but not identical, information to Get-PhysicalDisk

The Get-Disk cmdlet gets one or more Disk objects visible to the operating system, or optionally a filtered list.

The Get-Disk cmdlet gets one or more Disk objects visible to the operating system, or optionally a filtered list.

There isn’t a cmdlet to get logical disks

For volumes:

PS>  Get-Volume | fl DriveLetter, FileSystemLabel, FileSystem, DriveType, HealthStatus, OperationalSta
tus, SizeRemaining, Size
DriveLetter       : C
FileSystemLabel   : Windows
FileSystem        : NTFS
DriveType         : Fixed
HealthStatus      : Healthy
OperationalStatus : OK
SizeRemaining     : 108473528320
Size              : 248480002048

DriveLetter       :
FileSystemLabel   :
FileSystem        : NTFS
DriveType         : Fixed
HealthStatus      : Healthy
OperationalStatus : OK
SizeRemaining     : 122884096
Size              : 471855104

DriveLetter       :
FileSystemLabel   : Windows RE tools
FileSystem        : NTFS
DriveType         : Fixed
HealthStatus      : Healthy
OperationalStatus : OK
SizeRemaining     : 61980672
Size              : 366997504

DriveLetter       :
FileSystemLabel   : Recovery image
FileSystem        : NTFS
DriveType         : Fixed
HealthStatus      : Healthy
OperationalStatus : OK
SizeRemaining     : 476807168
Size              : 6396309504

As you can see from this quick comparison the same sorts of information is available from the storage cmdlets and WMI. In fact under the hood the storage cmdlets are using WMI – but a set of new classes defined in ROOT/Microsoft/Windows/Storage


June 10, 2016  6:59 AM

WMI Filters

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
CIM, Powershell, WMI

A common mistake with WMI/CIM filters is:

PS>  Get-WmiObject -Class Win32_LogicalDisk -Filter “DeviceId=C:”
Get-WmiObject : Invalid query “select * from Win32_LogicalDisk where DeviceId=C:”
At line:1 char:1
+ Get-WmiObject -Class Win32_LogicalDisk -Filter “DeviceId=C:”
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [Get-WmiObject], ManagementException
+ FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCo
mmand

The clue is in the invalid query error message

When you use the –Filter parameter and are testing a property of type string the value you are testing against has to be in quotes

PS>  Get-WmiObject -Class Win32_LogicalDisk -Filter “DeviceId=’C:'”
DeviceID     : C:
DriveType    : 3
ProviderName :
FreeSpace    : 108999528448
Size         : 248480002048
VolumeName   : Windows

The filter is defined as a string so you need to use single quotes inside the double quotes. You could mess around with all single quotes but then you have to escape the inner set of quotes – good luck with that – its an unnecessary exercise in frustration

How do you know the property is a string?

PS>  (Get-CimClass -ClassName Win32_LogicalDisk).CimClassProperties[‘DeviceId’]
Name               : DeviceID
Value              :
CimType            : String
Flags              : Property, Key, ReadOnly, NullValue
Qualifiers         : {CIM_Key, read, key, MappingStrings…}
ReferenceClassName :

 

The same rules for –Filter apply to Get-CimInstance

Get-CimInstance -ClassName Win32_LogicalDisk -Filter “DeviceId=’C:'”


June 9, 2016  3:47 AM

Converting strings to dates

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

You’ll see many examples of this:

PS>  [datetime]’12/25/2016′

25 December 2016 00:00:00

This works great if the date is in US format – MM/DD/YYYY

For those of us who use different date formats – such as England DD/MM/YYYY – this approach won’t work

PS>  [datetime]’25/12/2016′
Cannot convert value “25/12/2016” to type “System.DateTime”. Error: “String was not recognized as a
valid DateTime.”
At line:1 char:1
+ [datetime]’25/12/2016′
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastParseTargetInvocationWithFormatProvider

You need to rearrange the string  –  for example

$ds = ’25/12/2016′ -split ‘/’

PS>  [datetime](“{0}/{1}/{2}” -f $ds[1], $ds[0], $ds[2])

25 December 2016 00:00:00

PS>  Get-Date -Day $ds[0] -Month $ds[1] -Year $ds[2]

25 December 2016 10:44:45

or even simpler

PS>  Get-Date -Date ’25/12/2016′

25 December 2016 00:00:00

Not sure when the last option came in. Its in PowerShell v5


June 8, 2016  5:13 AM

32 or 64 and/or Administrator

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

When you run the PowerShell console (or ISE) the default icon runs a 32 or 64 bit version that matches your OS. On a 64 bit machine you have the option of running in 32bit (icons have a (x86) suffix on the title.

How can you tell whether you’re running in 32 or 64 bit mode?

One way is shown in this forum question – http://powershell.org/forums/topic/requires/#post-42226

I prefer the simpler:

if ([System.IntPtr]::Size -eq 8) {$size = ’64 bit’}
else {$size = ’32 bit’}

I don’t like automatically kicking into the required bit version if its wrong. I prefer to abort the processing with a message to run as 32 or 64 bit as appropriate

You can perform a similar test for administrator privileges (running elevated)

$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
$secprin = New-Object Security.Principal.WindowsPrincipal $currentUser
if ($secprin.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator))
{$admin = ‘Administrator’}
else {$admin = ‘non-Administrator’}

Though on later versions of Powershell its easier to use

#requires –RunAsAdministrator

 


June 7, 2016  4:03 AM

Local Administrators

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

Finding the local administrators on a system is a not infrequent action.  There are a number of ways to do this.

The oldest method is to use the ADSI WinNT provider

$group =[ADSI]”WinNT://$($env:COMPUTERNAME)/Administrators, group”
$members = @($group.psbase.Invoke(“Members”))
$members | Foreach {$_.GetType().InvokeMember(“Name”, ‘GetProperty’, $null, $_, $null)}

NOTE – this doesn’t work on my Windows 10 system – build 14352

I’d recommend avoiding the WinNT provider if you can

WMI provides this option

$group = Get-CimInstance -ClassName Win32_Group -Filter “Name=’Administrators'”
Get-CimAssociatedInstance -InputObject $group -ResultClassName Win32_UserAccount

You can also use a .NET based approach with the System.DirectoryServices.AccountManagement  namespace

using assembly System.DirectoryServices.AccountManagement
$ctype = [System.DirectoryServices.AccountManagement.ContextType]::Machine
$context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ctype, $($env:COMPUTERNAME)

$idtype = [System.DirectoryServices.AccountManagement.IdentityType]::Name
$grp = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($context, $idtype, “Administrators”)
$grp.Members | select SamAccountName

This is a bit more complicated as you have to load the assembly (using is new to PowerShell v5 – use Add-Type in earlier versions)

Set the context to the local machine and the identity type to Name

You can then use FindByIdentity() to get the local adminsitrators groups and look at the Members property to find the group members.

PowerShell v5 brings a Local Accounts module – Microsoft.PowerShell.LocalAccounts

Add-LocalGroupMember
Disable-LocalUser
Enable-LocalUser
Get-LocalGroup
Get-LocalGroupMember
Get-LocalUser
New-LocalGroup
New-LocalUser
Remove-LocalGroup
Remove-LocalGroupMember
Remove-LocalUser
Rename-LocalGroup
Rename-LocalUser
Set-LocalGroup
Set-LocalUser

NOTE – depending on your version of PowerShell v5 you may, or may not have this module. Its present in the later Windows 10 builds (on Insider Preview) and in Windows server 2016 TP 5. Eventually it’ll become available on all Windows 10 systems through Windows updates.

PS>  Get-LocalGroupMember -Group Administrators | select Name

Name
—-
RSsurfacePro2\Administrator
RSSURFACEPRO2\Richard


June 6, 2016  3:39 AM

PowerShell Summit 2017–request for topics

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

The PowerShell Summit is a community event. PowerShell.orh may organise it but we’re very aware that it is the PowerShell community’s event.

We’ve been planning the  2017 Summit for a while and we’ve reached a point in the process where we need your help.

What topics would you like to see covered at the 2017 Summit. We’ve published a request for topics at –http://powershell.org/request-for-topics/

Please let us know the topic areas you’d like covered and we can build that information into our agenda planning to ensure we have the Summit the community wants


June 1, 2016  1:54 PM

Dates in file and folder

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

If you want to incorporate the date in a file or folder name you can’t use Get-Date directly

PS>  Get-Date

01 June 2016 20:52:03

The simplest answer is to use the –Format or –Uformat parameters:

PS>  Get-Date -Format yyyyMMdd
20160601
PS>  Get-Date -UFormat %Y%m%d
20160601


May 31, 2016  3:19 PM

Counting members

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

If you have a collection of objects

$proc = get-process

you can get the number of members using the Length property

PS>  $proc.Length
71

$proc is of type System.Array

PS>  $proc.GetType()

IsPublic IsSerial Name                                     BaseType
——– ——– —-                                     ——–
True     True     Object[]                                 System.Array

MSDN documenattion is at https://msdn.microsoft.com/en-us/library/system.array(v=vs.110).aspx

PowerShell also adds a Count property that is an alias of Length

if you are after the number of members in a collection via the pipeline you need to use the Measure-Object cmdlet

PS>  $proc | Measure-Object
Count    : 71
Average  :
Sum      :
Maximum  :
Minimum  :
Property :

or measure directly

PS>  Get-Process | Measure-Object
Count    : 71
Average  :
Sum      :
Maximum  :
Minimum  :
Property :

Notice that Measure-Object can also be used to discover other statistics – the Average, Sum, Maximum and Minimum. These only work for numeric properties.


May 30, 2016  4:20 PM

Windows Server eras

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Windows Server 2016

This is worth a read

https://blogs.technet.microsoft.com/windowsserver/2016/05/26/moving-forward-with-powershell-and-windows-server-2016/

It discusses the evolution of Windows server and PowerShell. Read it in conjunction with the videos of Jeffrey Snover’s talks at WinOps and the European PowerShell conference this year.


Forgot Password

No problem! Submit your e-mail address below. We'll send you an e-mail containing your password.

Your password has been sent to: