Modules/M365DSCCheckProperties.psm1
|
<#
.SYNOPSIS Checks if properties of existing resources are up to date. .DESCRIPTION This function checks if properties of existing resources are up to date. Creates a report about missing or outdated properties of existing resources and a list of missing resources. .PARAMETER DestinationFolder Specifies the destination folder where the report will be saved. .PARAMETER Credential Specifies the credentials to use for connecting to Microsoft 365 workloads. .FUNCTIONALITY Internal #> function Get-PropertyReport { param ( [Parameter(Mandatory = $true)] [System.String] $DestinationFolder, [Parameter()] [System.Management.Automation.PSCredential] $Credential ) # list of cmdlet parameters to be ignored $invalidParameters = @( 'ErrorVariable', 'ErrorAction', 'Force', 'HttpPipelinePrepend', 'InformationVariable', 'InformationAction', 'MsftInternalProcessingMode', 'WarningVariable', 'WarningAction', 'OutVariable', 'OutBuffer', 'PipelineVariable', 'Verbose', 'WhatIf', 'Debug', 'Confirm', 'AsJob' ) # list of M365 DSC resource properties to be ignored $invalidProperties = @( 'ErrorVariable', 'ErrorAction', 'Force', 'HttpPipelinePrepend', 'InformationVariable', 'InformationAction', 'MsftInternalProcessingMode', 'WarningVariable', 'WarningAction', 'OutVariable', 'OutBuffer', 'PipelineVariable', 'Verbose', 'WhatIf', 'Debug', 'Confirm', 'AsJob' 'Credential', 'ApplicationId', 'ApplicationSecret', 'Ensure', 'TenantId', 'CertificateThumbprint', 'CertificatePath', 'CertificatePassword', 'AccessTokens', 'ManagedIdentity', 'IsSingleInstance' ) # list of M365 workloads to check $workloads = @( @{Name = 'MicrosoftTeams'; ModuleName = 'MicrosoftTeams'; Prefix = 'Teams'; } @{Name = 'ExchangeOnline'; ModuleName = 'ExchangeOnlineManagement'; CommandName = 'Get-Mailbox'; Prefix = 'EXO'; } @{Name = 'SecurityComplianceCenter'; ModuleName = 'ExchangeOnlineManagement'; CommandName = 'Set-ComplianceCase'; Prefix = 'SC'; } ) # mapping table for resources with names different from cmdlet name $cmdletMapping = @{ CasMailbox = 'CASMailboxSettings' Mailbox = 'SharedMailbox' MailboxRegionalConfiguration = 'MailboxSettings' EXOPerimeterConfig = 'PerimeterConfiguration' } $missingResources = @() $report = @() if ($null -eq $Credential) { $Credential = Get-Credential $PSBoundParameters.Add('Credential', $Credential) } $folderPath = Join-Path $PSScriptRoot -ChildPath '../DscResources' Write-Verbose "Folderpath of DSC resources: $folderPath" foreach ($module in $workloads) { if ($module.Name -ne 'MicrosoftTeams') { Write-Verbose "Connecting to {$($Module.Name)}" $null = New-M365DSCConnection -Workload ($Module.Name) -InboundParameters $PSBoundParameters } Write-Verbose "Getting list of cmdlets of {$($Module.ModuleName)}..." $CurrentModuleName = $Module.ModuleName if ($null -eq $CurrentModuleName -or $Module.CommandName) { Write-Verbose "Loading proxy for $($Module.ModuleName)" $foundModule = Get-Module | Where-Object -FilterScript { $_.ExportedCommands.Values.Name -ccontains $Module.CommandName } $CurrentModuleName = $foundModule.Name Import-Module $CurrentModuleName -Force -Global -ErrorAction SilentlyContinue } else { Import-Module $CurrentModuleName -Force -Global -ErrorAction SilentlyContinue $null = New-M365DSCConnection -Workload $Module.Name -InboundParameters $PSBoundParameters } $cmdlets = Get-Command -CommandType 'Function' -Module $CurrentModuleName $setCmdlets = $cmdlets | Where-Object -Property Name -Like 'Set-*' Write-Verbose "Found $($setCmdlets.Count) Set-* cmdlets for $($Module.ModuleName) ($($cmdlets.Count) in total)" $i = 1 foreach ($cmdlet in $setCmdlets) { Write-Progress -Activity 'Checking resources' -Status $cmdlet.Name -PercentComplete (($i / $setCmdlets.Length) * 100) $resourceExists = $false $resourceName = 'MSFT_' + $module.Prefix + $cmdlet.Name.Split('-')[1] if ($module.ModuleName -eq 'MicrosoftTeams' -and $resourceName -like '*TeamsCsTeams*') { $resourceName = $resourceName -replace ('TeamsCsTeams', 'Teams') } if ($module.ModuleName -eq 'MicrosoftTeams' -and $resourceName -like '*TeamsCs*') { $resourceName = $resourceName -replace ('TeamsCs', 'Teams') } $foundInFiles = Get-ChildItem -Path $folderPath | Where-Object -Property Name -Like $resourceName if ($null -eq $foundInFiles) { $resourceNameFromMapping = $cmdletMapping[$cmdlet.Name.Split('-')[1]] if ($null -ne $resourceNameFromMapping) { $resourceName = 'MSFT_' + $module.Prefix + $resourceNameFromMapping $foundInFiles = Get-ChildItem -Path $folderPath | Where-Object -Property Name -Like $resourceName if ($null -ne $foundInFiles) { $resourceExists = $true } } } else { $resourceExists = $true } if ($resourceExists) { # Get parameter of cmdlet Write-Verbose "Get parameters of cmdlet $($cmdlet.Name)" $targetParameters = @() $resourceParameters = @() $cmdletParameters = (Get-Command $cmdlet.Name).Parameters foreach ($parameter in $cmdletParameters.Keys) { if ($parameter -notin $invalidParameters) { $targetParameters += $parameter } } # Get properties of DSC resource Write-Verbose "Get properties of resource $resourceName" Import-Module $($folderPath + '/' + $resourceName) -Force $resourceProperties = (Get-Command Set-TargetResource -Module $resourceName).Parameters foreach ($property in $resourceProperties.Keys) { if ($property -notin $invalidProperties) { $resourceParameters += $property } } Remove-Module -Name $resourceName -Force -Confirm:$false # Compare properties Write-Verbose "Compare parameters of $resourceName" $difference = Compare-Object -ReferenceObject @($targetParameters | Select-Object) -DifferenceObject @($resourceParameters | Select-Object) -IncludeEqual $missingProperties = ($difference | Where-Object -Property SideIndicator -EQ '<=' ).InputObject $additionalProperties = ($difference | Where-Object -Property SideIndicator -EQ '=>' ).InputObject # Add to report $cmdletResult = [PSCustomObject]@{ 'M365DSCResource' = $resourceName 'Cmdlet' = $cmdlet.Name 'Service' = $module.Name 'MissingProperties' = $missingProperties -join ('; ') 'AdditionalProperties' = $additionalProperties -join ('; ') } $report += $cmdletResult } else { $missingResources += $resourceName Write-Verbose "Resource $resourceName not found." } $i++ } } # Export reports Write-Verbose 'Export reports' $report | Export-Csv -NoTypeInformation -Path "$DestinationFolder\M365DSC-Properties-Report.csv" -Delimiter ',' $missingResources | Out-File "$DestinationFolder\MissingDSCResources.csv" } Export-ModuleMember -Function @( 'Get-PropertyReport' ) |