Monthly Archives: February 2015

DRY (Don’t repeat yourself) principal

Admit it, we’re all guilty of this at some point. There’s no shame in it, but if you persist in your ways once it has been brought to your attention…

I had a WET (We Enjoy Typing, get your mind out of the gutter!) moment recently. It had to do with getting the header from a csv File. Here’s where a little knowledge can derail real quick. Headers in a csv file are the first line, right? So get the first line of the csv file, do a -split ‘delimeter’ and I’m good to go right? Nothing wrong with the logic, but see, I’ll still be doing an import-csv later on… DRY!

Instead of getting the header ahead, why not get it after I’ve done the import? How? Why thank you for asking… 🙂

#region: csvLocation
$csvLocation =
@"
LocationCode,StraatNaam,PostCode,Stad
1X,Elmstreet 26,1666 XX,NightMare
2X,PeterPan 01,0001 ZT,Neverland
3x,Mulan 5, 1111 TK, Disney
"@ |
ConvertFrom-Csv -Delimiter ','

$header = ($csvLocation | Get-Member | Where-Object {$_.membertype -eq 'noteproperty'}).name
 #endregion

I got that one on FB PowerShell group. It’s rapidly becoming my favorite social media hub for everything PowerShell related.

Here’s another: Lookup CSV row by value

This has helped me many a times in the past.

#region Group as a hashtable by 'LocationCode'
$lookupLocationCode = $csvLocation | Group-Object -AsHashTable -Property 'LocationCode'
#endregion

Now if I’m looking for a CSV row by LocationCode value that’s pretty straightfoward. At least that’s what I thought… DRY!

Here’s the better option:

$csvLocation.Where({$_.LocationCode -eq '1x'})

LocationCode        StraatNaam          PostCode            Stad
------------        ----------          --------            ----
1X                  Elmstreet 26        1666 XX             NightMare

With this option I can even add a lil’ extra say :

$csvLocation.Where({($_.LocationCode -eq '1x') -and ($_.Stad -eq 'NigtMare')  })

#Oops a 'typo' no result, let's retry...
$csvLocation.Where({($_.LocationCode -eq '1x') -and ($_.Stad -eq 'NightMare')  })

#Ah... Better...
LocationCode        StraatNaam          PostCode            Stad
------------        ----------          --------            ----
1X                  Elmstreet 26        1666 XX             NightMare

With group-object as hashtable I need to select the key beforehand, this is way better! Thanks Hey, Scripting guy!

Know your cmdlets!

So this one is kinda embarassing. I had an old function lying around to ping in PowerShell back in the days before Test-Connection. Thing is, I was still using it. Michaja van der Zouwen pointed out to me that there’s a cmdlet for that… “Oh ok, Thanks!” And as luck would have it, at the PowerShell Summit, Don Jones pointed out that if you’re using ping instead of Test-Connection then your’e doing it wrong! I laughed “like a farmer with a toothache (Denglish)” Hehe… English equivalent: A phony laugh. Ok it wasn’t ‘that’ phony… Take away: Don’t reinvent the wheel… That’s enough shaming for one day…

Whatever your present/future endeavour, think DRY! You won’t regret it…

Hope it’s worth something to you,

Ttyl,

Urv

Return of the splat!!!

‘Sup PSHomies? How’s it hashin’? 😛

What is a PSHomie pray tell? PSHomie (plural PSHomies) is a term of endearment used amongst PowerShell enthusiasts… Hehe…

Spreading the gospel that is PowerShell! I have my first splat disciple, he goes by the name of Ralf Kervezee. Ralf is an all-around IT guy. He recently took to PowerShell with gusto!!! Go Ralfie!!! We affectionately call him “stuiterbal” which roughly translates to “bouncy ball”. If you meet him in person you’ll understand why.

This meme kinda sums Ralf up:

hey_look_a_squirrel_shirt

Yeah that’s our Ralfie!!! 😉

I’ve been grooming Ralf to do Active Directory Migration based on PowerShell scripts. Now bear in mind Ralf is our “stuiterbal”, he doesn’t ask one question ,he asks five, and while I’m trying to answer the first he’s already moved on to something else. He enjoys figuring things out for himself, which is a good trait. I’ve seen many shamefully request ready-made scripts on the InterNet. If you find a script on the InterNet you should at least test it and know how it works, if you can’t figure it out then maybe you shouldn’t use it eh? Seem fair enough right?

So instead of giving him something ready-made, I gave him a script that helped him figure out how to go about solving his problem. I recently found something on the Net that didn’t work at first but I figured it out, and I must say I ended up appreciating the solution more. Give a man a fish… 😉

We both work at the same company, so we have a mutual CSV Header challenge. There’s a discussion on FB PowerShell group about overengineering your solutions. I’ve been guilty of that as well… Ralf wants to do everything in PowerShell and hey who can blame him? 😉 Ok Ralfie here goes…

First we’ll start with a mock CSV File with users:

#region: csvUsers
$csvUsers =
@"
UserID nieuw,LoginNaam Nieuw,Personeelsnummer,VoorNaam,AchterNaam,Afdeling,Bedrijf, PostCode,StraatNaam,Stad,LocationCode
150131,150131,150131,John,Wayne,IT,Acme,1222 XX,Elmstreet 26,Sciencefiction,1X
150141,150141,150141,Jane,Doe,HR,Acme,1222 XX,Elmstreet 26,Sciencefiction,2X
150211,150211,150211,Jack,Swift,IT,Acme,1222 XX,Elmstreet 26,Sciencefiction,3X
150211,150211,150211,James,Bond,CEO,Acme,0007 XX,Elmstreet 26,Sciencefiction,
"@ |
ConvertFrom-Csv -Delimiter ','
#endregion

Now I usually do my formatting in Excel but to entertain Ralfie, let’s do it in PowerShell, why? Because we can!!!

#region: csvLocation
$csvLocation =
@"
LocationCode,StraatNaam,PostCode,Stad
1X,Elmstreet 26,1666 XX,NightMare
2X,PeterPan 01,0001 ZT,Neverland
3x,Mulan 5, 1111 TK, Disney
"@ |
ConvertFrom-Csv -Delimiter ','

#Group as a hashtable by 'LocationCode'
$lookupLocationCode = $csvLocation | Group-Object -AsHashTable -Property 'LocationCode'
#endregion

Noticed the greyed line (no, not the scrollbar the other grey line)? That’s the key to changing location information. I don’t remember where I found this trick. I do know that Jeff Hicks recently post an article about group-object so I’m guessing it’s probably from Jeff.

And now we need to translate the CSV to something readable by a cmdlet. By the way, I noticed that tab delimiters could be an issue so I used comma this time, that way the example works…

#region: HashTable to translate csv Header
$hshHeader =@{
   'UserID nieuw' = 'SamAccountName'
   Personeelsnummer = 'EmployeeID'
   VoorNaam = 'GivenName'
   AchterNaam = 'SurName'
   Afdeling = 'Department'
   Bedrijf = 'Company'
   PostCode = 'PostalCode'
   StraatNaam = 'StreetAddress'
   Stad = 'City'
}

The hash table will help later on to convert the CSV Header into parameters a cmdlet understands. There’s an up and downside to this approach. The downside is that if the header changes in the csv File you need to update the changes here. no more blackbox effect. The upside, no manual editing. It’s a tradeoff, you decide if it’s worth it…

Here’s the main part. See if you can connect the dots… 😉

#region: Main

#Empty Array to save results
$arrResults = @()

foreach ($user in $csvUsers) {
   $UserProperties = @{}

   foreach ($property in $hshHeader.Keys) {
      $userValue = [string]$user.$property

      #Only add to the hashtable if it has a value otherwise splat will fail
      #You could make the value = $null as an alternative
      if (!([string]::isNullOrEmpty($userValue))) {
         $UserProperties += @{$($hshHeader[$property])=$userValue}
      }
   }

   #Add the hash Key Identity to the table. Identity key value equals SamAccountName
   #You can also remove from the hash table if necessary.
   $UserProperties.Add('Identity', $UserProperties.SamAccountName)

   #Change the current value to the one found in csvLocation
   if (!([string]::isNullOrEmpty($User.'LocationCode'))) {
      $UserProperties['City'] = $lookupLocationCode.$($User.'LocationCode').Stad
      $UserProperties['StreetAddress'] = $lookupLocationCode.$($User.'LocationCode').StraatNaam
      $UserProperties['PostalCode'] = $lookupLocationCode.$($User.'LocationCode').PostCode
   }
   else {
      $UserProperties['City'] = $null
      $UserProperties['StreetAddress'] = $null
      $UserProperties['PostalCode'] = $null
   }

   $arrResults += New-Object PSObject -Property $UserProperties
}
#endregion

And last but not least present new results in Out-GridView

#region: Present results in Out-GridView
$arrResults |
Select-Object EmployeeId,SamAccountName,GivenName,SurName,Department,Company,StreetAddress,PostalCode,City |
Out-GridView -Title "New CSV translated and updated with Location Code - $(Get-Date)"
#endregion

Noticed that James Bond doesn’t have a forwarding Address anymore (because he’s a secret agent and shouldn’t have been listed in the first place!). That’s the kind of fun you can have with PowerShell.

Here’s the full script

#region: csvUsers
$csvUsers =
@"
UserID nieuw,LoginNaam Nieuw,Personeelsnummer,VoorNaam,AchterNaam,Afdeling,Bedrijf, PostCode,StraatNaam,Stad,LocationCode
150131,150131,150131,John,Wayne,IT,Acme,1222 XX,Elmstreet 26,Sciencefiction,1X
150141,150141,150141,Jane,Doe,HR,Acme,1222 XX,Elmstreet 26,Sciencefiction,2X
150211,150211,150211,Jack,Swift,IT,Acme,1222 XX,Elmstreet 26,Sciencefiction,3X
150211,150211,150211,James,Bond,CEO,Acme,0007 XX,Elmstreet 26,Sciencefiction,
"@ |
ConvertFrom-Csv -Delimiter ','
#endregion

#region: csvLocation
$csvLocation = 
@"
LocationCode,StraatNaam,PostCode,Stad
1X,Elmstreet 26,1666 XX,NightMare
2X,PeterPan 01,0001 ZT,Neverland
3x,Mulan 5, 1111 TK, Disney
"@ |
ConvertFrom-Csv -Delimiter ','

#Group as a hashtable by 'LocationCode'
$lookupLocationCode = $csvLocation | Group-Object -AsHashTable -Property 'LocationCode'
#endregion

#region: HashTable to translate csv Header
$hshHeader =@{
   'UserID nieuw' = 'SamAccountName'
   Personeelsnummer = 'EmployeeID'
   VoorNaam = 'GivenName'
   AchterNaam = 'SurName'
   Afdeling = 'Department'
   Bedrijf = 'Company'
   PostCode = 'PostalCode'
   StraatNaam = 'StreetAddress'
   Stad = 'City'
}

#region: Main

#Empty Array to save results
$arrResults = @()

foreach ($user in $csvUsers) {
   $UserProperties = @{}

   foreach ($property in $hshHeader.Keys) {
      $userValue = [string]$user.$property

      #Only add to the hashtable if it has a value otherwise splat will fail
      #You could make the value = $null
      if (!([string]::isNullOrEmpty($userValue))) {
         $UserProperties += @{$($hshHeader[$property])=$userValue}
      }
   }

   #Add the hash Key Identity to the table. Identity key value equals SamAccountName
   #You can also remove from the hash table if necessary
   $UserProperties.Add('Identity', $UserProperties.SamAccountName)

   #Change the current value to the one found in csvLocation
   if (!([string]::isNullOrEmpty($User.'LocationCode'))) {
      $UserProperties['City'] = $lookupLocationCode.$($User.'LocationCode').Stad
      $UserProperties['StreetAddress'] = $lookupLocationCode.$($User.'LocationCode').StraatNaam
      $UserProperties['PostalCode'] = $lookupLocationCode.$($User.'LocationCode').PostCode
   }
   else {
      $UserProperties['City'] = $null
      $UserProperties['StreetAddress'] = $null
      $UserProperties['PostalCode'] = $null
   }

   $arrResults += New-Object PSObject -Property $UserProperties
}
#endregion

#region: Present results in Out-GridView
$arrResults | 
Select-Object EmployeeId,SamAccountName,GivenName,SurName,Department,Company,StreetAddress,PostalCode,City |
Out-GridView -Title "New CSV input updated with Location Code - $(Get-Date)"
#endregion

Well Ralfie there you have it, now it’s you turn to pay it forward…

Hope it’s worth something to you…

Ttyl,

Urv

Translating a CSV Header

Let’s face it! PowerShell and CSV are a match made in heaven!
Remember the good ol’ vbs days importing a csv file? Yeah… Good times! 😉

Whenever I need to do any bulk Active Directory activity, csv is my choice for source input.
In an ideal situation the headers should reflect the cmdlet parameter, you know, for splatting purposes. Trust me once you splat, you’ll never go back!!! (We get it Urv you’re a huge fan of splatting!!!)

I work in the Netherlands where Dutch is the common language. I’ve hinted in the past that I’d love to have my csv file with the right header in place. “Now see here Mr. Strachan (Oh oh, I dun messed up now…)”. “We could add a comment to the cell in xlsx that could show what the header means in dutch”, I muttered as a last defense… Yeah, ain’t gonna happen anytime soon…

What I did in the past was add a new line in xlsx to line up the right header entry to the correct cmdlet parameter, manually (don’t judge me). “Well, if you promise to keep the headers consistent then I’ll think of something”. Ok so here’s the something I thought about…

Import the csv and then create [PSCustomObjects] as you go along using the pipeline. Pretty straightforward actually. I’ve noticed that when scripting I tend to shy away from the pipeline. I usually gather everything first and then process.

@"
UserID nieuw	LoginNaamNieuw	Personeelsnummer	VoorNaam	AchterNaam	Afdeling	Bedrijf	PostCode	StraatNaam	Stad
150131	150131	150131	John	Wayne	IT	Acme	1222 XX	Elmstreet 26	Sciencefiction
150141	150141	150141	Jane	Doe	HR	Acme	1222 XX	Elmstreet 26	Sciencefiction
150211	150211	150211	Jack	Swift	IT	Acme	1222 XX	Elmstreet 26	Sciencefiction
"@ |
ConvertFrom-Csv -Delimiter "`t" |
foreach {
   [PSCustomObject]@{
      SamAccountName =  $_.'UserID nieuw'
      Name = $_.’UserID nieuw’
      EmployeeID = $_.Personeelsnummer
      GivenName = $_.Voornaam
      SurName = $_.Achternaam
      DisplayName = "$($_.VoorNaam) $($_.Achternaam)"
      Department =  $_.Afdeling
      Company = $_.Bedrijf
      Postalcode = $_.PostCode
      StreetAddress = $_.StraatNaam
      City = $_.Stad
   }
} |
Out-GridView -Title "Imported CSV with Different header  $(Get-date)"

I usually do all my pre formatting in Excel (been doing that for years). I’d add an extra column for DisplayName and do a concatenate in Excel. I try not to reformat data in my scripts. that way the script could remain a blackbox so to speak. Companies usually have different naming conventions. DisplayName, for instance, could be one of the following naming conventions:

  • “GivenName SurName”
  • “SurName GivenName
  • “GiveName Initials SurName”

By doing it in Excel it saved me the hassle of revisioning my scripts every time. I just need to know what it is. I decided to do it on the fly this time. I learned a new acronym recently: The DRY principal (Don’t repeat yourself)… Seems legit…

Instead of out-gridview you could now just as easily created the users using New-ADUser cmdlet. Just make sure the mandatory parameter is in place.

Well I caved, but in the process I got reacquainted with the pipeline, so I’m considering it a win! It’s all about the silver lining eh? 😉

Ttyl,

Urv

Getting Orphaned HomeDirectories – Take II

Speaking of correlations, here’s another spin on getting orphaned homedirectories.

The previous version will get you a crude report about your home directories. So what’s the next step? Who do you approach with your findings? What else can you tell me? And more appropriately, how do I go about rectifying my findings? The report started out as finding orphaned home directories but it could be a lot more! 🙂

Active Directory has quite a few Attributes to play with: location, Department, Manager, OfficePhone, DistinguishedName, just to name a few.

What if we were to add a few more attributes to the mix, say, location,department and manager? See where I’m going with this?

Of course the attributes need to be filled in, just a thought… 😉

Adding these attributes can help generate reports based on the correlations you deem necessary.

You can now gather all disabled accounts and report to the manager and inform him that these users will be archived without further objection. Need an overview of all users per location. Done! You can always present your findings to a branch manager. How about an an overview of all the users based on the department attribute? 🙂

But here’s the catch, we’re not going to do the correlation in PowerShell… Say what??? So what’s the point of all this? The point is to gather a dataset with which you can do your own data correlation. This way your not limiting yourself to potential insights otherwise overlooked.

This isn’t anything new, a DB guy would just smirk at this, “Oh so you finally figured out what Databases are for? Well good for you!”

So why the sudden interest in datasets and correlations and stuff? Well it all has to do with a book I recently read: Big Data: A Revolution That Will Transform How We Live, Work, and Think. A good read for anyone who wants to learn more about the Big Data movement. There’s definitely an upside (and a downside as well) when it comes to Big Data. Definitely worth the read!

It would seems that I too (ahem) have fallen prey into thinking that datasets have a singular purpose to which their value is tied. I started out with only reporting orphaned homedirectories, but by adding more attributes I got much more out of the dataset. What does manager, department or location have to do with orphaned home directories? At first sight, not much, but in the end it gave us more information about the orphaned homedirectories. I added the user’s OU as well on the odd chance department isn’t filled in, I’d still have a idea where to place the user based on the OU. What I found was that all the disabled accounts were moved to a “Disabled” OU. See? Instant added value! 🙂

Well, I think you get the picture.

Here’s the updated version:

[CmdletBinding()]
param(
   [string]$HomeFolderPath = '\\server\users$'
)

# Main loop, for each folder found under home folder path AD is queried to find a matching samaccountname
Get-ChildItem -LiteralPath "$HomeFolderPath" -Force | Where-Object {$_.PSIsContainer} | ForEach-Object {
   Try{
      $CurrentPath = Split-Path -Path $_ -Leaf
      $ADResult = ([adsisearcher]"(samaccountname=$CurrentPath)").Findone()

      #DRY Principle: Let's just go ahead and get these properties if ADResults isn't empty
      if ($ADResult){

         #Get Manager's Name if filled in
         $manager = $null
         if($($ADResult.Properties.manager) -ne $null) {
            $manager = Get-ADUser -Identity $($ADResult.Properties.manager) | Select-Object Name -ExpandProperty Name
         }
            
         #Let's just default status to enabled
         $status = 'Enabled'
         if (([boolean]($ADResult.Properties.useraccountcontrol[0] -band 2))){
            $status = 'Disabled'
         }

         #Get user Parent OU ADSPath
         try{
            $ouIndex = $($ADResult.Properties.distinguishedname).IndexOf('OU=')
            $OU = ($ADResult.Properties.distinguishedname).Substring($ouIndex)
         }
         catch{
            Write-Warning -Message "Object $($ADResult.Properties.distinguishedname) is in a Container. "
            $OU='Default Container'
         }

         $HashProps = [PSCustomObject]@{
            'SamAccountName' = $CurrentPath
            'Name' = $($ADResult.Properties.name)
            'FullPath' = $_.FullName
            'HomeDirectory' = $($ADResult.Properties.homedirectory)
            'Manager' = $manager
            'Location' = $($ADResult.Properties.l)
            'Department' = $($ADResult.Properties.department)
            'Account Status' = $status
            'OU' = $OU
         }
      }
      Else {
         #no matching samaccountname has been found
         $HashProps = [PSCustomObject]@{
            'SamAccountName' = $CurrentPath
            'Name'= 'N/A'
            'FullPath' = $_.FullName
            'HomeDirectory' = 'N/A'
            'Manager' = 'N/A'
            'Location' = 'N/A'
            'Department' = 'N/A'
            'Account Status' = 'Non-Existent'
            'OU' = 'N/A'
         }
      }
   }
   catch {
      Write-Warning $_
   }
   finally{
      #write output $HashProps
      $HashProps
   }
}

Hope it’s worth something to you.

Ttyl,

Urv

Getting oprhaned HomeDirectories

First things first, I have to give Jaap Brasser his props!

Jaap’s script does a whole lot more!

I liked the idea of finding orphaned home directories on a volume for reporting purposes. These days I’m more into gathering data than trying to interpret the data in the script as I go along. I’ll explain. By giving you the raw data, you can do your own correlation and come up with insights that best fits you!

Home directories are usually gathered under a Parent folder ‘Home’ (Yeah, just go with it please…) Let’s assume that the folders in the parent folder are usernames (\\server\Home\%USERNAME%? We good? Ok) We can then use that folder name to lookup an account In Active Directory. The outcome of that query could be either nonexistent,disabled or enabled. If it’s non-existent then why is it still there? If disabled, it’s just taking up space and should be archived as soon as possible. Once you move or delete non-existent or disabled user home directories, you’ll remain with user home directories you need to pay attention to. Less clutter, everyone’s happy!

Here’s where I added a little flavor of my own, I also added the Active Directory User Home Directory property to the mix. The account may be enabled but that doesn’t necessarily mean the folder is the one being used on the volume. I found that some users where using a subfolder within that directory. Granted the NTFS rights were there, still, wasn’t what I expected. Some homedirectory properties were empty, there is a folder but it isn’t being used. Adding the HomeDirectory gave me just a little bit more to work with and some more insight.

Export to CSV and fire up Excel and do your correlation there! Need to find enabled users home directory that are different? or empty? Why yes you can! 🙂

So here’s the script:


<#

    Author: ing. I.C.A. Strachan
    Version: 1.0.0
    Version History:

    Purpose: Find enabled,disabled and orphaned AD user accounts based on home directory
             name.

             Return UserName,FullPath,HomeDirectory,AccountStatus
             UserName: The folder found under $HomePolderPath is user as username
             FullPath: The fullpath of the folder found under $HomePathFolder
             HomeDirectory: Active Directory User HomeDirectory property of disabled and enabled AD Accounts
             AccountStatus: Either non-existent,enabled or disabled

#>

[CmdletBinding()]
param(
    [string]$HomeFolderPath = '\\server\home$',

    [switch]$Export
)

# Check if HomeFolderPath is found, exit with warning message if path is incorrect
if (!(Test-Path -LiteralPath $HomeFolderPath)){
    Write-Warning &quot;HomeFolderPath not found: $HomeFolderPath&quot;
    exit
}

#Empty array to hold results
$arrExportOrphanedHomeFolders = @()

# Main loop, for each folder found under home folder path AD is queried to find a matching samaccountname
Get-ChildItem -LiteralPath &quot;$HomeFolderPath&quot; -Force | Where-Object {$_.PSIsContainer} | ForEach-Object {
    Try{
        $CurrentPath = Split-Path -Path $_ -Leaf
        $ADResult = ([adsisearcher]&quot;(samaccountname=$CurrentPath)&quot;).Findone()

        # If no matching samaccountname is found this code is executed and displayed
        if (!($ADResult)) {
            $HashProps = @{
                'UserName' = $CurrentPath
                'FullPath' = $_.FullName
                'HomeDirectory' = 'N/A'
                'Account Status' ='Non-Existent'
            }

            # Output the object
            $arrExportOrphanedHomeFolders += New-Object -TypeName PSCustomObject -Property $HashProps

            # If samaccountname is found but the account is disabled this information is displayed
        }
        elseif (([boolean]($ADResult.Properties.useraccountcontrol[0] -band 2))) {
            $HashProps = @{
                'UserName' = $CurrentPath
                'FullPath' = $_.FullName
                'HomeDirectory' = $($ADResult.Properties.homedirectory)
                'Account Status' ='Disabled'
            }
            # Output the object
            $arrExportOrphanedHomeFolders += New-Object -TypeName PSCustomObject -Property $HashProps

            # Reserved for future use, folders that do have active user accounts
        }
        else {
            $HashProps = @{
                'UserName' = $CurrentPath
                'FullPath' = $_.FullName
                'HomeDirectory' = $($ADResult.Properties.homedirectory)
                'Account Status' ='Enabled'
            }
            # Output the object
            $arrExportOrphanedHomeFolders += New-Object -TypeName PSCustomObject -Property $HashProps
        }
    }
    catch {
        Write-Warning $_
    }
}

#Present results
if ($Export) {
    Write-Verbose &quot;Exporting results to $pwd\export\dsa\Get-OrphanedHomeFolders.csv&quot;
    $arrExportOrphanedHomeFolders | select UserName,FullPath,HomeDirectory, 'Account Status' |  Export-CSV -NoTypeInformation &quot;$pwd\export\dsa\Get-OrphanedHomeFolders.csv&quot; -delimiter ';' -Encoding UTF8
}
else {
    if (!($PSCmdlet.MyInvocation.BoundParameters['Verbose'].IsPresent)) {
        $arrExportOrphanedHomeFolders | select UserName,FullPath,HomeDirectory, 'Account Status' | Out-GridView -Title &quot;OrphanedHomeFolders - $(Get-Date)&quot;
    }
}

For reporting purposes, it’s a start… 😉

Hope it’s worth something to you!

Ttyl,

Urv

Getting folder tree a few levels deep

I don’t know about you guys, but there are times you need to figure out security Inheritance on a folder tree. You generally don’t have to go through the entire tree (if you’re lucky) just a few subfolders deep. When setting NTFS rights on a folder, rule of thumb is to go three levels deep (Ok maybe four) so that’s a good place to start. To get a general impression of a folder tree, you’d basically go three levels deep. Enumerating subfolders in levels, helps in circumventing the dreaded Max_256 error, although, if you run into errors at level 3, then brace yourself…

The first step is getting a list of all the subfolders you want to enumerate. Here’s my take on how get that list 😉

The trick to getting the subfolder is adding “\*” to the folder you want to enumerate.

“\*” will get me all of the first level subfolders

“\*\*” will get me all of the second level subfolders etc. etc.

The script is pretty straightforward.


<#

Author: ing. I.C.A. Strachan
Version: 1.0.0
Version History: 

Purpose: Get Subfolders from a Folder up to 6 levels deep
         whichever comes first. Output is presented in Out-GridView

Syntax: Get-FolderListLevel -FolderPath <Folder> -Level <Default is 3 >

#>

[CmdletBinding()]
param(
    [string]$FolderPath = $env:USERPROFILE ,

    [ValidateRange(1,6)]
    [int]$level=3
)

$arrExportFolders = @()

for ($i=0;$i -le $level;$i++) {
    $subLevel ='\*' * $i

    if ( $i -eq 0){
        $arrExportFolders += Get-Item "$($FolderPath)$($subLevel)" |
        Select-Object FullName,
        @{Name='Level'; Expression={$i}},
        @{Name='FolderName'; Expression={$_.name.PadLeft($_.name.length + ($i* 5),'_')}}
    }
    Else {
        $arrExportFolders += Get-ChildItem "$($FolderPath)$($subLevel)" -Directory|
        Select-Object FullName,
        @{Name='Level'; Expression={$i}},
        @{Name='FolderName'; Expression={$_.name.PadLeft($_.name.length + ($i* 5),'_')}}
    }
}

$arrExportFolders |
select-object FolderName,Level,FullName |
Sort-Object FullName |
Out-GridView -Title "Folder list $($FolderPath) - $($level) Levels deep - $(get-date)"

Line 37 is a little gem I got from Ashley McGlone’s  gPLink_report script It will pad the folder’s name attribute on the left side with 5x ‘_’ the current level.

This will give you an impression of the folder tree much like tree command 🙂

I’m sure you’ll find other uses once you have your list…

Hope it’s worth something to you!

Ttyl,

Urv