diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailContact/MSFT_EXOMailContact.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailContact/MSFT_EXOMailContact.psm1 index 6681f5f6fb..59c2ddf127 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailContact/MSFT_EXOMailContact.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailContact/MSFT_EXOMailContact.psm1 @@ -70,6 +70,86 @@ function Get-TargetResource [System.Boolean] $UsePreferMessageFormat, + [Parameter()] + [System.String] + $CustomAttribute1, + + [Parameter()] + [System.String] + $CustomAttribute2, + + [Parameter()] + [System.String] + $CustomAttribute3, + + [Parameter()] + [System.String] + $CustomAttribute4, + + [Parameter()] + [System.String] + $CustomAttribute5, + + [Parameter()] + [System.String] + $CustomAttribute6, + + [Parameter()] + [System.String] + $CustomAttribute7, + + [Parameter()] + [System.String] + $CustomAttribute8, + + [Parameter()] + [System.String] + $CustomAttribute9, + + [Parameter()] + [System.String] + $CustomAttribute10, + + [Parameter()] + [System.String] + $CustomAttribute11, + + [Parameter()] + [System.String] + $CustomAttribute12, + + [Parameter()] + [System.String] + $CustomAttribute13, + + [Parameter()] + [System.String] + $CustomAttribute14, + + [Parameter()] + [System.String] + $CustomAttribute15, + + [Parameter()] + [System.String[]] + $ExtensionCustomAttribute1, + + [Parameter()] + [System.String[]] + $ExtensionCustomAttribute2, + + [Parameter()] + [System.String[]] + $ExtensionCustomAttribute3, + + [Parameter()] + [System.String[]] + $ExtensionCustomAttribute4, + + [Parameter()] + [System.String[]] + $ExtensionCustomAttribute5, + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] @@ -135,7 +215,7 @@ function Get-TargetResource try { - $contact = Get-MailContact -Identity $Name -ErrorAction Continue + $contact = Get-MailContact -Identity $Name -ErrorAction SilentlyContinue if ($null -eq $contact) { @@ -170,6 +250,21 @@ function Get-TargetResource TenantId = $TenantId } + foreach ($i in (1..15)) + { + if ($contact."CustomAttribute$i") + { + $result."CustomAttribute$i" = $contact."CustomAttribute$i" + } + } + foreach ($i in (1..5)) + { + if ($contact."ExtensionCustomAttribute$i") + { + $result."ExtensionCustomAttribute$i" = $contact."ExtensionCustomAttribute$i" + } + } + Write-Verbose -Message "Found Mail Contact $($Name)" Write-Verbose -Message "Get-TargetResource Result: `n $(Convert-M365DscHashtableToString -Hashtable $result)" return $result @@ -258,6 +353,86 @@ function Set-TargetResource [System.Boolean] $UsePreferMessageFormat, + [Parameter()] + [System.String] + $CustomAttribute1, + + [Parameter()] + [System.String] + $CustomAttribute2, + + [Parameter()] + [System.String] + $CustomAttribute3, + + [Parameter()] + [System.String] + $CustomAttribute4, + + [Parameter()] + [System.String] + $CustomAttribute5, + + [Parameter()] + [System.String] + $CustomAttribute6, + + [Parameter()] + [System.String] + $CustomAttribute7, + + [Parameter()] + [System.String] + $CustomAttribute8, + + [Parameter()] + [System.String] + $CustomAttribute9, + + [Parameter()] + [System.String] + $CustomAttribute10, + + [Parameter()] + [System.String] + $CustomAttribute11, + + [Parameter()] + [System.String] + $CustomAttribute12, + + [Parameter()] + [System.String] + $CustomAttribute13, + + [Parameter()] + [System.String] + $CustomAttribute14, + + [Parameter()] + [System.String] + $CustomAttribute15, + + [Parameter()] + [System.String[]] + $ExtensionCustomAttribute1, + + [Parameter()] + [System.String[]] + $ExtensionCustomAttribute2, + + [Parameter()] + [System.String[]] + $ExtensionCustomAttribute3, + + [Parameter()] + [System.String[]] + $ExtensionCustomAttribute4, + + [Parameter()] + [System.String[]] + $ExtensionCustomAttribute5, + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] @@ -320,21 +495,24 @@ function Set-TargetResource Add-M365DSCTelemetryEvent -Data $data #endregion - $setParameters = $PSBoundParameters - $setParameters.Remove('Credential') | Out-Null - $setParameters.Remove('ApplicationId') | Out-Null - $setParameters.Remove('TenantId') | Out-Null - $setParameters.Remove('CertificateThumbprint') | Out-Null - $setParameters.Remove('CertificatePath') | Out-Null - $setParameters.Remove('CertificatePassword') | Out-Null - $setParameters.Remove('ManagedIdentity') | Out-Null - $setParameters.Remove('Ensure') | Out-Null - # Mail Contact doesn't exist but it should if ($Ensure -eq 'Present' -and $currentContact.Ensure -eq 'Absent') { + $parameters = Sync-M365DSCParameter -Command (Get-Command -Name New-MailContact) -Parameters $PSBoundParameters Write-Verbose -Message "The Mail Contact '$($Name)' does not exist but it should. Creating Mail Contact." - New-MailContact @setParameters + + try + { + New-MailContact @parameters -ErrorAction Stop + + $parameters = Sync-M365DSCParameter -Command (Get-Command -Name Set-MailContact) -Parameters $PSBoundParameters + $parameters.Identity = $Name + Set-MailContact @parameters -ErrorAction Stop + } + catch + { + Write-Error -ErrorRecord $_ + } } # Mail Contact exists but shouldn't elseif ($Ensure -eq 'Absent' -and $currentContact.Ensure -eq 'Present') @@ -345,10 +523,10 @@ function Set-TargetResource elseif ($Ensure -eq 'Present' -and $currentContact.Ensure -eq 'Present') { Write-Verbose -Message "Mail Contact '$($Name)' already exists. Updating settings" - Write-Verbose -Message "Updating Mail Contact '$($Name)' with values: $(Convert-M365DscHashtableToString -Hashtable $setParameters)" - $setParameters.Add('Identity', $Name) - $setParameters.Remove('OrganizationalUnit') | Out-Null - Set-MailContact @setParameters + $parameters = Sync-M365DSCParameter -Command (Get-Command -Name Set-MailContact) -Parameters $PSBoundParameters + Write-Verbose -Message "Updating Mail Contact '$($Name)' with values: $(Convert-M365DscHashtableToString -Hashtable $parameters)" + $parameters.Identity = $Name + Set-MailContact @parameters } } @@ -424,6 +602,86 @@ function Test-TargetResource [System.Boolean] $UsePreferMessageFormat, + [Parameter()] + [System.String] + $CustomAttribute1, + + [Parameter()] + [System.String] + $CustomAttribute2, + + [Parameter()] + [System.String] + $CustomAttribute3, + + [Parameter()] + [System.String] + $CustomAttribute4, + + [Parameter()] + [System.String] + $CustomAttribute5, + + [Parameter()] + [System.String] + $CustomAttribute6, + + [Parameter()] + [System.String] + $CustomAttribute7, + + [Parameter()] + [System.String] + $CustomAttribute8, + + [Parameter()] + [System.String] + $CustomAttribute9, + + [Parameter()] + [System.String] + $CustomAttribute10, + + [Parameter()] + [System.String] + $CustomAttribute11, + + [Parameter()] + [System.String] + $CustomAttribute12, + + [Parameter()] + [System.String] + $CustomAttribute13, + + [Parameter()] + [System.String] + $CustomAttribute14, + + [Parameter()] + [System.String] + $CustomAttribute15, + + [Parameter()] + [System.String[]] + $ExtensionCustomAttribute1, + + [Parameter()] + [System.String[]] + $ExtensionCustomAttribute2, + + [Parameter()] + [System.String[]] + $ExtensionCustomAttribute3, + + [Parameter()] + [System.String[]] + $ExtensionCustomAttribute4, + + [Parameter()] + [System.String[]] + $ExtensionCustomAttribute5, + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] @@ -477,15 +735,14 @@ function Test-TargetResource Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $PSBoundParameters)" $ValuesToCheck = $PSBoundParameters - $ValuesToCheck.Remove('Credential') | Out-Null - $ValuesToCheck.Remove('ApplicationId') | Out-Null - $ValuesToCheck.Remove('TenantId') | Out-Null - $ValuesToCheck.Remove('CertificateThumbprint') | Out-Null - $ValuesToCheck.Remove('CertificatePath') | Out-Null - $ValuesToCheck.Remove('CertificatePassword') | Out-Null - $ValuesToCheck.Remove('ManagedIdentity') | Out-Null - - $ValuesToCheck.Remove('OrganizationalUnit') | Out-Null + [void]$ValuesToCheck.Remove('Credential') + [void]$ValuesToCheck.Remove('ApplicationId') + [void]$ValuesToCheck.Remove('TenantId') + [void]$ValuesToCheck.Remove('CertificateThumbprint') + [void]$ValuesToCheck.Remove('CertificatePath') + [void]$ValuesToCheck.Remove('CertificatePassword') + [void]$ValuesToCheck.Remove('ManagedIdentity') + [void]$ValuesToCheck.Remove('OrganizationalUnit') $TestResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` -Source $($MyInvocation.MyCommand.Source) ` @@ -581,7 +838,7 @@ function Export-TargetResource -ModulePath $PSScriptRoot ` -Results $Results ` -Credential $Credential - $dscContent.Append($currentDSCBlock) | Out-Null + [void]$dscContent.Append($currentDSCBlock) Save-M365DSCPartialExport -Content $currentDSCBlock ` -FileName $Global:PartialExportFileName diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailContact/MSFT_EXOMailContact.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailContact/MSFT_EXOMailContact.schema.mof index 6cd8241225..f5eefa0f76 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailContact/MSFT_EXOMailContact.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailContact/MSFT_EXOMailContact.schema.mof @@ -16,6 +16,26 @@ class MSFT_EXOMailContact : OMI_BaseResource [Write, Description("The OrganizationalUnit parameter specifies the location in Active Directory where the new contact is created.")] String OrganizationalUnit; [Write, Description("The SendModerationNotifications parameter specifies when moderation notification messages are sent. Valid values are: ALways, Internal, Never"), ValueMap{"Always","Internal","Never"}, Values{"Always","Internal","Never"}] String SendModerationNotifications; [Write, Description("The UsePreferMessageFormat specifies whether the message format settings configured for the mail user or mail contact override the global settings configured for the remote domain or configured by the message sender")] Boolean UsePreferMessageFormat; + [Write, Description("The CustomAttribute1 parameter specifies the value of the CustomAttribute1")] String CustomAttribute1; + [Write, Description("The CustomAttribute2 parameter specifies the value of the CustomAttribute2")] String CustomAttribute2; + [Write, Description("The CustomAttribute3 parameter specifies the value of the CustomAttribute3")] String CustomAttribute3; + [Write, Description("The CustomAttribute4 parameter specifies the value of the CustomAttribute4")] String CustomAttribute4; + [Write, Description("The CustomAttribute5 parameter specifies the value of the CustomAttribute5")] String CustomAttribute5; + [Write, Description("The CustomAttribute6 parameter specifies the value of the CustomAttribute6")] String CustomAttribute6; + [Write, Description("The CustomAttribute7 parameter specifies the value of the CustomAttribute7")] String CustomAttribute7; + [Write, Description("The CustomAttribute8 parameter specifies the value of the CustomAttribute8")] String CustomAttribute8; + [Write, Description("The CustomAttribute9 parameter specifies the value of the CustomAttribute9")] String CustomAttribute9; + [Write, Description("The CustomAttribute10 parameter specifies the value of the CustomAttribute10")] String CustomAttribute10; + [Write, Description("The CustomAttribute11 parameter specifies the value of the CustomAttribute11")] String CustomAttribute11; + [Write, Description("The CustomAttribute12 parameter specifies the value of the CustomAttribute12")] String CustomAttribute12; + [Write, Description("The CustomAttribute13 parameter specifies the value of the CustomAttribute13")] String CustomAttribute13; + [Write, Description("The CustomAttribute14 parameter specifies the value of the CustomAttribute14")] String CustomAttribute14; + [Write, Description("The CustomAttribute15 parameter specifies the value of the CustomAttribute15")] String CustomAttribute15; + [Write, Description("The ExtensionCustomAttribute1 parameter specifies the value of the ExtensionCustomAttribute1")] String ExtensionCustomAttribute1[]; + [Write, Description("The ExtensionCustomAttribute2 parameter specifies the value of the ExtensionCustomAttribute2")] String ExtensionCustomAttribute2[]; + [Write, Description("The ExtensionCustomAttribute3 parameter specifies the value of the ExtensionCustomAttribute3")] String ExtensionCustomAttribute3[]; + [Write, Description("The ExtensionCustomAttribute4 parameter specifies the value of the ExtensionCustomAttribute4")] String ExtensionCustomAttribute4[]; + [Write, Description("The ExtensionCustomAttribute5 parameter specifies the value of the ExtensionCustomAttribute5")] String ExtensionCustomAttribute5[]; [Write, Description("Specifies if this Contact should exist."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; [Write, Description("Credentials of the Exchange Global Admin"), EmbeddedInstance("MSFT_Credential")] string Credential; [Write, Description("Id of the Azure Active Directory application to authenticate with.")] String ApplicationId; diff --git a/Modules/Microsoft365DSC/Examples/Resources/EXOMailContact/1-NewMailContact.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOMailContact/1-NewMailContact.ps1 index 12aa54841b..74c7a2ac87 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/EXOMailContact/1-NewMailContact.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOMailContact/1-NewMailContact.ps1 @@ -8,7 +8,7 @@ Configuration Example param( [Parameter(Mandatory = $true)] [PSCredential] - $credsCredential + $Credscredential ) Import-DscResource -ModuleName Microsoft365DSC @@ -16,20 +16,22 @@ Configuration Example { EXOMailContact 'TestMailContact' { - Alias = "TestMailContact"; - Credential = $Credscredential; - DisplayName = "My Test Contact"; - Ensure = "Present"; - ExternalEmailAddress = "SMTP:test@tailspintoys.com"; - MacAttachmentFormat = "BinHex"; - MessageBodyFormat = "TextAndHtml"; - MessageFormat = "Mime"; - ModeratedBy = @(); - ModerationEnabled = $False; - Name = "My Test Contact"; - OrganizationalUnit = "nampr03a010.prod.outlook.com/Microsoft Exchange Hosted Organizations/$OrganizationName"; - SendModerationNotifications = "Always"; - UsePreferMessageFormat = $True; + Alias = 'TestMailContact' + Credential = $Credscredential + DisplayName = 'My Test Contact' + Ensure = 'Present' + ExternalEmailAddress = 'SMTP:test@tailspintoys.com' + MacAttachmentFormat = 'BinHex' + MessageBodyFormat = 'TextAndHtml' + MessageFormat = 'Mime' + ModeratedBy = @() + ModerationEnabled = $false + Name = 'My Test Contact' + OrganizationalUnit = "nampr03a010.prod.outlook.com/Microsoft Exchange Hosted Organizations/$OrganizationName" + SendModerationNotifications = 'Always' + UsePreferMessageFormat = $true + CustomAttribute1 = 'Custom Value 1' + ExtensionCustomAttribute5 = 'Extension Custom Value 1', 'Extension Custom Value 2' } } } diff --git a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 index 08eb7511fd..9f10e145ac 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 @@ -4331,6 +4331,136 @@ function Get-M365DSCConfigurationConflict return $results } +<# + .Description + This function returns a hashtable with aligned to the parameter pattern of the given cmdlet. + + .Example + $param = @{ + Path = 'C:\Test' + DoesNotExist = '123' + } + Sync-M365DSCParameter -Command (Get-Command -Name Get-ChildItem) -Parameters $param + + .Functionality + Private +#> +function Sync-M365DSCParameter +{ + [Cmdletbinding()] + param ( + [Parameter(Mandatory = $true)] + [ValidateScript( { + $_ -is [System.Management.Automation.FunctionInfo] -or + $_ -is [System.Management.Automation.CmdletInfo] -or + $_ -is [System.Management.Automation.ExternalScriptInfo] -or + $_ -is [System.Management.Automation.AliasInfo] + })] + [object]$Command, + + [hashtable]$Parameters, + + [switch]$ConvertValue + ) + + if (-not $PSBoundParameters.ContainsKey('Parameters')) + { + $Parameters = ([hashtable]$ALBoundParameters).Clone() + } + else + { + $Parameters = ([hashtable]$Parameters).Clone() + } + + if ($Command -is [System.Management.Automation.AliasInfo]) + { + $command = & (Get-Module -Name $Command.Source) { + param([string]$Name) + + Get-Command -Name $Name + } $Command.Definition + } + + $commonParameters = [System.Management.Automation.Internal.CommonParameters].GetProperties().Name + $commandParameterKeys = $Command.Parameters.Keys.GetEnumerator() | ForEach-Object { $_ } + $parameterKeys = $Parameters.Keys.GetEnumerator() | ForEach-Object { $_ } + + $keysToRemove = Compare-Object -ReferenceObject $commandParameterKeys -DifferenceObject $parameterKeys | + Select-Object -ExpandProperty InputObject + + $keysToRemove = $keysToRemove + $commonParameters | Select-Object -Unique #remove the common parameters + + foreach ($key in $keysToRemove) + { + $Parameters.Remove($key) + } + + if ($ConvertValue.IsPresent) + { + $keysToUpdate = @{} + foreach ($kvp in $Parameters.GetEnumerator()) + { + if (-not $kvp.Value) # $null or empty string will not trip up conversion + { + continue + } + + $targetType = $Command.Parameters[$kvp.Key].ParameterType + $sourceType = $kvp.Value.GetType() + $targetValue = $kvp.Value -as $targetType + + if (-not $targetValue -and $targetType.ImplementedInterfaces -contains [Collections.IList]) + { + $targetValue = $targetType::new() + foreach ($v in $kvp.Value) + { + $targetValue.Add($v) + } + } + + if (-not $targetValue -and $targetType.ImplementedInterfaces -contains [Collections.IDictionary] ) + { + $targetValue = $targetType::new() + foreach ($k in $kvp.Value.GetEnumerator()) + { + $targetValue.Add($k.Key, $k.Value) + } + } + + if (-not $targetValue -and ($sourceType.ImplementedInterfaces -contains [Collections.IList] -and $targetType.ImplementedInterfaces -notcontains [Collections.IList])) + { + Write-Verbose -Message "Value of source parameter $($kvp.Key) is a collection, but target parameter is not. Selecting first object" + $targetValue = $kvp.Value | Select-Object -First 1 + } + + if (-not $targetValue) + { + Write-Error -Message "Conversion of source parameter $($kvp.Key) (Type: $($sourceType.FullName)) to type $($targetType.FullName) was impossible" + return + } + + $keysToUpdate[$kvp.Key] = $targetValue + } + } + + if ($keysToUpdate) + { + foreach ($kvp in $keysToUpdate.GetEnumerator()) + { + $Parameters[$kvp.Key] = $kvp.Value + } + } + + if ($PSBoundParameters.ContainsKey('Parameters')) + { + $Parameters + } + else + { + $global:ALBoundParameters = $Parameters + } +} + Export-ModuleMember -Function @( 'Assert-M365DSCBlueprint', 'Confirm-ImportedCmdletIsAvailable', @@ -4373,5 +4503,6 @@ Export-ModuleMember -Function @( 'Update-M365DSCDependencies', 'Update-M365DSCExportAuthenticationResults', 'Update-M365DSCModule', - 'Write-M365DSCLogEvent' + 'Write-M365DSCLogEvent', + 'Sync-M365DSCParameter' )