Public/SEPPmailAPI-User.ps1
|
<#
.SYNOPSIS Retrieves a single SEPPmail user by email address. .DESCRIPTION This cmdlet returns the full property set of one existing SEPPmail user, identified by the user's email address (REST path 'user/{email}'). Optionally, usage statistics for the user can be included with the -statistics switch. Umlauts and special characters in the returned object are converted from the API's ISO-8859-1 representation back to native PowerShell strings. .PARAMETER eMail The email address of the user to retrieve. Mandatory, case-insensitive and accepted from the pipeline (by value or by property name). .PARAMETER statistics If set, the returned user object also contains usage statistics data. .PARAMETER SMAHost SEPPmail API hostname. Defaults to the configured value. .PARAMETER SMAPort SEPPmail API port. Defaults to the configured value. .PARAMETER SMAVersion SEPPmail API version. Defaults to the configured value. .PARAMETER SMACred API credentials (PSCredential). Defaults to the configured value. .PARAMETER SMASkipCertCheck Skip SSL certificate validation. Use only in test environments. .INPUTS System.String You can pipe an email address to this cmdlet. .OUTPUTS System.Management.Automation.PSCustomObject Returns the user object with all its properties. .NOTES - Requires an active SEPPmail API session (New-SMAConfiguration). .LINK Find-SMAUser .LINK New-SMAUser .LINK Set-SMAUser .LINK Remove-SMAUser .EXAMPLE PS C:\> Get-SMAUser -eMail 'm.musterfrau@contoso.com' Returns the user object for the given email address. .EXAMPLE PS C:\> Get-SMAUser -eMail 'm.musterfrau@contoso.com' -statistics Returns the user object including usage statistics. .EXAMPLE PS C:\> 'm.musterfrau@contoso.com' | Get-SMAUser Retrieves the user via pipeline input. .EXAMPLE PS C:\> Find-SMAUser -partialMatch 'contoso' | Get-SMAUser Finds users by a partial match and retrieves the full object for each match. #> function Get-SMAUser { [CmdletBinding( DefaultParameterSetName = 'Default', SupportsShouldProcess = $false, ConfirmImpact = 'Low' )] param ( #region REST-API path and query parameters [Parameter( ParameterSetName = 'Default', Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0, HelpMessage = 'Email address' )] [ValidateNotNullOrEmpty()] [ValidatePattern('^\S+@\S+$')] [string]$email, [Parameter( ParameterSetName = 'Default', Mandatory = $false, HelpMessage = 'Include statistics data' )] [switch]$statistics, #endregion #region SMA host parameters [Parameter( Mandatory = $false )] [String]$SMAHost = $Script:activeCfg.SMAHost, [Parameter( Mandatory = $false )] [int]$SMAPort = $Script:activeCfg.SMAPort, [Parameter( Mandatory = $false )] [String]$SMAVersion = $Script:activeCfg.SMAPIVersion, [Parameter( Mandatory=$false )] [System.Management.Automation.PSCredential]$SMACred=$Script:activeCfg.SMACred, [Parameter( Mandatory=$false )] [switch]$SMASkipCertCheck=$Script:activeCfg.SMAskipCertCheck #endregion ) begin { if (! (verifyVars -VarList $Script:requiredVarList)) { Throw($missingVarsMessage); } $smaParams=@{ Host=$SMAHost; Port=$SMAPort; Version=$SMAVersion; } } process { Write-Verbose "Creating URL path" $uriPath = "{0}/{1}" -f 'user', $eMail.ToLower() Write-Verbose "Building full request uri" if ($statistics) { $boundParam = @{ statistics = $statistics } } $uri = New-SMAQueryString -uriPath $uriPath -qParam $boundParam @smaParams Write-verbose "Crafting Invokeparam for Invoke-SMARestMethod" $invokeParam = @{ Uri = $uri Method = 'GET' Cred = $SMACred SkipCertCheck = $SMASkipCertCheck } Write-Verbose "Call Invoke-SMARestMethod $uri" $UserRaw = Invoke-SMARestMethod @invokeParam Write-Verbose 'Filter data and return as PSObject' $GetUser = $UserRaw.Psobject.properties.value Write-Verbose 'Converting Umlauts from ISO-8859-1' $user = ConvertFrom-SMAPIFormat -inputObject $Getuser # Userobject if ($User) { return $User } else { Write-Information 'Nothing to return' } } end {} } <# .SYNOPSIS Finds locally existing users by email address, name, uid or a partial match. .DESCRIPTION This cmdlet lets you quickly find users in the SEPPmail database by various properties. It offers four mutually exclusive search variants (parameter sets): 1. By name - use -name with a part of the user's display name. 2. By uid - use -uid with an EXACT match of the uid. 3. By email - use -email with an EXACT match of the email address. 4. By partialMatch - use -partialMatch to match name, uid or email partially. Without a search parameter all users are returned. The result can be further narrowed down with -customer, -limit, -active, -activeWithinDays and -customerAssignment, which can be combined with any of the search variants. .PARAMETER email Exact email address to search for (parameter set 'email'). .PARAMETER partialMatch A partial string matched against name, uid or email (parameter set 'partialMatch'). .PARAMETER name A part of the user's display name to search for (parameter set 'name'). .PARAMETER uid The exact uid of the user to search for (parameter set 'uid'). .PARAMETER customer For MSPs and multi-customer environments: limit the query to a specific customer. .PARAMETER limit Limit the output to <n> objects. 0 (default) returns all matching users. .PARAMETER active Limit the output to active ($true) or inactive ($false) users. .PARAMETER activeWithinDays Limit the output to users who have sent emails within the given number of days (1-20000). .PARAMETER customerAssignment Limit the output to users assigned to a customer either 'explicit' (deliberately assigned) or 'implicit' (e.g. the [default] customer). .PARAMETER SMAHost SEPPmail API hostname. Defaults to the configured value. .PARAMETER SMAPort SEPPmail API port. Defaults to the configured value. .PARAMETER SMAVersion SEPPmail API version. Defaults to the configured value. .PARAMETER SMACred API credentials (PSCredential). Defaults to the configured value. .PARAMETER SMASkipCertCheck Skip SSL certificate validation. Use only in test environments. .OUTPUTS System.Management.Automation.PSCustomObject Returns one or more user objects with their details. .NOTES - Requires an active SEPPmail API session (New-SMAConfiguration). .LINK Get-SMAUser .LINK New-SMAUser .LINK Set-SMAUser .LINK Remove-SMAUser .EXAMPLE PS C:\> Find-SMAUser Emits all users and their details. .EXAMPLE PS C:\> Find-SMAUser -email 'john.doe@fabrikam.eu' Emits the specific user with this exact email address. .EXAMPLE PS C:\> Find-SMAUser -name 'john' Emits all users with 'john' in their display name. .EXAMPLE PS C:\> Find-SMAUser -partialMatch 'fabrikam' Emits all users with 'fabrikam' in either name, uid or email. .EXAMPLE PS C:\> Find-SMAUser -partialMatch 'Doe' Emits all users with 'Doe' in either name, uid or email, i.e. name 'John Doe' with email 'john@fabrikam.eu'. .EXAMPLE PS C:\> Find-SMAUser -uid 'john' Emits the specific user with the exact uid 'john'. .EXAMPLE PS C:\> Find-SMAUser -customer 'Contoso' Emits all users of a particular customer. .EXAMPLE PS C:\> Find-SMAUser -limit 5 Emits all users but stops output at 5 objects. .EXAMPLE PS C:\> Find-SMAUser -active:$false Emits only inactive users. .EXAMPLE PS C:\> Find-SMAUser -activeWithinDays 14 Emits only users which have sent emails within the last 14 days. .EXAMPLE PS C:\> Find-SMAUser -partialMatch 'tailspintoys.com' -activeWithinDays 14 -customer 'tailspintoys' -limit 50 Use a combination of parameters to pre-filter the output as much as possible. .EXAMPLE PS C:\> Find-SMAUser -customerAssignment implicit Shows users which are assigned to a customer - mostly the [default] customer - implicitly, so with no deliberate action. .EXAMPLE PS C:\> Find-SMAUser -customerAssignment explicit Shows users which are explicitly assigned to a customer - a created, managed customer account. #> function Find-SMAUser { [CmdletBinding(DefaultParameterSetName = 'Default')] [OutputType([PSCustomObject[]])] param ( #region Define the 4 parametersets and the uniqe params in it # -list is hardcoded to $false as its used in the Find-SMAUSer cmdlet [Parameter( ParameterSetName = 'email', Mandatory = $true, Position = 0, HelpMessage = 'Find users by their e-Mail address' )] [string]$email, [Parameter( ParameterSetName = 'partialMatch', Mandatory = $true, Position = 0, HelpMessage = 'Find users by a partial match of their e-Mail address' )] [string]$partialMatch = ' ', [Parameter( ParameterSetName = 'name', Mandatory = $true, Position = 0, HelpMessage = 'Find users by their display name (parts of the name allowed)' )] [string]$name, [Parameter( ParameterSetName = 'uid', Mandatory = $true, Position = 0, HelpMessage = 'Find users by their exact uid' )] [string]$uid, #endregion #region common API params for all parametersets [Parameter( Mandatory = $false, HelpMessage = 'For MSP´s and multi-customer environments, limit query for a specific customer' )] [Parameter(ParameterSetName = 'default')] [Parameter(ParameterSetName = 'partialMatch')] [Parameter(ParameterSetName = 'name')] [Parameter(ParameterSetName = 'uid')] [Parameter(ParameterSetName = 'email')] [string]$customer, [Parameter( Mandatory = $false, HelpMessage = 'limit output to <n> objects' )] [Parameter(ParameterSetName = 'default')] [Parameter(ParameterSetName = 'partialMatch')] [Parameter(ParameterSetName = 'name')] [Parameter(ParameterSetName = 'uid')] [Parameter(ParameterSetName = 'email')] [ValidateRange(0, [int]::MaxValue)] [int]$limit = 0, [Parameter( Mandatory = $false, HelpMessage = 'limit output to active or inactive objects' )] [Parameter(ParameterSetName = 'default')] [Parameter(ParameterSetName = 'partialMatch')] [Parameter(ParameterSetName = 'name')] [Parameter(ParameterSetName = 'uid')] [Parameter(ParameterSetName = 'email')] [bool]$active, [Parameter( Mandatory = $false, HelpMessage = 'limit output to users which have sent e-mails within a certain timeframe (days)' )] [Parameter(ParameterSetName = 'default')] [Parameter(ParameterSetName = 'partialMatch')] [Parameter(ParameterSetName = 'name')] [Parameter(ParameterSetName = 'uid')] [Parameter(ParameterSetName = 'email')] [ValidateRange(1, 20000)] [int]$activeWithinDays, [Parameter( Mandatory = $false, HelpMessage = 'limit output to users who have been explicite or implicit assigned to a customer' )] [Parameter(ParameterSetName = 'default')] [Parameter(ParameterSetName = 'partialMatch')] [Parameter(ParameterSetName = 'name')] [Parameter(ParameterSetName = 'uid')] [Parameter(ParameterSetName = 'email')] [ValidateSet('explicit','implicit')] [string]$customerAssignment, #endregion #region SMAParameters [Parameter(Mandatory = $false)] [String]$SMAHost = $Script:activeCfg.SMAHost, [Parameter(Mandatory = $false)] [int]$SMAPort = $Script:activeCfg.SMAPort, [Parameter(Mandatory = $false)] [String]$SMAVersion = $Script:activeCfg.SMAPIVersion, [Parameter(Mandatory=$false)] [System.Management.Automation.PSCredential]$SMACred=$Script:activeCfg.SMACred, [Parameter(Mandatory=$false)] [switch]$SMASkipCertCheck=$Script:activeCfg.SMAskipCertCheck #endregion SMAParameters ) begin { if (! (verifyVars -VarList $Script:requiredVarList)) { Throw($missingVarsMessage); } $smaParams=@{ Host=$SMAHost; Port=$SMAPort; Version=$SMAVersion; } } process { Write-Verbose "Building full request uri including all bound parameters" $boundParam = @{ 'list' = $true 'limit' = $limit } # Now check if any of the parameters was set by the commandline and set their values for the querystring # Dynamisch BoundParameters hinzufügen $apiParams = @('name', 'email', 'partialMatch', 'uid', 'customer', 'active', 'activeWithinDays', 'customerAssignment' ) foreach ($param in $apiParams) { if ($PSBoundParameters.ContainsKey($param)) { $boundParam[$param] = $PSBoundParameters[$param] } } $uri = New-SMAQueryString -uriPath 'user' -qParam $boundParam @smaParams; Write-verbose "Crafting Invokeparam for Invoke-SMARestMethod" $invokeParam = @{ Uri = $uri Method = 'GET' Cred = $SMACred SkipCertCheck = $SMASkipCertCheck } Write-Verbose "Call Invoke-SMARestMethod $uri" $UserRaw = Invoke-SMARestMethod @invokeParam Write-Verbose 'Filter data and return as PSObject' Write-Verbose 'Converting Umlauts from ISO-8859-1 and DateTime correctly' $user = foreach ($u in $userRaw) {ConvertFrom-SMAPIFormat -inputobject $u} if ($User) { Write-Verbose "Returning $($User.Count) user(s)" return $User } else { Write-Verbose 'No users found matching the criteria' # Optional: Return empty array statt $null return @() } } end {} } <# .SYNOPSIS Creates a new SEPPmail user. .DESCRIPTION This cmdlet creates a new SEPPmail user with the specified properties. Three mandatory parameters are required: uid, email, and name. The uid (User ID) can be identical to the email address or a unique GUID. Additional optional properties can be set during user creation, including passwords, customer assignment, encryption/signing permissions, and more. .PARAMETER uid Unique identifier for the user. Can be: - The email address (e.g., 'm.musterfrau@contoso.com') - A GUID (e.g., '245b8741-4724-4434-8343-bc26c9a10586') This parameter is mandatory. .PARAMETER eMail The user's email address. This parameter is mandatory and case-insensitive. .PARAMETER name The user's full display name (e.g., "Maria Musterfrau"). This parameter is mandatory. .PARAMETER password Initial password for the user as SecureString. If omitted, the user will need to set a password on first login. Example: 'MyPassword123!' | ConvertTo-SecureString -AsPlainText -Force .PARAMETER customer **CASE-SENSITIVE** Customer assignment for MSP/multi-customer environments. Use '[default]' or '[none]' for special assignments. Default: Empty (unassigned) .PARAMETER mayNotEncrypt If $true, disables encryption functionality for this user. Default: $false (encryption enabled) .PARAMETER mayNotSign If $true, disables signing functionality for this user. Default: $false (signing enabled) .PARAMETER locked If $true, creates the user account in locked state. Locked users cannot send or receive encrypted emails. Default: $false (unlocked) .PARAMETER notifications Email notification settings for the user. Valid values: 'never', 'always', 'domain default' Default: 'domain default' .PARAMETER mpkiSubjectPart User-specific static subject part for MPKI certificates. Example: 'OU=Sales,O=Contoso' .PARAMETER memberOf SEPPmail group membership. Array of group names. Example: @('Marketing', 'Sales') .PARAMETER mailAccountUID POP/IMAP account user ID (typically an email address). Used for external mail account integration. .PARAMETER mailAccountPassword POP/IMAP account password as SecureString. Example: 'password123' | ConvertTo-SecureString -AsPlainText -Force .PARAMETER mailAccountHost POP/IMAP mail server hostname (e.g., 'mail.contoso.com'). .PARAMETER mailAccountSSL Enable ($true) or disable ($false) SSL/TLS for POP/IMAP connection. Default: $false .PARAMETER mustChangePassword If $true, forces the user to change their password on next login. Default: $false .PARAMETER mfaExemption If $true, exempts this user from Multi-Factor Authentication requirements. Default: $false .PARAMETER mfaSecret MFA secret string for TOTP configuration. Used for pre-configuring MFA for the user. .PARAMETER SMAHost SEPPmail API hostname. Defaults to configured value. .PARAMETER SMAPort SEPPmail API port. Defaults to configured value. .PARAMETER SMAVersion SEPPmail API version. Defaults to configured value. .PARAMETER SMACred API credentials (PSCredential). Defaults to configured value. .PARAMETER SMASkipCertCheck Skip SSL certificate validation. Use only in test environments. .INPUTS System.String, System.Security.SecureString You can pipe user properties to this cmdlet via property names. .OUTPUTS System.String Returns the email address of the newly created user. .NOTES - Requires an active SEPPmail API session (New-SMAConfiguration) - Supports -WhatIf and -Confirm parameters - uid and email can be identical or different (uid can be a GUID) - Use SecureString for sensitive parameters (password, mailAccountPassword) - Groups must exist before assigning users to them - Customer assignment is case-sensitive .LINK https://github.com/seppmail/SEPPmailAPI/blob/main/docs/New-SMAUser.md .LINK Get-SMAUser .LINK Set-SMAUser .LINK Remove-SMAUser .LINK Find-SMAUser .EXAMPLE PS C:\> New-SMAUser -uid 'm.musterfrau@contoso.com' -email 'm.musterfrau@contoso.com' -name 'Maria Musterfrau' Creates a basic user with uid identical to email address. User will need to set password on first login. .EXAMPLE PS C:\> $uid = (New-Guid).Guid PS C:\> New-SMAUser -uid $uid -email 'm.musterfrau@contoso.com' -name 'Maria Musterfrau' Creates a user with a GUID as uid instead of using the email address. Useful for scenarios where email addresses might change. .EXAMPLE PS C:\> $password = 'P@ssw0rd123!' | ConvertTo-SecureString -AsPlainText -Force PS C:\> New-SMAUser -uid 'm.musterfrau@contoso.com' -email 'm.musterfrau@contoso.com' ` -name 'Maria Musterfrau' -password $password Creates a user with an initial password set. .EXAMPLE PS C:\> $userInfo = @{ uid = '245b8741-4724-4434-8343-bc26c9a10586' email = 'm.musterfrau@contoso.com' name = 'Maria Musterfrau' locked = $false mayNotEncrypt = $false mayNotSign = $false password = ('aBc1$6tgR' | ConvertTo-SecureString -AsPlainText -Force) customer = 'Contoso' notifications = 'never' mpkiSubjectPart = 'OU=Sales,O=Contoso' memberOf = @('Marketing', 'Sales') } PS C:\> New-SMAUser @userInfo Comprehensive example using parameter splatting to create a user with multiple properties. .EXAMPLE PS C:\> New-SMAUser -uid 'john.doe@fabrikam.com' -email 'john.doe@fabrikam.com' ` -name 'John Doe' -customer 'Fabrikam' -mustChangePassword $true Creates a user assigned to 'Fabrikam' customer who must change password on first login. .EXAMPLE PS C:\> $mailPassword = 'ExternalMailPwd123!' | ConvertTo-SecureString -AsPlainText -Force PS C:\> New-SMAUser -uid 'alice@contoso.com' -email 'alice@contoso.com' -name 'Alice Smith' ` -mailAccountUID 'alice@external.com' -mailAccountPassword $mailPassword ` -mailAccountHost 'mail.external.com' -mailAccountSSL $true Creates a user with external POP/IMAP account integration. .EXAMPLE PS C:\> New-SMAUser -uid 'bob@contoso.com' -email 'bob@contoso.com' -name 'Bob Brown' ` -mayNotEncrypt $true -mayNotSign $true Creates a user without encryption and signing capabilities. .EXAMPLE PS C:\> New-SMAUser -uid 'eve@contoso.com' -email 'eve@contoso.com' -name 'Eve Johnson' ` -notifications 'always' -memberOf @('IT', 'Admins') Creates a user with email notifications enabled and membership in multiple groups. .EXAMPLE PS C:\> New-SMAUser -uid 'test@contoso.com' -email 'test@contoso.com' -name 'Test User' ` -locked $true Creates a locked user account (useful for pre-provisioning). .EXAMPLE PS C:\> $users = Import-Csv 'users.csv' PS C:\> foreach ($user in $users) { New-SMAUser -uid $user.email -email $user.email -name $user.name -customer $user.customer } Bulk user creation from CSV file. CSV format: email,name,customer .EXAMPLE PS C:\> New-SMAUser -uid 'admin@contoso.com' -email 'admin@contoso.com' ` -name 'Admin User' -mfaExemption $false -WhatIf Preview user creation without actually creating the user using -WhatIf. #> function New-SMAUser { [CmdletBinding(SupportsShouldProcess)] param ( #region REST-API Parameters [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Unique ID, mostly the e-Mail address' )] [string]$uid, [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'User E-Mail address' )] [string]$eMail, [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'The users full name' )] [string]$name, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'You may set the password for the user or leave it blank. API default is blank' )] [SecureString]$password, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = '!!CASE_SENSITIVE!! For MSP´s, multi-customer and cloud environments, set the users customer, API default is blank' )] [string]$customer, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Disable the encrypt functionality for the user, API default is $false' )] [boolean]$mayNotEncrypt, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Disable the sign functionality for the user, API default is $false' )] [boolean]$mayNotSign, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Lock this user, API default is $false' )] [boolean]$locked, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Define if and how the user gets notified, API standard is domain default' )] [ValidateSet('never','always','domain default')] [string]$notifications, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Userspecific static subject part' )] [string]$mpkiSubjectPart, #MemberOF Setting will be removes #:TODO #[Parameter( # Mandatory = $false, # ValueFromPipelineByPropertyName = $true, # HelpMessage = 'SEPPmail group membership' # )] #[string[]]$memberOf, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'POP (IMAP) account userID (e-mail address)' )] [string]$mailAccountUID, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'POP (IMAP) account password as securestring' )] [secureString]$mailAccountPassword, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'POP (IMAP) account host' )] [string]$mailAccountHost, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'POP (IMAP) account host uses SSL' )] [boolean]$mailAccountSSL, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Require password change at net login' )] [boolean]$mustChangePassword, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Disable MFA requirement' )] [boolean]$mfaExemption, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'MFA secret string' )] [securestring]$mfaSecret, #endregion #region SMAparams [Parameter(Mandatory = $false)] [String]$SMAHost = $Script:activeCfg.SMAHost, [Parameter(Mandatory = $false)] [int]$SMAPort = $Script:activeCfg.SMAPort, [Parameter(Mandatory = $false)] [String]$SMAVersion = $Script:activeCfg.SMAPIVersion, [Parameter(Mandatory=$false)] [System.Management.Automation.PSCredential]$SMACred=$Script:activeCfg.SMACred, [Parameter(Mandatory=$false)] [switch]$SMASkipCertCheck=$Script:activeCfg.SMAskipCertCheck #endregion ) begin { if (! (verifyVars -VarList $Script:requiredVarList)) { Throw($missingVarsMessage); }; # end if try { Write-Verbose "Building full request uri" $smaParams=@{ Host=$SMAHost; Port=$SMAPort; Version=$SMAVersion; }; # end smaParams $uri = New-SMAQueryString -uriPath 'user' @smaParams; } catch { Write-Error "Error $error.CategoryInfo occured" } } process { Write-Verbose 'Crafting empty $body JSON' $bodyHt = @{} Write-Verbose 'Adding parameter values to $body JSON' $apiParams = @( 'uid', 'name', 'email', 'customer', 'locked', 'mayNotEncrypt', 'mayNotSign', 'notifications', 'password', 'mpkiSubjectPart', #'memberOf', 'mailAccountUID', 'mailAccountPassword', 'mailAccountHost', 'mailAccountSSL', 'mustChangePassword', 'mfaExemption', 'mfaSecret' ) foreach ($param in $apiParams) { if ($PSBoundParameters.ContainsKey($param)) { $bodyHt[$param] = $PSBoundParameters[$param] } } Write-Verbose "Transforming Securestrings to plaintext for API consumption" if ($bodyHt.password) {$bodyHt.password = ConvertFrom-SecureString -SecureString $bodyHt.password -AsPlainText} if ($bodyHt.mailAccountPassword) {$bodyHt.mailAccountPassword = ConvertFrom-SecureString -SecureString $bodyHt.mailAccountPassword -AsPlainText} if ($bodyHt.mfaSecret) {$bodyHt.mfaSecret = ConvertFrom-SecureString -SecureString $bodyHt.mfaSecret -AsPlainText} $body = $bodyHt|ConvertTo-JSON Write-verbose "Crafting InvokeParam for Invoke-SMARestMethod" $invokeParam = @{ Uri = $uri Method = 'POST' body = $body Cred = $SMACred SkipCertCheck = $SMASkipCertCheck } if ($PSCmdLet.ShouldProcess($($bodyht.Email),"Create user")) { Write-Verbose "Call Invoke-SMARestMethod $uri" $UserRaw = Invoke-SMARestMethod @invokeParam #debug $userraw Write-Verbose 'Returning e-Mail address of new users' ($userraw.message -split ' ')[3] } } end {} } <# .SYNOPSIS Modifies properties of an existing SEPPmail user. .DESCRIPTION This cmdlet allows you to modify an existing SEPPmail user's properties. You must specify the user's email address to identify the user. Changes are applied immediately via the SEPPmail API. At least one property (besides email) must be specified for the update. .PARAMETER eMail The email address of the user to modify. This parameter is mandatory and case-insensitive. .PARAMETER name The full display name of the user (e.g., "Martha Musterfrau"). .PARAMETER customer **CASE-SENSITIVE** Customer assignment for MSP/multi-customer environments. Use '[default]' or '[none]' for special assignments. Note: Changing the customer may require re-authentication. .PARAMETER locked Lock ($true) or unlock ($false) the user account. Locked users cannot send or receive encrypted emails. .PARAMETER mayNotEncrypt Disable ($true) or enable ($false) encryption functionality for this user. .PARAMETER mayNotSign Disable ($true) or enable ($false) signing functionality for this user. .PARAMETER mustChangePassword Force the user to change their password on next login. .PARAMETER notifications Email notification settings for the user. Valid values: 'never', 'always', 'domain default' .PARAMETER mpkiSubjectPart User-specific static subject part for MPKI certificates. .PARAMETER memberOf SEPPmail group membership. Array of group names. .PARAMETER mailAccountUID POP/IMAP account user ID (typically an email address). .PARAMETER mailAccountPassword POP/IMAP account password as SecureString. Example: 'password123' | ConvertTo-SecureString -AsPlainText -Force .PARAMETER mailAccountHost POP/IMAP mail server hostname (e.g., 'mail.contoso.com'). .PARAMETER mailAccountSSL Enable ($true) or disable ($false) SSL/TLS for POP/IMAP connection. .PARAMETER mfaExemption Exempt ($true) this user from Multi-Factor Authentication requirements. .PARAMETER mfaSecret MFA secret string as SecureString for TOTP configuration. .PARAMETER SMAHost SEPPmail API hostname. Defaults to configured value. .PARAMETER SMAPort SEPPmail API port. Defaults to configured value. .PARAMETER SMAVersion SEPPmail API version. Defaults to configured value. .PARAMETER SMACred API credentials (PSCredential). Defaults to configured value. .PARAMETER SMASkipCertCheck Skip SSL certificate validation. Use only in test environments. .INPUTS System.String You can pipe email addresses to this cmdlet. .OUTPUTS System.String Returns the email address of the modified user. .NOTES - Requires an active SEPPmail API session (New-SMAConfiguration) - Supports -WhatIf and -Confirm parameters - Changing the customer may cause "user not found" errors (known API issue) - Use SecureString for sensitive parameters (passwords, MFA secrets) .LINK https://github.com/seppmail/SEPPmailAPI/blob/main/docs/Set-SMAUser.md .LINK Get-SMAUser .LINK New-SMAUser .LINK Remove-SMAUser .EXAMPLE PS C:\> Set-SMAUser -eMail 'm.musterfrau@contoso.com' -name 'Martha Musterfrau' Changes the display name of the specified user. .EXAMPLE PS C:\> Set-SMAUser -eMail 'm.musterfrau@contoso.com' -locked $true Locks the user account, preventing login and email processing. .EXAMPLE PS C:\> Set-SMAUser -eMail 'm.musterfrau@contoso.com' -customer 'Contoso' -notifications 'always' Assigns the user to the 'Contoso' customer and enables email notifications. .EXAMPLE PS C:\> $securePass = 'aBc1$6tgR' | ConvertTo-SecureString -AsPlainText -Force PS C:\> Set-SMAUser -eMail 'm.musterfrau@contoso.com' -mailAccountPassword $securePass Updates the POP/IMAP password using a SecureString. .EXAMPLE PS C:\> $userInfo = @{ eMail = 'm.musterfrau@contoso.com' name = 'Marie Musterfrau' locked = $false mayNotEncrypt = $false mayNotSign = $false customer = 'Contoso' notifications = 'never' mpkiSubjectPart = 'OU=Sales' memberOf = @('Marketing', 'Sales') mailAccountUID = 'm.musterfrau' mailAccountPassword = ('aBc1$6tgR' | ConvertTo-SecureString -AsPlainText -Force) mailAccountHost = 'mail.contoso.com' mailAccountSSL = $true mustChangePassword = $true } PS C:\> Set-SMAUser @userInfo Comprehensive example using parameter splatting to update multiple properties. .EXAMPLE PS C:\> Get-SMAUser -email 'm.musterfrau@contoso.com' | Set-SMAUser -locked $false Pipeline example: Unlock a user account. .EXAMPLE PS C:\> 'alice@contoso.com', 'bob@contoso.com' | Set-SMAUser -notifications 'always' Bulk update: Enable notifications for multiple users via pipeline. .EXAMPLE PS C:\> Set-SMAUser -eMail 'm.musterfrau@contoso.com' -memberOf @('Group1', 'Group2') -WhatIf Preview changes without applying them using -WhatIf. .EXAMPLE PS C:\> Set-SMAUser -eMail 'm.musterfrau@contoso.com' -mfaExemption $true Exempt a user from Multi-Factor Authentication requirements. #> function Set-SMAUser #TODO: Wait for resolved issue user not found when changing the customer { [CmdletBinding( SupportsShouldProcess = $true, Confirmimpact = 'Medium' )] [OutputType([string])] param ( #region API parameters [Parameter( Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'User E-Mail address' )] [ValidateNotNullOrEmpty()] [ValidatePattern('^\S+@\S+$')] [string]$eMail, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'The users full name' )] [string]$name, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = '!!CASE_SENSITIVE!! For MSP´s, multi-customer and cloud environments, set the users customer, API default is blank' )] [SMAParamFilter('StringBool')] [ValidatePattern('^[a-zA-Z0-9\-_]+$|^\[(default|none)\]$')] [string]$customer, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Disable the encrypt functionality for the user, API default is $false' )] [boolean]$mayNotEncrypt, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Disable the sign functionality for the user, API default is $false' )] [boolean]$mayNotSign, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Trigger a password change on next logon' )] [boolean]$mustChangePassword, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Lock this user, API default is $false' )] [boolean]$locked, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Define if and how the user gets notified, API standard is domain default' )] [ValidateSet('never','always','domain default')] [string]$notifications, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Userspecific static subject part' )] [string]$mpkiSubjectPart, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SEPPmail group membership' )] [string[]]$memberOf, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'POP (IMAP) account userID (e-mail address)' )] [string]$mailAccountUID, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'POP (IMAP) account password as securestring' )] [secureString]$mailAccountPassword, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'POP (IMAP) account host' )] [string]$mailAccountHost, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'POP (IMAP) account host uses SSL' )] [boolean]$mailAccountSSL, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Disable MFA requirement' )] [boolean]$mfaExemption, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'MFA secret string' )] [SecureString]$mfaSecret, #endregion #region SMAParams [Parameter(Mandatory = $false)] [String]$SMAHost = $Script:activeCfg.SMAHost, [Parameter(Mandatory = $false)] [int]$SMAPort = $Script:activeCfg.SMAPort, [Parameter(Mandatory = $false)] [String]$SMAVersion = $Script:activeCfg.SMAPIVersion, [Parameter(Mandatory=$false)] [System.Management.Automation.PSCredential]$SMACred=$Script:activeCfg.SMACred, [Parameter(Mandatory=$false)] [switch]$SMASkipCertCheck=$Script:activeCfg.SMAskipCertCheck #endregion ) begin { if (! (verifyVars -VarList $Script:requiredVarList)) { Throw($missingVarsMessage); } $smaParams=@{ Host=$SMAHost; Port=$SMAPort; Version=$SMAVersion; } } process { Write-Verbose "Creating URL path" $uriPath = "{0}/{1}" -f 'user', $eMail.ToLower() Write-Verbose "Building full request uri" $boundParam = @{ } $uri = New-SMAQueryString -uriPath $uriPath -qParam $boundParam @smaParams; Write-Verbose 'Crafting mandatory $body JSON' $bodyHt = @{} Write-Verbose 'Adding parameter values to $body JSON' $apiParams = @( 'name', 'customer', 'locked', 'mayNotEncrypt', 'mayNotSign', 'mpkiSubjectPart', 'notifications', 'mustChangePassword', 'memberOf', 'mailAccountUID', 'mailAccountPassword', 'mailAccountHost', 'mailAccountSSL', 'mfaExemption', 'mfaSecret' ) foreach ($param in $apiParams) { if ($PSBoundParameters.ContainsKey($param)) { $bodyHt[$param] = $PSBoundParameters[$param] } } Write-Verbose "Transforming Securestrings to plaintext for API consumption" if ($bodyHt.mailAccountPassword) {$bodyHt.mailAccountPassword = ConvertFrom-SecureString -SecureString $bodyHt.mailAccountPassword -AsPlainText} if ($bodyHt.mfaSecret) {$bodyHt.mfaSecret = ConvertFrom-SecureString -SecureString $bodyHt.mfaSecret -AsPlainText} $body = $bodyHt | ConvertTo-JSON if ($bodyHt.Count -eq 0) { Write-Warning "No parameters specified for update. User '$eMail' remains unchanged." return } Write-verbose "Crafting Invokeparam for Invoke-SMARestMethod" $invokeParam = @{ Uri = $uri Method = 'PUT' body = $body Cred = $SMACred SkipCertCheck = $SMASkipCertCheck } if ($PSCmdLet.ShouldProcess($($bodyHt.Email),"Change user")) { Write-Verbose "Call Invoke-SMARestMethod $uri" $UserRaw = Invoke-SMARestMethod @invokeParam Write-Verbose 'Returning e-Mail addresses of updated user' return ($userRaw.message -split ' ')[3] } } end {} } <# .SYNOPSIS Removes a SEPPmail user from the system. .DESCRIPTION This cmdlet deletes a SEPPmail user identified by their email address. By default, the user account is removed but can be recovered. Use the -purge parameter for permanent deletion. Certificates and private keys are deleted by default. Use -keepKeys to preserve them for potential future use if the user is recreated. .PARAMETER eMail The email address of the user to remove. This parameter is mandatory and case-insensitive. .PARAMETER keepKeys If specified, certificates and private keys will NOT be deleted. This is useful when you plan to recreate the user later with the same email address, as the keys will be automatically re-attached. .PARAMETER purge If specified, the user is PERMANENTLY deleted from the database. WARNING: This action is irreversible. The user and all associated data will be lost forever and cannot be recovered. .PARAMETER SMAHost SEPPmail API hostname. Defaults to configured value from New-SMAConfiguration. .PARAMETER SMAPort SEPPmail API port. Defaults to configured value from New-SMAConfiguration. .PARAMETER SMAVersion SEPPmail API version. Defaults to configured value from New-SMAConfiguration. .PARAMETER SMACred API credentials (PSCredential). Defaults to configured credentials. .PARAMETER SMASkipCertCheck Skip SSL certificate validation. Use only in test environments. .INPUTS System.String You can pipe email addresses to this cmdlet. .OUTPUTS System.String Returns the email address of the removed user. .NOTES - Requires an active SEPPmail API session (New-SMAConfiguration) - Supports -WhatIf and -Confirm parameters for safety - Default behavior: User removed but recoverable, keys deleted - Use -keepKeys to preserve certificates for user recreation - Use -purge for permanent, irreversible deletion Warning: The -purge parameter permanently deletes all user data! .LINK https://github.com/seppmail/SEPPmailAPI/blob/main/docs/Remove-SMAUser.md .LINK Get-SMAUser .LINK New-SMAUser .LINK Set-SMAUser .EXAMPLE PS C:\> Remove-SMAUser -email 'm.musterfrau@contoso.com' Removes the user and deletes all associated certificates and keys. The user can potentially be recovered through database restore. .EXAMPLE PS C:\> Remove-SMAUser -email 'm.musterfrau@contoso.com' -keepKeys Removes the user but preserves their certificates and private keys. If you recreate the user with the same email, the keys will be automatically re-attached. .EXAMPLE PS C:\> Remove-SMAUser -email 'm.musterfrau@contoso.com' -purge PERMANENTLY deletes the user from the database. This action is irreversible - all user data is lost forever. .EXAMPLE PS C:\> Remove-SMAUser -email 'm.musterfrau@contoso.com' -WhatIf Preview what would happen without actually removing the user. Safe way to verify the operation before execution. .EXAMPLE PS C:\> Remove-SMAUser -email 'm.musterfrau@contoso.com' -Confirm:$false Remove the user without prompting for confirmation. Use with caution in automated scripts. .EXAMPLE PS C:\> 'alice@contoso.com', 'bob@contoso.com' | Remove-SMAUser -keepKeys Bulk removal: Remove multiple users via pipeline while preserving their keys. .EXAMPLE PS C:\> Get-SMAUser -email 'inactive@contoso.com' | Remove-SMAUser Pipeline example: First verify the user exists, then remove it. .EXAMPLE PS C:\> $users = Find-SMAUser -active $false -activeWithinDays 365 PS C:\> $users | Where-Object { $_.email -like '*@oldcontoso.com' } | Remove-SMAUser -keepKeys -WhatIf Complex example: Find inactive users from a specific domain, preview their removal while keeping keys. .EXAMPLE PS C:\> Remove-SMAUser -email 'testuser@contoso.com' -purge -Confirm Permanent deletion with explicit confirmation prompt. Best practice when using -purge parameter. #> function Remove-SMAUser { [CmdletBinding(SupportsShouldProcess)] param ( #region API Parameters [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Position = 0, HelpMessage = 'User E-Mail address' )] [string]$eMail, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'If true, certificates and private keys will not be deleted' )] [switch]$keepKeys, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'If true, the user is REALLY deleted from the database and is LOST forever' )] [switch]$purge, #endregion #region SMA Parameters [Parameter(Mandatory = $false)] [String]$SMAHost = $Script:activeCfg.SMAHost, [Parameter(Mandatory = $false)] [int]$SMAPort = $Script:activeCfg.SMAPort, [Parameter(Mandatory = $false)] [String]$SMAVersion = $Script:activeCfg.SMAPIVersion, [Parameter(Mandatory=$false)] [System.Management.Automation.PSCredential]$SMACred=$Script:activeCfg.SMACred, [Parameter(Mandatory=$false)] [switch]$SMASkipCertCheck=$Script:activeCfg.SMAskipCertCheck #endregion ) begin { if (! (verifyVars -VarList $Script:requiredVarList)) { Throw($missingVarsMessage); } $smaParams=@{ Host=$SMAHost; Port=$SMAPort; Version=$SMAVersion; } } process { Write-Verbose "Creating URL path" $uriPath = "{0}/{1}" -f 'user', $eMail Write-Verbose "Preparing full request uri" $boundParam = @{ } if ($keepKeys) {$boundParam.keepKeys = $keepKeys} if ($purge) {$boundParam.purge = $purge} $uri = New-SMAQueryString -uriPath $uriPath -qParam $boundParam @smaParams; Write-verbose "Crafting Invokeparam for Invoke-SMARestMethod" $invokeParam = @{ Uri = $uri Method = 'DELETE' Cred = $SMACred SkipCertCheck = $SMASkipCertCheck } if ($PSCmdLet.ShouldProcess($email,"Remove User")) { Write-Verbose "Call Invoke-SMARestMethod $uri" $UserRaw = Invoke-SMARestMethod @invokeParam Write-Verbose 'Returning e-Mail addresses of removed user' ($userRaw.message -split ' ')[3] } } end {} } # SIG # Begin signature block # MIIVyAYJKoZIhvcNAQcCoIIVuTCCFbUCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCB8DMwJjxc0EOO # h0PHyd2rU3i8FZP/3TiJqrlsfBIqEKCCEgQwggVvMIIEV6ADAgECAhBI/JO0YFWU # jTanyYqJ1pQWMA0GCSqGSIb3DQEBDAUAMHsxCzAJBgNVBAYTAkdCMRswGQYDVQQI # DBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoM # EUNvbW9kbyBDQSBMaW1pdGVkMSEwHwYDVQQDDBhBQUEgQ2VydGlmaWNhdGUgU2Vy # dmljZXMwHhcNMjEwNTI1MDAwMDAwWhcNMjgxMjMxMjM1OTU5WjBWMQswCQYDVQQG # EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRTZWN0aWdv # IFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQCN55QSIgQkdC7/FiMCkoq2rjaFrEfUI5ErPtx94jGgUW+s # hJHjUoq14pbe0IdjJImK/+8Skzt9u7aKvb0Ffyeba2XTpQxpsbxJOZrxbW6q5KCD # J9qaDStQ6Utbs7hkNqR+Sj2pcaths3OzPAsM79szV+W+NDfjlxtd/R8SPYIDdub7 # P2bSlDFp+m2zNKzBenjcklDyZMeqLQSrw2rq4C+np9xu1+j/2iGrQL+57g2extme # me/G3h+pDHazJyCh1rr9gOcB0u/rgimVcI3/uxXP/tEPNqIuTzKQdEZrRzUTdwUz # T2MuuC3hv2WnBGsY2HH6zAjybYmZELGt2z4s5KoYsMYHAXVn3m3pY2MeNn9pib6q # RT5uWl+PoVvLnTCGMOgDs0DGDQ84zWeoU4j6uDBl+m/H5x2xg3RpPqzEaDux5mcz # mrYI4IAFSEDu9oJkRqj1c7AGlfJsZZ+/VVscnFcax3hGfHCqlBuCF6yH6bbJDoEc # QNYWFyn8XJwYK+pF9e+91WdPKF4F7pBMeufG9ND8+s0+MkYTIDaKBOq3qgdGnA2T # OglmmVhcKaO5DKYwODzQRjY1fJy67sPV+Qp2+n4FG0DKkjXp1XrRtX8ArqmQqsV/ # AZwQsRb8zG4Y3G9i/qZQp7h7uJ0VP/4gDHXIIloTlRmQAOka1cKG8eOO7F/05QID # AQABo4IBEjCCAQ4wHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYD # VR0OBBYEFDLrkpr/NZZILyhAQnAgNpFcF4XmMA4GA1UdDwEB/wQEAwIBhjAPBgNV # HRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUFBwMDMBsGA1UdIAQUMBIwBgYE # VR0gADAIBgZngQwBBAEwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21v # ZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEE # KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZI # hvcNAQEMBQADggEBABK/oe+LdJqYRLhpRrWrJAoMpIpnuDqBv0WKfVIHqI0fTiGF # OaNrXi0ghr8QuK55O1PNtPvYRL4G2VxjZ9RAFodEhnIq1jIV9RKDwvnhXRFAZ/ZC # J3LFI+ICOBpMIOLbAffNRk8monxmwFE2tokCVMf8WPtsAO7+mKYulaEMUykfb9gZ # pk+e96wJ6l2CxouvgKe9gUhShDHaMuwV5KZMPWw5c9QLhTkg4IUaaOGnSDip0TYl # d8GNGRbFiExmfS9jzpjoad+sPKhdnckcW67Y8y90z7h+9teDnRGWYpquRRPaf9xH # +9/DUp/mBlXpnYzyOmJRvOwkDynUWICE5EV7WtgwggYaMIIEAqADAgECAhBiHW0M # UgGeO5B5FSCJIRwKMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYD # VQQKEw9TZWN0aWdvIExpbWl0ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENv # ZGUgU2lnbmluZyBSb290IFI0NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5 # NTlaMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzAp # BgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0G # CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxjI # ztNsfvxYB5UXeWUzCxEeAEZGbEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NV # DgFigOMYzB2OKhdqfWGVoYW3haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/3 # 6F09fy1tsB8je/RV0mIk8XL/tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05Zw # mRmTnAO5/arnY83jeNzhP06ShdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm # +qxp4VqpB3MV/h53yl41aHU5pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUe # dyz8rNyfQJy/aOs5b4s+ac7IH60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz4 # 4MPZ1f9+YEQIQty/NQd/2yGgW+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBM # dlyh2n5HirY4jKnFH/9gRvd+QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQY # MBaAFDLrkpr/NZZILyhAQnAgNpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritU # pimqF6TNDDAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNV # HSUEDDAKBggrBgEFBQcDAzAbBgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsG # A1UdHwREMEIwQKA+oDyGOmh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1 # YmxpY0NvZGVTaWduaW5nUm9vdFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsG # AQUFBzAChjpodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2Rl # U2lnbmluZ1Jvb3RSNDYucDdjMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0 # aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURh # w1aVcdGRP4Wh60BAscjW4HL9hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0Zd # OaWTsyNyBBsMLHqafvIhrCymlaS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajj # cw5+w/KeFvPYfLF/ldYpmlG+vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNc # WbWDRF/3sBp6fWXhz7DcML4iTAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalO # hOfCipnx8CaLZeVme5yELg09Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJs # zkyeiaerlphwoKx1uHRzNyE6bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z7 # 6mKnzAfZxCl/3dq3dUNw4rg3sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5J # KdGvspbOrTfOXyXvmPL6E52z1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHH # j95Ejza63zdrEcxWLDX6xWls/GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2 # Bev6SivBBOHY+uqiirZtg0y9ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/ # L9Uo2bC5a4CH2RwwggZvMIIE16ADAgECAhBIqMP3CCLHOHtOKuaWNyeFMA0GCSqG # SIb3DQEBDAUAMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0 # ZWQxKzApBgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYw # HhcNMjYwNDE1MDAwMDAwWhcNMjcwNzE0MjM1OTU5WjBmMQswCQYDVQQGEwJERTEP # MA0GA1UECAwGQmF5ZXJuMSIwIAYDVQQKDBlTRVBQbWFpbCBEZXV0c2NobGFuZCBH # bWJIMSIwIAYDVQQDDBlTRVBQbWFpbCBEZXV0c2NobGFuZCBHbWJIMIICIjANBgkq # hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvAFzE8MbJpvQt+IdIh1M+bKYsJBFDk4b # 9ySe25IrCi00B9o5XmQtIw42MqyIKbUq1tDARtp9KTQedEP9W+rflAF2l+0Z046J # kiqumU9/enbqWLDyln1aS/p7HOgwZFMhnsR9zH0MfFckiklUmkzJO+vmzYAK7ZmD # xajNLJs0gkGRU2/BecAx/TSvLXMaKONsKZCyMKQCnwo1mCY/tFl5EgUz7YQFrPOR # BQGfQke/hkdBfQDqNRsi/J6+KhJWc6LvgQihdRg/INQbQsTxlow18NWvyFsjjueH # 7kG6HR4YKfbv07xgrsIh8xvq9ZJ1SBhLXmkg4SdoQGASjqR6o3keAX+bDRFf+hml # WWJp/FqVHR5QomF3vbK2/bbz4jAclYSPx/sPasNJ0YnKFkgmowZ7Ysa0KA0/egBg # tI4gJ+8V7zrqIVEG3rMQh9KCdMnJqP2aM9o4gUzQvE1M4x606liX9EWwdLLS+fe7 # 9o+Fzo5oH4wBE/En6hQQkzseHHu+TXCDd6zUUZ/PlTK0gTaDIRXt6UzPNqJ4RiRC # W2pNFcPt078qqVTuwKUXoE4ufxGgXKFrZlCYST/9eG1TnW2oq19nz8A333GCsL3g # poNIKvfmDyGMMNzvx2aeqn2v6e75z8kH19iGSNZ51xT+WgS9F1aIvjz08/T7XAv7 # iDPF1/gPIp8CAwEAAaOCAakwggGlMB8GA1UdIwQYMBaAFA8qyyCHKLjsb0iuK1Sm # KaoXpM0MMB0GA1UdDgQWBBS30/Tq+alF3j2BY5up8n5zpAU23DAOBgNVHQ8BAf8E # BAMCB4AwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAzBKBgNVHSAE # QzBBMDUGDCsGAQQBsjEBAgEDAjAlMCMGCCsGAQUFBwIBFhdodHRwczovL3NlY3Rp # Z28uY29tL0NQUzAIBgZngQwBBAEwSQYDVR0fBEIwQDA+oDygOoY4aHR0cDovL2Ny # bC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVibGljQ29kZVNpZ25pbmdDQVIzNi5jcmww # eQYIKwYBBQUHAQEEbTBrMEQGCCsGAQUFBzAChjhodHRwOi8vY3J0LnNlY3RpZ28u # Y29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ0NBUjM2LmNydDAjBggrBgEFBQcw # AYYXaHR0cDovL29jc3Auc2VjdGlnby5jb20wHgYDVR0RBBcwFYETc3VwcG9ydEBz # ZXBwbWFpbC5jaDANBgkqhkiG9w0BAQwFAAOCAYEAi7fmb5UYoemWG3CC4K2UZWVr # R6GOfi8gbJKgjPbKO4zrCrU/x6cOdyp6scKZfUEGFDf8KH6pP4pAQv1Hsbi49gU2 # kxoUWLlCiipn05qJY663DHx9hlStej/ZdEatou0wyCDiG5xD7kmG+1t6iLyyCBgE # B88tJpzTjI61qXmBTS/FGEOAsB4SDEW1ngA7bc5FOv4IUKA43hp8M+N3GeYFzDqw # JELYEfVVYheBW3o7q4VrCdfFEuaQihOtvfDfYpP6ANgekNn8HdsMT8rx9D1I50Rl # i/qQFo2BOuPyb2SIQPzJvCs5wgi5qgp1nHiN6igumu2Cz7BmGjOazGUgCSUY5Qwy # E8+F+R2tVM+2O15rfX01+e56ZfojBEiEjMwfPHs3fa3V3gokWWNwUMkton/v0R/n # l2zjmOr2okohOINZEDh9frg21zUCN5ZD8Y4zQWuiJLCvvvBZs0JR4c9xl2k2wtw/ # QLPhGU69zM3smGpRoLE8M6zvUvSU7jXjvefazUniMYIDGjCCAxYCAQEwaDBUMQsw # CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSswKQYDVQQDEyJT # ZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgQ0EgUjM2AhBIqMP3CCLHOHtOKuaW # NyeFMA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAw # GQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisG # AQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIP28Ru7eweSO6dFizCmkx2i2iY+p8z9g # Zq3sUV7S0PSLMA0GCSqGSIb3DQEBAQUABIICAJkY53EDiY/1zvJMvsAPJ/8KOhc0 # madoIGSFsbhoYXrkxXiLxsrcuoIYp6v1f3kk9OOiZ90r3UYgz5SNAoACj/BDbf83 # e5xiC/9osid3GVJsP3ZG/z6PpjKLlfxE3DRngyPo5R8I0WgKRfTyPGKRBh8a00b3 # 3l3fg7/WlVsUbX5jS01DCy4WjcrfsCeUkLN9BuWp3BhFx0mOQ4+v8Ykquet9MtQh # fJ2MHKBHxO0fNJuAVoZNdDr4DKXl2LTdMiIV0WvRkpJKU0yZ+O3tpzkNELQwhVn1 # u7nikq/761M9ilrtEEsbnB5PhURPRl66qgKuRzWxu44gH8mYI+dThBIPoSC3Jy+U # 4C/tC5Z6f1m9RBRio3P6I6ooj4jH0+PQ6SsuT9Czy+KBbPqItjHiCge85xq/LVed # P9r01mXDa3k0/Ner0ABhphn9bFjJG67WXLyfIkSaFjs+56TBImQeknRm+5ohrvUw # DXj5FTw/5wSvPf+SCwPh3wo0nesqP+NwsCh2t8l2694vzQKYHH8ZZ6KIvFU6VISN # g5RdrTC7Hcgq/cpXoeJESKX6oHZGlindwhoL0I2fUtRMXTqRvex00gjcs2G53oii # uEA61qvFr7R0+4DIe9YYtI1YPZ09LxKu1hsIXcwNzcDAXPSyRg2JzlHu+4PWlygk # wGTzu0vy2xI1vcgv # SIG # End signature block |