Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Added update script for customattributes #11

Merged
merged 6 commits into from
Jan 13, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 27 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -90,31 +90,33 @@ The correlation configuration is used to specify which properties will be used t

The following lifecycle actions are available:

| Action | Description |
| ------------------------------------------- | ---------------------------------------------------------- |
| create.ps1 | Correlate to an account |
| delete.ps1 | Set mailbox auto reply configuration |
| disable.ps1 | Sets Hide from address list to true |
| enable.ps1 | Sets Hide from address list to false |
| sharedMailboxes - permissions.ps1 | List sharedMailboxes as permissions |
| sharedMailboxes - grantPermission.ps1 | Grant sharedMailbox membership to an account |
| sharedMailboxes - revokePermission.ps1 | Revoke sharedMailbox membership from an account |
| sharedMailboxes - resources.ps1 | Create sharedMailboxes from resources |
| sharedMailboxes - subPermissions.ps1 | Grant/Revoke sharedMailbox membership from an account |
| groups - permissions.ps1 | List distribution groups as permissions |
| groups - grantPermission.ps1 | Grant distribution group membership to an account |
| groups - revokePermission.ps1 | Revoke distribution group membership from an account |
| groups - resources.ps1 | Create distribution groups from resources |
| groups - subPermissions.ps1 | Grant/Revoke distribution group membership from an account |
| folderPermission - permissions.ps1 | Mailbox folder permissions settings |
| folderPermission - grantPermission.ps1 | Grant folder permissions settings |
| regionalConfiguration - permissions.ps1 | Mailbox regional configuration settings |
| regionalConfiguration - grantPermission.ps1 | Grant regional configuration settings |
| litigationHold - permissions.ps1 | Mailbox litigation hold settings |
| litigationHold - grantPermission.ps1 | Grant litigation hold settings |
| litigationHold - revokePermission.ps1 | Revoke litigation hold settings |
| configuration.json | Default _configuration.json_ |
| fieldMapping.json | Default _fieldMapping.json_ |
| Action | Description |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| correlateOnly - create.ps1 | Correlate to an account |
| create.ps1 | Correlate account and sets Hide from address list to mapped value (default true), only if mapped |
| delete.ps1 | Set mailbox auto reply configuration |
| disable.ps1 | Sets Hide from address list to mapped value (default true) |
| enable.ps1 | Sets Hide from address list to mapped value (default false) |
| update.ps1 | Sets custom attribute(s) |
| sharedMailboxes - permissions.ps1 | List sharedMailboxes as permissions |
| sharedMailboxes - grantPermission.ps1 | Grant sharedMailbox membership to an account |
| sharedMailboxes - revokePermission.ps1 | Revoke sharedMailbox membership from an account |
| sharedMailboxes - resources.ps1 | Create sharedMailboxes from resources |
| sharedMailboxes - subPermissions.ps1 | Grant/Revoke sharedMailbox membership from an account |
| groups - permissions.ps1 | List distribution groups as permissions |
| groups - grantPermission.ps1 | Grant distribution group membership to an account |
| groups - revokePermission.ps1 | Revoke distribution group membership from an account |
| groups - resources.ps1 | Create distribution groups from resources |
| groups - subPermissions.ps1 | Grant/Revoke distribution group membership from an account |
| folderPermission - permissions.ps1 | Mailbox folder permissions settings |
| folderPermission - grantPermission.ps1 | Grant folder permissions settings |
| regionalConfiguration - permissions.ps1 | Mailbox regional configuration settings |
| regionalConfiguration - grantPermission.ps1 | Grant regional configuration settings |
| litigationHold - permissions.ps1 | Mailbox litigation hold settings |
| litigationHold - grantPermission.ps1 | Grant litigation hold settings |
| litigationHold - revokePermission.ps1 | Revoke litigation hold settings |
| configuration.json | Default _configuration.json_ |
| fieldMapping.json | Default _fieldMapping.json_ |

### Field mapping

321 changes: 321 additions & 0 deletions correlateOnly/create.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
#################################################
# HelloID-Conn-Prov-Target-Microsoft-Exchange-Online-Create
# Correlate to account
# PowerShell V2
#################################################

# Enable TLS1.2
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor [System.Net.SecurityProtocolType]::Tls12

# Define PowerShell commands to import
$commands = @(
"Get-User",
"Get-EXOMailbox"
)

#region functions
function Resolve-ExchangeOnlineError {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[object]
$ErrorObject
)
process {
$httpErrorObj = [PSCustomObject]@{
ScriptLineNumber = $ErrorObject.InvocationInfo.ScriptLineNumber
Line = $ErrorObject.InvocationInfo.Line
ErrorDetails = $ErrorObject.Exception.Message
FriendlyMessage = $ErrorObject.Exception.Message
}
if (-not [string]::IsNullOrEmpty($ErrorObject.ErrorDetails.Message)) {
$httpErrorObj.ErrorDetails = $ErrorObject.ErrorDetails.Message
}
elseif ($ErrorObject.Exception.GetType().FullName -eq 'System.Net.WebException') {
if ($null -ne $ErrorObject.Exception.Response) {
$streamReaderResponse = [System.IO.StreamReader]::new($ErrorObject.Exception.Response.GetResponseStream()).ReadToEnd()
if (-not [string]::IsNullOrEmpty($streamReaderResponse)) {
$httpErrorObj.ErrorDetails = $streamReaderResponse
}
}
}
try {
$errorObjectConverted = $ErrorObject | ConvertFrom-Json -ErrorAction Stop

if ($null -ne $errorObjectConverted.error_description) {
$httpErrorObj.FriendlyMessage = $errorObjectConverted.error_description
}
elseif ($null -ne $errorObjectConverted.error) {
if ($null -ne $errorObjectConverted.error.message) {
$httpErrorObj.FriendlyMessage = $errorObjectConverted.error.message
if ($null -ne $errorObjectConverted.error.code) {
$httpErrorObj.FriendlyMessage = $httpErrorObj.FriendlyMessage + " Error code: $($errorObjectConverted.error.code)"
}
}
else {
$httpErrorObj.FriendlyMessage = $errorObjectConverted.error
}
}
else {
$httpErrorObj.FriendlyMessage = $ErrorObject
}
}
catch {
$httpErrorObj.FriendlyMessage = $httpErrorObj.ErrorDetails
}
Write-Output $httpErrorObj
}
}

function Convert-StringToBoolean($obj) {
if ($obj -is [PSCustomObject]) {
foreach ($property in $obj.PSObject.Properties) {
$value = $property.Value
if ($value -is [string]) {
$lowercaseValue = $value.ToLower()
if ($lowercaseValue -eq "true") {
$obj.$($property.Name) = $true
}
elseif ($lowercaseValue -eq "false") {
$obj.$($property.Name) = $false
}
}
elseif ($value -is [PSCustomObject] -or $value -is [System.Collections.IDictionary]) {
$obj.$($property.Name) = Convert-StringToBoolean $value
}
elseif ($value -is [System.Collections.IList]) {
for ($i = 0; $i -lt $value.Count; $i++) {
$value[$i] = Convert-StringToBoolean $value[$i]
}
$obj.$($property.Name) = $value
}
}
}
return $obj
}
#endregion functions

try {
#region account
# Define correlation
$correlationField = $actionContext.CorrelationConfiguration.accountField
$correlationValue = $actionContext.CorrelationConfiguration.personFieldValue

# Define account object
$account = [PSCustomObject]$actionContext.Data.PsObject.Copy()

# Define properties to query
$accountPropertiesToQuery = @("guid") + $account.PsObject.Properties.Name | Select-Object -Unique

# Remove properties of account object with null-values
$account.PsObject.Properties | ForEach-Object {
# Remove properties with null-values
if ($_.Value -eq $null) {
$account.PsObject.Properties.Remove("$($_.Name)")
}
}
# Convert the properties of account object containing "TRUE" or "FALSE" to boolean
$account = Convert-StringToBoolean $account
#endRegion account

#region Verify correlation configuration and properties
$actionMessage = "verifying correlation configuration and properties"

if ($actionContext.CorrelationConfiguration.Enabled -eq $true) {
if ([string]::IsNullOrEmpty($correlationField)) {
throw "Correlation is enabled but not configured correctly."
}

if ([string]::IsNullOrEmpty($correlationValue)) {
throw "The correlation value for [$correlationField] is empty. This is likely a mapping issue."
}
}
else {
throw "Configuration of correlation is mandatory."
}
#endregion Verify correlation configuration and properties

#region Import module
$actionMessage = "importing module [ExchangeOnlineManagement]"

$importModuleSplatParams = @{
Name = "ExchangeOnlineManagement"
Cmdlet = $commands
Verbose = $false
ErrorAction = "Stop"
}

$null = Import-Module @importModuleSplatParams

Write-Information "Imported module [$($importModuleSplatParams.Name)]"
#endregion Create access token

#region Create access token
$actionMessage = "creating access token"

$createAccessTokenBody = @{
grant_type = "client_credentials"
client_id = $actionContext.Configuration.AppId
client_secret = $actionContext.Configuration.AppSecret
resource = "https://outlook.office365.com"
}

$createAccessTokenSplatParams = @{
Uri = "https://login.microsoftonline.com/$($actionContext.Configuration.TenantID)/oauth2/token"
Headers = $headers
Method = "POST"
ContentType = "application/x-www-form-urlencoded"
UseBasicParsing = $true
Body = $createAccessTokenBody
Verbose = $false
ErrorAction = "Stop"
}

$createAccessTokenResonse = Invoke-RestMethod @createAccessTokenSplatParams

Write-Information "Created access token."
#endregion Create access token

#region Connect to Microsoft Exchange Online
# Docs: https://learn.microsoft.com/en-us/powershell/module/exchange/connect-exchangeonline?view=exchange-ps
$actionMessage = "connecting to Microsoft Exchange Online"

$createExchangeSessionSplatParams = @{
Organization = $actionContext.Configuration.Organization
AppID = $actionContext.Configuration.AppId
AccessToken = $createAccessTokenResonse.access_token
CommandName = $commands
ShowBanner = $false
ShowProgress = $false
TrackPerformance = $false
SkipLoadingCmdletHelp = $true
SkipLoadingFormatData = $true
ErrorAction = "Stop"
}

$null = Connect-ExchangeOnline @createExchangeSessionSplatParams

Write-Information "Connected to Microsoft Exchange Online"
#endregion Connect to Microsoft Exchange Online

#region Get account
# Docs: https://learn.microsoft.com/en-us/powershell/module/exchange/get-exomailbox?view=exchange-ps
$actionMessage = "querying account where [$($correlationField)] = [$($correlationValue)]"

$getMicrosoftExchangeOnlineAccountSplatParams = @{
Filter = "$($correlationField) -eq '$($correlationValue)'"
Verbose = $false
ErrorAction = "Stop"
}

$getMicrosoftExchangeOnlineAccountResponse = $null
$getMicrosoftExchangeOnlineAccountResponse = Get-EXOMailbox @getMicrosoftExchangeOnlineAccountSplatParams
$correlatedAccount = $getMicrosoftExchangeOnlineAccountResponse | Select-Object $accountPropertiesToQuery

Write-Information "Queried account where [$($correlationField)] = [$($correlationValue)]. Result: $($correlatedAccount | ConvertTo-Json)"
#endregion Get account

#region Calulate action
$actionMessage = "calculating action"
if (($correlatedAccount | Measure-Object).count -eq 1) {
$actionAccount = "Correlate"
}
elseif (($correlatedAccount | Measure-Object).count -eq 0) {
$actionAccount = "NotFound"
}
elseif (($correlatedAccount | Measure-Object).count -gt 1) {
$actionAccount = "MultipleFound"
}
#endregion Calulate action

#region Process
switch ($actionAccount) {
"Correlate" {
#region Correlate account
$actionMessage = "correlating to account"

$outputContext.AccountReference = "$($correlatedAccount.Guid)"
$outputContext.Data = $correlatedAccount.PsObject.Copy()

$outputContext.AuditLogs.Add([PSCustomObject]@{
Action = "CorrelateAccount" # Optionally specify a different action for this audit log
Message = "Correlated to account with AccountReference: $($outputContext.AccountReference | ConvertTo-Json) on [$($correlationField)] = [$($correlationValue)]."
IsError = $false
})

$outputContext.AccountCorrelated = $true
#endregion Correlate account

break
}

"MultipleFound" {
#region Multiple accounts found
$actionMessage = "correlating to account"

# Throw terminal error
throw "Multiple accounts found where [$($correlationField)] = [$($correlationValue)]. Please correct this so the persons are unique."
#endregion Multiple accounts found

break
}

"NotFound" {
#region No account found
$actionMessage = "correlating to account"

# Throw terminal error
throw "No account found where [$($correlationField)] = [$($correlationValue)] while this connector only supports correlation."
#endregion No account found

break
}
}
#endregion Process
}
catch {
$ex = $PSItem
if ($($ex.Exception.GetType().FullName -eq 'Microsoft.PowerShell.Commands.HttpResponseException') -or
$($ex.Exception.GetType().FullName -eq 'System.Net.WebException')) {
$errorObj = Resolve-ExchangeOnlineError -ErrorObject $ex
$auditMessage = "Error $($actionMessage). Error: $($errorObj.FriendlyMessage)"
$warningMessage = "Error at Line [$($errorObj.ScriptLineNumber)]: $($errorObj.Line). Error: $($errorObj.ErrorDetails)"
}
else {
$auditMessage = "Error $($actionMessage). Error: $($ex.Exception.Message)"
$warningMessage = "Error at Line [$($ex.InvocationInfo.ScriptLineNumber)]: $($ex.InvocationInfo.Line). Error: $($ex.Exception.Message)"
}

Write-Warning $warningMessage

$outputContext.AuditLogs.Add([PSCustomObject]@{
# Action = "" # Optional
Message = $auditMessage
IsError = $true
})
}
finally {
#region Disconnect from Microsoft Exchange Online
# Docs: https://learn.microsoft.com/en-us/powershell/module/exchange/disconnect-exchangeonline?view=exchange-ps
$actionMessage = "disconnecting to Microsoft Exchange Online"

$deleteExchangeSessionSplatParams = @{
Confirm = $false
ErrorAction = "Stop"
}

$null = Disconnect-ExchangeOnline @deleteExchangeSessionSplatParams

Write-Information "Disconnected from Microsoft Exchange Online"
#endregion Disconnect from Microsoft Exchange Online

# Check if auditLogs contains errors, if no errors are found, set success to true
if (-NOT($outputContext.AuditLogs.IsError -contains $true)) {
$outputContext.Success = $true
}

# Check if accountreference is set, if not set, set this with default value as this must contain a value
if ([String]::IsNullOrEmpty($outputContext.AccountReference) -and $actionContext.DryRun -eq $true) {
$outputContext.AccountReference = "DryRun: Currently not available"
}
}
Loading