DSCResources/MSFT_SPFarmSolution/MSFT_SPFarmSolution.psm1
$script:SPDscUtilModulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\SharePointDsc.Util' Import-Module -Name $script:SPDscUtilModulePath function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter(Mandatory = $true)] [System.String] $LiteralPath, [Parameter()] [System.String[]] $WebAppUrls = @(), [Parameter()] [ValidateSet("Present", "Absent")] [System.String] $Ensure = "Present", [Parameter()] [System.String] $Version = "1.0.0.0", [Parameter()] [System.Boolean] $Deployed = $true, [Parameter()] [ValidateSet("14", "15", "All")] [System.String] $SolutionLevel, [Parameter()] [System.Management.Automation.PSCredential] $InstallAccount ) Write-Verbose -Message "Getting farm solution '$Name' settings" $result = Invoke-SPDscCommand -Credential $InstallAccount ` -Arguments $PSBoundParameters ` -ScriptBlock { $params = $args[0] $solution = Get-SPSolution -Identity $params.Name ` -ErrorAction SilentlyContinue ` -Verbose:$false if ($null -ne $solution) { $currentState = "Present" $deployed = $solution.Deployed $version = $Solution.Properties["Version"] $deployedWebApplications = @($solution.DeployedWebApplications ` | Select-Object -ExpandProperty Url) } else { $currentState = "Absent" $deployed = $false $version = "0.0.0.0" $deployedWebApplications = @() } return @{ Name = $params.Name LiteralPath = $LiteralPath Deployed = $deployed Ensure = $currentState Version = $version WebAppUrls = $deployedWebApplications SolutionLevel = $params.SolutionLevel } } return $result } function Set-TargetResource { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter(Mandatory = $true)] [System.String] $LiteralPath, [Parameter()] [System.String[]] $WebAppUrls = @(), [Parameter()] [ValidateSet("Present", "Absent")] [System.String] $Ensure = "Present", [Parameter()] [System.String] $Version = "1.0.0.0", [Parameter()] [System.Boolean] $Deployed = $true, [Parameter()] [ValidateSet("14", "15", "All")] [System.String] $SolutionLevel, [Parameter()] [System.Management.Automation.PSCredential] $InstallAccount ) Write-Verbose -Message "Setting farm solution '$Name' settings" $CurrentValues = Get-TargetResource @PSBoundParameters $PSBoundParameters.Ensure = $Ensure $PSBoundParameters.Version = $Version $PSBoundParameters.Deployed = $Deployed if ($Ensure -eq "Present") { if ($CurrentValues.Ensure -eq "Absent") { Write-Verbose -Message "Upload solution to the farm." $result = Invoke-SPDscCommand -Credential $InstallAccount ` -Arguments $PSBoundParameters ` -ScriptBlock { $params = $args[0] $runParams = @{ } $runParams.Add("LiteralPath", $params.LiteralPath) $runParams.Add("Verbose", $false) $solution = Add-SPSolution @runParams $solution.Properties["Version"] = $params.Version $solution.Update() return $solution } $CurrentValues.Version = $result.Properties["Version"] } if ($CurrentValues.Version -ne $Version) { # If the solution is not deployed and the versions do not match we have to # remove the current solution and add the new one if (-not $CurrentValues.Deployed) { Write-Verbose -Message ("Remove current version " + ` "('$($CurrentValues.Version)') of solution...") $result = Invoke-SPDscCommand -Credential $InstallAccount ` -Arguments $PSBoundParameters ` -ScriptBlock { $params = $args[0] $runParams = @{ } $runParams.Add("Identity", $params.Name) $runParams.Add("Confirm", $false) $runParams.Add("Verbose", $false) Remove-SPSolution $runParams $runParams = @{ } $runParams.Add("LiteralPath", $params.LiteralPath) $solution = Add-SPSolution @runParams $solution.Properties["Version"] = $params.Version $solution.Update() return $solution } $CurrentValues.Version = $result.Properties["Version"] } else { Write-Verbose -Message ("Update solution from " + ` "'$($CurrentValues.Version)' to $Version...") $result = Invoke-SPDscCommand -Credential $InstallAccount ` -Arguments $PSBoundParameters ` -ScriptBlock { $params = $args[0] $solution = Get-SPSolution -Identity $params.Name -Verbose:$false $runParams = @{ } $runParams.Add("Identity", $params.Name) $runParams.Add("LiteralPath", $params.LiteralPath) $runParams.Add("GACDeployment", $solution.ContainsGlobalAssembly) $runParams.Add("Confirm", $false) $runParams.Add("Local", $false) $runParams.Add("Verbose", $false) Update-SPSolution @runParams $solution = Get-SPSolution -Identity $params.Name -Verbose:$false $solution.Properties["Version"] = $params.Version $solution.Update() # Install new features... Install-SPFeature -AllExistingFeatures -Confirm:$false } } } } else { # If ensure is absent we should also retract the solution first $Deployed = $false } if ($Deployed -ne $CurrentValues.Deployed) { Write-Verbose -Message ("The deploy state of $Name is " + ` "'$($CurrentValues.Deployed)' but should be '$Deployed'.") if ($CurrentValues.Deployed) { # Retract Solution globally $result = Invoke-SPDscCommand -Credential $InstallAccount ` -Arguments $PSBoundParameters ` -ScriptBlock { $params = $args[0] $runParams = @{ } $runParams.Add("Identity", $params.Name) $runParams.Add("Confirm", $false) $runParams.Add("Verbose", $false) $solution = Get-SPSolution -Identity $params.Name -Verbose:$false if ($solution.ContainsWebApplicationResource) { if ($null -eq $params.WebAppUrls -or $params.WebAppUrls.Length -eq 0) { $runParams.Add("AllWebApplications", $true) Uninstall-SPSolution @runParams } else { foreach ($webApp in $params.WebAppUrls) { $runParams["WebApplication"] = $webApp Uninstall-SPSolution @runParams } } } else { Uninstall-SPSolution @runParams } } } else { # Deploy solution $result = Invoke-SPDscCommand -Credential $InstallAccount ` -Arguments $PSBoundParameters ` -ScriptBlock { $params = $args[0] $solution = Get-SPSolution -Identity $params.Name -Verbose:$false $runParams = @{ Identity = $solution GACDeployment = $solution.ContainsGlobalAssembly Local = $false Verbose = $false } if ($params.ContainsKey("SolutionLevel") -eq $true) { $runParams.Add("CompatibilityLevel", $params.SolutionLevel) } if (!$solution.ContainsWebApplicationResource) { Install-SPSolution @runParams } else { if ($null -eq $params.WebAppUrls -or $params.WebAppUrls.Length -eq 0) { $runParams.Add("AllWebApplications", $true) Install-SPSolution @runParams } else { foreach ($webApp in $params.WebAppUrls) { $runParams["WebApplication"] = $webApp try { Write-Verbose "Installing solution in Web Application $webApp" Install-SPSolution @runParams -ErrorAction Stop } catch { # There may be an ongoing deployment to another web application location. # Try the exponential backoff approach. $backOff = 2 while ($backOff -le 256) { try { Write-Verbose "There is an active deployment ongoing. Waiting $backOff seconds." Start-Sleep -Seconds $backOff Install-SPSolution @runParams -ErrorAction Stop break } catch { $backOff = $backOff * 2 } } } } } } } } } if ($Ensure -eq "Present") { Write-Verbose -Message "Waiting for farm solution '$Name' job" Wait-SPDscSolutionJob -SolutionName $Name -InstallAccount $InstallAccount } if ($Ensure -eq "Absent" -and $CurrentValues.Ensure -ne "Absent") { Write-Verbose -Message "Removing farm solution '$Name'" $result = Invoke-SPDscCommand -Credential $InstallAccount ` -Arguments $PSBoundParameters ` -ScriptBlock { $params = $args[0] $runParams = @{ Identity = $params.Name Confirm = $false Verbose = $false } Remove-SPSolution @runParams } } } function Test-TargetResource { [CmdletBinding()] [OutputType([Boolean])] param ( [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter(Mandatory = $true)] [System.String] $LiteralPath, [Parameter()] [System.String[]] $WebAppUrls = @(), [Parameter()] [ValidateSet("Present", "Absent")] [System.String] $Ensure = "Present", [Parameter()] [System.String] $Version = "1.0.0.0", [Parameter()] [System.Boolean] $Deployed = $true, [Parameter()] [ValidateSet("14", "15", "All")] [System.String] $SolutionLevel, [Parameter()] [System.Management.Automation.PSCredential] $InstallAccount ) Write-Verbose -Message "Testing farm solution '$Name' settings" $PSBoundParameters.Ensure = $Ensure $CurrentValues = Get-TargetResource @PSBoundParameters if ($CurrentValues.ContainsKey("WebAppUrls") -and $CurrentValues.WebAppUrls.Count -ne 0) { $CurrentValues.WebAppUrls = $CurrentValues.WebAppUrls.TrimEnd("/") } if ($PSBoundParameters.ContainsKey("WebAppUrls") -and $PSBoundParameters.WebAppUrls.Count -ne 0) { $PSBoundParameters.WebAppUrls = $PSBoundParameters.WebAppUrls.TrimEnd("/") } Write-Verbose -Message "Current Values: $(Convert-SPDscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-SPDscHashtableToString -Hashtable $PSBoundParameters)" $valuesToCheck = @("Ensure", "Version", "Deployed") if ($WebAppUrls.Count -gt 0) { $valuesToCheck += "WebAppUrls" } $result = Test-SPDscParameterState -CurrentValues $CurrentValues ` -Source $($MyInvocation.MyCommand.Source) ` -DesiredValues $PSBoundParameters ` -ValuesToCheck $valuesToCheck Write-Verbose -Message "Test-TargetResource returned $result" return $result } function Wait-SPDscSolutionJob { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $SolutionName, [Parameter()] [System.Management.Automation.PSCredential] $InstallAccount ) Start-Sleep -Seconds 5 $args = @{ Name = $SolutionName } $null = Invoke-SPDscCommand -Credential $InstallAccount ` -Arguments $args ` -ScriptBlock { $params = $args[0] $gc = Start-SPAssignment -Verbose:$false $solution = Get-SPSolution -Identity $params.Name -Verbose:$false -AssignmentCollection $gc if ($solution.JobExists -eq $true) { Write-Verbose -Message "Waiting for solution '$($params.Name)'..." $loopCount = 0 while ($solution.JobExists -and $loopCount -lt 600) { $solution = Get-SPSolution -Identity $params.Name -Verbose:$false -AssignmentCollection $gc Write-Verbose -Message ("$([DateTime]::Now.ToShortTimeString()) - Waiting for a " + ` "job for solution '$($params.Name)' to complete") $loopCount++ Start-Sleep -Seconds 5 } Write-Verbose -Message "Result: $($solution.LastOperationResult)" Write-Verbose -Message "Details: $($solution.LastOperationDetails)" } else { Write-Verbose -Message "Solution '$($params.Name)' has no job pending." return @{ LastOperationResult = "DeploymentSucceeded" LastOperationDetails = "Solution '$($params.Name)' has no job pending." } } Stop-SPAssignment $gc -Verbose:$false return @{ LastOperationResult = $solution.LastOperationResult LastOperationDetails = $solution.LastOperationDetails } } } function Export-TargetResource { $VerbosePreference = "SilentlyContinue" $ParentModuleBase = Get-Module "SharePointDsc" -ListAvailable | Select-Object -ExpandProperty Modulebase $module = Join-Path -Path $ParentModuleBase -ChildPath "\DSCResources\MSFT_SPFarmSolution\MSFT_SPFarmSolution.psm1" -Resolve $Content = '' $params = Get-DSCFakeParameters -ModulePath $module $solutions = Get-SPSolution $i = 1 $total = $solutions.Length foreach ($solution in $solutions) { try { Write-Host "Scanning Solution [$i/$total] {$($solution.Name)}" $PartialContent = " SPFarmSolution " + [System.Guid]::NewGuid().ToString() + "`r`n" $PartialContent += " {`r`n" $params.Name = $solution.Name $results = Get-TargetResource @params if ($results.ContainsKey("ContainsGlobalAssembly")) { $results.Remove("ContainsGlobalAssembly") } $filePath = "`$AllNodes.Where{`$Null -ne `$_.SPSolutionPath}.SPSolutionPath+###" + $solution.Name + "###" $results["LiteralPath"] = $filePath $results = Repair-Credentials -results $results $currentBlock = Get-DSCBlock -Params $results -ModulePath $module $currentblock = Convert-DSCStringParamToVariable -DSCBlock $currentBlock -ParameterName "LiteralPath" $currentBlock = Convert-DSCStringParamToVariable -DSCBlock $currentBlock -ParameterName "PsDscRunAsCredential" $currentBlock = $currentBlock.Replace("###", "`"") $PartialContent += $currentBlock $PartialContent += " }`r`n" $Content += $PartialContent } catch { $_ $Global:ErrorLog += "[Farm Solution]" + $solution.Name + "`r`n" $Global:ErrorLog += "$_`r`n`r`n" } $i++ } return $Content } Export-ModuleMember -Function *-TargetResource |