Folder Structure

Automate All The Things

At the office we were discussing a new process that needed a new folder in our client documents folder. To add the folder to every single client’s directory, I created a very quick little PowerShell command that did it for me. Something like this:

Get-Item "D:\Clients\*\5. Misc\" | %{New-Item "$($_.FullName)\1. New Folder"}

Totally worked. Took about 5 minutes to write it off the top of my head, and I had created hundreds of folders.

Let’s do a quick breakdown of what I did there. First, I used Get-Item with a wildcard to give me an object for every 5. Misc folder for every client. I then piped that into a ForEach-Object using the % abbreviation. The ForEach-Object command will loop through every directory, running the command for each. In this case I simply used a New-Item command to create the folder under each 5. Misc folder.

But it reminded me that creating all those client folders was a manual process, which is kinda dumb. In addition, changes were all being done manually, things like archiving old clients. Lastly, nothing was enforcing using the proper structure.

The Simple Solution

So I sat down on a lunch break and wrote out the following script. It isn’t full featured, but for less than an hour’s work it does the job quite nicely. Recommended improvements would be parameterization of inputs, and possibly CVS inputs. My testing did not show any issues, but if I had to guess I’d say long file names would be a problem, especially with the archiving sections.

Features:

  1. Create Client Folders
  2. Create Client Sub-folders
  3. Archive Old Clients with Timestamp
  4. Move Unmanaged Client Sub-folders and Documents into an Orphan Folder

For future updates, check out the Set-DirectoryStructure page on GitHub

$Clients = "ABC", "NMO", "XYZ"
$Folders = "1. Vendor Info", "2. Network Infrastructure", "2. Network Infrastructure\1. Firewall Configs", "3. Reports"
$ClientArchive = "Z. Archived"
$OrphanFolder = "Z. Orphans"

$TargetFolder = "D:\Clients"

#Check if path already exists, if it doesn't, create it
function New-DirectoryIfNeeded {
    param (
        [Parameter(Mandatory = $true)]
        $Path
    )
    if (!(Test-Path $Path)) {
        New-Item $Path -ItemType Directory
    }
    return (Test-Path $Path)
}

if (Test-Path $TargetFolder) {
    ForEach ($Client in $Clients) {
        #Make the Client Folder
        $ClientFolder = New-DirectoryIfNeeded "$TargetFolder\$Client"
        if ($ClientFolder) {
            $Folders | ForEach-Object { 
                $Folder = New-DirectoryIfNeeded "$TargetFolder\$Client\$_"
            }
            #Create Orphan Folder
            $OrphanFolderObj = New-DirectoryIfNeeded "$TargetFolder\$Client\$OrphanFolder"
            #Move Orphaned Objects to the Orphan folder
            if ($OrphanFolderObj) {
                $ExistingFolders = Get-ChildItem "$TargetFolder\$Client"
                $ExistingFolders | ForEach-Object {
                    if ($_.Name -notin $Folders -and $_.Name -ne $OrphanFolder) {
                        $NewName = "$($_.Name) - Archived $(Get-Date -Format "yyyy-MM-dd_hhmmss")"
                        $NewObject = Rename-Item -Path $_.FullName -NewName $NewName -PassThru
                        Move-Item -Path $NewObject.FullName -Destination "$TargetFolder\$Client\$OrphanFolder\"
                    }
                }
            }

        }
    } 
    #Create Client Archive
    $ClientArchiveFolder = New-DirectoryIfNeeded "$TargetFolder\$ClientArchive"
    #Move any items from the root folder into archived if they aren't current clients
    if ($ClientArchiveFolder) {
        $ExistingClientFolders = Get-ChildItem $TargetFolder
        $ExistingClientFolders | ForEach-Object {
            if ($_.Name -notin $Clients -and $_.Name -ne $ClientArchive) {
                $NewName = "$($_.Name) - Archived $(Get-Date -Format "yyyy-MM-dd_hhmmss")"
                $NewObject = Rename-Item -Path $_.FullName -NewName $NewName -PassThru
                Move-Item -Path $NewObject.FullName -Destination "$TargetFolder\$ClientArchive"
            }
        }
    }
}