Category Archives: Uncategorized

AD Security Group matrix

‘Sup PSHomies,

So last blog was about adding members to a security group efficiently. Which got me thinking, can I reverse this process? If given the security groups with specified members, can I recreate the csv? I do love me a challenge!

So to pick up where we left off, I’ll be using the $addADGroupMembers to repopulate the csv…

#Security Matrix
$Groups = $addADGroupMembers.Keys
function Convert-ArrayToHash($a){
    begin { $hash = @{} }
    process { $hash[$_] = $null }
    end { return $hash }

$template = [PSCustomObject]([Ordered]@{UserID=$null} + $($Groups | Convert-ArrayToHash))

$addADGroupMembers has all the group names we need. I’m converting the group names into an empty hashtable. The $template variable is a custom object I’ll be using to move things  along, I’ll explain as we go…


Now for the tricky part…

$arrMatrix = @()

$Groups |
   $GroupName = $_
      $addADGroupMembers.$_ |
         if($arrMatrix.Count -eq 0) {
            $newItem = $template.PSObject.Copy()
            $newItem.UserID = $_
            $newItem.$GroupName = '1'
            $arrMatrix += $newItem
               $index = [array]::IndexOf($arrMatrix.UserID, $_)
               $arrMatrix[$index].$GroupName = '1'
               $newItem = $template.PSObject.Copy()
               $newItem.UserID = $_
               $newItem.$GroupName = '1'
               $arrMatrix += $newItem

First we have an array to save the results. We only need to worry about groups with members. The $template has been initialized with $null. The first time it runs, $arrMatrix.Count will be zero, so just add this group to get things started. Here’s where it gets interesting, in order to add a newItem to the array I have to clone it first. Adding and saving this way makes sure I have a row with a unique UserID. Truth be told I had to google to figure this one out. Modifying $template and then assigning it to $newItem will only assign the reference. Change the value once more and every item in the array changes! I read it somewhere, it was fun to stumble on this… The more you know…

The next trick was to find the index of a UserID already saved. Google to the rescue! I found this neat trick of using [array]::IndexOf(). This will give you the first index with that value. Lucky for me, my UserIDs are unique 😉
Once I have my index I can add a value of ‘1’ to the group if the UserID is a member. If I can’t find a UserID then a new unique UserID is added to the $arrMatrix

Ok enough chit-chat here’s some code to play with

This should be your endresult


No too shabby eh? 😛

Ok Urv that’s all good and well but when am I going to use this?

Why thank you for asking!

If you’ve ever been in charge of implementing Role Based Access Control then you could appreciate this. A security matrix like this is where I’d start, only now you don’t have to start from scratch… 😉

Here’s how it works…


I created a security group Rol-Consultant for RBAC purposes. This group is a member of all the APP-* groups giving any member access by way of group nesting. Users who are a member of Rol-Consultant don’t have to be a direct member for access. The down side of RBAC is it’s all or nothing, exceptions are real deal breakers…

I did a blog about reporting a user’s nested group membership. Let take user ‘dlbouchlaghmi’. This is what his effective user group membership looks like in list form


The security matrix makes it a bit more visual. Granted, it takes some getting use to but the information is there. Now you can ‘fix’ any issues and reapply the way you see fit! 😉

This has been on my radar for quite some time. Processing security groups this way, makes scripting, I wouldn’t say easier, but more easier to manipulate if you catch my drift…

Ok here’s the code to get the ADSecuirtyMatrix. Do be careful with groups with large memberships. I tried my hand at a group with more than 3800 member, took a couple of minutes, but it worked.

I got one more use to go… Some Operation validation! Stay tuned…

Hope it’s worth something to you…



Get DSA object creation date

‘Sup PSHomies,

The project manager is on to me! 😛

PM: “PowerShell is awesome!!!”

Me: “You know it!!!”

PM: “Glad you agree! So here’s what I need, We need a weekly update of all users, groups in the source domain based on their creation date…”

Me: “Wait, what just happened?

PM: “You were agreeing that PowerShell is awesome? (Grinning)”

Me: “Well played… I ain’t even mad mad at ya…”

And so begins another PowerShell journey 🙂

The idea here is to be proactive in our migration. Instead of asking for a list of newly created users/groups we’ll gather the information ourselves. All the customer has to do now is validate our list as to who is relevant. Which got me thinking, why stop at user/groups?

Remember I needed to recreate the DFS Structure? The data that I used was from February. What if there were newly created DFS links in the mean time? Turns out my hunch paid off!

Now curiosity got the better of me… “What about accounts that were deleted?” We’re migrating in batches. My first action is always to verify that the accounts in the batch exist. Having a list of deleted objects helps verifying it wasn’t a typo and that this object isn’t relevant anymore…

Here’s what I came up with:

I added Contact and PrintQueues as a bonus 😉

For reporting it’s ImportExcel to the rescue!


At first I added a extra property in order to lookup the creation date using yyyyMMdd date format. Turns out it wasn’t necessary. You can just a easily use a filter on Created


The PM is quite pleased… PowerShell is Awesome!!! But you already knew that… 😉

Hope it’s worth something to you




DFSn links Operational readiness

‘Sup PSHomies,

So here is the follow up on the previous blog.

I recreated the shares, check! Now it’s time to create the DFSn Links & targets!

Here’s the code to create the necessary links and targets:

Quick run down. The csv is straight forward Link, Target (tab delimited). I created a few checkpoints (again, better safe than sorry).First, I get a list of all actual DFS links & targets for reference. If the target doesn’t exist create it. That’s where the SMB share preparation came in 😉 If the link doesn’t exist create it. If the target exist, verify that it’s set to the specified link.

In my case I only have 1:1 Dfs link/target relationships. So this worked for me. Be sure to test this before trying it out in production.

I needed to recreate 1386 DFS links. This took a few minutes. 10 failed, not bad considering the amount. Now the old Irwin would have panicked at the sight of something going wrong (Whooosahhh). I kept it together and let script ride out. Once it was done I did my OVF to see which ones  failed 😉

This is one of those rare instances that I created the missing links by hand (don’t judge me I was on a tight schedule). Turns out a few had some form of a special character in the name. Creating them by hand didn’t raise an issue.

Last but not least I wanted to generate a ReportUnit HTML of the DFSn test results. Generating the ReportUnit HTML file didn’t go as I expected. It did succeed in the end., but generating a HTML report for 2772 was a bit too much. Having said that…

There’s a module for Formatting Pester result using PScribo created by Erwan Quélin. Definitely check it out if you want to do everything in PowerShell (Who doesn’t? :-P)

Usage is pretty easy:


Here’s a quick screenshot of Format-Pester output:



Once I resolved the failed test, I ran the test again, nothing but purple and green!

A quick notification in Slack using PSSlack by @pscookiemonster (Even though I’m the only one using Slack at the moment, give it time…)


So I learned that the size of the test when using ReportUnit.exe could be an issue, but if you want to do everything in PowerShell then Format-Pester is a better fit.

Hope it’s worth something to you



File servers configuration report

Sup’ PSHomies,

I recently blogged about how awesome PScribo is, couldn’t forget my other favorite go to module when it comes to reporting: ImportExcel brought to us by Doug Finke!

I’ve blogged about my love for csv files and what the possibilities are when it comes to exporting and what to look out for.

My reason for exporting to csv is mostly to create Excel reports. Well if that’s your case you might as well just cut out the middle-man and go straight to the source!

Excel is a great way to analyze your data. I know my way around Excel so that helps.

The title of this blog is about file servers configuration. Think volumes, shares etc etc. The idea is to gather all File server related data (Now where have you seen that before 😛 ) and create a xlsx file report.

I don’t know about you but it seems like the community is producing more and more high quality blogs on just about every subject you can think of! I can barely keep up!

I enjoyed Jaap Brasser’s take on storing your credentials. Thanks for sharing Jaap! 😉

I’m using a scriptblock in combination with invoke-command to gather the needed information . The scriptblock returns a custom object.

$sbStorage = {
        Volume = $(Get-Volume | Select-Object *)
        SMBShare = $(Get-SmbShare | Select-Object *)
        SMBShareAccess = $(Get-SmbShare | Get-SmbShareAccess)
        SMBShareNTFS = $(Get-SmbShare |
            Where-Object{ $_.Name -ne 'IPC$'} |
            Get-Acl |
                Select-Object @{Name='Path';Expression={($_.Path).Replace('Microsoft.PowerShell.Core\FileSystem::','')}},
        Disk = $(Get-Disk | Select-Object *)
        VSSShadows = $(vssadmin.exe list Shadows)
        VSSWriters = $(vssadmin.exe list Writers)

#region Main
$snapshotStorage = $Servers |
  Invoke-Command  -ComputerName $_.ComputerName -ScriptBlock $sbStorage -Credential $cred

I’m using admin credentials in my test lab so this will also work without -Credential. This was sufficient in my case.

Here’s a quick rundown on what the script basically does. Let’s take a look at documenting Shares to get the general idea…

#region Get Shares
$snapshotStorage |
    $ComputerName = $_.PSComputerName

    $_.SMBShare |
            ComputerName = $ComputerName
            Name = $_.Name
            Path = $_.Path
            Description = $_.Description
            ShareState = $_.ShareState
Export-Csv .\export\storage\FS_SMBShares-$($exportDate).csv -Encoding UTF8 -Delimiter "`t" -NoTypeInformation

#Export to xlsx file
Import-Csv .\export\storage\FS_SMBShares-$($exportDate).csv -Encoding UTF8 -Delimiter "`t" |
Export-Excel -Path $xlsxFile -WorkSheetname SMBShares -AutoSize -BoldTopRow -FreezeTopRow

For each server we’ve captured share information to $_.SMBShares. Simply select the properties you want and save to a custom object. Saving to csv and then exporting to Excel makes sure that everything is a string . This is one of those times that having your object cast as string comes in handy ;-). Also, most of us don’t have Excel on every server. Having the csv files let’s you quickly assess what you can expect in your Excel report.

Export-Excel has quite an extensive parameterset

Export-Excel syntax

No need to open the xlsx file, autosize and freeze to the toprow, just add the following switches -AutoSize -BoldTopRow -FreezeTopRow and your good to go!

Here’s a quick impression of the xlsx file

File Server Excel Report

I can’t say enough good things about Import-Excel. Doug Finke actively maintains this module and is open to suggestions to make it better! Thanks for making reporting in Excel so much easier Doug!

Hope it’s worth something to you



Active Directory Operations Test

‘Sup PSHomies,

Last blog I demonstrated how to create a HTML report from the Active Directory configuration snapshot. Here’s yet another way to get more use out of the Active Directory configuration snapshot.

I started out with the intention of reporting, then it hit me, why not use the snapshot for Operation readiness? Let’s dive in, I’ll explain as we go along…

Before we get started, you’ll need to have your Active Directory specification at hand. Modify $ADConfiguration according to your specifications.

#region Active Directory configuration as you expect it to be. Modify to reflect your Active Directory
$ADConfiguration = @{
    Forest = @{
        FQDN = 'pshirwin.local'
        ForestMode = 'Windows2012R2Forest'
        GlobalCatalogs = @(
        SchemaMaster = 'DC-DSC-01.pshirwin.local'
        DomainNamingMaster = 'DC-DSC-01.pshirwin.local'

    Domain = @{
        NetBIOSName = 'PSHIRWIN'
        DomainMode = 'Windows2012R2Domain'
        RIDMaster = 'DC-DSC-01.pshirwin.local'
        PDCEmulator = 'DC-DSC-01.pshirwin.local'
        InfrastructureMaster = 'DC-DSC-01.pshirwin.local'
        DistinguishedName = 'DC=pshirwin,DC=local'
        DNSRoot = 'pshirwin.local'
        DomainControllers = @(
    PasswordPolicy = @{
        PasswordHistoryCount = 24
        LockoutThreshold = 0
        LockoutDuration = '00:30:00'
        LockoutObservationWindow = '00:30:00'
        MaxPasswordAge = '42.00:00:00'
        MinPasswordAge = '1.00:00:00'
        MinPasswordLength = 8
        ComplexityEnabled = $true
    Sites = @('Default-First-Site-Name')
    SiteLinks = @(
            Name = 'DEFAULTIPSITELINK'
            Cost = 100
            ReplicationFrequencyInMinutes = 180
    SubNets = @()

Quick sidestep, we’re in the middle of implementing a new Infrastructure for a customer. Some post configuration had to be done, FSMO roles rearranged, Global catalogs etc. etc., you know the drill. I got my hand on the Active Directory specifications and filled it in. I did a AD configuration snapshot and was now ready to compare. My colleagues were in the middle of post configuring Active Directory. I noticed that the FSMO roles weren’t as expected. I was missing a Domain Controller and some Sites, subnets and sitelinks. I did a AD snapshot the next day, ran my operation readiness test and surprise, everything was as expected! It wasn’t my intention to supervise my colleagues, but I could give them the good news that the Active Directory is configured as specified.

To give you an idea of what to expect, I did the operation readiness test on my lab. Here’s the script:

And here’s the result:
AD Operation Readiness
My testlab is quite simple.

Validating operation readiness will definitely help you keep things in check! No second guessing: “Did I configure server x as a Global catalog? With the AD Configuration snapshot you can be certain how you left things! “I know for a fact I configured the server as a Global catalog last week.” Compare your past snapshot to what you’re expecting. Create a new snaphot and compare again. If it’s different… Well… Sometimes colleagues forget to communicate changes that have been made… At least you don’t have to second guess yourself 😉 As an OPS guys Operation readiness has my vote!

Hope it’s worth something to you



Coding for speed

The results are in! Great summary about the Hadoop PowerShell Challenge by Stephen Owen! It was fun to see the different approaches. When it comes to speed you can’t beat native C# code! Great tips! Worth the read!

I must say that I learned a lot about speed, and how coding structure matters when you’re going for the gold, as I was reviewing the entries from the Hadoop PowerShell challenge. The winners are at the end of this post, so zip down there to see if you won!

I’ll use the post to cover some of what we learned from the entries here!  Here’s our top three tips for making your PowerShell scripts run just that much faster!

When searching through files, don’t use Get Content

As it turns out Select-String (PowerShell’s text searching cmdlet) is capable of mounting a file in memory, no need to gc it first. It’s also MUCH slimmer in memory, and has speed for days.  Look at the performance difference in a common scenario, search 10 directories of files with it, and with Get-Content.

Using Select-String alone is a 31x Speed Increase!  

View original post 1,421 more words

Pester to validate ADUser properties

‘Sup PSHomies,

See if you can relate. You’re in the middle of a migration, the users need to be created asap. You get a xlsx file with all the necessary properties. A quick Copy/Paste to csv file, Import-Csv  user.csv -Delimiter "`t" | New-ADUser and presto! Whew! Glad we got that out of the way 😉

Feels pretty awesome right? 15 minutes after, your project manager comes asking: “Say, which file did you use?” The one you sent me last week, why? “Uh, there’s a new version on sharepoint, did you use that one?” Well I did ask which file I should use (in my defense I did, that’s why I always email, written proof!). “Well there’s an updated version, could you make sure the users get updated? Thanks!!!” Sigh, here we go again…

At this point I can do two things:

  1. Just delete and recreate. Thing is you’ll loose SIDs and access to homedirectory etc etc. Not exactly ideal.
  2. Update the user properties. Definitely a better option. Still tricky especially using the Set-ADUser cmdlet, but that’s another story.

But before you go off to update the user settings, how about validating what has been changed? Maybe the damage isn’t that bad. I mean if it’s under five changes, I just might  do it manually… Oh who am I kidding? Wait, gimme a minute to catch my breathe from laughing! 😛

Enter Pester for ADUser validation!

With a Pester script to validate your ADUser settings, you’ll never have to second guess if the settings are as they should.

Here’s the result:ADPesterResults

Here’s a quick rundown of the script:

First I’ll just get all the user settings using $verify = Get-ADUser -Identity $user.SamAccountName -Properties *.

$ADPropertiesToVerify =  ($csvADUsers | Get-Member | Where-Object {$_.membertype -eq 'noteproperty'}).name will get me all the properties in the csv file. No need to map properties manually. Now I can loop through any amount of properties!

Next up, making sure empty properties get $null

if (([string]::isNullOrEmpty($user.$attribute))) {
   $user.$attribute = $null

$null isn’t equal to empty (Ofcourse you already knew that!)

Now compare what’s in the csv to what Get-ADUser found:

if($attribute -eq 'Path'){
   it "User is located in $($user.$attribute)" {
   it "User property $($attribute) value is $($verify.$attribute)" {
      $user.$attribute | Should be $verify.$attribute

Quick note: I used Path to create the user in a specific OU. There’s no Path property in Get-ADUser. So I did the next best thing, just verify that path is part of the user’s distinguishedname 😉

I also added a little bonus to verify the user’s homedirectory exists and that the user is also the owner.

Being able to validate will definitely give you peace of mind…

Hope it’s worth something to you