DSCResources/MSFT_SPUserProfileSyncService/MSFT_SPUserProfileSyncService.psm1
function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [parameter(Mandatory = $true)] [System.String] $UserProfileServiceAppName, [parameter(Mandatory = $false)] [ValidateSet("Present","Absent")] [System.String] $Ensure = "Present", [parameter(Mandatory = $true)] [System.Management.Automation.PSCredential] $FarmAccount, [parameter(Mandatory = $false)] [System.Boolean] $RunOnlyWhenWriteable, [parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] $InstallAccount ) Write-Verbose -Message "Getting user profile sync service for $UserProfileServiceAppName" if ((Get-SPDSCInstalledProductVersion).FileMajorPart -ne 15) { throw [Exception] ("Only SharePoint 2013 is supported to deploy the user profile sync " + ` "service via DSC, as 2016 does not use the FIM based sync service.") } $result = Invoke-SPDSCCommand -Credential $InstallAccount ` -Arguments $PSBoundParameters ` -ScriptBlock { $params = $args[0] $syncServices = Get-SPServiceInstance -Server $env:COMPUTERNAME ` -ErrorAction SilentlyContinue if ($null -eq $syncServices) { return @{ UserProfileServiceAppName = $params.UserProfileServiceAppName Ensure = "Absent" FarmAccount = $params.FarmAccount RunOnlyWhenWriteable = $params.RunOnlyWhenWriteable InstallAccount = $params.InstallAccount } } $syncService = $syncServices | Where-Object -FilterScript { $_.GetType().Name -eq "UserProfileServiceInstance" } if ($null -eq $syncService) { $domain = (Get-CimInstance -ClassName Win32_ComputerSystem).Domain $currentServer = "$($env:COMPUTERNAME).$domain" $syncServices = Get-SPServiceInstance -Server $currentServer ` -ErrorAction SilentlyContinue $syncService = $syncServices | Where-Object -FilterScript { $_.GetType().Name -eq "UserProfileServiceInstance" } } if ($null -eq $syncService) { return @{ UserProfileServiceAppName = $params.UserProfileServiceAppName Ensure = "Absent" FarmAccount = $params.FarmAccount RunOnlyWhenWriteable = $params.RunOnlyWhenWriteable InstallAccount = $params.InstallAccount } } if ($null -ne $syncService.UserProfileApplicationGuid -and ` $syncService.UserProfileApplicationGuid -ne [Guid]::Empty) { $upa = Get-SPServiceInstance -Identity $syncService.UserProfileApplicationGuid ` -ErrorAction SilentlyContinue } if ($syncService.Status -eq "Online") { $localEnsure = "Present" } else { $localEnsure = "Absent" } $spFarm = Get-SPFarm if ($params.FarmAccount.UserName -eq $spFarm.DefaultServiceAccount.Name) { $farmAccount = $params.FarmAccount } else { $farmAccount = $spFarm.DefaultServiceAccount.Name } return @{ UserProfileServiceAppName = $upa.Name Ensure = $localEnsure FarmAccount = $farmAccount RunOnlyWhenWriteable = $params.RunOnlyWhenWriteable InstallAccount = $params.InstallAccount } } return $result } function Set-TargetResource { [CmdletBinding()] param ( [parameter(Mandatory = $true)] [System.String] $UserProfileServiceAppName, [parameter(Mandatory = $false)] [ValidateSet("Present","Absent")] [System.String] $Ensure = "Present", [parameter(Mandatory = $true)] [System.Management.Automation.PSCredential] $FarmAccount, [parameter(Mandatory = $false)] [System.Boolean] $RunOnlyWhenWriteable, [parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] $InstallAccount ) Write-Verbose -Message "Setting user profile sync service for $UserProfileServiceAppName" $PSBoundParameters.Ensure = $Ensure if ((Get-SPDSCInstalledProductVersion).FileMajorPart -ne 15) { throw [Exception] ("Only SharePoint 2013 is supported to deploy the user profile sync " + ` "service via DSC, as 2016 does not use the FIM based sync service.") } if ($PSBoundParameters.ContainsKey("RunOnlyWhenWriteable") -eq $true) { $databaseReadOnly = Test-SPDscUserProfileDBReadOnly ` -UserProfileServiceAppName $UserProfileServiceAppName ` -InstallAccount $InstallAccount if ($databaseReadOnly) { Write-Verbose -Message ("User profile database is read only, setting user profile " + ` "sync service to not run on the local server") $PSBoundParameters.Ensure = "Absent" } else { $PSBoundParameters.Ensure = "Present" } } # Add the FarmAccount to the local Admins group, if it's not already there $isLocalAdmin = Test-SPDSCUserIsLocalAdmin -UserName $FarmAccount.UserName if (!$isLocalAdmin) { Add-SPDSCUserToLocalAdmin -UserName $FarmAccount.UserName # Cycle the Timer Service so that it picks up the local Admin token Restart-Service -Name "SPTimerV4" } try { Invoke-SPDSCCommand -Credential $FarmAccount -Arguments $PSBoundParameters -ScriptBlock { $params = $args[0] $currentServer = $env:COMPUTERNAME $syncServices = Get-SPServiceInstance -Server $currentServer ` -ErrorAction SilentlyContinue $syncService = $syncServices | Where-Object -FilterScript { $_.GetType().Name -eq "UserProfileServiceInstance" } if ($null -eq $syncService) { $domain = (Get-CimInstance -ClassName Win32_ComputerSystem).Domain $currentServer = "$currentServer.$domain" $syncService = $syncServices | Where-Object -FilterScript { $_.GetType().Name -eq "UserProfileServiceInstance" } } if ($null -eq $syncService) { throw "Unable to locate a user profile service instance on $currentServer to start" } # Start the Sync service if it should be running on this server if (($params.Ensure -eq "Present") -and ($syncService.Status -ne "Online")) { $serviceApps = Get-SPServiceApplication -Name $params.UserProfileServiceAppName ` -ErrorAction SilentlyContinue if ($null -eq $serviceApps) { throw [Exception] ("No user profile service was found " + ` "named $($params.UserProfileServiceAppName)") } $ups = $serviceApps | Where-Object -FilterScript { $_.GetType().FullName -eq "Microsoft.Office.Server.Administration.UserProfileApplication" } $userName = $params.FarmAccount.UserName $password = $params.FarmAccount.GetNetworkCredential().Password $ups.SetSynchronizationMachine($currentServer, $syncService.ID, $userName, $password) Start-SPServiceInstance -Identity $syncService.ID $desiredState = "Online" } # Stop the Sync service in all other cases else { Stop-SPServiceInstance -Identity $syncService.ID -Confirm:$false $desiredState = "Disabled" } $count = 0 $maxCount = 10 while (($count -lt $maxCount) -and ($syncService.Status -ne $desiredState)) { if ($syncService.Status -ne $desiredState) { Start-Sleep -Seconds 60 } # Get the current status of the Sync service Write-Verbose ("$([DateTime]::Now.ToShortTimeString()) - Waiting for user profile " + ` "sync service to become '$desiredState' (waited $count of " + ` "$maxCount minutes)") $syncService = $syncServices | Where-Object -FilterScript { $_.GetType().Name -eq "UserProfileServiceInstance" } $count++ } } } finally { # Remove the FarmAccount from the local Admins group, if it was added above if (!$isLocalAdmin) { Remove-SPDSCUserToLocalAdmin -UserName $FarmAccount.UserName } } } function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [System.String] $UserProfileServiceAppName, [parameter(Mandatory = $false)] [ValidateSet("Present","Absent")] [System.String] $Ensure = "Present", [parameter(Mandatory = $true)] [System.Management.Automation.PSCredential] $FarmAccount, [parameter(Mandatory = $false)] [System.Boolean] $RunOnlyWhenWriteable, [parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] $InstallAccount ) Write-Verbose -Message "Testing user profile sync service for $UserProfileServiceAppName" $PSBoundParameters.Ensure = $Ensure if ((Get-SPDSCInstalledProductVersion).FileMajorPart -ne 15) { throw [Exception] ("Only SharePoint 2013 is supported to deploy the user profile sync " + ` "service via DSC, as 2016 does not use the FIM based sync service.") } $CurrentValues = Get-TargetResource @PSBoundParameters if ($PSBoundParameters.ContainsKey("RunOnlyWhenWriteable") -eq $true) { $databaseReadOnly = Test-SPDscUserProfileDBReadOnly ` -UserProfileServiceAppName $UserProfileServiceAppName ` -InstallAccount $InstallAccount if ($databaseReadOnly) { Write-Verbose -Message ("User profile database is read only, setting user profile " + ` "sync service to not run on the local server") $PSBoundParameters.Ensure = "Absent" } else { $PSBoundParameters.Ensure = "Present" } } Write-Verbose -Message "Testing for User Profile Synchronization Service" return Test-SPDscParameterState -CurrentValues $CurrentValues ` -DesiredValues $PSBoundParameters ` -ValuesToCheck @("Ensure") } function Test-SPDscUserProfileDBReadOnly() { param( [Parameter(Mandatory = $true)] [String] $UserProfileServiceAppName, [parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] $InstallAccount ) $databaseReadOnly = Invoke-SPDSCCommand -Credential $InstallAccount ` -Arguments $UserProfileServiceAppName ` -ScriptBlock { $UserProfileServiceAppName = $args[0] $serviceApps = Get-SPServiceApplication -Name $UserProfileServiceAppName ` -ErrorAction SilentlyContinue if ($null -eq $serviceApps) { throw [Exception] ("No user profile service was found " + ` "named $UserProfileServiceAppName") } $ups = $serviceApps | Where-Object -FilterScript { $_.GetType().FullName -eq "Microsoft.Office.Server.Administration.UserProfileApplication" } $propType = $ups.GetType() $propData = $propType.GetProperties([System.Reflection.BindingFlags]::Instance -bor ` [System.Reflection.BindingFlags]::NonPublic) $profileProp = $propData | Where-Object -FilterScript { $_.Name -eq "ProfileDatabase" } $profileDBName = $profileProp.GetValue($ups).Name $database = Get-SPDatabase | Where-Object -FilterScript { $_.Name -eq $profileDBName } return $database.IsReadyOnly } return $databaseReadOnly } Export-ModuleMember -Function *-TargetResource |