Making Sense of Replication Schedules in PowerShell

Hi all! Jan-Hendrik Peters, PFE, here to talk to you today about using PowerShell to view replication schedules. Automation with PowerShell is a part of our daily life. However, there are certain things that are for some reason not achievable with PowerShell out of the box. One of them is getting useful output for replication schedules in Active Directory.

We all know this problem. You are enjoying PowerShell and its great features and want to automate everything. Sooner or later, you are encountering issues with the default output format and resort to the way things used to be: Using a GUI.

If you are interested in finding out about scripting, read on. If you are pressed on time: https://gist.github.com/nyanhp/d9a1b591b5a69e300f640d53a02e0b44

To test what I did, I always make use of AutomatedLab. AutomatedLab is an open source project I am contributing to that can set up lab environments on Hyper-V, Azure and VMWare for you. You can find the script I used for my lab there as well: https://github.com/AutomatedLab/AutomatedLab/blob/master/LabSources/SampleScripts/Workshops/PowerShell%20Lab%20-%20HyperV.ps1

All sample scripts are part of the module, which you can install from GitHub or from the PowerShell Gallery by using Install-Module AutomatedLab.

One of my colleagues recently came to me with an issue his customer was facing. They simply wanted to get the replication schedule for their sites. While this sounds like a very easy task, the outcome was not what they desired.

This does not look right. What is an ActiveDirectorySchedule, and how can I use it? We wanted something like this:

To get rid of navigating to Sites and Services, finding the right schedule and viewing it in a nice calendar I will show you step by step how to get from unusable data to nicely formatted data. On the side we will also learn how to properly create a PowerShell function.

This blog post will show you how to make sense of normally unusable output and teach you PowerShell function design.

What are we dealing with?

The first, crucial point when dealing with these disappointments is finding out what we are up against. So, I would like to elaborate a little on an underrated tool that we all have access to: The cmdlet Get-Member.

We all know that PowerShell is an object-oriented shell that is built on the .NET framework. Being object-oriented means that we are dealing with classes that define how the objects, or instances of a class, look like. Get-Member harnesses this power and can show you all the .NET parts of the objects (i.e. the output of cmdlets) like properties and methods.

Properties are readable and, in many cases, writeable properties of an object that contain additional information. These additional pieces of information are of course also objects with more properties and methods.

Methods are pieces of code that can be executed to achieve certain results and may use the object’s properties to do so.

How does this look like with our little cmdlet?

As you can see, there are methods and properties of our site. The property we are most interested in in this example is called ReplicationSchedule.

Hmm. So our ReplicationSchedule is indeed a more complex object. We can use simple datatypes like datetime, timespan, int, string, bool, array and hashtable without issues. However, when it comes to more complex data types we must apply a little more elbow grease.

To make matters worse, there is no method or property to simply get the schedule in a readable format. The output of Get-Member revealed a property called RawSchedule, which sounds promising. Using this, we hit another brick wall:

Our property RawSchedule has the datatype bool[,,] – a three-dimensional array. Wow. This is where we need the online documentation. A quick search on MSDN for “System.DirectoryServices.ActiveDirectory.ActiveDirectorySchedule” reveals the documentation of the underlying .NET class. Luckily RawSchedule is well documented there at least.

Our array is encoded, so that the first index refers to the number of the weekday, the second index to the hour (0 – 23) and the third index to the 15-minute interval that the replication is active in. So how does that help?

Adapting

In our script we now must find a way to tie the Boolean entries to a tuple of weekday, hour and interval. The first idea that comes to mind is using loops to get to the desired result.

The first thing we need is our weekday indices. These are quite easy to come by. Remember me raving about Get-Member? Let’s pipe Get-Date to Get-Member. In the output you can see a property called DayOfWeek. Displaying this property returns a string – not what we need, right?

Wrong. While we certainly do not need a string, the object type is called DayOfWeek. DayOfWeek represents something that developers know as enumeration. It is simply a zero-based list of entries.

To see all available values of an enumeration we can use a so-called static method. Why static? Because this method does not need an actual object to perform its task. The .NET class Enum possesses the static method GetValues, that lists all values for an enumeration.

[System.Enum]::GetValues([System.DayOfWeek])

Casting the integers from 0 to 6 to a DayOfWeek quickly shows: 0..6 | Foreach-Object {[System.DayOfWeek]$_}

The hours are far easier, as they range from 0 to 23 in our zero-based three-dimensional array.

From these bits and pieces, we can finally cobble together a couple of loops that iterate over the array and return the Boolean values for each time slot.

Reusability

By now, we have rather unstructured code that is not easily reusable. I cannot tell you how many variations on the theme of ‘Set ACL on a folder’ or ‘Get something from AD’ I have seen up until now. In many companies there still is no centralized code repository that everyone can use, and most people are happily reinventing the wheel day-in, day-out.

In PowerShell, reusability is achieved by defining functions and by placing those functions in modules. Those modules can then be used by anyone and not only a select few.

Placing our code in a function is not that exciting. The keyword function and a script block would be enough. We could use $site as a parameter and be a bit more flexible. As you might recall however, using Write-Host is evil (http://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/). We would much rather use proper objects that we can then format at our leisure, export to CSV and so on.

Since we are building something good here, why not add some proper validation and pipeline support as well? While this sounds like a daunting task it is rather simple.

Pipeline input is achieved by using entire objects passing through the pipeline as well as using only certain property values. In our case we will start with property values.

[CmdletBinding()]
param
(
[Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
[System.DirectoryServices.ActiveDirectory.ActiveDirectorySchedule]$ReplicationSchedule,
[Parameter(ValueFromPipelineByPropertyName $true, Mandatory = $true)]
           [string]$DistinguishedName
)

By using the ReplicationSchedule as the parameter name and setting this parameter up for pipeline input by property name, we can now simply pipe our culprit from the beginning of this post in its entirety to our new cmdlet, Get-ADReplicationSchedule.

Get-AdReplicationSitelink
-Identity
“Munich – Abidjan”
-Properties
ReplicationSchedule
|
Get-ADReplicationSchedule

Get-AdReplicationSite
-Identity
Toronto
|
Get-ADReplicationSchedule

The full function then looks like this:

You might notice a couple of changes in the final code as well. For instance, I replaced the Write-Host statements entirely and am now simply creating objects for each site or site link that is piped to the cmdlet. In the script, the proper data types are used as well.

The resulting cmdlet is very flexible:

Filter it with Where-Object, format it with Format-List and Format-Table, view it in a GridView, whatever you fancy. Write-Host never generates useable output, which is something you should keep in mind at all times.

This concludes my first post on ASKPFEPLAT. I hope you have learned something new today and even if not: Enjoy the little script and make it your own!