Tiny PowerShell Project 2 - Recursion and Call Depth Overflow
Recursion in PowerShell (and in programming in general) refers to a technique where a function or method calls itself repeatedly until a certain condition is met. It is a powerful programming concept used for solving problems that can be broken down into smaller, similar sub-problems.
Recursion is a powerful technique, but it's essential to handle it with care, as it can lead to stack overflow errors if not managed properly. The following code is extracted from a Fivver project in which I was asked to list out and extract a sharedmailbox subfolders.
Add-Type -Assembly "Microsoft.Office.Interop.Outlook"
$Outlook = New-Object -ComObject Outlook.Application
$Namespace = $Outlook.GetNamespace("MAPI")
$Recipient = "sharedmailbox@xx.com"
$RecipientAddressEntry = $Namespace.CreateRecipient($Recipient)
$RecipientAddressEntry.Resolve()
$SharedMailbox = $Namespace.GetSharedDefaultFolder($RecipientAddressEntry, [Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox)
$Subfolders = $SharedMailbox.Folders
# Create an array to store folder names
$FolderNames = @()
# Loop through the subfolders recursively
function Get-NestedSubfolders($ParentFolder) {
foreach ($Folder in $ParentFolder.Folders) {
# Add folder name to the array
$FolderNames += $Folder.Name
# Recursive call to retrieve nested subfolders
Get-NestedSubfolders -ParentFolder $Folder
}
}
# Start retrieving subfolders
Get-NestedSubfolders -ParentFolder $Subfolders
# Export the folder names array as CSV
$FolderNames | Export-Csv -Path "folder_names.csv" -NoTypeInformation
So let's examine the above code together, the first 8 lines are necessary for getting us access to the sharedmailbox and reading the main folders. $FolderNames variable is an array that we're intending to use for storing the subfolder names. The function Get-NestedSubfolders is a recursive function that loops over the main folders' folders, adds the subfolders of each parent folders in the loop to our array, and calls itself again with that subfolder to see whether there are more nested subfolders within the children of a parent folder.
If you haven't met this error before, the above code could potentially introduce you to the recursion call depth overflow. The error message you could encounter is due to excessive recursion in the code. To avoid the call depth overflow issue, we can modify the script to use a stack-based approach instead of recursion.
Before I show you the refactored code, let's first learn about a structure that can help us. A stack is a collection data structure that follows the Last-In-First-Out (LIFO) principle, meaning the last element added to the stack will be the first one to be removed. It can be visualized as a stack of plates or books, where you add or remove plates from the top.
In PowerShell, System.Collection.Stack provides a way to work with stacks, and it supports various operations like adding items to the stack, removing items from the stack, and checking if the stack is empty.
Here's an example of how to perform CRUD (Create, Read, Update, Delete) operations with a stack using PowerShell:
# Create a new stack
$stack = New-Object System.Collections.Stack
# CRUD operations
# Create (Push): Add elements to the stack
$stack.Push("Apple")
$stack.Push("Banana")
$stack.Push("Orange")
# Read (Peek): View the top element without removing it
$topElement = $stack.Peek()
Write-Output "Top element of the stack: $topElement"
# Read (Iterate): View all elements in the stack (from top to bottom)
Write-Output "Elements in the stack (from top to bottom):"
$stack | ForEach-Object { Write-Output $_ }
# Update (Replace): Pop the top element and push a new element
$replacedElement = $stack.Pop()
$stack.Push("Cherry")
# Read (Iterate): View all elements in the stack after the update
Write-Output "Elements in the stack after the update (from top to bottom):"
$stack | ForEach-Object { Write-Output $_ }
# Delete (Pop): Remove elements from the stack
$removedElement = $stack.Pop()
$removedElement2 = $stack.Pop()
# Read (Iterate): View all elements in the stack after the deletion
Write-Output "Elements in the stack after the deletions (from top to bottom):"
$stack | ForEach-Object { Write-Output $_ }
In this example, we create a new stack using $stack = New-Object System.Collections.Stack. Then we perform CRUD operations on the stack:
Create (Push): We add elements "Apple," "Banana," and "Orange" to the stack using $stack.Push().
Read (Peek): We view the top element (last added element) without removing it using $stack.Peek().
Read (Iterate): We iterate through the elements in the stack using ForEach-Object and view them from top to bottom.
Update (Replace): We replace the top element by popping it using $stack.Pop() and then push a new element "Cherry" using $stack.Push().
Delete (Pop): We remove elements from the stack using $stack.Pop().
Please note that the stack operations "Peek" and "Pop" will throw an error if the stack is empty. Therefore, it's essential to check if the stack is not empty before performing these operations using $stack.Count -gt 0.
Add-Type -Assembly "Microsoft.Office.Interop.Outlook"
$Outlook = New-Object -ComObject Outlook.Application
$Namespace = $Outlook.GetNamespace("MAPI")
$Recipient = "sharedmailbox@xx.com"
$RecipientAddressEntry = $Namespace.CreateRecipient($Recipient)
$RecipientAddressEntry.Resolve()
$SharedMailbox = $Namespace.GetSharedDefaultFolder($RecipientAddressEntry, [Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox)
# Create a stack to store folder objects
$FolderStack = New-Object System.Collections.Stack
# Create an array to store custom folder objects
$FolderObjects = @()
# Push the root folder to the stack
$FolderStack.Push($SharedMailbox)
# Process folders in a loop until the stack is empty
while ($FolderStack.Count -gt 0) {
$CurrentFolder = $FolderStack.Pop()
# Create a custom folder object and add it to the array
$FolderObject = [PSCustomObject]@{
Name = $CurrentFolder.Name
}
$FolderObjects += $FolderObject
# Add nested subfolders to the stack
$Subfolders = $CurrentFolder.Folders
foreach ($Folder in $Subfolders) {
$FolderStack.Push($Folder)
}
}
# Export the folder objects array as CSV
$FolderObjects | Export-Csv -Path "folder_names.csv" -NoTypeInformation
In the main stack, we start with our main folders, adding them to the stack. We enter a while loop, which is checking on the existence of items within the stack(i.e. whether elements exist within the stack). Entering into the while loop, we take the last element of the stack as our current folder and turn it into a pscustom object, capturing its name. Then we add that object into the array that we created earlier. As we know arrays are immutable so this action of our create a new array and discard the existing one. Then we check to see whether the current folder has any subfolders and if they do we loop over these sub folders adding them to the stack. Finally, once we meet the exist criteria of the stack(when there is no element left) we're ready to export the folder objects array into csv.
As you can see this is a very strong construct and for problems where recursion is not necessary, using stacks or iterative approaches might be more efficient.
Subscribe to my newsletter
Read articles from Hooman Pegahmehr directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Hooman Pegahmehr
Hooman Pegahmehr
Hooman Pegahmehr is a performance-driven, analytical, and strategic Technology Management Professional, employing information technology best practices to manage software and web development lifecycle in alignment with client requirements. He builds high-quality, scalable, and reliable software, systems, and architecture while ensuring secure technology service delivery as well as transcending barriers between technology, creativity, and business, aligning each to capture the highest potential of organization resources and technology investments. He offers 8+ years of transferable experience in creating scalable web applications and platforms using JavaScript software stack, including MongoDB, Express, React, and Node, coupled with a focus on back-end development, data wrangling, API design, security, and testing. He utilizes a visionary perspective and innovative mindset to collect and translate technical requirements into functionalities within the application while writing codes and producing production-ready systems for thousands of users. He designs, develops, and maintains fully functioning platforms using modern web-based technologies, including MERN Stack (MongoDB, Express, React, Node). As a dynamic and process-focused IT professional, Hooman leverages cutting-edge technologies to cultivate differentiated solutions and achieve competitive advantages while supporting new systems development lifecycle. He excels in creating in-house solutions, replacing and modernizing legacy systems, and eliminating outsourcing costs. He exhibits verifiable success in building highly responsive full-stack applications and incident management systems using advanced analytical dashboards while translating complex concepts in a simplified manner. Through dedication towards promoting a culture of collaboration, Hooman empowers and motivates diverse personnel to achieve technology-focused business objectives while administering coaching, training, and development initiatives to elevate personnel performance and achieve team synergy. He earned a winning reputation for transforming, revitalizing, streamlining, and optimizing multiple programs and web-based applications to drive consistent communications across cross-functional organization-wide departments. He manages multiple projects from concept to execution, utilizing prioritization and time management capabilities to complete deliverables on time, under budget, and in alignment with requirements.