Heterogeneous environments

When we talk about heterogeneous environments the assumption is that we mean a mixture of Windows and Linux machines. Windows and Linux can be viewed as providing the end points of a spectrum of management issues. In reality there is another spectrum – that spectrum exists between Windows machines.

Thinking ONLY of server administration you could easily have Windows 2008, Windows 2008 R2, Windows 2012, Windows 2012 R2 and Windows 2016 servers in your environment. And that’s only the supported operating systems. From experience I’d expect that many organisations still have Windows 2003 servers and I suspect there are still some Windows 2000 servers in quite a few organisations. I wouldn’t be shocked to find a few Windows NT machines still in use!

The five supported Windows Server OSs are bad enough. Windows 2016 is moving to twice yearly updates starting this autumn (that’s fall for non-English readers). Windows 2008 & 2008 R2 go out of mainstream support in  2020 so giving the option for 5 versions of 2016 to be released before it vanishes. The Server 2012 family is supported until October  2023!

The point is that you need to think how you’re going to support a potentially large number of operating system editions going forward. I seriously doubt that anyone will be updating their whole server estate on a twice yearly basis.

Multiple small releases enable updates to particular areas of Windows being released quickly. So one  release may update Hyper-V while another updates containers or storage.

Working out which Windows builds – and you will have to start thinking at the build level – will support particular versions of Exchange, SQL Server, SharePoint or any of the other Microsoft products. Expect other vendors of software that runs on top of windows server to panic when you ask them about supporting particular builds – they’re usually so slow at supporting new versions that the next is in beta. That behaviour is going to cause issues.

You could avoid some of this by saying you’ll skip builds – 1, 2, 3 or X years worth – but what happens when a new feature fixes a problem you’ve got now!

Heterogeneity is going to become more widespread. On the plus side it may kill the option of whole sale upgrades and massive server migration projects. You’ll just be permanently upgrading!

At the moment its not too big an issue but I expect over time that  you’ll need to think about build number

PS> Get-CimInstance -ClassName win32_operatingsystem | select -ExpandProperty BuildNumber

as much as version

PS> Get-CimInstance -ClassName win32_operatingsystem | select -ExpandProperty Version

And that’s before we get to Linux.

If you’re writing scripts that do different things based on Windows version think about dropping down to build number as well. You can always use a –gt <build number> approach.

You’re going to be living in interesting times.

Posted in Windows Server 2016 | Leave a comment

PowerShell string concatenation

Strings –  a list of characters such as ‘abcd’ – are a common feature of programming or scripting. Sometimes you need to join – concatenate – two or more strings together. This is how PowerShell string concatenation works.

First you need to know that strings can be defined with single quotes or double quotes:

PS> $sq = ‘A single quoted string’
PS> $sq
A single quoted string

PS> $dq = “A double quoted string”
PS> $dq
A double quoted string

The difference is that you can substitute into a double quoted string. Best practice is to use single quotes UNLESS you intend to substitute into the string

PS> ‘I am $sq’
I am $sq
PS> “I am $sq”
I am A single quoted string

String concatenation can be performed using the concatenation operator – a plus sign or through string substitution

PS> $s1 = ‘abcd’ + ‘defg’
PS> $s1

You can use variables

PS> $s1 = ‘abcd’
PS> $s2 = ‘defg’
PS> $s3 =  $s1 + $s2
PS> $s3

When you concatenate strings like this you’re actually creating a new string not extending an existing one.

Alternatively, use string substitution

PS> $s4 = “$s1$s2”
PS> $s4

Posted in Powershell | Leave a comment

PowerShell operators

PowerShell has operators – lots of operators. So many operators that it took us TWO chapters to work through them all in PowerShell in Action, third edition – https://www.manning.com/books/windows-powershell-in-action-third-edition. Here’s how you can discover the PowerShell operators.

PS> Get-Help about*operator*


A quick listing gives us:

Arithmetic operators (+, -, *, /, %)

Assignment operators (=, +=, -=, *=, /=, %=)

Comparison operators (-eq, -ne, -gt, -lt, -le, -ge)  (-match, -notmatch) (-like, -notlike)

(-in, -notin, -contains,  -notcontains) (-bAND, -bOR, -bXOR, -bNOT)

Logical opertors (-and, -or, -xor, -not, !)

Redirection operators (>, >>, 2>, 2>, and 2>&1)

Type operators (-is, -isnot, -as)

And a  bunch of special operators @(), & (call), [] (cast),  comma, dot, –f , index opertor, pipeline operator, range operator, :: static member operator, $()

Also don’t forget –split, –join, –replace

Posted in Powershell | Leave a comment

Comparing AD group membership on EmployeeId

Back in this post – http://itknowledgeexchange.techtarget.com/powershell/comparing-group-membership/ I showed how to compare the membership of two groups using Compare-Object. The comparison was based on the samAccountName. A question raised the issue of comparing AD group membership on EmployeeId

In the case in particular users have multiple accounts BUT the EmployeeId is correct on all and will therefore show matching users. Assuming the EmployeeId is correct on all accounts it still leaves a problem.

When you run Get-ADGroupMember you get a very limited number of properties returned:

PS>  Get-ADGroupMember -Identity Testgroup1

 distinguishedName : CN=JONES James,OU=UserAccounts,DC=Manticore,DC=org
 name              : JONES James
 objectClass       : user
 objectGUID        : 027cb406-a3b0-4f45-9bbd-db47ccfb9212
 SamAccountName    : JamesJones
 SID               : S-1-5-21-759617655-3516038109-1479587680-1225

First thing I needed to do was set up some users with an EmployeeId

$ei = 1

 Get-ADUser -Filter {Name -like "*Jones*"} -Properties EmployeeId |

 foreach {
   $id =  23945 + $ei
   $psitem | Set-ADUser -EmployeeID $id

  $ei = $ei + (Get-Random -Minimum 3 -Maximum 12)


Get a set of users – including the EmployeeId – and forech of them set the id. The id is randomly generated based on a starting value and increment.

Now that the users have an Employeeid you can use that for comparison purposes

$group1 = Get-ADGroupMember -Identity Testgroup1 | 
 foreach {
   Get-ADUser -Identity $psitem.distinguishedName -Properties EmployeeId | 
   select -ExpandProperty EmployeeId

$group2 = Get-ADGroupMember -Identity Testgroup2 | 
 foreach {
   Get-ADUser -Identity $psitem.distinguishedName -Properties EmployeeId | 
   select -ExpandProperty EmployeeId

 Compare-Object -ReferenceObject $group1 -DifferenceObject $group2 -IncludeEqual |             
 where SideIndicator -eq "==" |            
 foreach {            
  $id = ($_.InputObject)        
  Get-ADUser -Filter {EmployeeId -eq $id} -Properties EmployeeId            

Get the membership of the first group and for each member use Get-ADUser to return the EmployeeId. Repeat for the second group.

Use  Compare-Object to compare the two sets of group members – you’re looking for matches indicated by “==”

Foreach match get the AD user account filtering on the EmployeeID.

The PROBLEM with this approach is that you’ll get all user accounts returned that have the particular EmployeeId.   You can replace the line

Get-ADUser -Filter {EmployeeId -eq $id} -Properties EmployeeId


Get-ADUser -Filter {EmployeeId -eq $id} -Properties EmployeeId, MemberOf | where {$_.MemberOf -like “*Testgroup1*” -AND $_.MemberOf -like  “*Testgroup2*”}

Which should resolve the problem

Posted in PowerShell and Active Directory | Leave a comment

PowerShell substrings

PowerShell is all about working with objects but you often have to drop to a lower level and work with properties and their values. Many objects have properties that are strings – a string is one of the standard PowerShell literals – and sometimes you want to extract part of a string – a substring. This post shows how PowerShell substrings work.

First thing to note is that PowerShell doesn’t have a substring command or keyword. A string in PowerShell is an instance of the System.String class and so you use the Substring method of the String class.

You have 2 options – technically known as overloads. You can view a methods overloads by using the method name without brackets

PS> ‘abcdefghijklmnopqrstuvwxyz’.Substring

string Substring(int startIndex)
string Substring(int startIndex, int length)

In the first case you give a starting index into the string and the substring starts at that point and takes everything to the end of the string:

PS> 0..25 | foreach {‘abcdefghijklmnopqrstuvwxyz’.Substring($psitem)}

Remember that in .NET indices start at 0 so the string is 26 characters long with indices 0-25

If you supply an index that would be beyond the end of the string:

PS> ‘abcdefghijklmnopqrstuvwxyz’.Substring(26)

PS> ‘abcdefghijklmnopqrstuvwxyz’.Substring(30)
Exception calling “Substring” with “1” argument(s): “startIndex cannot be larger than length of string.
Parameter name: startIndex”
At line:1 char:1
+ ‘abcdefghijklmnopqrstuvwxyz’.Substring(30)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
     + FullyQualifiedErrorId : ArgumentOutOfRangeException

In the first case you get nothing back because the string is 26 characters long. Anything larger than that and you get an error.

The second overload of substring involves supplying a starting index and the number of characters you want to take (including the starting character):

PS> 0..25 | foreach {‘abcdefghijklmnopqrstuvwxyz’.Substring($psitem, 3)}
Exception calling “Substring” with “2” argument(s): “Index and length must refer to a location within the string.
Parameter name: length”
At line:1 char:18
+ 0..25 | foreach {‘abcdefghijklmnopqrstuvwxyz’.Substring($psitem, 3)}
+                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
     + FullyQualifiedErrorId : ArgumentOutOfRangeException

Exception calling “Substring” with “2” argument(s): “Index and length must refer to a location within the string.
Parameter name: length”
At line:1 char:18
+ 0..25 | foreach {‘abcdefghijklmnopqrstuvwxyz’.Substring($psitem, 3)}
+                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
     + FullyQualifiedErrorId : ArgumentOutOfRangeException

Notice the error when you would go beyond the end of the string

PowerShell substrings depend on using the SubString method of the System.String class. The examples here will help you decide which overload to use.

Posted in Powershell | Leave a comment

Further information on PowerShell 2.0 deprecation

The PowerShell team have provided further information about the deprecation of PowerShell 2.0


One point that didn’t come out is that if you remove PowerShell 2.0 your CIM sessions can all run over WS-MAN. DCOM isn’t required any more – YAY

Posted in PowerShell V2 | Leave a comment

PowerShell pause

PowerShell pause – how can you pause a PowerShell script?

Two ways come to mind.

First if you just want the script to pause for a specified time period then you can use Start-Sleep

1..10 |
foreach {
   if ($PSItem -eq 5) {
     Write-Warning -Message “Starting sleep”
     Start-Sleep -Seconds 5

Run this and you’ll see the numbers 1-5 output then then warning message. After the delay you’ll see the numbers 6-10 output.

But what if you want to control the pause manually? Not sure if there are advantages to this approach but if you do need to do this you can use Read-Host

1..10 |
foreach {
   if ($PSItem -eq 5) {
     Read-Host -Prompt “Press Enter key to continue”

You’ll see the numbers 1-5 output then the message

Press Enter key to continue:

After pressing the enter key the script continues and outputs 6-10

Don’t know why you’d want to do this in an automation scenario but the technique is there if you need it – I don’t recommend the approach.

There are also a few cmdlets that can be used under specific circumstances:


Also check the –wait parameter on Restart-Computer

Posted in Powershell Basics | 3 Comments