Monthly Archives: November 2017

Reverse engineering email address from OneDrive personal site Url

Sup’ PSHomies,

Migrations… Gotta love ’em!!!

I’m doing a OneDrive for Business tenant to tenant migration using Sharegate. If you ever decide to do any sort of SharePoint affiliated migration, best to use Sharegate! Their support is top notch! Shout out to Jimmy De Santis!

Sharegate has a cmdlet Get-OneDriveUrl that will retrieve or provision you ODFB URL (OneDrive For Business). Depending on a user’s Primary SMTP Address the URL format may not be what you expected.

Here’s a link to the script to get your ODFB information of your organization.  Be sure to follow the setup instructions.

This will generate a simple text file (I used it as-is, a rare occasion, I know… Don’t judge me…). To generate the URL, you need to swap any ‘.’ and ‘@’ for ‘_’.  I’m looking to reverse this process. Why? Well because I hate manual work. I’m almost certain I have to do this again sometime in the near future…

Those who forget to automate are doomed to repeat their work…

Words to live by…

My hunch paid off! By reverse engineering the email address from the ODFB URL, I could use that email address to retrieve the ODFB URL using Get-OneDriveUrl. I needed to provision the URLs in the target domain, fortunately the email addresses in the Target tenant are uniform.

Ok here’s the code:

$personalSites = @"
personalSite
/personal/a_v_d_hoogt_simba_nl/
/personal/h_altafah_simba_nl/
/personal/c_v_d_meulen_simba_nl/
/personal/stand_simba_nl/
/personal/floortje_janssen_simba_nl/
/personal/j_dubendorffer_sudoku_nl/
/personal/m_d_haas_sudoku_nl/
/personal/j_slimmer_sudoku_nl/
/personal/m_rijs_sudoku_nl/
/personal/b_janssen_despacito_com/
/personal/m_pools_despacito_com/
/personal/r_stewardess_despacito_com/
/personal/t_koestsier_despacito_com/
"@ | ConvertFrom-Csv
$personalSites |
ForEach-Object {
#Get ODFB part
Try {
$arrODFBUrl = ($($_.personalSite).Split('//'))[-2].Split('_')
$count = @($arrODFBUrl).Count
$index = 1
$emailUsedForODFB = $arrODFBUrl[0]
Do {
if ($arrODFBUrl[$index] -like 'despacito*') {
$emailUsedForODFB = "$($emailUsedForODFB)@$($arrODFBUrl[$index])"
}
elseif ($arrODFBUrl[$index] -like 'simba*') {
$emailUsedForODFB = "$($emailUsedForODFB)@$($arrODFBUrl[$index])"
}
elseif ($arrODFBUrl[$index] -like 'sudoku*') {
$emailUsedForODFB = "$($emailUsedForODFB)@$($arrODFBUrl[$index])"
}
else {
$emailUsedForODFB = "$($emailUsedForODFB).$($arrODFBUrl[$index])"
}
$index++
}until($index -eq $count)
#Some ODFB may have a '1' at the end. Just remove that
if ($emailUsedForODFB -like '*nl1') {
$emailUsedForODFB = $emailUsedForODFB -replace ".$"
}
$samAccountName = $emailUsedForODFB.Split('@')[0].ToLower()
$targetEmail = '{0}@urv.onmicrosoft.com' -f $samAccountName
[PSCustomObject]@{
SamAccountName = $samAccountName
SourceEmail = $emailUsedForODFB
SourcePersonalUrl = $($_.personalSite)
SourceGeneratedUrl = 'https://irwins-my.sharepoint.com/personal/{0}/' -f $emailUsedForODFB.Replace('@', '_').Replace('.','_').ToLower()
TargetEmail = $targetEmail
TargetGeneratedUrl = 'https://urv-my.sharepoint.com/personal/{0}/' -f $targetEmail.Replace('@', '_').Replace('.','_').ToLower()
}
}
catch {
Write-Warning 'Not a valid personal site format'
}
} |
Out-GridView

Having the email address I can generate the source URL and verify if it is what I expected it to be. The samaccountname remained the same, so it was easy enough to generate the new email address and the expected target URL which once I provision, can also be verified that it is what I expected it to be. Still with me? Just have a look at the image… 😉

Generated ODFB Urls

You can generate quite the information by just have a personal ODFB Url!

Not being content with just the generated information, I did one last check using Sharegate’s Get-OneDriveUrl:

$csvUrls = Import-Csv C:\scripts\sources\csv\VerifyUrls.csv -Delimiter "`t" -Encoding UTF8
#region Initialize Stuff
Import-Module ShareGate -Verbose:$false
# User account for an Office 365 global admin in your organization
# https://www.jaapbrasser.com/quickly-and-securely-storing-your-credentials-powershell/
$HashCreds = Import-CliXml -Path "${env:\userprofile}\Hash.Cred"
#Admin urls
$targetAdminUrl = 'https://urv-admin.sharepoint.com'
$sourceAdminUrl = 'https://irwins-admin.sharepoint.com'
#Connect to Tenant Site
$targetTenantSite = Connect-Site -Url $targetAdminUrl -Credential $HashCreds.'urv-target-o365admin'
$sourceTenantSite = Connect-Site -Url $sourceAdminUrl -Credential $HashCreds.'irwins-source-o365admin'
Function Get-ODFBUrls{
param($sourceUserEmail,$targetUserEmail)
#Provision using Sharegate and wait for it to finish.
$targetOneDriveUrl = Get-OneDriveUrl -Tenant $targetTenantSite -Email $targetUserEmail
$sourceOneDriveUrl = Get-OneDriveUrl -Tenant $sourceTenantSite -Email $sourceUserEmail
[PSCustomObject]@{
SourceEmail = $sourceUserEmail
SourceODFBUrl = $sourceOneDriveUrl
TargetEmail = $targetUserEmail
TargetODFBUrl = $targetOneDriveUrl
}
}
#region Get ODFB Urls
$csvUrls |
ForEach-Object{
Get-ODFBUrls -sourceUserEmail $_.SourceEmail -targetUserEmail $_.TargetEmail
}|
Export-Csv C:\scripts\export\ODFBUrls-27112017.csv -Encoding UTF8 -Delimiter "`t" -NoTypeInformation
#endregion

This aligns the source and target URLs nicely! Now I can migrate from tenant to tenant without to much hassle… Well at least that’s the plan… 😉

Hope it’s worth something to you,

Ttyl,

Urv

Taking ADUser validation a step further…

‘Sup PSHomies,

This is me every single time I sit down to use Pester…

PesterOVF.PNG

Hehe…

While I do enjoy using Pester for operational validation, what do you do with the ones that fail? Most of the time you’re the one doing the validation in the chain of process and everything goes right! Because you’re that good… 😉 Hehe…  I’ve been recently asked to update some users where some attributes didn’t get populated during a migration… Are you thinking what I’m thinking? 😉

The first thing I did was export the attributes they needed, easy enough. Next was to change the necessary attributes to the correct value. The ones that differ will fail (Of course they will Urv, not a total retard here… get on with it… )

Here’s where having proper descriptions can go a long way.

Breadcrumbing

This is what a Test ErrorRecord looks like when you capture the results:PesterError

At first I thought of using regex on the ErrorRecord. Some of the  attributes aren’t set which gave me some issues, so I decided to breadcrumb the name. First do a split on the colon ‘:’ grab the last part and do a split again using ‘/’ to get the attribute name and value. Don’t forget to trim() 🙂

There were some other Attributes that weren’t part of the default parameter set of the Set-ADUser cmdlet. To change those attributes you need to use the DisplayName and the Replace Operator. For the attributes “not set” that need to be cleared, use the Clear operator. Just don’t use both the parameter and the DisplayName! I had EmailAddress and mail in the CSV file, one passed and the other failed… I got rid of mail…

Ok here’s the code to get things done:

First get failed Pester tests.

<#
Author: I. Strachan
Version:
Version History:
Purpose: Validate AD User attributes
#>
[CmdletBinding(SupportsShouldProcess = $True)]
Param(
$csvFile = 'd:\scripts\ps1\source\csv\SetUser.csv'
)
#region Import Csv file
$csvUsers = Import-Csv -Path $csvFile -Delimiter "`t" -Encoding UTF8
$userProperties = ($csvUsers | Get-Member -MemberType NoteProperty).Name |
Where-Object {$_ -ne 'OU'}
#endregion
#region Main
$csvUsers |
Foreach-Object {
$Expected = $_
Describe "Processing User: $($Expected.SamAccountName)" {
Context "Verifying AD User properties for $($Expected.DisplayName)" {
#Get AD user properties
$Actual = Get-ADUser -Identity $Expected.SamAccountName -Properties $userProperties
ForEach( $property in $userProperties){
if (([string]::isNullOrEmpty($Expected.$property))) {
$Expected.$property = $null
$lableExpected = '<not set>'
}
else{
$lableExpected = $Expected.$property
}
it "Verifying user property: $($property) / $($lableExpected)"{
$Actual.$property | Should be $Expected.$property
}
}
}
}
}
#endregion
#Run PesterTest and save results
$resultsTest = Invoke-Pester D:\scripts\ps1\dsa\ADUser.Properties.Tests.ps1 -PassThru
#Get All failed tests
$failedTests = $resultsTest.TestResult.where{$_.Passed -eq $false}
$failedTests |
ForEach-Object{
$result = $_.Name.Split(':')[-1]
$arrResult = $result.Split('/')
[PSCustomObject]@{
SamAccountName = ($_.Describe.split(':').Trim())[-1]
Property = $arrResult[0].Trim()
Expected = $arrResult[1].Trim()
}
} -OutVariable failedObjects
#Export Failed objects
$failedObjects |
Export-Csv -Path D:\Scripts\ps1\source\csv\FailedTests.csv -Delimiter "`t" -NoTypeInformation -Encoding UTF8

This will give me a csv file with the following Columns, SamAccountName, Property & Expected (Value).

The set failed Pester tests will either set or clear the attribute depending on it’s value. If it’s $null it will be cleared.

<#
Author: I. Strachan
Version:
Version History:
Purpose: Set ADUser attributes of failed tests
#>
[CmdletBinding(SupportsShouldProcess = $True)]
Param(
$csvFile = 'D:\Scripts\ps1\source\csv\FailedTests.csv'
)
$FailedTests = Import-Csv -Path $csvFile -Delimiter "`t" -Encoding UTF8
#Get Set-ADUser Parameters
$setADUserParameters = (Get-Command Set-ADUser).ParameterSets.Parameters.Where{ $_.IsDynamic -eq $true} | Select-Object -ExpandProperty Name
#Get User Property
$FailedTests |
Foreach-object{
#Set Expected to null if <not set>
$Expected = @{$true = $null; $false = $_.Expected}[$_.Expected -eq '<not set>']
If ($setADUserParameters.Contains($_.Property)){
$paramSetADUser = @{
Identity = $_.SamAccountName
$_.Property = $Expected
}
Set-ADUser @paramSetADUser
}
else{
if($Expected){
Set-ADUser -Identity $($_.SamAccountName) -Replace @{$_.Property = $Expected}
}
else{
Set-ADUser -Identity $($_.SamAccountName) -Clear $($_.Property)
}
}
}

Here are some screenshots to give you an idea what to expect.

Ideally you’d only have a few failed tests. I wouldn’t use this to reset entire User attributes. Moving and/or renaming an object isn’t supported… yet! 😉

So there you have it, taking failed Pester tests values and setting them accordingly!

Hope it’s worth something to you,

Ttyl,

Urv

 

 

Microsoft Teams MessageCards

‘Sup PSHomies,

I was excited to give the MS Teams module a go! I was secretly hoping for a cmdlet to send messages to channels. Unfortunately no such luck… yet! If push comes to shove you can always take the Graph route! 😉

I posted the blog on social media and that generated some interesting ideas. Emotions and opinions varied quite a bit. Prateek raised an interesting question on whether slack would become obsolete in the near future. MS Teams is gaining momentum and it integrates nicely in Microsoft’s eco system, so why wouldn’t you use it? It doesn’t have to be black or white, use whatever adds value to your business I’d say!

This is where social media can be a treasure trove at times, got an interesting tip from Brett Miller on posting messages to channels using webhooks! Ah! the missing cmdlet I was hoping for! “So basically it’s just an Invoke-RestMethod?” Cool! I gave it a spin:

New-TeamMessage

Nice! Hmmm… Webhooks eh? Sounds familiar… Where did I read about that? Ah yes! Stefan Stranger did a blog about that. To be honest, I did read his blog, but at that time I didn’t have access to Teams… 😉 Just follow his instructions and you’re golden!

Here’s when the fun really started! Come to find out that there’s a lot more you can post using the message card reference! There’s even a card playground where you can try out your message cards. It’s all JSON, no problem, we’ve got cmdlets for that.

There’s a whole design guide on Messagecards. Best of all it supports MarkDown format! I tried a sending a table but that didn’t work as planned.

Just imagine the automation possibilities here… I understand now why ChatOps is really taking off…

My first attempt at DSL

Looking at the MessageCard format I thought: “This would be interesting as a DSL (Domain Specific Language) implementation… Hmmm…” I’ve been meaning to look into DSL after reading Kevin Marquette’s blog series on the subject. Seems now is a good time as any to start! This is what I came up with so far… It’s a work in progress ( sharing is caring) , but it works!

#region MessageCard function helpers
function MessageCard {
param([scriptblock]$ScriptBlock)
$newScript = "@{$($ScriptBlock.ToString())}"
$newScriptBlock = [scriptblock]::Create($newScript)
& $newScriptBlock
}
function section {
param([scriptblock]$ScriptBlock)
$newScript = "[Ordered]@{$($ScriptBlock.ToString())}"
$newScriptBlock = [scriptblock]::Create($newScript)
& $newScriptBlock
}
function fact {
param([scriptblock]$ScriptBlock)
$Invoked = $ScriptBlock.Invoke()
$Invoked.Keys |
ForEach-Object {
@{
Name = $_
Value = $Invoked.$_
}
}
}
#endregion
#region Main
$NewMessage = MessageCard {
summary = 'This is the summary property'
title = "This is the card's title property"
text = 'This is the cards text property.'
sections = @(
section {
activityTitle = 'Activity Title'
activitySubtitle = 'Activity **Sub**Title'
images = @(
@{
image = "http://connectorsdemo.azurewebsites.net/images/WIN12_Scene_01.jpg&quot;
title = "This is the image alternate text Pic 01"
}
@{
image = "http://connectorsdemo.azurewebsites.net/images/WIN12_Anthony_02.jpg&quot;
title = "This is the image alternate text Pic 02"
}
)
PotentialAction = @(
@{
'@type' = 'Actioncard'
Name = 'Comment'
Inputs = @(
@{
'@type' = 'TextInput'
Id = 'Comment'
isMultiLine = $true
Title = 'Input title property'
}
)
Actions = @(
@{
'@type' = 'HttpPOST'
Name = 'Save'
Target = 'http://...'
}
)
}
@{
'@type' = 'Actioncard'
Name = 'Due Date'
Inputs = @(
@{
'@type' = 'DateInput'
Id = 'dueDate'
Title = 'Input due date'
}
)
Actions = @(
@{
'@type' = 'HttpPOST'
Name = 'Save'
Target = 'http://...'
}
)
}
)
}
section {
title = 'Details:'
facts = fact {
@{
GivenName = 'Irwin'
SurName = 'Strachan'
}
}
}
section {
activityTitle = 'Default MD Support'
activitySubtitle = 'Activity **Subtitle**'
facts = fact {
@{
Email = 'Irwin@DosSantos.ca'
WebSite = '[pshirwins](https://pshirwin.wordpress.com)'
}
}
}
)
}
$restparams = @{
Uri = "Your WebHook Uri"
Method = 'POST'
Body = $($NewMessage | ConvertTo-Json -Depth 6)
ContentType = 'application/JSON'
}
Invoke-RestMethod @restparams
#endregion

And here’s the result:

New-MessageCard

I used the full card format as reference.  I realize that others in the community could easily knock this out the park. Maybe we can make this a community project? I would love to see what the possibilities are here…

Turns out not having a Send-TeamMessage wasn’t as painful as I thought it would be. Many ways to Rome…

Shout out to Brett & Stefan! Gotta love the PowerShell community!!!

Hope it’s worth something to you,

Ttyl,

Urv

PSConfAsia 2017

I recently had the pleasure of presenting at PSConf.asia in Singapore. This was my very first conference as a speaker! As a first time speaker a great way to get started.

Jaap Brasser approached me at PSConf.Eu about doing a session (or two) at PSConf.asia. At first I was hesitant…  me, a speaker… Hmmm… I need to think about… Let me get back to you on that… Jaap was clever enough to appease my ego, now how could I say no?

I decided to get out of my comfort zone and said yes, much to Jaap’s delight! Alright! Another PSConf, this time in Singapore, how cool is that!

So after I said yes I did some googling… Let’s see Singapore… Wait what? 12 Hours??? Oh boy… Flying isn’t really my favorite passtime…

ba plane

Hehe… Fortunately I had night flights so…

Singapore has my kinda weather, tropical! I came well prepared. Poor Rob Sewell was melting in Singapore. Transportation is awesome in Singapore. From and to the conference was about 1 SGD.

The organizers did an awesome job! Great location! I met up with old acquaintances and made some new ones!

Finally got to interact with David das Neves. I wanted to attend his session but I opted for Steve Hosking session on Graph. In case you missed it Graph is going to interconnect everything. Definitely have a look at it. I liked David’s approach on having a Class for different types of log format. So I asked him “Hey no  love for Robocopy?” Next thing I knew it was in it! You can read all about hereRaimund Andrée was also there. I told him how NTFSSecurity module saved me on quite a few occasions. He has some serious plans for the module on PowerShell core, so stay tuned!

Rick Taylor won a book on Windows PowerShell for Developers by Doug Finke! Rick was like: “Wait I know this guy!” So I said we need to take a pic featuring Doug’s book! :-). Max Trinidad is a great guy! Been in IT for some time and has loads and loads of skills and it definitely shows!

The keynote is always the highlight of the Conference. Angel Calvo delivered the keynote like a boss! How can I do the key note justice? It’s all about Digital Transformation.

Digital Transformation

Digital tranformation

This is something we all need to consider. It’s not a question of if but when and how. If you’re content with where you’re at right now then you will be obsolete in the near future. What I really appreciated, was the fact that Angel acknowledged that this change can be overwhelming, but it isn’t an all-or-nothing situation. Start where you can, with what you can and take it from there. This digital transformation may take you places you never thought possible. I spoke to Amanda Debler , her transformation has her working more with kurbernetes, how cool is that?

Azure is about adding value to your business

If you’re still under the impression that Azure is just for offloading servers to the cloud, you’re sorely mistaken. We got some excellent demos by Micheal Greene and Ravi Kiran Chintalapudi on Azure Management Services. Azure is about delivering value to your business. And what has value? Data! Lots and lots of it! Having your servers, application, services just to name a few in Azure, will give you the chance to transform metric and monitoring data into added value for your business. As an OPS guy I always thought of monitoring as a necessary evil. Ravi showed us how to manipulate Azure data that is at your deposition and make it valuable! That one server that isn’t patched adequately or that server that’s missing a configuration, the data is there, it’s up to you to turn that into valuable information.

ChatOps

Michael Greene’s demo on ChatOps blew my mind!

ChatOps

I’ve seen chatbots in action but this will take things to the next level! Here are a few links Michael shared to give you an idea where they’re heading:

PowerShell and the future

When Jeffrey Snover said that PowerShell is finished, I died a little inside. Fortunately I was better prepared this time around when Angel said the same 🙂 . PowerShell has gained critical mass. Moving forward, PowerShell Core is where all attention will be placed. Steve Lee had some interesting charts and number he pulled from GitHub (It’s all about data). Mark Kraus is the nr 1 contributor on GitHub! Michael Greene’s go-to PowerShell version is Core! Joey even had a demo on cross-platform administration starting a session on a Windows and Linux. Unfortunately Joey forget about the Timezone difference, so his servers were down.

Keynote conclusion

Conclusion

Pretty much sums it up, no need to reiterate… 🙂

Networking event

Conferences are a great way  to interact with the Microsoft Team and delegates. It was awesome meeting Michael Greene and Steve lee in the flesh! I decided on a different approach when it came to mingling. I wanted to connect on a personal level. I can always reach out when I’m working on something. So here I am sitting in an Irish pub in Singapore knocking over a few with Michael, Amanda, Jason and Max! I asked Michael  what his thoughts were on running Windows for Workgroup 3.11 in Azure… Hypothetically that is :-P. What followed was a lively discussion! Jason had some real great stories to tell! Max told us about his first job in IT back in ’78 when debugging was literally ‘debugging’. Amanda was like: “I wasn’t even born yet…”

Benjamin had Joey finish his demo in the pub. Joey’s demo didn’t go through because of timezone difference (His Azure Servers shutdown automatically after-hours), but Benjamin wasn’t haven’t that. Joey complied and did the demo in the pub, with all attending cheering him on like proper hooligans! For outsiders we may as well have been watching a football game.

I even have my own fan club!

Suresh

Suresh follows my blog and was pretty excited to meet me! Lil’ ol’ me! Suresh made the 12 hour flight worth it! I enjoy meeting my PowerShell heroes in the flesh, I just never considered myself special… Thanks for the support!!! Appreciate it!

Slides & Code

Before I forget here’s the GitHub link to all my presentation slides and code. So Fabian Dibot was at my Infrastructure session and I almost didn’t recognize him! Both him and Mathias Jessen had fun sending twitter messages during my session 😛 . Tip: make sure you disable browser notification before starting you presentation… I know, a rookie mistake, you caught me guys… 😛

Bartek Bielawski was at my Dependencies session! That was a big honor for me! Bartek is the guy you go to when you’re really stuck! While doing my session I was trying to get a read on Bartek’s facial expression. At some point I could see him scanning the code… was that a smile? Ah! no comment whew! I couldn’t wait to ask him for feedback. Getting a compliment from Bartek on presentation preparation  & code definitely gave me a confidence boost! IMHO I enjoyed presenting this session the most. I did this as a flash session at one of our DuPSUG gatherings. I took a different route using AzureAD instead of plain ol’ AD and came across some fun stuff. AD and AzureAD have different parametersets 😉

I’m really glad I did the PSConf.asia sessions. Milton Goh was hinting on a surprise next year? 🙂 If I’m fortunate to be asked next year, I won’t hesitate!

Thanks for a wonderful experience PSConf.asia! I can’t wait to see where your Digital Transformation takes you!

PSConfAsia

Ttyl,

Urv

 

 

Microsoft Teams cmdlets are here!

‘Sup PSHomies,

Microsoft Teams has released the long anticipated Teams module! And with that a great blog to get you started.

Microsoft Teams is Microsoft’s version of Slack (Ok, I oversimplified that… I know). I’ve been following MSTeams development with Graph Explorer for some time now (Something you should definitely look into).

So I followed the blog’s instructions and everything works as expected!

The cmdlets support the pipeline.  (Get-Command -Name <cmdlet>).Parameterset will get you an overview of what’s supported and/or mandatory. Generallyspeaking, GroupId is mandatory and accepted from the pipeline.

TeamChannel supports value from pipeline

Here’s some code to help test drive the cmdlets.

#region Connect
Connect-MicrosoftTeams
#endregion
#region MSTeams cmdlets
#Get all cmdlets
Get-Command -Module *Teams*
#Only interested in Get-*?
Get-Command -Module *Teams* -Name Get*
#Find out what mandatory and/or accepts ValueFrom* for Get-TeamUser
(Get-Command -Name Get-TeamUser).ParameterSets
#endregion
#region Test drive cmdlets
#Get Team Displays all Teams (Ofcourse it does)
Get-Team
#Get Team Channels. GroupId ValueFromPipelineByPropertyName = True
Get-Team | Get-TeamChannel
#Group TeamChannels by Teams
Get-Team |
ForEach-Object{
$result = Get-TeamChannel -GroupId $_.GroupId
[PSCustomObject]@{
Team = $_.DisplayName
TeamChannel = $result
}
} -OutVariable TeamChannels
#Get Team Users. GroupId ValueFromPipelineByPropertyName = True
Get-Team | Get-TeamUser
#Get TeamUsers
Get-Team |
ForEach-Object{
$result = Get-TeamUser -GroupId $_.GroupId
[PSCustomObject]@{
Team = $_.DisplayName
Users = $result
}
} -OutVariable TeamUsers
#Get TeamMemberSettings
Get-Team |
ForEach-Object{
$result = Get-TeamMemberSettings -GroupId $_.GroupId
[PSCustomObject]@{
Team = $_.DisplayName
TeamMemberSettings = $result
}
} -OutVariable TeamMemberSettings
#endregion

So one thing I was hoping for, was to have a cmdlet to post to TeamChannels. For now posting can be done using Graph beta support for teams. Looking forward to having cmdlets for posting as well (fingers crossed!). Before the cmdlets you needed to do some pre-configuration in order to post using Graph, so the cmdlets definitely makes that step easier!

Well that’s it in a nutshell, happy Testing

Hope it’s worth something to you,

Ttyl,

Urv