Converting to UTC Time (Thanks, SharePoint)

Earlier this year, I created and implemented a constrained, PowerShell endpoint on two SharePoint Central Admin servers. The purpose was to allow non-administrative users the ability to remotely connect via PSRemoting, or PowerShell Web Access, to create new SharePoint Site Collections (SCs). By default, I provided a few Get- SharePoint cmdlets, as well as, the New-SPSite SharePoint cmdlet. A month or so after requesting and then receiving the Set-SPSite cmdlet, the team using the endpoint, requested another cmdlet: Remove-SPSite – a cmdlet that made me a little leery. While I trusted the people who would use the cmdlet, I needed to sleep at night, and so I needed to add some precautions.

So, what to do? I created a “proxy” function in place of the SharePoint Remove-SPSite cmdlet. While the cmdlet wouldn’t be available, the function would be. The best part about writing your own function, for inclusion in a constrained endpoint, has to be the ability to make use of cmdlets in the function, that are not available in the endpoint. The Remove-SPSite cmdlet is used in my function, even when it isn’t available to the people that use the endpoint. Simply, badass.

While I’m not going to share the complete function, I will explain the logic I decided to use. I allowed people using the endpoint, the ability to delete SCs under one condition – that the SC had been created within the last six hours. This helped protect any SCs that had been created prior to six hours ago. To do this, I decided I would simply get the current date and compare it to the created date of the SC – small problem.

First, let’s return the SC that I created.

PS C:\> Get-SPSite -Identity https://teamsite.sp.mydomain.com

Url                                                     CompatibilityLevel
---                                                     ------------------
https:/teamsite.sp.mydomain.com                         15

Now, let’s return the time when the SC was created. The problem, in the example below, is that I didn’t create the site at 2 a.m. From my point in time, that was 7 hours in the future. With the help of a coworker, I realized that the time being used by SharePoint on the back end, was Coordinated Universal Time, or UTC, regardless that all the other SharePoint times were set for my time zone.

PS C:\> (Get-SPSite -Identity https://teamsite.sp.mydomain.com).RootWeb.Created

Monday, November 17, 2014 2:05:10 AM

My first thought was that this wasn’t going to be an effective way to protect the SCs, until I remembered, I can convert the current time. The easiest way to do this was to use the DateTime object’s method, .ToUniversalTime(). That’s right, take the current time, convert it to Universal Time, and then use that for comparison.

In the example below, we do several things. The first three lines set some variables. In line 1, we capture the SC to a variable. In line 2, we create the variable that holds the numeric value of hours we are willing to allow, and in line 3 we create the DateTime object in UTC that we use to compare against the SharePoint SC.

$SPSite = Get-SPSite -Identity https://teamsite.sp.mydomain.com
$Hours = 6
$TimeHoursAgo = ((Get-Date).ToUniversalTime()).AddHours(-$Hours)

If ($SPSite.RootWeb.Created -gt $TimeHoursAgo) {
    Remove-SPSite -Identity $SPSite -Confirm:$True
} Else {
    Write-Warning -Verbose "Unable to delete Site Collection: $($SPSite.Url) (Over $Hours hours old))."
}

The rest of the example does the comparison between the DateTime object we’ve created and the created date of the SC. If the SC was created after our $TimeHoursAgo variable (meaning that it is greater than $TimeHoursAgo), then it can be deleted, and if it wasn’t, then it cannot be deleted.

Notice the .AddHours() method, above, is using a negative sign before the $Hours variable – this is used to move back in time. Also, pay attention to the embedded parenthesis as the value $TimeHoursAgo is being assigned. They indicate the order in which the command is executing, beginning with the most embedded set. You can ignore the parenthesis that are used as a part of the methods, such as .ToUniversalTime(), even if they contain something between the parenthesis. I’ve included an example of what that line would look like had it been written procedurally.

PS C:\> $Hours = 6
PS C:\> $TimeHoursAgo = Get-Date
PS C:\> $TimeHoursAgo = $TimeHoursAgo.ToUniversalTime()
PS C:\> $TimeHoursAgo = $TimeHoursAgo.AddHours(-$Hours)

Keep in mind that various Microsoft technologies may use UTC on the back end, even if the GUI shows your proper time zone.

Leave a Reply

Your email address will not be published.