Using PowerShell to Build a Directory Tree Map

Hey Y’all,
I wrote an interesting bit of PowerShell, yesterday, that I wanted to share with the masses. I’m currently setting up a new repository for work and wanted to include a file structure map in the file. After doing the obligatory Google search, I tried using the PowerShell tree function, but I wasn’t crazy about the characters used to style the tree. Running the command looks a little something like this:

==> tree /F /A
Folder PATH listing for volume Windows
Volume serial number is xxxx-xxxx
|   .editorconfig
|   .gitignore
    |   |
    |   |
    |   +---runbooks
    |   |
    |   |
    |   \---_helpers
    |       |
    |       |
    |       +---Repository Management
    |       |       Build-DirectoryTreeMap.ps1
    |       |
    |       \---Runbook Management
    |               Publish-AzureRunbook.ps1

As you can see, while functional, it’s not exactly pretty. And the empty(-ish) lines are just plain unnecessary. Enter my “Build-DirectoryTreeMap” PowerShell cmdlet:

# Unicode Character References
# [char]0x250C[char]0x2500 = '┌─'
# [char]0x251C[char]0x2500 = '├─'
# [char]0x2514[char]0x2500 = '└─'
# [char]0x2502 = '|'

function Build-DirectoryTreeMap {
			Recursively generate a unicode-based directory map.

			This cmdlet is used to generate a unicode-based directory map that recursively displays a directory's folder structure.
			This is predominantly used in markdown documentation.

		.PARAMETER rootPath
			The path from which to run an iteration of Build-DirectoryTreeMap.
			Note: Defaults to the current directory (e.g., ".")

		.PARAMETER lineStart
			A string that is used to prefix the rootPath's child items.
			If running this script, leave $lineStart set to "".
			This parameter is mostly for use in the recursive execution of this cmdlet.
			Note: Defaults to an empty string (e.g., "")

			A System.Collections.ArrayList of strings that represents the directory tree's structure.
			If running this script, leave $tree set to @("$([char]0x250C)$([char]0x2500) .\").
			This parameter is mostly for use in the recusive execution of this cmdlet.
			Note: Defaults to a System.Collections.ArrayList with one line, pre-added (e.g., @("$([char]0x250C)$([char]0x2500) .\"))

			Build-DirectoryTreeMap -rootPath ".\src";

			Build-DirectoryTreeMap -rootPath "C:\Working";


		[string] $rootPath = ".",
		[string] $lineStart = "",
		[System.Collections.ArrayList] $tree = @("$([char]0x250C)$([char]0x2500) .\")

	$StructureIndicators = @{ "Child" = "$([char]0x251C)$([char]0x2500)";
		"Last" = "$([char]0x2514)$([char]0x2500)"; "Skip" = "$([char]0x2502)"; };

	$childItems = Get-ChildItem $rootPath | Sort-Object -Property @{ Expression = { $_ -is [System.IO.DirectoryInfo] }; Ascending = $false}, Name;

	$childCount = 1;
	if ($childItems -isnot [System.IO.FileSystemInfo]) {
		$childCount = $childItems.Length;

	for ($i = 0; $i -lt $childCount; $i++) {
		$childItem = $childItems[$i];

		$line = $lineStart;

		if ($i -eq ($childCount-1)) {
			$line += "$($StructureIndicators.Last)";
		} else {
			$line += "$($StructureIndicators.Child)";

		if ($null -ne $childItem -and $childItem.Name.Trim() -ne "") {
			$tree.add("$line $($childItem.Name)") | Out-Null;

		if ($childItem -is [System.IO.DirectoryInfo]) {
			$tree = Build-DirectoryTreeMap -rootPath $childItem.FullName -lineStart "$lineStart$($StructureIndicators.Skip)  " -tree $tree;

	return [System.Collections.ArrayList] $tree;

Running the above-defined cmdlet, in the same directory as the tree command, results in the following:

==> Build-DirectoryTreeMap
┌─ .\
├─ docs
│  ├─
│  └─
├─ src
│  ├─ models
│  │  └─
│  ├─ scripts
│  │  ├─ _helpers
│  │  │  ├─ Repository Management
│  │  │  │  └─ Build-DirectoryTreeMap.ps1
│  │  │  ├─ Runbook Management
│  │  │  │  └─ Publish-AzureRunbook.ps1
│  │  │  └─
│  │  ├─ runbooks
│  │  │  └─
│  │  └─
│  ├─ UIs
│  │  └─
│  └─
├─ .editorconfig
├─ .gitignore

See? Isn’t that nicer? As you can see, the function actually works to parse the directory structure, by recursively running itself, if the “current node” is a directory. Always feels nice when you nail a bit of recursion, doesn’t it? Anyways, that’s all. Hope y’all enjoy!

VagrantFile Chapter 1: How it works?

Introduction to Vagrant

First off, let’s define Vagrant.


Vagrant is a tool for building and managing virtual machine environments in a single workflow. With an easy-to-use workflow and focus on automation, Vagrant lowers development environment setup time, increases production parity, and makes the “works on my machine” excuse a relic of the past. 1

As the quote indicates, Vagrant is a means up quickly, and efficiently creating a fresh development environment that, once defined can be recreated, with little effort, on almost anyone’s machine. This could mean that a developer’s first day on the job can shorten a weeks worth of installations and setup into one or two easy commands, depending on the environment that you’ve defined. This can almost seem like magic, but in the course of this deep-dive into vagrant’s workings, I will attempt to pull back the curtains, and reveal the secret behind the sauce.

So, why vagrant?

Vagrant provides a suite of configurations, that make creating a “configurable, reproducible, and portable” 1 environment built around several virtual environment “providers.” 2 Ever had a developer say something like, “it works on my machine?” Well, if you create an environment that can be shared and duplicated on all your developers’ machines, so long as that environment isn’t fiddled with, you can be reasonably sure that what runs on one machine, runs on the rest, or more aptly, what breaks on one machine, breaks on them all. From Dev to DevOps, UI Designers to QA Testers, and everyone in between, vagrant just fits. A vagrant-defined environment can create, recreate, and provide a sterile environment to help everyone with their development needs. And I, for one, and completely sold (barring my dislike of Ruby… heh).


That’s all I’ll write, for now, but here’s an outline of what’s to come:

  • Initialization: Defining your Vagrant environment in a VagrantFile;
  • Boxes & Providers: Specifying which OS, base configuration, and provider, you want to build your setup around;
  • Networks: Using a VagantFile to define the network that your environment will use to talk between boxes, and to the world;
  • Synced Folders: Configuring directories that you wish to sync up to your environment;
  • Provisioning: Using scripts to pre-configure, and run your environment, after it’s been defined; and
  • Advanced Configuration: Things I’ve learned along the way, plus a library I put together to configure your environment, in JSON


The Vagrant Deep-Dive Begins

Hey all,

I will be starting a deep dive into virtualization, using Vagrant, and partnering it with technologies like docker, VirtualBox, Hyper-V, and the like. If anyone has any requests, please chime in, in the comments section of this, or any child category post.



Kevin Cefalu

I have CDO; it’s like OCD, but in alphabetical order.


Credit: Carl Franklin ::

Using Powershell to edit $Env:Path variable

Hey all,

Just came across a minor stumbling block in a build process at work. It seems that a deployment PowerShell script was failing a string comparison.  This would cause the script to duplicate a value until the %PATH% variable would either reach the theoretical limit of 32,767 characters, or clear itself out. I didn’t even know this was possible, but here’s how I proposed to fix it.

$pathToAdd = "C:\Path\To\Add";

[System.Collections.ArrayList]$pathList = $Env:Path.Split(';') | select -Unique;

if ($pathList -notcontains $pathToAdd) 

[System.Environment]::SetEnvironmentVariable('PATH', ($pathList -join ';'), 'Machine');


Classic ASP Got’cha: Removing Session Variables in a Loop

Recently, I was asked to go through some old, OLD, code and fix a few bugs that had been identified in a system that used classic ASP. It was discussed, and determined that the easiest solution was to store a bunch of values in Session, and pass that along to the targeted endpoint. Of course I’d have preferred just about any other method, but time and security was a factor, so I dove right in.

At a particular point in the task I realized that because classic ASP and ASP.NET has issues sharing the same session, my Session.Clear() method, at the endpoint was not clearing all of the session variables properly. So i bashed out a loop to remove extraneous entries, each time, before storing new values. That should do it, right? Wrong…

As it turns out, classic ASP “For Each” loops that are used to remove elements in the iterated collection, update as you remove items, but seem to skip over the next element, because it takes the place of the element you just removed. This means that I had to identify the keys of the elements I wanted to remove, in one loop, and then remove them in a completely separate loop.

The code snippet I came up with looks a little something like this:

Dim keyPrefix
Dim keysToRemove
Dim arrayOfKeysToRemove
keyPrefix = "Key__"
keysToRemove = ""

For Each item in Session.Contents
    If InStr(item, keyPrefix) > 0 then
        keysToRemove = keysToRemove + "," + item
    End If

arrayOfKeysToRemove = Split(keysToRemove, ",")
For i = 0 to UBound(arrayOfKeysToRemove)

I hope this helps someone out! Took me long enough to figure it out, myself.