So I found the commands in Exchange Powershell to add a user to have rights to a shared mailbox. Then I wrote it out as a script.
Import-Module ExchangeOnlineManagement $AdminAddress = Read-Host -Prompt "Enter your admin e-mail address" Connect-ExchangeOnline -UserPrincipalName $AdminAddress -ShowProgress $true $SharedCalendar = Read-Host -Prompt "E-mail address of shared mailbox" $RightsRecipient = Read-Host -Prompt "E-mail address of person getting rights to calendar" $RightsLevel = Read-Host -Prompt "Enter either Editor or Reviewer" Add-MailboxFolderPermission -Identity $SharedCalendar:\calendar -user $RightsRecipient -AccessRights $RightsLevel
Now, this works… assuming you don’t have any typos and you have all your ducks in a row. HOWEVER, I wanted to cover most of my bases, and demonstrate to my team how to write a more ‘robust’, error-checking script with documentation, to be used as a reference for future PS scripting. So I came up with this-
# JMarcum - 2024-Jan-05 // Always sign and date your code, and
# add revision notes below the 'Purpose' section.
# PURPOSE:
# Grant requested access level to shared calendar in O365 to specified user
### FUNCTIONS
# Validate text choices for granting calendar rights
Function ValidateOption {
Param (
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[ValidateSet('E','R')][string]$RightsLevel
)
}
# Validate e-mail address as actual mailbox
function Test-ADUser {
param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$mail
)
try {
# Cast address as mailaddress and see if 'valid'
$null -ne ([mailaddress]$mail)
# If e-mail address format is valid, see if user in forest (or the scope of account you are running this as)
$null -ne ([ADSISearcher]"(mail=$mail)").FindOne()
}
catch {
return $false
}
finally {
$Error.Clear()
}
}
### CODE
# Break if error
$ErrorActionPreference = 'Stop'
# Connect to O365 using admin credentials
try {
Import-Module ExchangeOnlineManagement
$AdminAddress = Read-Host -Prompt "Enter your admin e-mail address"
Connect-ExchangeOnline -UserPrincipalName $AdminAddress -ShowProgress $true
}
catch {
Write-Host "Run the following commands in PowerShell before running this script:"
Write-Host "Install-Module -Name ExchangeOnlineManagement"
Write-Host "Install-Module -Name ExchangeOnlineManagement -Scope CurrentUser"
Exit
}
# Get addresses of calendar to share and user to give rights to
# Shared Calendar e-mail address
$SharedCalendar = ''
do {
try {
$SharedCalendar = Read-Host -Prompt "E-mail address of shared mailbox"
if (($SharedCalendar -eq '') -or ((Test-ADUser $SharedCalendar) -eq $false)) {
throw
}
}
catch {
Write-Host "Please check address and re-enter"
$SharedCalendar = 'invalid'
}
finally {
$Error.Clear()
}
} until (Test-ADUser $SharedCalendar)
# E-mail address of person being granted rights to shared mailbox
$RightsRecipient = ''
do {
try {
$RightsRecipient = Read-Host -Prompt "E-mail address of person getting rights to calendar"
if (($RightsRecipient -eq '') -or ((Test-ADUser $RightsRecipient) -eq $false)) {
throw
}
}
catch {
Write-Host "Please check address and re-enter"
$RightsRecipient = 'invalid'
}
finally {
$Error.Clear()
}
} until (Test-ADUser $RightsRecipient)
# Get requested calendar rights for user
$RightsLevelTest = ''
do {
try {
$RightsLevelTest = Read-Host -Prompt "(E)ditor/(R)eviewer"
ValidateOption $RightsLevelTest
}
catch {
Write-Host "Please type either E for Editor or R for Reviewer"
$RightsLevelTest = ''
}
finally {
$Error.Clear()
}
} until ($RightsLevelTest -ne '')
# Assign correct value to rights level
if ($RightsLevelTest -eq 'R') {
$RightsLevel = "Reviewer"
} else {
$RightsLevel = "Editor"
}
# Grant requested rights to calendar
try {
Add-MailboxFolderPermission -Identity $SharedCalendar:\calendar -user $RightsRecipient -AccessRights $RightsLevel
}
catch {
Write-Host "Please re-run as account with O365 Admin rights"
}
With this code, I have accounted for a majority of the ‘oops’ that can occur in a script that calls various services. Yes, I could have gotten more involved in certain checks (like confirming Exchange Admin rights at the beginning), but this was more a “proof of concept”.
Hopefully it helps some people out there!