DSCResources/MSFT_SPOSearchResultSource/MSFT_SPOSearchResultSource.psm1
$InfoMapping = @( @{ Protocol = 'Local' Type = 'SharePoint' ProviderID = 'fa947043-6046-4f97-9714-40d4c113963d' }, @{ Protocol = 'Remote' Type = 'SharePoint' ProviderID = '1e0c8601-2e5d-4ccb-9561-53743b5dbde7' }, @{ Protocol = 'Exchange' Type = 'SharePoint' ProviderID = '3a17e140-1574-4093-bad6-e19cdf1c0122' }, @{ Protocol = 'OpenSearch' Type = 'SharePoint' ProviderID = '3a17e140-1574-4093-bad6-e19cdf1c0121' }, @{ Protocol = 'Local' Type = 'People' ProviderID = 'e4bcc058-f133-4425-8ffc-1d70596ffd33' }, @{ Protocol = 'Remote' Type = 'People' ProviderID = 'e377caaa-fcaf-4a1b-b7a1-e69a506a07aa' } ) function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter()] [System.String] $Description, [Parameter(Mandatory = $true)] [ValidateSet('Local', 'Remote', 'OpenSearch', 'Exchange')] [System.String] $Protocol, [Parameter()] [System.String] $SourceURL, [Parameter()] [ValidateSet('SharePoint', 'People')] [System.String] $Type = 'SharePoint', [Parameter()] [System.String] $QueryTransform, [Parameter()] [System.Boolean] $ShowPartialSearch = $true, [Parameter()] [System.Boolean] $UseAutoDiscover, [Parameter()] [ValidateSet('Present')] [System.String] $Ensure = 'Present', [Parameter()] [System.Management.Automation.PSCredential] $Credential, [Parameter()] [System.String] $ApplicationId, [Parameter()] [System.String] $TenantId, [Parameter()] [System.Management.Automation.PSCredential] $ApplicationSecret, [Parameter()] [System.String] $CertificateThumbprint, [Parameter()] [System.String] $CertificatePath, [Parameter()] [System.Management.Automation.PSCredential] $CertificatePassword, [Parameter()] [Switch] $ManagedIdentity, [Parameter()] [System.String[]] $AccessTokens ) Write-Verbose -Message "Setting configuration for Result Source instance $Name" $ConnectionMode = New-M365DSCConnection -Workload 'PnP' ` -InboundParameters $PSBoundParameters #Ensure the proper dependencies are installed in the current environment. Confirm-M365DSCDependencies #region Telemetry $ResourceName = $MyInvocation.MyCommand.ModuleName -replace 'MSFT_', '' $CommandName = $MyInvocation.MyCommand $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` -CommandName $CommandName ` -Parameters $PSBoundParameters Add-M365DSCTelemetryEvent -Data $data #endregion $nullReturn = @{ Name = $Name Protocol = $Protocol Ensure = 'Absent' } try { if ($null -eq $Script:RecentExtract) { $Script:RecentExtract = [Xml] (Get-PnPSearchConfiguration -Scope Subscription) } $source = $Script:RecentExtract.SearchConfigurationSettings.SearchQueryConfigurationSettings.SearchQueryConfigurationSettings.Sources.Source ` | Where-Object -FilterScript { $_.Name -eq $Name } if ($null -eq $source) { Write-Verbose -Message "The specified Result Source {$($Name)} doesn't already exist." return $nullReturn } $ExoSource = [string] $source.ConnectionUrlTemplate $SourceHasAutoDiscover = $false if ('http://auto?autodiscover=true' -eq $ExoSource) { $SourceHasAutoDiscover = $true } $allowPartial = $source.QueryTransform.OverridePropertiesForSeralization.KeyValueOfstringanyType ` | Where-Object -FilterScript { $_.Key -eq 'AllowPartialResults' } $mapping = $InfoMapping | Where-Object -FilterScript { $_.ProviderID -eq $source.ProviderId } $returnValue = @{ Name = $Name Description = [string] $source.Description Protocol = $mapping.Protocol Type = $mapping.Type QueryTransform = [string] $source.QueryTransform._QueryTemplate SourceURL = [string] $source.ConnectionUrlTemplate UseAutoDiscover = $SourceHasAutoDiscover Credential = $Credential ApplicationId = $ApplicationId TenantId = $TenantId ApplicationSecret = $ApplicationSecret CertificatePassword = $CertificatePassword CertificatePath = $CertificatePath CertificateThumbprint = $CertificateThumbprint Managedidentity = $ManagedIdentity.IsPresent Ensure = 'Present' AccessTokens = $AccessTokens } if ($null -ne $allowPartial) { $returnValue.Add('ShowPartialSearch', [System.Boolean]$allowPartial.Value.InnerText) } return $returnValue } catch { New-M365DSCLogEntry -Message 'Error retrieving data:' ` -Exception $_ ` -Source $($MyInvocation.MyCommand.Source) ` -TenantId $TenantId ` -Credential $Credential return $nullReturn } } function Set-TargetResource { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter()] [System.String] $Description, [Parameter(Mandatory = $true)] [ValidateSet('Local', 'Remote', 'OpenSearch', 'Exchange')] [System.String] $Protocol, [Parameter()] [System.String] $SourceURL, [Parameter()] [ValidateSet('SharePoint', 'People')] [System.String] $Type = 'SharePoint', [Parameter()] [System.String] $QueryTransform, [Parameter()] [System.Boolean] $ShowPartialSearch = $true, [Parameter()] [System.Boolean] $UseAutoDiscover, [Parameter()] [ValidateSet('Present')] [System.String] $Ensure = 'Present', [Parameter()] [System.Management.Automation.PSCredential] $Credential, [Parameter()] [System.String] $ApplicationId, [Parameter()] [System.String] $TenantId, [Parameter()] [System.Management.Automation.PSCredential] $ApplicationSecret, [Parameter()] [System.String] $CertificateThumbprint, [Parameter()] [System.String] $CertificatePath, [Parameter()] [System.Management.Automation.PSCredential] $CertificatePassword, [Parameter()] [Switch] $ManagedIdentity, [Parameter()] [System.String[]] $AccessTokens ) Write-Verbose -Message "Setting configuration for Result Source instance $Name" #Ensure the proper dependencies are installed in the current environment. Confirm-M365DSCDependencies #region Telemetry $ResourceName = $MyInvocation.MyCommand.ModuleName -replace 'MSFT_', '' $CommandName = $MyInvocation.MyCommand $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` -CommandName $CommandName ` -Parameters $PSBoundParameters Add-M365DSCTelemetryEvent -Data $data #endregion $ConnectionMode = New-M365DSCConnection -Workload 'PnP' ` -InboundParameters $PSBoundParameters if ($Ensure -eq 'Absent') { Write-Verbose -Message "Removing SPOSearchResultSource {$Name}" Remove-PnPSearchConfiguration -Configuration $Name -Scope Subscription return } Write-Verbose -Message 'Reading SearchConfigurationSettings XML file' $SearchConfigTemplatePath = Join-Path -Path $PSScriptRoot ` -ChildPath '..\..\Dependencies\SearchConfigurationSettings.xml' ` -Resolve $SearchConfigXML = [Xml] (Get-Content $SearchConfigTemplatePath -Raw) # Get the result source back if it already exists. if ($null -eq $Script:RecentExtract) { $Script:RecentExtract = [XML] (Get-PnPSearchConfiguration -Scope Subscription) } $source = $Script:RecentExtract.SearchConfigurationSettings.SearchQueryConfigurationSettings.SearchQueryConfigurationSettings.Sources.Source ` | Where-Object -FilterScript { $_.Name -eq $Name } if ($null -ne $source) { $currentID = $source.Id } Write-Verbose -Message 'Generating new SearchConfigurationSettings XML file' $newSource = $SearchConfigXML.CreateElement('d4p1:Source', ` 'http://schemas.datacontract.org/2004/07/Microsoft.Office.Server.Search.Administration.Query') Write-Verbose -Message 'Setting ConnectionUrlTemplate' $node = $SearchConfigXML.CreateElement('d4p1:ConnectionUrlTemplate', ` 'http://schemas.datacontract.org/2004/07/Microsoft.Office.Server.Search.Administration.Query') $node.InnerText = $SourceUrl $newSource.AppendChild($node) | Out-Null Write-Verbose -Message 'Setting CreatedDate' $node = $SearchConfigXML.CreateElement('d4p1:CreatedDate', ` 'http://schemas.datacontract.org/2004/07/Microsoft.Office.Server.Search.Administration.Query') $node.InnerText = [DateTime]::Now.ToString('yyyy-MM-ddThh:mm:ss.00') $newSource.AppendChild($node) | Out-Null Write-Verbose -Message 'Setting Description' $node = $SearchConfigXML.CreateElement('d4p1:Description', ` 'http://schemas.datacontract.org/2004/07/Microsoft.Office.Server.Search.Administration.Query') $node.InnerText = $Description $newSource.AppendChild($node) | Out-Null Write-Verbose -Message 'Setting Existing Id' $node = $SearchConfigXML.CreateElement('d4p1:Id', ` 'http://schemas.datacontract.org/2004/07/Microsoft.Office.Server.Search.Administration.Query') if ($null -ne $currentID) { $node.InnerText = $currentId } else { $node.InnerText = (New-Guid).ToString() } $newSource.AppendChild($node) | Out-Null Write-Verbose -Message 'Setting Name' $node = $SearchConfigXML.CreateElement('d4p1:Name', ` 'http://schemas.datacontract.org/2004/07/Microsoft.Office.Server.Search.Administration.Query') $node.InnerText = $Name $newSource.AppendChild($node) | Out-Null Write-Verbose -Message 'Setting ProviderId' $mapping = $InfoMapping | Where-Object -FilterScript { $_.Protocol -eq $Protocol -and $_.Type -eq $Type } $node = $SearchConfigXML.CreateElement('d4p1:ProviderId', ` 'http://schemas.datacontract.org/2004/07/Microsoft.Office.Server.Search.Administration.Query') $node.InnerText = $mapping.ProviderID $catch = $newSource.AppendChild($node) Write-Verbose -Message 'Setting QueryTransform' $queryTransformNode = $SearchConfigXML.CreateElement('d4p1:QueryTransform', ` 'http://schemas.datacontract.org/2004/07/Microsoft.Office.Server.Search.Administration.Query') $queryTransformNode.SetAttribute('xmlns:d6p1', 'http://www.microsoft.com/sharepoint/search/KnownTypes/2008/08') Write-Verbose -Message 'Setting QueryTransform:Id' $node = $SearchConfigXML.CreateElement('d6p1:Id', ` 'http://www.microsoft.com/sharepoint/search/KnownTypes/2008/08') $node.InnerText = (New-Guid).ToString() $queryTransformNode.AppendChild($node) Write-Verbose -Message 'Setting QueryTransform:ParentType' $queryTransformNode = $SearchConfigXML.CreateElement('d6p1:ParentType', ` 'http://www.microsoft.com/sharepoint/search/KnownTypes/2008/08') $node.InnerText = 'Source' $queryTransformNode.AppendChild($node) Write-Verbose -Message 'Setting QueryTransform:QueryPropertyExpressions' $QueryPropertyExpressions = $SearchConfigXML.CreateElement('d6p1:QueryPropertyExpressions', ` 'http://www.microsoft.com/sharepoint/search/KnownTypes/2008/08') Write-Verbose -Message 'Setting QueryTransform:QueryPropertyExpressions:MaxSize' $node = $SearchConfigXML.CreateElement('d6p1:MaxSize', ` 'http://www.microsoft.com/sharepoint/search/KnownTypes/2008/08') $node.InnerText = '2147483647' $QueryPropertyExpressions.AppendChild($node) Write-Verbose -Message 'Setting QueryTransform:QueryPropertyExpressions:OrderedItems' $node = $SearchConfigXML.CreateElement('d6p1:OrderedItems', ` 'http://www.microsoft.com/sharepoint/search/KnownTypes/2008/08') $QueryPropertyExpressions.AppendChild($node) $queryTransformNode.AppendChild($QueryPropertyExpressions) Write-Verbose -Message 'Setting QueryTransform:_IsReadOnly' $node = $SearchConfigXML.CreateElement('d6p1:_IsReadOnly', ` 'http://www.microsoft.com/sharepoint/search/KnownTypes/2008/08') $node.InnerText = 'true' $queryTransformNode.AppendChild($node) Write-Verbose -Message 'Setting QueryTransform:_QueryTemplate' $node = $SearchConfigXML.CreateElement('d6p1:_QueryTemplate', ` 'http://www.microsoft.com/sharepoint/search/KnownTypes/2008/08') $node.InnerText = $QueryTransform $queryTransformNode.AppendChild($node) | Out-Null Write-Verbose -Message 'Setting QueryTransform:_SourceId' $node = $SearchConfigXML.CreateElement('d6p1:_SourceId', ` 'http://www.microsoft.com/sharepoint/search/KnownTypes/2008/08') $node.SetAttribute('i:nil', 'true') $queryTransformNode.AppendChild($node) Write-Verbose -Message 'Inserting QueryTransform' $newSource.AppendChild($queryTransformNode) | Out-Null Write-Verbose -Message 'Inserting new Source Node' $xmlNode = $SearchConfigXML.SearchConfigurationSettings.SearchQueryConfigurationSettings.SearchQueryConfigurationSettings.Sources.OwnerDocument.ImportNode($newSource, $true) $SearchConfigXML.SearchConfigurationSettings.SearchQueryConfigurationSettings.SearchQueryConfigurationSettings.Sources.AppendChild($xmlNode) Write-Verbose -Message 'Saving XML file in a temporary location' $tempPath = Join-Path -Path $ENV:TEMP ` -ChildPath ((New-Guid).ToString().Split('-')[0] + '.config') $SearchConfigXML.OuterXml | Out-File $tempPath # Create the Result Source if it doesn't already exist Write-Verbose -Message 'Applying new Search Configuration back to the Office365 Tenant' Set-PnPSearchConfiguration -Scope Subscription -Path $tempPath } function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter()] [System.String] $Description, [Parameter(Mandatory = $true)] [ValidateSet('Local', 'Remote', 'OpenSearch', 'Exchange')] [System.String] $Protocol, [Parameter()] [System.String] $SourceURL, [Parameter()] [ValidateSet('SharePoint', 'People')] [System.String] $Type = 'SharePoint', [Parameter()] [System.String] $QueryTransform, [Parameter()] [System.Boolean] $ShowPartialSearch = $true, [Parameter()] [System.Boolean] $UseAutoDiscover, [Parameter()] [ValidateSet('Present')] [System.String] $Ensure = 'Present', [Parameter()] [System.Management.Automation.PSCredential] $Credential, [Parameter()] [System.String] $ApplicationId, [Parameter()] [System.String] $TenantId, [Parameter()] [System.Management.Automation.PSCredential] $ApplicationSecret, [Parameter()] [System.String] $CertificateThumbprint, [Parameter()] [System.String] $CertificatePath, [Parameter()] [System.Management.Automation.PSCredential] $CertificatePassword, [Parameter()] [Switch] $ManagedIdentity, [Parameter()] [System.String[]] $AccessTokens ) #Ensure the proper dependencies are installed in the current environment. Confirm-M365DSCDependencies #region Telemetry $ResourceName = $MyInvocation.MyCommand.ModuleName -replace 'MSFT_', '' $CommandName = $MyInvocation.MyCommand $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` -CommandName $CommandName ` -Parameters $PSBoundParameters Add-M365DSCTelemetryEvent -Data $data #endregion Write-Verbose -Message "Testing configuration for Result Source instance $Name" $CurrentValues = Get-TargetResource @PSBoundParameters Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $PSBoundParameters)" $TestResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` -Source $($MyInvocation.MyCommand.Source) ` -DesiredValues $PSBoundParameters Write-Verbose -Message "Test-TargetResource returned $TestResult" return $TestResult } function Export-TargetResource { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter()] [System.Management.Automation.PSCredential] $Credential, [Parameter()] [System.String] $ApplicationId, [Parameter()] [System.String] $TenantId, [Parameter()] [System.Management.Automation.PSCredential] $ApplicationSecret, [Parameter()] [System.String] $CertificateThumbprint, [Parameter()] [System.String] $CertificatePath, [Parameter()] [System.Management.Automation.PSCredential] $CertificatePassword, [Parameter()] [Switch] $ManagedIdentity, [Parameter()] [System.String[]] $AccessTokens ) try { $ConnectionMode = New-M365DSCConnection -Workload 'PnP' ` -InboundParameters $PSBoundParameters #Ensure the proper dependencies are installed in the current environment. Confirm-M365DSCDependencies #region Telemetry $ResourceName = $MyInvocation.MyCommand.ModuleName -replace 'MSFT_', '' $CommandName = $MyInvocation.MyCommand $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` -CommandName $CommandName ` -Parameters $PSBoundParameters Add-M365DSCTelemetryEvent -Data $data #endregion $SearchConfig = [Xml] (Get-PnPSearchConfiguration -Scope Subscription -ErrorAction Stop) [array]$sources = $SearchConfig.SearchConfigurationSettings.SearchQueryConfigurationSettings.SearchQueryConfigurationSettings.Sources.Source $dscContent = '' $i = 1 $sourcesLength = $sources.Length if ($sources.Length -eq 0) { Write-Host $Global:M365DSCEmojiGreenCheckMark } else { Write-Host "`r`n" -NoNewline } foreach ($source in $sources) { if ($null -ne $Global:M365DSCExportResourceInstancesCount) { $Global:M365DSCExportResourceInstancesCount++ } $mapping = $InfoMapping | Where-Object -FilterScript { $_.ProviderID -eq $source.ProviderId } Write-Host " |---[$i/$($sourcesLength)] $($source.Name)" -NoNewline $Params = @{ Name = $source.Name Protocol = $mapping.Protocol Credential = $Credential ApplicationId = $ApplicationId TenantId = $TenantId ApplicationSecret = $ApplicationSecret CertificateThumbprint = $CertificateThumbprint Managedidentity = $ManagedIdentity.IsPresent CertificatePath = $CertificatePath CertificatePassword = $CertificatePassword AccessTokens = $AccessTokens } $Results = Get-TargetResource @Params if ($Results -is [System.Collections.Hashtable] -and $Results.Count -gt 2) { $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` -Results $Results $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` -ConnectionMode $ConnectionMode ` -ModulePath $PSScriptRoot ` -Results $Results ` -Credential $Credential $dscContent += $currentDSCBlock Save-M365DSCPartialExport -Content $currentDSCBlock ` -FileName $Global:PartialExportFileName Write-Host $Global:M365DSCEmojiGreenCheckmark } else { Write-Host $Global:M365DSCEmojiRedX } $i++ } return $dscContent } catch { Write-Host $Global:M365DSCEmojiRedX New-M365DSCLogEntry -Message 'Error during Export:' ` -Exception $_ ` -Source $($MyInvocation.MyCommand.Source) ` -TenantId $TenantId ` -Credential $Credential return '' } } Export-ModuleMember -Function *-TargetResource |