Skip to content

Instantly share code, notes, and snippets.

@noahpeltier
Last active March 14, 2025 16:01
Show Gist options
  • Save noahpeltier/0b8e4548accdd2faa3adebbdcb9c1c17 to your computer and use it in GitHub Desktop.
Save noahpeltier/0b8e4548accdd2faa3adebbdcb9c1c17 to your computer and use it in GitHub Desktop.
Winforms GUI that displays Exchange Online public folders in a tree view with options for adding and/or modifying existing permissions.
# Optionally, connect to Exchange Online if not already connected:
# Import-Module ExchangeOnlineManagement
if (-not (get-command get-mailbox -ErrorAction SilentlyContinue) ) {
Connect-ExchangeOnline
}
# Load required .NET assemblies for WinForms
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# Define permission options based on Microsoft's public folder permission levels
$permissionOptions = @("Owner", "PublishingEditor", "Editor", "PublishingAuthor", "Author", "NonEditingAuthor", "Reviewer", "Contributor", "None")
# Global variable to hold the current folder path
$Global:currentFolder = $null
# Function to refresh the permissions ListView for the currently selected folder
function Refresh-Permissions {
if (-not $Global:currentFolder) { return }
$listView.Items.Clear()
try {
$permissions = Get-PublicFolderClientPermission -Identity $Global:currentFolder
foreach ($perm in $permissions) {
$user = $perm.User.ToString()
$access = $perm.AccessRights -join ", "
$item = New-Object System.Windows.Forms.ListViewItem($user)
$item.SubItems.Add($access)
$listView.Items.Add($item)
}
}
catch {
[System.Windows.Forms.MessageBox]::Show("Error retrieving permissions for folder: $Global:currentFolder","Error",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Error)
}
}
# Create the main form
$form = New-Object System.Windows.Forms.Form
$form.Text = "Exchange Online Public Folders Manager"
$form.Size = New-Object System.Drawing.Size(900,600)
$form.StartPosition = "CenterScreen"
# Create left panel for the TreeView (fixed width)
$panelLeft = New-Object System.Windows.Forms.Panel
$panelLeft.Dock = [System.Windows.Forms.DockStyle]::Left
$panelLeft.Width = 250
$panelLeft.Padding = New-Object System.Windows.Forms.Padding(10)
# Create right panel for controls (fixed width)
$panelRight = New-Object System.Windows.Forms.Panel
$panelRight.Dock = [System.Windows.Forms.DockStyle]::Right
$panelRight.Width = 250
$panelRight.Padding = New-Object System.Windows.Forms.Padding(10)
# Create center panel for the ListView (fills remaining space)
$panelCenter = New-Object System.Windows.Forms.Panel
$panelCenter.Dock = [System.Windows.Forms.DockStyle]::Fill
$panelCenter.Padding = New-Object System.Windows.Forms.Padding(10)
# ---------------------------
# Left Panel - TreeView for Public Folders
# ---------------------------
$treeView = New-Object System.Windows.Forms.TreeView
$treeView.Dock = [System.Windows.Forms.DockStyle]::Fill
$panelLeft.Controls.Add($treeView)
# ---------------------------
# Center Panel - ListView for Permissions
# ---------------------------
$listView = New-Object System.Windows.Forms.ListView
$listView.Dock = [System.Windows.Forms.DockStyle]::Fill
$listView.View = [System.Windows.Forms.View]::Details
$listView.FullRowSelect = $true
$listView.GridLines = $true
$listView.Columns.Add("User",150) | Out-Null
$listView.Columns.Add("Permission Level",130) | Out-Null
$panelCenter.Controls.Add($listView)
# ---------------------------
# Right Panel - Controls for Modifying Permissions
# ---------------------------
# Note: Use relative positioning within the right panel.
# The panel's width is fixed (250), so set control widths to fill (minus padding).
$controlWidth = $panelRight.Width - 20 # subtracting left/right padding
$btnRemove = New-Object System.Windows.Forms.Button
$btnRemove.Location = New-Object System.Drawing.Point(10,20)
$btnRemove.Size = New-Object System.Drawing.Size($controlWidth,30)
$btnRemove.Text = "Remove Selected User"
$btnRemove.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
$panelRight.Controls.Add($btnRemove)
$lblUpdate = New-Object System.Windows.Forms.Label
$lblUpdate.Location = New-Object System.Drawing.Point(10,70)
$lblUpdate.Size = New-Object System.Drawing.Size($controlWidth,20)
$lblUpdate.Text = "New Permission (Update):"
$lblUpdate.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
$panelRight.Controls.Add($lblUpdate)
$cmbUpdatePermission = New-Object System.Windows.Forms.ComboBox
$cmbUpdatePermission.Location = New-Object System.Drawing.Point(10,95)
$cmbUpdatePermission.Size = New-Object System.Drawing.Size($controlWidth,25)
$cmbUpdatePermission.DropDownStyle = 'DropDownList'
$cmbUpdatePermission.Items.AddRange($permissionOptions)
$cmbUpdatePermission.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
$panelRight.Controls.Add($cmbUpdatePermission)
$btnUpdate = New-Object System.Windows.Forms.Button
$btnUpdate.Location = New-Object System.Drawing.Point(10,130)
$btnUpdate.Size = New-Object System.Drawing.Size($controlWidth,30)
$btnUpdate.Text = "Update Selected User"
$btnUpdate.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
$panelRight.Controls.Add($btnUpdate)
$lblAddHeader = New-Object System.Windows.Forms.Label
$lblAddHeader.Location = New-Object System.Drawing.Point(10,180)
$lblAddHeader.Size = New-Object System.Drawing.Size($controlWidth,20)
$lblAddHeader.Text = "Add New User:"
$lblAddHeader.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
$panelRight.Controls.Add($lblAddHeader)
$lblNewUser = New-Object System.Windows.Forms.Label
$lblNewUser.Location = New-Object System.Drawing.Point(10,210)
$lblNewUser.Size = New-Object System.Drawing.Size(80,20)
$lblNewUser.Text = "User:"
$lblNewUser.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
$panelRight.Controls.Add($lblNewUser)
$txtNewUser = New-Object System.Windows.Forms.TextBox
$txtNewUser.Location = New-Object System.Drawing.Point(100,210)
$txtNewUser.Size = New-Object System.Drawing.Size(($controlWidth - 90),25)
$txtNewUser.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
$panelRight.Controls.Add($txtNewUser)
$lblNewPerm = New-Object System.Windows.Forms.Label
$lblNewPerm.Location = New-Object System.Drawing.Point(10,245)
$lblNewPerm.Size = New-Object System.Drawing.Size(80,20)
$lblNewPerm.Text = "Permission:"
$lblNewPerm.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
$panelRight.Controls.Add($lblNewPerm)
$cmbNewPermission = New-Object System.Windows.Forms.ComboBox
$cmbNewPermission.Location = New-Object System.Drawing.Point(100,245)
$cmbNewPermission.Size = New-Object System.Drawing.Size(($controlWidth - 90),25)
$cmbNewPermission.DropDownStyle = 'DropDownList'
$cmbNewPermission.Items.AddRange($permissionOptions)
$cmbNewPermission.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
$panelRight.Controls.Add($cmbNewPermission)
$btnAdd = New-Object System.Windows.Forms.Button
$btnAdd.Location = New-Object System.Drawing.Point(10,280)
$btnAdd.Size = New-Object System.Drawing.Size($controlWidth,30)
$btnAdd.Text = "Add New User"
$btnAdd.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
$panelRight.Controls.Add($btnAdd)
# ---------------------------
# Add panels to the form
# Order is important when docking. The Fill-docked panel should be added first.
$form.Controls.Add($panelCenter)
$form.Controls.Add($panelRight)
$form.Controls.Add($panelLeft)
# ---------------------------
# Populate the TreeView with Public Folders
# ---------------------------
try {
$publicFolders = Get-PublicFolder -Identity "\" -Recurse -ResultSize Unlimited
}
catch {
[System.Windows.Forms.MessageBox]::Show("Error retrieving public folders. Please ensure you are connected to Exchange Online.","Error",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Error)
return
}
# Hashtable to store folder path => TreeNode mappings
$treeNodes = @{}
$sortedFolders = $publicFolders | Sort-Object { ($_.Identity.ToString().TrimEnd("\") -split "\\").Count }
foreach ($folder in $sortedFolders) {
$folderPath = $folder.Identity.ToString()
if ($folderPath -eq "\") {
$node = New-Object System.Windows.Forms.TreeNode("Public Folders")
$node.Tag = $folderPath
$treeView.Nodes.Add($node) | Out-Null
$treeNodes[$folderPath] = $node
}
else {
$folderName = $folder.Name
$index = $folderPath.LastIndexOf("\")
if ($index -le 0) {
$parentPath = "\"
}
else {
$parentPath = $folderPath.Substring(0, $index)
if ($parentPath -eq "") { $parentPath = "\" }
}
$node = New-Object System.Windows.Forms.TreeNode($folderName)
$node.Tag = $folderPath
if ($treeNodes.ContainsKey($parentPath)) {
$treeNodes[$parentPath].Nodes.Add($node) | Out-Null
}
else {
$treeView.Nodes.Add($node) | Out-Null
}
$treeNodes[$folderPath] = $node
}
}
# When a node is selected, update the current folder and refresh permissions
$treeView.Add_AfterSelect({
param($sender, $e)
$Global:currentFolder = $e.Node.Tag
Refresh-Permissions
})
# ---------------------------
# Button Event Handlers
# ---------------------------
# Remove Selected User
$btnRemove.Add_Click({
if (-not $Global:currentFolder) {
[System.Windows.Forms.MessageBox]::Show("No folder selected.","Warning",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Warning)
return
}
if ($listView.SelectedItems.Count -eq 0) {
[System.Windows.Forms.MessageBox]::Show("No user selected in the permissions list.","Warning",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Warning)
return
}
$selectedUser = $listView.SelectedItems[0].Text
try {
Remove-PublicFolderClientPermission -Identity $Global:currentFolder -User $selectedUser -Confirm:$false
[System.Windows.Forms.MessageBox]::Show("User '$selectedUser' removed successfully.","Info",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Information)
Refresh-Permissions
}
catch {
[System.Windows.Forms.MessageBox]::Show("Error removing user '$selectedUser'.","Error",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Error)
}
})
# Update Selected User's Permissions
$btnUpdate.Add_Click({
if (-not $Global:currentFolder) {
[System.Windows.Forms.MessageBox]::Show("No folder selected.","Warning",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Warning)
return
}
if ($listView.SelectedItems.Count -eq 0) {
[System.Windows.Forms.MessageBox]::Show("No user selected in the permissions list.","Warning",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Warning)
return
}
$newPermission = $cmbUpdatePermission.Text.Trim()
if ([string]::IsNullOrEmpty($newPermission)) {
[System.Windows.Forms.MessageBox]::Show("Please select a new permission level from the dropdown.","Warning",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Warning)
return
}
$Script:selectedUser = $listView.SelectedItems[0].Text
try {
# Remove and then re-add with the new permission level
Remove-PublicFolderClientPermission -Identity $Global:currentFolder -User $selectedUser -Confirm:$false
Add-PublicFolderClientPermission -Identity $Global:currentFolder -User $selectedUser -AccessRights $newPermission
[System.Windows.Forms.MessageBox]::Show("User '$selectedUser' updated to '$newPermission'.","Info",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Information)
Refresh-Permissions
}
catch {
[System.Windows.Forms.MessageBox]::Show("Error updating user '$selectedUser' permission to $($newPermission).`n$($_.Exception.Message)","Error",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Error)
}
})
# Add New User
$btnAdd.Add_Click({
if (-not $Global:currentFolder) {
[System.Windows.Forms.MessageBox]::Show("No folder selected.","Warning",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Warning)
return
}
$newUser = $txtNewUser.Text.Trim()
$newPermission = $cmbNewPermission.Text.Trim()
if ([string]::IsNullOrEmpty($newUser) -or [string]::IsNullOrEmpty($newPermission)) {
[System.Windows.Forms.MessageBox]::Show("Please enter both a user and select a permission level.","Warning",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Warning)
return
}
try {
Add-PublicFolderClientPermission -Identity $Global:currentFolder -User $newUser -AccessRights $newPermission
[System.Windows.Forms.MessageBox]::Show("User '$newUser' added with '$newPermission'.","Info",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Information)
Refresh-Permissions
}
catch {
[System.Windows.Forms.MessageBox]::Show("Error adding user '$newUser'.","Error",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Error)
}
})
# Ensure the form gets focus when shown and run the form
$form.Add_Shown({ $form.Activate() })
[System.Windows.Forms.Application]::EnableVisualStyles();
[void] $form.ShowDialog()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment