diff --git a/CHANGELOG.md b/CHANGELOG.md index 4df9162..fcfa803 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- OfficeOnlineServerDsc + - Updated to latest pipeline files. + ## [1.5.0] - 2020-04-03 ## Added @@ -38,4 +43,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed check for MachineToJoin. The resource only needs to check for farm join, especially with the new ProductUpdate resource. -For older change log history see the [historic changelog](HISTORIC_CHANGELOG.md). +## [1.4.0.0] - 2019-05-15 + +### Changed + +- OfficeOnlineServerInstall + - Updated resource to make sure the Windows Environment + variables are loaded into the PowerShell session; + - Updated error code checks to force reboot; +- OfficeOnlineServerMachine + - Updated resource to make sure the Windows Environment + variables are loaded into the PowerShell session; + +### Fixed + +- Created LICENSE file to match the Microsoft Open Source Team standard. + - Fixes [Issue #35](https://github.com/PowerShell/OfficeOnlineServerDsc/issues/35) + +## [1.3.0.0] - 2019-04-03 + +### Added + +- Changes to OfficeOnlineServerDsc + - Added pull request template and issue templates. +- OfficeOnlineServerInstall + - Added check to test if the setup file is blocked or not; + - Added ability to install from a UNC path, by adding server + to IE Local Intranet Zone. This will prevent an endless wait + caused by security warning; +- OfficeOnlineServerInstallLanguagePack + - Added check to test if the setup file is blocked or not; + - Added ability to install from a UNC path, by adding server + to IE Local Intranet Zone. This will prevent an endless wait + caused by security warning; + +## [1.2.0.0] - 2018-02-08 + +### Fixed + +- Added fix for Multiple Language Pack Installs + +## [1.1.0.0] - 2017-12-20 + +### Added + +- Added support for Language Packs installation; + +## [1.0.0.0] - 2017-03-08 + +### Added + +- Added documentation to the module to finalise for release; + +### Changed + +- Renamed resources to shorten names before release; + - 'OfficeOnlineServerWebAppsFarm' becomes 'OfficeOnlineServerFarm'; + - 'OfficeOnlineServerWebAppsMachine' becomes 'OfficeOnlineServerMachine'; + +## [0.2.0.0] - 2016-09-21 + +### Fixed + +- Fixed a bug that caused OfficeOnlineServerMachine to fail a test when; + the machine to join was specified using a fully qualified domain name (FQDN); + +### [0.1.0.0] - 2016-08-16 + +### Added + +- First release of OfficeOnlineServerDsc; diff --git a/GitVersion.yml b/GitVersion.yml index 2050659..8493c01 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -24,15 +24,3 @@ branches: ignore: sha: [] merge-message-formats: {} - - -# feature: -# tag: useBranchName -# increment: Minor -# regex: f(eature(s)?)?[/-] -# source-branches: ['master'] -# hotfix: -# tag: fix -# increment: Patch -# regex: (hot)?fix(es)?[/-] -# source-branches: ['master'] diff --git a/HISTORIC_CHANGELOG.md b/HISTORIC_CHANGELOG.md deleted file mode 100644 index 71f48cb..0000000 --- a/HISTORIC_CHANGELOG.md +++ /dev/null @@ -1,77 +0,0 @@ -# Historic change log for OfficeOnlineServerDsc - -The release notes in the PowerShell Module manifest cannot exceed 10000 -characters. Due to a bug in the CI deploy pipeline this is not handled. -This file is to temporary move the older change log history to keep the -change log short. - -## [1.4.0.0] - 2019-05-15 - -### Changed - -- OfficeOnlineServerInstall - - Updated resource to make sure the Windows Environment - variables are loaded into the PowerShell session; - - Updated error code checks to force reboot; -- OfficeOnlineServerMachine - - Updated resource to make sure the Windows Environment - variables are loaded into the PowerShell session; - -### Fixed - -- Created LICENSE file to match the Microsoft Open Source Team standard. - - Fixes [Issue #35](https://github.com/PowerShell/OfficeOnlineServerDsc/issues/35) - -## [1.3.0.0] - 2019-04-03 - -### Added - -- Changes to OfficeOnlineServerDsc - - Added pull request template and issue templates. -- OfficeOnlineServerInstall - - Added check to test if the setup file is blocked or not; - - Added ability to install from a UNC path, by adding server - to IE Local Intranet Zone. This will prevent an endless wait - caused by security warning; -- OfficeOnlineServerInstallLanguagePack - - Added check to test if the setup file is blocked or not; - - Added ability to install from a UNC path, by adding server - to IE Local Intranet Zone. This will prevent an endless wait - caused by security warning; - -## [1.2.0.0] - 2018-02-08 - -### Fixed - -- Added fix for Multiple Language Pack Installs - -## [1.1.0.0] - 2017-12-20 - -### Added - -- Added support for Language Packs installation; - -## [1.0.0.0] - 2017-03-08 - -### Added - -- Added documentation to the module to finalise for release; - -### Changed - -- Renamed resources to shorten names before release; - - 'OfficeOnlineServerWebAppsFarm' becomes 'OfficeOnlineServerFarm'; - - 'OfficeOnlineServerWebAppsMachine' becomes 'OfficeOnlineServerMachine'; - -## [0.2.0.0] - 2016-09-21 - -### Fixed - -- Fixed a bug that caused OfficeOnlineServerMachine to fail a test when; - the machine to join was specified using a fully qualified domain name (FQDN); - -### [0.1.0.0] - 2016-08-16 - -### Added - -- First release of OfficeOnlineServerDsc; diff --git a/LICENSE b/LICENSE index ef9f813..0683f75 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ - MIT License +MIT License - Copyright (c) DSC Community contributors. +Copyright (c) DSC Community contributors. - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 index 8831e7b..2df3b6c 100644 --- a/RequiredModules.psd1 +++ b/RequiredModules.psd1 @@ -1,10 +1,9 @@ @{ - # Set up a mini virtual environment... PSDependOptions = @{ AddToPath = $true Target = 'output\RequiredModules' Parameters = @{ - Repository = '' + Repository = 'PSGallery' } } @@ -15,18 +14,11 @@ ModuleBuilder = 'latest' ChangelogManagement = 'latest' Sampler = 'latest' + 'Sampler.GitHubTasks' = 'latest' MarkdownLinkCheck = 'latest' 'DscResource.Test' = 'latest' 'DscResource.AnalyzerRules' = 'latest' xDscResourceDesigner = 'latest' 'DscResource.DocGenerator' = 'latest' PSDscResources = 'latest' - - - # PSPKI = 'latest' - # 'DscResource.Common' = @{ - # Target = 'Source/Modules' - # Version = 'latest' - # } } - diff --git a/Resolve-Dependency.ps1 b/Resolve-Dependency.ps1 index 4928e62..260b21b 100644 --- a/Resolve-Dependency.ps1 +++ b/Resolve-Dependency.ps1 @@ -1,289 +1,919 @@ +<# + .DESCRIPTION + Bootstrap script for PSDepend. + + .PARAMETER DependencyFile + Specifies the configuration file for the this script. The default value is + 'RequiredModules.psd1' relative to this script's path. + + .PARAMETER PSDependTarget + Path for PSDepend to be bootstrapped and save other dependencies. + Can also be CurrentUser or AllUsers if you wish to install the modules in + such scope. The default value is 'output/RequiredModules' relative to + this script's path. + + .PARAMETER Proxy + Specifies the URI to use for Proxy when attempting to bootstrap + PackageProvider and PowerShellGet. + + .PARAMETER ProxyCredential + Specifies the credential to contact the Proxy when provided. + + .PARAMETER Scope + Specifies the scope to bootstrap the PackageProvider and PSGet if not available. + THe default value is 'CurrentUser'. + + .PARAMETER Gallery + Specifies the gallery to use when bootstrapping PackageProvider, PSGet and + when calling PSDepend (can be overridden in Dependency files). The default + value is 'PSGallery'. + + .PARAMETER GalleryCredential + Specifies the credentials to use with the Gallery specified above. + + .PARAMETER AllowOldPowerShellGetModule + Allow you to use a locally installed version of PowerShellGet older than + 1.6.0 (not recommended). Default it will install the latest PowerShellGet + if an older version than 2.0 is detected. + + .PARAMETER MinimumPSDependVersion + Allow you to specify a minimum version fo PSDepend, if you're after specific + features. + + .PARAMETER AllowPrerelease + Not yet written. + + .PARAMETER WithYAML + Not yet written. + + .PARAMETER UseModuleFast + Specifies to use ModuleFast instead of PowerShellGet to resolve dependencies + faster. + + .PARAMETER PSResourceGet + Specifies to use ModuleFast instead of PowerShellGet to resolve dependencies + faster. + + .NOTES + Load defaults for parameters values from Resolve-Dependency.psd1 if not + provided as parameter. +#> [CmdletBinding()] param ( - [Parameter()] - [String] + [System.String] $DependencyFile = 'RequiredModules.psd1', [Parameter()] - [String] - # Path for PSDepend to be bootstrapped and save other dependencies. - # Can also be CurrentUser or AllUsers if you wish to install the modules in such scope - # Default to $PWD.Path/output/modules - $PSDependTarget = (Join-Path $PSScriptRoot './output/RequiredModules'), + [System.String] + $PSDependTarget = (Join-Path -Path $PSScriptRoot -ChildPath 'output/RequiredModules'), [Parameter()] - [uri] - # URI to use for Proxy when attempting to Bootstrap PackageProvider & PowerShellGet + [System.Uri] $Proxy, [Parameter()] - # Credential to contact the Proxy when provided - [PSCredential]$ProxyCredential, + [System.Management.Automation.PSCredential] + $ProxyCredential, [Parameter()] [ValidateSet('CurrentUser', 'AllUsers')] - [String] - # Scope to bootstrap the PackageProvider and PSGet if not available + [System.String] $Scope = 'CurrentUser', [Parameter()] - [String] - # Gallery to use when bootstrapping PackageProvider, PSGet and when calling PSDepend (can be overridden in Dependency files) + [System.String] $Gallery = 'PSGallery', [Parameter()] - [PSCredential] - # Credentials to use with the Gallery specified above + [System.Management.Automation.PSCredential] $GalleryCredential, - [Parameter()] - [switch] - # Allow you to use a locally installed version of PowerShellGet older than 1.6.0 (not recommended, default to $False) + [System.Management.Automation.SwitchParameter] $AllowOldPowerShellGetModule, [Parameter()] - [String] - # Allow you to specify a minimum version fo PSDepend, if you're after specific features. + [System.String] $MinimumPSDependVersion, [Parameter()] - [Switch] + [System.Management.Automation.SwitchParameter] $AllowPrerelease, [Parameter()] - [Switch] - $WithYAML + [System.Management.Automation.SwitchParameter] + $WithYAML, + + [Parameter()] + [System.Collections.Hashtable] + $RegisterGallery, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UseModuleFast, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UsePSResourceGet, + + [Parameter()] + [System.String] + $PSResourceGetVersion ) -# Load Defaults for parameters values from Resolve-Dependency.psd1 if not provided as parameter try { - Write-Verbose -Message "Importing Bootstrap default parameters from '$PSScriptRoot/Resolve-Dependency.psd1'." - $ResolveDependencyDefaults = Import-PowerShellDataFile -Path (Join-Path $PSScriptRoot '.\Resolve-Dependency.psd1' -Resolve -ErrorAction Stop) - $ParameterToDefault = $MyInvocation.MyCommand.ParameterSets.Where{ $_.Name -eq $PSCmdlet.ParameterSetName }.Parameters.Keys - if ($ParameterToDefault.Count -eq 0) + if ($PSVersionTable.PSVersion.Major -le 5) { - $ParameterToDefault = $MyInvocation.MyCommand.Parameters.Keys + if (-not (Get-Command -Name 'Import-PowerShellDataFile' -ErrorAction 'SilentlyContinue')) + { + Import-Module -Name Microsoft.PowerShell.Utility -RequiredVersion '3.1.0.0' + } } - # Set the parameters available in the Parameter Set, or it's not possible to choose yet, so all parameters are an option - foreach ($ParamName in $ParameterToDefault) + + Write-Verbose -Message 'Importing Bootstrap default parameters from ''$PSScriptRoot/Resolve-Dependency.psd1''.' + + $resolveDependencyConfigPath = Join-Path -Path $PSScriptRoot -ChildPath '.\Resolve-Dependency.psd1' -Resolve -ErrorAction 'Stop' + + $resolveDependencyDefaults = Import-PowerShellDataFile -Path $resolveDependencyConfigPath + + $parameterToDefault = $MyInvocation.MyCommand.ParameterSets.Where{ $_.Name -eq $PSCmdlet.ParameterSetName }.Parameters.Keys + + if ($parameterToDefault.Count -eq 0) { - if (-Not $PSBoundParameters.Keys.Contains($ParamName) -and $ResolveDependencyDefaults.ContainsKey($ParamName)) + $parameterToDefault = $MyInvocation.MyCommand.Parameters.Keys + } + + # Set the parameters available in the Parameter Set, or it's not possible to choose yet, so all parameters are an option. + foreach ($parameterName in $parameterToDefault) + { + if (-not $PSBoundParameters.Keys.Contains($parameterName) -and $resolveDependencyDefaults.ContainsKey($parameterName)) { - Write-Verbose -Message "Setting $ParamName with $($ResolveDependencyDefaults[$ParamName])" + Write-Verbose -Message "Setting parameter '$parameterName' to value '$($resolveDependencyDefaults[$parameterName])'." + try { - $variableValue = $ResolveDependencyDefaults[$ParamName] - if ($variableValue -is [string]) + $variableValue = $resolveDependencyDefaults[$parameterName] + + if ($variableValue -is [System.String]) { $variableValue = $ExecutionContext.InvokeCommand.ExpandString($variableValue) } - $PSBoundParameters.Add($ParamName, $variableValue) - Set-Variable -Name $ParamName -value $variableValue -Force -ErrorAction SilentlyContinue + + $PSBoundParameters.Add($parameterName, $variableValue) + + Set-Variable -Name $parameterName -Value $variableValue -Force -ErrorAction 'SilentlyContinue' } catch { - Write-Verbose -Message "Error adding default for $ParamName : $($_.Exception.Message)" + Write-Verbose -Message "Error adding default for $parameterName : $($_.Exception.Message)." } } } } catch { - Write-Warning -Message "Error attempting to import Bootstrap's default parameters from $(Join-Path $PSScriptRoot '.\Resolve-Dependency.psd1'): $($_.Exception.Message)." + Write-Warning -Message "Error attempting to import Bootstrap's default parameters from '$resolveDependencyConfigPath': $($_.Exception.Message)." } -Write-Progress -Activity "Bootstrap:" -PercentComplete 0 -CurrentOperation "NuGet Bootstrap" +# Handles when both ModuleFast and PSResourceGet is configured or/and passed as parameter. +if ($UseModuleFast -and $UsePSResourceGet) +{ + Write-Information -MessageData 'Both ModuleFast and PSResourceGet is configured or/and passed as parameter.' -InformationAction 'Continue' + + if ($PSVersionTable.PSVersion -ge '7.2') + { + $UsePSResourceGet = $false -if (!(Get-PackageProvider -Name NuGet -ForceBootstrap -ErrorAction SilentlyContinue)) + Write-Information -MessageData 'PowerShell 7.2 or higher being used, prefer ModuleFast over PSResourceGet.' -InformationAction 'Continue' + } + else + { + $UseModuleFast = $false + + Write-Information -MessageData 'Older PowerShell or Windows PowerShell being used, prefer PSResourceGet since ModuleFast is not supported on this version of PowerShell.' -InformationAction 'Continue' + } +} + +if ($UseModuleFast) { - $providerBootstrapParams = @{ - Name = 'nuget' - force = $true - ForceBootstrap = $true - ErrorAction = 'Stop' + try + { + $invokeWebRequestParameters = @{ + Uri = 'bit.ly/modulefast' # cSpell: disable-line + ErrorAction = 'Stop' + } + + $moduleFastBootstrapScript = Invoke-WebRequest @invokeWebRequestParameters + + <# + Using this method instead of the one mentioned in the instructions from + https://github.com/JustinGrote/ModuleFast to avoid the PSScriptAnalyzer + rule PSAvoidUsingInvokeExpression. + #> + $moduleFastBootstrapScriptBlock = [ScriptBlock]::Create($moduleFastBootstrapScript) + + <# + We could pass parameters to the bootstrap script when calling Invoke(). + But currently the default parameter values works just fine. + #> + $moduleFastBootstrapScriptBlock.Invoke() } + catch + { + Write-Warning -Message ('ModuleFast could not be bootstrapped. Reverting to PowerShellGet. Error: {0}' -f $_.Exception.Message) - switch ($PSBoundParameters.Keys) + $UseModuleFast = $false + } +} + +if ($UsePSResourceGet) +{ + $psResourceGetModuleName = 'Microsoft.PowerShell.PSResourceGet' + + # If PSResourceGet was used prior it will be locked and we can't replace it. + if ((Test-Path -Path "$PSDependTarget/$psResourceGetModuleName" -PathType 'Container') -and (Get-Module -Name $psResourceGetModuleName)) + { + Write-Information -MessageData ('{0} is already saved and loaded into the session. To refresh the module open a new session and resolve dependencies again.' -f $psResourceGetModuleName) -InformationAction 'Continue' + } + else { - 'Proxy' + Write-Debug -Message ('{0} do not exist, saving the module to RequiredModules.' -f $psResourceGetModuleName) + + $psResourceGetDownloaded = $false + + try + { + if (-not $PSResourceGetVersion) + { + # Default version to use if non is specified in parameter or in configuration. + $PSResourceGetVersion = '0.9.0-rc1' + } + + $invokeWebRequestParameters = @{ + # TODO: This should be hardcoded to a stable release in the future. + # TODO: Should support proxy parameters passed to the script. + Uri = "https://www.powershellgallery.com/api/v2/package/$psResourceGetModuleName/$PSResourceGetVersion" + OutFile = "$PSDependTarget/$psResourceGetModuleName.nupkg" # cSpell: ignore nupkg + ErrorAction = 'Stop' + } + + $previousProgressPreference = $ProgressPreference + $ProgressPreference = 'SilentlyContinue' + + # Bootstrapping Microsoft.PowerShell.PSResourceGet. + Invoke-WebRequest @invokeWebRequestParameters + + $ProgressPreference = $previousProgressPreference + + $psResourceGetDownloaded = $true + } + catch { - $providerBootstrapParams.Add('Proxy', $Proxy) + Write-Warning -Message ('{0} could not be bootstrapped. Reverting to PowerShellGet. Error: {1}' -f $psResourceGetModuleName, $_.Exception.Message) + } + + $UsePSResourceGet = $false + + if ($psResourceGetDownloaded) + { + # On Windows PowerShell the command Expand-Archive do not like .nupkg as a zip archive extension. + $zipFileName = ((Split-Path -Path $invokeWebRequestParameters.OutFile -Leaf) -replace 'nupkg', 'zip') + + $renameItemParameters = @{ + Path = $invokeWebRequestParameters.OutFile + NewName = $zipFileName + Force = $true + } + + Rename-Item @renameItemParameters + + $psResourceGetZipArchivePath = Join-Path -Path (Split-Path -Path $invokeWebRequestParameters.OutFile -Parent) -ChildPath $zipFileName + + $expandArchiveParameters = @{ + Path = $psResourceGetZipArchivePath + DestinationPath = "$PSDependTarget/$psResourceGetModuleName" + Force = $true + } + + Expand-Archive @expandArchiveParameters + + Remove-Item -Path $psResourceGetZipArchivePath + + Import-Module -Name $expandArchiveParameters.DestinationPath -Force + + # Successfully bootstrapped PSResourceGet, so let's use it. + $UsePSResourceGet = $true } - 'ProxyCredential' + } + + if ($UsePSResourceGet) + { + $psResourceGetModule = Get-Module -Name $psResourceGetModuleName + + $psResourceGetModuleVersion = $psResourceGetModule.Version.ToString() + + if ($psResourceGetModule.PrivateData.PSData.Prerelease) { - $providerBootstrapParams.Add('ProxyCredential', $ProxyCredential) + $psResourceGetModuleVersion += '-{0}' -f $psResourceGetModule.PrivateData.PSData.Prerelease } - 'Scope' + + Write-Information -MessageData ('Using {0} v{1}.' -f $psResourceGetModuleName, $psResourceGetModuleVersion) -InformationAction 'Continue' + + if ($UsePowerShellGetCompatibilityModule) { - $providerBootstrapParams.Add('Scope', $Scope) + $savePowerShellGetParameters = @{ + Name = 'PowerShellGet' + Path = $PSDependTarget + Repository = 'PSGallery' + TrustRepository = $true + } + + if ($UsePowerShellGetCompatibilityModuleVersion) + { + $savePowerShellGetParameters.Version = $UsePowerShellGetCompatibilityModuleVersion + + # Check if the version is a prerelease. + if ($UsePowerShellGetCompatibilityModuleVersion -match '\d+\.\d+\.\d+-.*') + { + $savePowerShellGetParameters.Prerelease = $true + } + } + + Save-PSResource @savePowerShellGetParameters + + Import-Module -Name "$PSDependTarget/PowerShellGet" } } +} - if ($AllowPrerelease) +if (-not ($UseModuleFast -or $UsePSResourceGet)) +{ + if ($PSVersionTable.PSVersion.Major -le 5) { - $providerBootstrapParams.Add('AllowPrerelease', $true) + <# + Making sure the imported PackageManagement module is not from PS7 module + path. The VSCode PS extension is changing the $env:PSModulePath and + prioritize the PS7 path. This is an issue with PowerShellGet because + it loads an old version if available (or fail to load latest). + #> + Get-Module -ListAvailable PackageManagement | + Where-Object -Property 'ModuleBase' -NotMatch 'powershell.7' | + Select-Object -First 1 | + Import-Module -Force } - Write-Information "Bootstrap: Installing NuGet Package Provider from the web (Make sure Microsoft addresses/ranges are allowed)" - $null = Install-PackageProvider @providerBootstrapParams - $latestNuGetVersion = (Get-PackageProvider -Name NuGet -ListAvailable | Select-Object -First 1).Version.ToString() - Write-Information "Bootstrap: Importing NuGet Package Provider version $latestNuGetVersion to current session." - $Null = Import-PackageProvider -Name NuGet -RequiredVersion $latestNuGetVersion -Force -} + Write-Progress -Activity 'Bootstrap:' -PercentComplete 0 -CurrentOperation 'NuGet Bootstrap' -Write-Progress -Activity "Bootstrap:" -PercentComplete 10 -CurrentOperation "Ensuring Gallery $Gallery is trusted" + $importModuleParameters = @{ + Name = 'PowerShellGet' + MinimumVersion = '2.0' + MaximumVersion = '2.8.999' + ErrorAction = 'SilentlyContinue' + PassThru = $true + } -# Fail if the given PSGallery is not Registered -$Policy = (Get-PSRepository $Gallery -ErrorAction Stop).InstallationPolicy -Set-PSRepository -Name $Gallery -InstallationPolicy Trusted -ErrorAction Ignore -try -{ - Write-Progress -Activity "Bootstrap:" -PercentComplete 25 -CurrentOperation "Checking PowerShellGet" - # Ensure the module is loaded and retrieve the version you have - $PowerShellGetVersion = (Import-Module PowerShellGet -PassThru -ErrorAction SilentlyContinue).Version + if ($AllowOldPowerShellGetModule) + { + $importModuleParameters.Remove('MinimumVersion') + } + + $powerShellGetModule = Import-Module @importModuleParameters - Write-Verbose "Bootstrap: The PowerShellGet version is $PowerShellGetVersion" - # Versions below 1.6.0 are considered old, unreliable & not recommended - if (!$PowerShellGetVersion -or ($PowerShellGetVersion -lt [System.version]'1.6.0' -and !$AllowOldPowerShellGetModule)) + # Install the package provider if it is not available. + $nuGetProvider = Get-PackageProvider -Name 'NuGet' -ListAvailable -ErrorAction 'SilentlyContinue' | + Select-Object -First 1 + + if (-not $powerShellGetModule -and -not $nuGetProvider) { - Write-Progress -Activity "Bootstrap:" -PercentComplete 40 -CurrentOperation "Installing newer version of PowerShellGet" - $InstallPSGetParam = @{ - Name = 'PowerShellGet' - Force = $True - SkipPublisherCheck = $true - AllowClobber = $true - Scope = $Scope - Repository = $Gallery + $providerBootstrapParameters = @{ + Name = 'NuGet' + Force = $true + ForceBootstrap = $true + ErrorAction = 'Stop' + Scope = $Scope } switch ($PSBoundParameters.Keys) { 'Proxy' { - $InstallPSGetParam.Add('Proxy', $Proxy) + $providerBootstrapParameters.Add('Proxy', $Proxy) } + 'ProxyCredential' { - $InstallPSGetParam.Add('ProxyCredential', $ProxyCredential) + $providerBootstrapParameters.Add('ProxyCredential', $ProxyCredential) } - 'GalleryCredential' + + 'AllowPrerelease' { - $InstallPSGetParam.Add('Credential', $GalleryCredential) + $providerBootstrapParameters.Add('AllowPrerelease', $AllowPrerelease) } } - Install-Module @InstallPSGetParam - Remove-Module PowerShellGet -force -ErrorAction SilentlyContinue - Import-Module PowerShellGet -Force - $NewLoadedVersion = (Get-Module PowerShellGet).Version.ToString() - Write-Information "Bootstrap: PowerShellGet version loaded is $NewLoadedVersion" - Write-Progress -Activity "Bootstrap:" -PercentComplete 60 -CurrentOperation "Installing newer version of PowerShellGet" + Write-Information -MessageData 'Bootstrap: Installing NuGet Package Provider from the web (Make sure Microsoft addresses/ranges are allowed).' + + $null = Install-PackageProvider @providerBootstrapParameters + + $nuGetProvider = Get-PackageProvider -Name 'NuGet' -ListAvailable | Select-Object -First 1 + + $nuGetProviderVersion = $nuGetProvider.Version.ToString() + + Write-Information -MessageData "Bootstrap: Importing NuGet Package Provider version $nuGetProviderVersion to current session." + + $Null = Import-PackageProvider -Name 'NuGet' -RequiredVersion $nuGetProviderVersion -Force } - # Try to import the PSDepend module from the available modules - try + if ($RegisterGallery) + { + if ($RegisterGallery.ContainsKey('Name') -and -not [System.String]::IsNullOrEmpty($RegisterGallery.Name)) + { + $Gallery = $RegisterGallery.Name + } + else + { + $RegisterGallery.Name = $Gallery + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 7 -CurrentOperation "Verifying private package repository '$Gallery'" -Completed + + $previousRegisteredRepository = Get-PSRepository -Name $Gallery -ErrorAction 'SilentlyContinue' + + if ($previousRegisteredRepository.SourceLocation -ne $RegisterGallery.SourceLocation) + { + if ($previousRegisteredRepository) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 9 -CurrentOperation "Re-registrering private package repository '$Gallery'" -Completed + + Unregister-PSRepository -Name $Gallery + + $unregisteredPreviousRepository = $true + } + else + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 9 -CurrentOperation "Registering private package repository '$Gallery'" -Completed + } + + Register-PSRepository @RegisterGallery + } + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 10 -CurrentOperation "Ensuring Gallery $Gallery is trusted" + + # Fail if the given PSGallery is not registered. + $previousGalleryInstallationPolicy = (Get-PSRepository -Name $Gallery -ErrorAction 'Stop').Trusted + + if ($previousGalleryInstallationPolicy -ne $true) + { + # Only change policy if the repository is not trusted + Set-PSRepository -Name $Gallery -InstallationPolicy 'Trusted' -ErrorAction 'Ignore' + } +} + +try +{ + if (-not ($UseModuleFast -or $UsePSResourceGet)) { - $ImportPSDependParam = @{ + Write-Progress -Activity 'Bootstrap:' -PercentComplete 25 -CurrentOperation 'Checking PowerShellGet' + + # Ensure the module is loaded and retrieve the version you have. + $powerShellGetVersion = (Import-Module -Name 'PowerShellGet' -PassThru -ErrorAction 'SilentlyContinue').Version + + Write-Verbose -Message "Bootstrap: The PowerShellGet version is $powerShellGetVersion" + + # Versions below 2.0 are considered old, unreliable & not recommended + if (-not $powerShellGetVersion -or ($powerShellGetVersion -lt [System.Version] '2.0' -and -not $AllowOldPowerShellGetModule)) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 40 -CurrentOperation 'Fetching newer version of PowerShellGet' + + # PowerShellGet module not found, installing or saving it. + if ($PSDependTarget -in 'CurrentUser', 'AllUsers') + { + Write-Debug -Message "PowerShellGet module not found. Attempting to install from Gallery $Gallery." + + Write-Warning -Message "Installing PowerShellGet in $PSDependTarget Scope." + + $installPowerShellGetParameters = @{ + Name = 'PowerShellGet' + Force = $true + SkipPublisherCheck = $true + AllowClobber = $true + Scope = $Scope + Repository = $Gallery + MaximumVersion = '2.8.999' + } + + switch ($PSBoundParameters.Keys) + { + 'Proxy' + { + $installPowerShellGetParameters.Add('Proxy', $Proxy) + } + + 'ProxyCredential' + { + $installPowerShellGetParameters.Add('ProxyCredential', $ProxyCredential) + } + + 'GalleryCredential' + { + $installPowerShellGetParameters.Add('Credential', $GalleryCredential) + } + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 60 -CurrentOperation 'Installing newer version of PowerShellGet' + + Install-Module @installPowerShellGetParameters + } + else + { + Write-Debug -Message "PowerShellGet module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" + + $saveModuleParameters = @{ + Name = 'PowerShellGet' + Repository = $Gallery + Path = $PSDependTarget + Force = $true + MaximumVersion = '2.8.999' + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 60 -CurrentOperation "Saving PowerShellGet from $Gallery to $Scope" + + Save-Module @saveModuleParameters + } + + Write-Debug -Message 'Removing previous versions of PowerShellGet and PackageManagement from session' + + Get-Module -Name 'PowerShellGet' -All | Remove-Module -Force -ErrorAction 'SilentlyContinue' + Get-Module -Name 'PackageManagement' -All | Remove-Module -Force + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 65 -CurrentOperation 'Loading latest version of PowerShellGet' + + Write-Debug -Message 'Importing latest PowerShellGet and PackageManagement versions into session' + + if ($AllowOldPowerShellGetModule) + { + $powerShellGetModule = Import-Module -Name 'PowerShellGet' -Force -PassThru + } + else + { + Import-Module -Name 'PackageManagement' -MinimumVersion '1.4.8.1' -Force + + $powerShellGetModule = Import-Module -Name 'PowerShellGet' -MinimumVersion '2.2.5' -Force -PassThru + } + + $powerShellGetVersion = $powerShellGetModule.Version.ToString() + + Write-Information -MessageData "Bootstrap: PowerShellGet version loaded is $powerShellGetVersion" + } + + # Try to import the PSDepend module from the available modules. + $getModuleParameters = @{ + Name = 'PSDepend' + ListAvailable = $true + } + + $psDependModule = Get-Module @getModuleParameters + + if ($PSBoundParameters.ContainsKey('MinimumPSDependVersion')) + { + try + { + $psDependModule = $psDependModule | Where-Object -FilterScript { $_.Version -ge $MinimumPSDependVersion } + } + catch + { + throw ('There was a problem finding the minimum version of PSDepend. Error: {0}' -f $_) + } + } + + if (-not $psDependModule) + { + Write-Debug -Message 'PSDepend module not found.' + + # PSDepend module not found, installing or saving it. + if ($PSDependTarget -in 'CurrentUser', 'AllUsers') + { + Write-Debug -Message "Attempting to install from Gallery '$Gallery'." + + Write-Warning -Message "Installing PSDepend in $PSDependTarget Scope." + + $installPSDependParameters = @{ + Name = 'PSDepend' + Repository = $Gallery + Force = $true + Scope = $PSDependTarget + SkipPublisherCheck = $true + AllowClobber = $true + } + + if ($MinimumPSDependVersion) + { + $installPSDependParameters.Add('MinimumVersion', $MinimumPSDependVersion) + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 75 -CurrentOperation "Installing PSDepend from $Gallery" + + Install-Module @installPSDependParameters + } + else + { + Write-Debug -Message "Attempting to Save from Gallery $Gallery to $PSDependTarget" + + $saveModuleParameters = @{ + Name = 'PSDepend' + Repository = $Gallery + Path = $PSDependTarget + Force = $true + } + + if ($MinimumPSDependVersion) + { + $saveModuleParameters.add('MinimumVersion', $MinimumPSDependVersion) + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 75 -CurrentOperation "Saving PSDepend from $Gallery to $PSDependTarget" + + Save-Module @saveModuleParameters + } + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 80 -CurrentOperation 'Importing PSDepend' + + $importModulePSDependParameters = @{ Name = 'PSDepend' ErrorAction = 'Stop' Force = $true } - if ($MinimumPSDependVersion) + if ($PSBoundParameters.ContainsKey('MinimumPSDependVersion')) + { + $importModulePSDependParameters.Add('MinimumVersion', $MinimumPSDependVersion) + } + + # We should have successfully bootstrapped PSDepend. Fail if not available. + $null = Import-Module @importModulePSDependParameters + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 81 -CurrentOperation 'Invoke PSDepend' + + if ($WithYAML) { - $ImportPSDependParam.add('MinimumVersion', $MinimumPSDependVersion) + Write-Progress -Activity 'Bootstrap:' -PercentComplete 82 -CurrentOperation 'Verifying PowerShell module PowerShell-Yaml' + + if (-not (Get-Module -ListAvailable -Name 'PowerShell-Yaml')) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 85 -CurrentOperation 'Installing PowerShell module PowerShell-Yaml' + + Write-Verbose -Message "PowerShell-Yaml module not found. Attempting to Save from Gallery '$Gallery' to '$PSDependTarget'." + + $SaveModuleParam = @{ + Name = 'PowerShell-Yaml' + Repository = $Gallery + Path = $PSDependTarget + Force = $true + } + + Save-Module @SaveModuleParam + } + else + { + Write-Verbose -Message 'PowerShell-Yaml is already available' + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 88 -CurrentOperation 'Importing PowerShell module PowerShell-Yaml' } - $null = Import-Module @ImportPSDependParam } - catch + + if (Test-Path -Path $DependencyFile) { - # PSDepend module not found, installing or saving it - if ($PSDependTarget -in 'CurrentUser', 'AllUsers') + if ($UseModuleFast -or $UsePSResourceGet) { - Write-Debug "PSDepend module not found. Attempting to install from Gallery $Gallery" - Write-Warning "Installing PSDepend in $PSDependTarget Scope" - $InstallPSDependParam = @{ - Name = 'PSDepend' - Repository = $Gallery - Force = $true - Scope = $PSDependTarget - SkipPublisherCheck = $true - AllowClobber = $true + $requiredModules = Import-PowerShellDataFile -Path $DependencyFile + + $requiredModules = $requiredModules.GetEnumerator() | + Where-Object -FilterScript { $_.Name -ne 'PSDependOptions' } + + $modulesToSave = @( + 'PSDepend' # Always include PSDepend for backward compatibility. + ) + + foreach ($requiredModule in $requiredModules) + { + # If the RequiredModules.psd1 entry is an Hashtable then special handling is needed. + if ($requiredModule.Value -is [System.Collections.Hashtable]) + { + $saveModuleHashtable = @{ + ModuleName = $requiredModule.Name + } + + if ($requiredModule.Value.Version -and $requiredModule.Value.Version -ne 'latest') + { + $saveModuleHashtable.RequiredVersion = $requiredModule.Value.Version + } + + # ModuleFast does no support preview releases yet. + if ($UsePSResourceGet) + { + if ($requiredModule.Value.Parameters.AllowPrerelease -eq $true) + { + $saveModuleHashtable.Prerelease = $true + } + } + + $modulesToSave += $saveModuleHashtable + } + else + { + if ($requiredModule.Value -eq 'latest') + { + $modulesToSave += $requiredModule.Name + } + else + { + $modulesToSave += @{ + ModuleName = $requiredModule.Name + RequiredVersion = $requiredModule.Value + } + } + } } - if ($MinimumPSDependVersion) + if ($WithYAML) { - $InstallPSDependParam.add('MinimumVersion', $MinimumPSDependVersion) + $modulesToSave += 'PowerShell-Yaml' } - Write-Progress -Activity "Bootstrap:" -PercentComplete 75 -CurrentOperation "Installing PSDepend from $Gallery" - Install-Module @InstallPSDependParam + if ($UseModuleFast) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 90 -CurrentOperation 'Invoking ModuleFast' + + Write-Progress -Activity 'ModuleFast:' -PercentComplete 0 -CurrentOperation 'Restoring Build Dependencies' + + $moduleFastPlan = $modulesToSave | Get-ModuleFastPlan + + if ($moduleFastPlan) + { + # Clear all modules in plan from the current session so they can be fetched again. + $moduleFastPlan.Name | Get-Module | Remove-Module -Force + + $installModuleFastParameters = @{ + ModulesToInstall = $moduleFastPlan + Destination = $PSDependTarget + NoPSModulePathUpdate = $true + NoProfileUpdate = $true + Update = $true + Confirm = $false + } + + Install-ModuleFast @installModuleFastParameters + } + else + { + Write-Verbose -Message 'All modules were already up to date' + } + + Write-Progress -Activity 'ModuleFast:' -PercentComplete 100 -CurrentOperation 'Dependencies restored' -Completed + } + + if ($UsePSResourceGet) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 90 -CurrentOperation 'Invoking PSResourceGet' + + $progressPercentage = 0 + + Write-Progress -Activity 'PSResourceGet:' -PercentComplete $progressPercentage -CurrentOperation 'Restoring Build Dependencies' + + $percentagePerModule = [System.Math]::Floor(100 / $modulesToSave.Length) + + foreach ($currentModule in $modulesToSave) + { + Write-Progress -Activity 'PSResourceGet:' -PercentComplete $progressPercentage -CurrentOperation 'Restoring Build Dependencies' -Status ('Saving module {0}' -f $savePSResourceParameters.Name) + + $savePSResourceParameters = @{ + Path = $PSDependTarget + TrustRepository = $true + Confirm = $false + } + + if ($currentModule -is [System.Collections.Hashtable]) + { + $savePSResourceParameters.Name = $currentModule.ModuleName + + if ($currentModule.RequiredVersion) + { + $savePSResourceParameters.Version = $currentModule.RequiredVersion + } + + if ($currentModule.Prerelease) + { + $savePSResourceParameters.Prerelease = $currentModule.Prerelease + } + } + else + { + $savePSResourceParameters.Name = $currentModule + } + + # Modules that Sampler depend on that cannot be refreshed without a new session. + $skipModule = @('powershell-yaml') + + if ($savePSResourceParameters.Name -in $skipModule -and (Get-Module -Name 'powershell-yaml')) + { + Write-Progress -Activity 'PSResourceGet:' -PercentComplete $progressPercentage -CurrentOperation 'Restoring Build Dependencies' -Status ('Skipping module {0}' -f $savePSResourceParameters.Name) + + Write-Information -MessageData ('Skipping the module {0} since it cannot be refresh while loaded into the session. To refresh the module open a new session and resolve dependencies again.' -f $savePSResourceParameters.Name) -InformationAction 'Continue' + } + else + { + # Clear all module from the current session so any new version fetched will be re-imported. + Get-Module -Name $savePSResourceParameters.Name | Remove-Module -Force + + Save-PSResource @savePSResourceParameters -ErrorVariable 'savePSResourceError' + + if ($savePSResourceError) + { + Write-Warning -Message 'Save-PSResource could not save (replace) one or more dependencies. This can be due to the module is loaded into the session (and referencing assemblies). Close the current session and open a new session and try again.' + } + } + + $progressPercentage += $percentagePerModule + } + + Write-Progress -Activity 'PSResourceGet:' -PercentComplete 100 -CurrentOperation 'Dependencies restored' -Completed + } } else { - Write-Debug "PSDepend module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" - $SaveModuleParam = @{ - Name = 'PSDepend' - Repository = $Gallery - Path = $PSDependTarget - } + Write-Progress -Activity 'Bootstrap:' -PercentComplete 90 -CurrentOperation 'Invoking PSDepend' - if ($MinimumPSDependVersion) - { - $SaveModuleParam.add('MinimumVersion', $MinimumPSDependVersion) + Write-Progress -Activity 'PSDepend:' -PercentComplete 0 -CurrentOperation 'Restoring Build Dependencies' + + $psDependParameters = @{ + Force = $true + Path = $DependencyFile } - Write-Progress -Activity "Bootstrap:" -PercentComplete 75 -CurrentOperation "Saving & Importing PSDepend from $Gallery to $Scope" - Save-Module @SaveModuleParam + # TODO: Handle when the Dependency file is in YAML, and -WithYAML is specified. + Invoke-PSDepend @psDependParameters + + Write-Progress -Activity 'PSDepend:' -PercentComplete 100 -CurrentOperation 'Dependencies restored' -Completed } } - finally + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 100 -CurrentOperation 'Bootstrap complete' -Completed +} +finally +{ + if ($RegisterGallery) { - Write-Progress -Activity "Bootstrap:" -PercentComplete 100 -CurrentOperation "Loading PSDepend" - # We should have successfully bootstrapped PSDepend. Fail if not available - Import-Module PSDepend -ErrorAction Stop + Write-Verbose -Message "Removing private package repository '$Gallery'." + Unregister-PSRepository -Name $Gallery } - if ($WithYAML) + if ($unregisteredPreviousRepository) { - if (-Not (Get-Module -ListAvailable -Name 'PowerShell-Yaml')) + Write-Verbose -Message "Reverting private package repository '$Gallery' to previous location URI:s." + + $registerPSRepositoryParameters = @{ + Name = $previousRegisteredRepository.Name + InstallationPolicy = $previousRegisteredRepository.InstallationPolicy + } + + if ($previousRegisteredRepository.SourceLocation) { - Write-Verbose "PowerShell-Yaml module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" - $SaveModuleParam = @{ - Name = 'PowerShell-Yaml' - Repository = $Gallery - Path = $PSDependTarget - } + $registerPSRepositoryParameters.SourceLocation = $previousRegisteredRepository.SourceLocation + } - Save-Module @SaveModuleParam - Import-Module "PowerShell-Yaml" -ErrorAction Stop + if ($previousRegisteredRepository.PublishLocation) + { + $registerPSRepositoryParameters.PublishLocation = $previousRegisteredRepository.PublishLocation } - else + + if ($previousRegisteredRepository.ScriptSourceLocation) { - Write-Verbose "PowerShell-Yaml is already available" + $registerPSRepositoryParameters.ScriptSourceLocation = $previousRegisteredRepository.ScriptSourceLocation } + + if ($previousRegisteredRepository.ScriptPublishLocation) + { + $registerPSRepositoryParameters.ScriptPublishLocation = $previousRegisteredRepository.ScriptPublishLocation + } + + Register-PSRepository @registerPSRepositoryParameters } - Write-Progress -Activity "PSDepend:" -PercentComplete 0 -CurrentOperation "Restoring Build Dependencies" - if (Test-Path $DependencyFile) + # Only try to revert installation policy if the repository exist + if ((Get-PSRepository -Name $Gallery -ErrorAction 'SilentlyContinue')) { - $PSDependParams = @{ - Force = $true - Path = $DependencyFile + if ($previousGalleryInstallationPolicy -ne $true) + { + # Reverting the Installation Policy for the given gallery if it was not already trusted + Set-PSRepository -Name $Gallery -InstallationPolicy 'Untrusted' } - - # TODO: Handle when the Dependency file is in YAML, and -WithYAML is specified - Invoke-PSDepend @PSDependParams } - Write-Progress -Activity "PSDepend:" -PercentComplete 100 -CurrentOperation "Dependencies restored" -Completed -} -finally -{ - # Reverting the Installation Policy for the given gallery - Set-PSRepository -Name $Gallery -InstallationPolicy $Policy - Write-Verbose "Project Bootstrapped, returning to Invoke-Build" + + Write-Verbose -Message 'Project Bootstrapped, returning to Invoke-Build.' } diff --git a/Resolve-Dependency.psd1 b/Resolve-Dependency.psd1 index 4697aa0..2cd8513 100644 --- a/Resolve-Dependency.psd1 +++ b/Resolve-Dependency.psd1 @@ -1,6 +1,10 @@ @{ - Gallery = 'PSGallery' + Gallery = 'PSGallery' AllowPrerelease = $false - WithYAML = $true # Will also bootstrap PowerShell-Yaml to read other config files -} + WithYAML = $true + UsePSResourceGet = $true + PSResourceGetVersion = '1.0.0' + UsePowerShellGetCompatibilityModule = $true + UsePowerShellGetCompatibilityModuleVersion = '3.0.22-beta22' +} diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9f18532..29cc8eb 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -3,29 +3,39 @@ trigger: include: - master paths: - exclude: - - CHANGELOG.md + include: + - src/* tags: include: - "v*" exclude: - "*-*" +variables: + buildFolderName: output + buildArtifactName: output + testResultFolderName: testResults + testArtifactName: testResults + sourceFolderName: src + defaultBranch: master + stages: - stage: Build jobs: - job: Package_Module displayName: 'Package Module' pool: - vmImage: 'ubuntu 16.04' + vmImage: 'windows-latest' steps: - - task: GitVersion@5 - name: gitVersion - displayName: 'Evaluate Next Version' - inputs: - runtime: 'core' - configFilePath: 'GitVersion.yml' - + - pwsh: | + dotnet tool install --global GitVersion.Tool + $gitVersionObject = dotnet-gitversion | ConvertFrom-Json + $gitVersionObject.PSObject.Properties.ForEach{ + Write-Host -Object "Setting Task Variable '$($_.Name)' with value '$($_.Value)'." + Write-Host -Object "##vso[task.setvariable variable=$($_.Name);]$($_.Value)" + } + Write-Host -Object "##vso[build.updatebuildnumber]$($gitVersionObject.FullSemVer)" + displayName: Calculate ModuleVersion (GitVersion) - task: PowerShell@2 name: package displayName: 'Build & Package Module' @@ -34,14 +44,14 @@ stages: arguments: '-ResolveDependency -tasks pack' pwsh: true env: - ModuleVersion: $(gitVersion.NuGetVersionV2) - - - task: PublishBuildArtifacts@1 + ModuleVersion: $(NuGetVersionV2) + - task: PublishPipelineArtifact@1 displayName: 'Publish Build Artifact' inputs: - pathToPublish: 'output/' - artifactName: 'output' - publishLocation: 'Container' + targetPath: '$(buildFolderName)/' + artifact: $(buildArtifactName) + publishLocation: 'pipeline' + parallel: true - stage: Test dependsOn: Build @@ -49,17 +59,15 @@ stages: - job: Test_HQRM displayName: 'HQRM' pool: - vmImage: 'windows-2019' + vmImage: 'windows-latest' timeoutInMinutes: 0 steps: - - task: DownloadBuildArtifacts@0 + - task: DownloadPipelineArtifact@2 displayName: 'Download Build Artifact' inputs: buildType: 'current' - downloadType: 'single' - artifactName: 'output' - downloadPath: '$(Build.SourcesDirectory)' - + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' - task: PowerShell@2 name: test displayName: 'Run HQRM Test' @@ -67,7 +75,6 @@ stages: filePath: './build.ps1' arguments: '-Tasks hqrmtest' pwsh: false - - task: PublishTestResults@2 displayName: 'Publish Test Results' condition: succeededOrFailed() @@ -79,25 +86,15 @@ stages: - job: Test_Unit_2013 displayName: 'Unit OWA 2013' pool: - vmImage: 'windows-2019' + vmImage: 'windows-latest' timeoutInMinutes: 0 steps: - - powershell: | - $repositoryOwner,$repositoryName = $env:BUILD_REPOSITORY_NAME -split '/' - - echo "##vso[task.setvariable variable=RepositoryOwner;isOutput=true]$repositoryOwner" - echo "##vso[task.setvariable variable=RepositoryName;isOutput=true]$repositoryName" - name: dscBuildVariable - displayName: 'Set Environment Variables' - - - task: DownloadBuildArtifacts@0 + - task: DownloadPipelineArtifact@2 displayName: 'Download Build Artifact' inputs: buildType: 'current' - downloadType: 'single' - artifactName: 'output' - downloadPath: '$(Build.SourcesDirectory)' - + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' - task: PowerShell@2 name: test displayName: 'Run Unit Test for OWA 2013' @@ -105,44 +102,33 @@ stages: filePath: './build.ps1' arguments: "-Tasks test -PesterScript @(@{ Path = '$(Build.SourcesDirectory)/Tests/Unit'; Parameters = @{WACCmdletModule = '$(Build.SourcesDirectory)/Tests/Unit/Stubs/15.0.4569.1506/OfficeWebApps.psm1' }})" pwsh: false - - task: PublishTestResults@2 displayName: 'Publish Test Results' condition: succeededOrFailed() inputs: testResultsFormat: 'NUnit' - testResultsFiles: 'output/testResults/NUnit*.xml' - testRunTitle: 'Unit (Windows Server Core)' - - - task: PublishBuildArtifacts@1 - displayName: 'Publish CodeCoverage Artifact' + testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml' + testRunTitle: 'Unit OWA 2013' + - task: PublishPipelineArtifact@1 + displayName: 'Publish Test Artifact' + condition: succeededOrFailed() inputs: - pathToPublish: '$(Build.SourcesDirectory)/output/testResults' + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)/' artifactName: 'CodeCoverage_2013' - publishLocation: 'Container' + parallel: true - job: Test_Unit_2016 displayName: 'Unit OOS 2016' pool: - vmImage: 'windows-2019' + vmImage: 'windows-latest' timeoutInMinutes: 0 steps: - - powershell: | - $repositoryOwner,$repositoryName = $env:BUILD_REPOSITORY_NAME -split '/' - - echo "##vso[task.setvariable variable=RepositoryOwner;isOutput=true]$repositoryOwner" - echo "##vso[task.setvariable variable=RepositoryName;isOutput=true]$repositoryName" - name: dscBuildVariable - displayName: 'Set Environment Variables' - - - task: DownloadBuildArtifacts@0 + - task: DownloadPipelineArtifact@2 displayName: 'Download Build Artifact' inputs: buildType: 'current' - downloadType: 'single' - artifactName: 'output' - downloadPath: '$(Build.SourcesDirectory)' - + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' - task: PowerShell@2 name: test displayName: 'Run Unit Test for OOS 2016' @@ -150,81 +136,105 @@ stages: filePath: './build.ps1' arguments: "-Tasks test -PesterScript @(@{ Path = '$(Build.SourcesDirectory)/Tests/Unit'; Parameters = @{WACCmdletModule = '$(Build.SourcesDirectory)/Tests/Unit/Stubs/16.0.6814.2226/OfficeWebApps.psm1' }})" pwsh: false - - task: PublishTestResults@2 displayName: 'Publish Test Results' condition: succeededOrFailed() inputs: testResultsFormat: 'NUnit' - testResultsFiles: 'output/testResults/NUnit*.xml' - testRunTitle: 'Unit (Windows Server Core)' - - - task: PublishBuildArtifacts@1 - displayName: 'Publish CodeCoverage Artifact' + testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml' + testRunTitle: 'Unit OOS 2016' + - task: PublishPipelineArtifact@1 + displayName: 'Publish Test Artifact' + condition: succeededOrFailed() inputs: - pathToPublish: '$(Build.SourcesDirectory)/output/testResults' + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)/' artifactName: 'CodeCoverage_2016' - publishLocation: 'Container' + parallel: true - - job: MergeCodeCoverage + - job: Code_Coverage + displayName: 'Publish Code Coverage' + condition: succeededOrFailed() dependsOn: - - 'Test_Unit_2013' - - 'Test_Unit_2016' - displayName: 'Merge Code Coverage' + - 'Test_Unit_2013' + - 'Test_Unit_2016' pool: - vmImage: 'windows-2019' + vmImage: 'ubuntu-latest' timeoutInMinutes: 0 steps: - - powershell: | - $repositoryOwner,$repositoryName = $env:BUILD_REPOSITORY_NAME -split '/' - - echo "##vso[task.setvariable variable=RepositoryOwner;isOutput=true]$repositoryOwner" - echo "##vso[task.setvariable variable=RepositoryName;isOutput=true]$repositoryName" - name: dscBuildVariable - displayName: 'Set Environment Variables' - - - task: DownloadBuildArtifacts@0 + - task: DownloadPipelineArtifact@2 displayName: 'Download Build Artifact' inputs: buildType: 'current' - downloadType: 'single' - artifactName: 'output' - downloadPath: '$(Build.SourcesDirectory)' - - - task: DownloadBuildArtifacts@0 - displayName: 'Download Build Artifact' + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' + - task: DownloadPipelineArtifact@2 + displayName: 'Download Test Artifact OWA 2013' inputs: buildType: 'current' - downloadType: 'single' artifactName: 'CodeCoverage_2013' - downloadPath: '$(Build.SourcesDirectory)/output' - - - task: DownloadBuildArtifacts@0 - displayName: 'Download Build Artifact' + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)/CodeCoverage_2013' + - task: DownloadPipelineArtifact@2 + displayName: 'Download Test Artifact OOS 2016' inputs: buildType: 'current' - downloadType: 'single' artifactName: 'CodeCoverage_2016' - downloadPath: '$(Build.SourcesDirectory)/output' - + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)/CodeCoverage_2016' - task: PowerShell@2 name: merge displayName: 'Merge Code Coverage files' inputs: filePath: './build.ps1' arguments: "-Tasks merge" - pwsh: false - + pwsh: true - task: PublishCodeCoverageResults@1 - displayName: 'Publish Code Coverage' + displayName: 'Publish Code Coverage to Azure DevOps' + condition: succeededOrFailed() inputs: codeCoverageTool: 'JaCoCo' - summaryFileLocation: 'output/CodeCov_Merged.xml' - pathToSources: '$(Build.SourcesDirectory)/output/$(dscBuildVariable.RepositoryName)' + summaryFileLocation: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)/JaCoCo_Merged.xml' + pathToSources: '$(Build.SourcesDirectory)/$(sourceFolderName)/' + - script: | + bash <(curl -s https://codecov.io/bash) -f "./$(buildFolderName)/$(testResultFolderName)/JaCoCo_Merged.xml" + displayName: 'Publish Code Coverage to Codecov.io' + condition: succeededOrFailed() + + ## DISABLE due to integration tests are not yet converted to the new pipeline module (the continuous delivery pipeline) + # - job: Test_Integration + # displayName: 'Integration' + # pool: + # vmImage: 'windows-latest' + # timeoutInMinutes: 0 + # steps: + # - task: DownloadPipelineArtifact@2 + # displayName: 'Download Build Artifact' + # inputs: + # buildType: 'current' + # artifactName: $(buildArtifactName) + # targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' + # - task: PowerShell@2 + # name: configureWinRM + # displayName: 'Configure WinRM' + # inputs: + # targetType: 'inline' + # script: 'winrm quickconfig -quiet' + # pwsh: false + # - task: PowerShell@2 + # name: test + # displayName: 'Run Integration Test' + # inputs: + # filePath: './build.ps1' + # arguments: "-Tasks test -CodeCoverageThreshold 0 -PesterScript 'tests/Integration'" + # pwsh: false + # - task: PublishTestResults@2 + # displayName: 'Publish Test Results' + # condition: succeededOrFailed() + # inputs: + # testResultsFormat: 'NUnit' + # testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml' + # testRunTitle: 'Integration' - stage: Deploy dependsOn: Test - # Only execute deploy stage if we're on master and previous stage succeeded condition: | and( succeeded(), @@ -238,15 +248,14 @@ stages: - job: Deploy_Module displayName: 'Deploy Module' pool: - vmImage: 'ubuntu 16.04' + vmImage: 'ubuntu-latest' steps: - - task: DownloadBuildArtifacts@0 - displayName: 'Download Build Artifact' + - task: DownloadPipelineArtifact@2 + displayName: 'Download Pipeline Artifact' inputs: buildType: 'current' - downloadType: 'single' - artifactName: 'output' - downloadPath: '$(Build.SourcesDirectory)' + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildArtifactName)' - task: PowerShell@2 name: publishRelease displayName: 'Publish Release' @@ -257,6 +266,8 @@ stages: env: GitHubToken: $(GitHubToken) GalleryApiToken: $(GalleryApiToken) + ReleaseBranch: $(defaultBranch) + MainGitBranch: $(defaultBranch) - task: PowerShell@2 name: sendChangelogPR displayName: 'Send Changelog PR' @@ -266,4 +277,5 @@ stages: pwsh: true env: GitHubToken: $(GitHubToken) - + ReleaseBranch: $(defaultBranch) + MainGitBranch: $(defaultBranch) diff --git a/build.ps1 b/build.ps1 index 4630cb8..f4a0fae 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,381 +1,538 @@ <# + .DESCRIPTION + Bootstrap and build script for PowerShell module CI/CD pipeline. -.DESCRIPTION - Bootstrap and build script for PowerShell module pipeline + .PARAMETER Tasks + The task or tasks to run. The default value is '.' (runs the default task). + .PARAMETER CodeCoverageThreshold + The code coverage target threshold to uphold. Set to 0 to disable. + The default value is '' (empty string). + + .PARAMETER BuildConfig + Not yet written. + + .PARAMETER OutputDirectory + Specifies the folder to build the artefact into. The default value is 'output'. + + .PARAMETER BuiltModuleSubdirectory + Subdirectory name to build the module (under $OutputDirectory). The default + value is '' (empty string). + + .PARAMETER RequiredModulesDirectory + Can be a path (relative to $PSScriptRoot or absolute) to tell Resolve-Dependency + and PSDepend where to save the required modules. It is also possible to use + 'CurrentUser' och 'AllUsers' to install missing dependencies. You can override + the value for PSDepend in the Build.psd1 build manifest. The default value is + 'output/RequiredModules'. + + .PARAMETER PesterScript + One or more paths that will override the Pester configuration in build + configuration file when running the build task Invoke_Pester_Tests. + + If running Pester 5 test, use the alias PesterPath to be future-proof. + + .PARAMETER PesterTag + Filter which tags to run when invoking Pester tests. This is used in the + Invoke-Pester.pester.build.ps1 tasks. + + .PARAMETER PesterExcludeTag + Filter which tags to exclude when invoking Pester tests. This is used in + the Invoke-Pester.pester.build.ps1 tasks. + + .PARAMETER DscTestTag + Filter which tags to run when invoking DSC Resource tests. This is used + in the DscResource.Test.build.ps1 tasks. + + .PARAMETER DscTestExcludeTag + Filter which tags to exclude when invoking DSC Resource tests. This is + used in the DscResource.Test.build.ps1 tasks. + + .PARAMETER ResolveDependency + Not yet written. + + .PARAMETER BuildInfo + The build info object from ModuleBuilder. Defaults to an empty hashtable. + + .PARAMETER AutoRestore + Not yet written. + + .PARAMETER UseModuleFast + Specifies to use ModuleFast instead of PowerShellGet to resolve dependencies + faster. + + .PARAMETER UsePSResourceGet + Specifies to use PSResourceGet instead of PowerShellGet to resolve dependencies + faster. This can also be configured in Resolve-Dependency.psd1. + + .PARAMETER UsePowerShellGetCompatibilityModule + Specifies to use the compatibility module PowerShellGet. This parameter + only works then the method of downloading dependencies is PSResourceGet. + This can also be configured in Resolve-Dependency.psd1. #> [CmdletBinding()] param ( [Parameter(Position = 0)] - [string[]]$Tasks = '.', + [System.String[]] + $Tasks = '.', [Parameter()] - [String] + [System.String] $CodeCoverageThreshold = '', [Parameter()] - [validateScript( + [System.String] + [ValidateScript( { Test-Path -Path $_ } )] $BuildConfig, [Parameter()] - # A Specific folder to build the artefact into. + [System.String] $OutputDirectory = 'output', [Parameter()] - # Subdirectory name to build the module (under $OutputDirectory) + [System.String] $BuiltModuleSubdirectory = '', - # Can be a path (relative to $PSScriptRoot or absolute) to tell Resolve-Dependency & PSDepend where to save the required modules, - # or use CurrentUser, AllUsers to target where to install missing dependencies - # You can override the value for PSDepend in the Build.psd1 build manifest - # This defaults to $OutputDirectory/modules (by default: ./output/modules) [Parameter()] + [System.String] $RequiredModulesDirectory = $(Join-Path 'output' 'RequiredModules'), [Parameter()] - [object[]] + # This alias is to prepare for the rename of this parameter to PesterPath when Pester 4 support is removed + [Alias('PesterPath')] + [System.Object[]] $PesterScript, - # Filter which tags to run when invoking Pester tests - # This is used in the Invoke-Pester.pester.build.ps1 tasks [Parameter()] - [string[]] + [System.String[]] $PesterTag, - # Filter which tags to exclude when invoking Pester tests - # This is used in the Invoke-Pester.pester.build.ps1 tasks [Parameter()] - [string[]] + [System.String[]] $PesterExcludeTag, - # Filter which tags to run when invoking DSC Resource tests - # This is used in the DscResource.Test.build.ps1 tasks [Parameter()] - [string[]] + [System.String[]] $DscTestTag, - # Filter which tags to exclude when invoking DSC Resource tests - # This is used in the DscResource.Test.build.ps1 tasks [Parameter()] - [string[]] + [System.String[]] $DscTestExcludeTag, [Parameter()] [Alias('bootstrap')] - [switch]$ResolveDependency, + [System.Management.Automation.SwitchParameter] + $ResolveDependency, [Parameter(DontShow)] [AllowNull()] + [System.Collections.Hashtable] $BuildInfo, [Parameter()] - [switch] - $AutoRestore + [System.Management.Automation.SwitchParameter] + $AutoRestore, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UseModuleFast, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UsePSResourceGet, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UsePowerShellGetCompatibilityModule ) -# The BEGIN block (at the end of this file) handles the Bootstrap of the Environment before Invoke-Build can run the tasks -# if the -ResolveDependency (aka Bootstrap) is specified, the modules are already available, and can be auto loaded +<# + The BEGIN block (at the end of this file) handles the Bootstrap of the Environment + before Invoke-Build can run the tasks if the parameter ResolveDependency (or + parameter alias Bootstrap) is specified. +#> process { - if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') { - # Only run the process block through InvokeBuild (Look at the Begin block at the bottom of this script) + # Only run the process block through InvokeBuild (look at the Begin block at the bottom of this script). return } - # Execute the Build Process from the .build.ps1 path. - Push-Location -Path $PSScriptRoot -StackName BeforeBuild + # Execute the Build process from the .build.ps1 path. + Push-Location -Path $PSScriptRoot -StackName 'BeforeBuild' try { - Write-Host -ForeGroundColor magenta "[build] Parsing defined tasks" + Write-Host -Object "[build] Parsing defined tasks" -ForeGroundColor Magenta - # Load Default BuildInfo if not provided as parameter - if (!$PSBoundParameters.ContainsKey('BuildInfo')) + # Load the default BuildInfo if the parameter BuildInfo is not set. + if (-not $PSBoundParameters.ContainsKey('BuildInfo')) { try { - if (Test-Path $BuildConfig) + if (Test-Path -Path $BuildConfig) { - $ConfigFile = (Get-Item -Path $BuildConfig) - Write-Host "[build] Loading Configuration from $ConfigFile" - $BuildInfo = switch -Regex ($ConfigFile.Extension) + $configFile = Get-Item -Path $BuildConfig + + Write-Host -Object "[build] Loading Configuration from $configFile" + + $BuildInfo = switch -Regex ($configFile.Extension) { # Native Support for PSD1 '\.psd1' { + if (-not (Get-Command -Name Import-PowerShellDataFile -ErrorAction SilentlyContinue)) + { + Import-Module -Name Microsoft.PowerShell.Utility -RequiredVersion 3.1.0.0 + } + Import-PowerShellDataFile -Path $BuildConfig } + # Support for yaml when module PowerShell-Yaml is available '\.[yaml|yml]' { - Import-Module -ErrorAction Stop -Name 'powershell-yaml' - ConvertFrom-Yaml -Yaml (Get-Content -Raw $ConfigFile) + Import-Module -Name 'powershell-yaml' -ErrorAction Stop + + ConvertFrom-Yaml -Yaml (Get-Content -Raw $configFile) } - # Native Support for JSON and JSONC (by Removing comments) + + # Support for JSON and JSONC (by Removing comments) when module PowerShell-Yaml is available '\.[json|jsonc]' { - $JSONC = (Get-Content -Raw -Path $ConfigFile) - $JSON = $JSONC -replace '(?m)\s*//.*?$' -replace '(?ms)/\*.*?\*/' - # This should probably be converted to hashtable for splatting - $JSON | ConvertFrom-Json + $jsonFile = Get-Content -Raw -Path $configFile + + $jsonContent = $jsonFile -replace '(?m)\s*//.*?$' -replace '(?ms)/\*.*?\*/' + + # Yaml is superset of JSON. + ConvertFrom-Yaml -Yaml $jsonContent } + + # Unknown extension, return empty hashtable. default { - Write-Error "Extension '$_' not supported. using @{}" + Write-Error -Message "Extension '$_' not supported. using @{}" + @{ } } } } else { - Write-Host -Object "Configuration file $BuildConfig not found" -ForegroundColor Red + Write-Host -Object "Configuration file '$($BuildConfig.FullName)' not found" -ForegroundColor Red + + # No config file was found, return empty hashtable. $BuildInfo = @{ } } } catch { - Write-Host -Object "Error loading Config $ConfigFile.`r`n Are you missing dependencies?" -ForegroundColor Yellow - Write-Host -Object "Make sure you run './build.ps1 -ResolveDependency -tasks noop' to restore the Required modules the first time" -ForegroundColor Yellow + $logMessage = "Error loading Config '$($BuildConfig.FullName)'.`r`nAre you missing dependencies?`r`nMake sure you run './build.ps1 -ResolveDependency -tasks noop' before running build to restore the required modules." + + Write-Host -Object $logMessage -ForegroundColor Yellow + $BuildInfo = @{ } - Write-Error $_.Exception.Message + + Write-Error -Message $_.Exception.Message } } - # If the Invoke-Build Task Header is specified in the Build Info, set it + # If the Invoke-Build Task Header is specified in the Build Info, set it. if ($BuildInfo.TaskHeader) { - Set-BuildHeader ([scriptblock]::Create($BuildInfo.TaskHeader)) + Set-BuildHeader -Script ([scriptblock]::Create($BuildInfo.TaskHeader)) + } + + <# + Add BuildModuleOutput to PSModule Path environment variable. + Moved here (not in begin block) because build file can contains BuiltSubModuleDirectory value. + #> + if ($BuiltModuleSubdirectory) + { + if (-not (Split-Path -IsAbsolute -Path $BuiltModuleSubdirectory)) + { + $BuildModuleOutput = Join-Path -Path $OutputDirectory -ChildPath $BuiltModuleSubdirectory + } + else + { + $BuildModuleOutput = $BuiltModuleSubdirectory + } + } # test if BuiltModuleSubDirectory set in build config file + elseif ($BuildInfo.ContainsKey('BuiltModuleSubDirectory')) + { + $BuildModuleOutput = Join-Path -Path $OutputDirectory -ChildPath $BuildInfo['BuiltModuleSubdirectory'] + } + else + { + $BuildModuleOutput = $OutputDirectory } - # Import Tasks from modules via their exported aliases when defined in BUild Manifest - # https://github.com/nightroman/Invoke-Build/tree/master/Tasks/Import#example-2-import-from-a-module-with-tasks - if ($BuildInfo.containsKey('ModuleBuildTasks')) + # Pre-pending $BuildModuleOutput folder to PSModulePath to resolve built module from this folder. + if ($powerShellModulePaths -notcontains $BuildModuleOutput) { - foreach ($Module in $BuildInfo['ModuleBuildTasks'].Keys) + Write-Host -Object "[build] Pre-pending '$BuildModuleOutput' folder to PSModulePath" -ForegroundColor Green + + $env:PSModulePath = $BuildModuleOutput + [System.IO.Path]::PathSeparator + $env:PSModulePath + } + + <# + Import Tasks from modules via their exported aliases when defined in Build Manifest. + https://github.com/nightroman/Invoke-Build/tree/master/Tasks/Import#example-2-import-from-a-module-with-tasks + #> + if ($BuildInfo.ContainsKey('ModuleBuildTasks')) + { + foreach ($module in $BuildInfo['ModuleBuildTasks'].Keys) { try { - Write-Host -ForegroundColor DarkGray -Verbose "Importing tasks from module $Module" - $LoadedModule = Import-Module $Module -PassThru -ErrorAction Stop - foreach ($TaskToExport in $BuildInfo['ModuleBuildTasks'].($Module)) + Write-Host -Object "Importing tasks from module $module" -ForegroundColor DarkGray + + $loadedModule = Import-Module -Name $module -PassThru -ErrorAction Stop + + foreach ($TaskToExport in $BuildInfo['ModuleBuildTasks'].($module)) { - $LoadedModule.ExportedAliases.GetEnumerator().Where{ - # using -like to support wildcard - Write-Host -ForegroundColor DarkGray "`t Loading $($_.Key)..." + $loadedModule.ExportedAliases.GetEnumerator().Where{ + Write-Host -Object "`t Loading $($_.Key)..." -ForegroundColor DarkGray + + # Using -like to support wildcard. $_.Key -like $TaskToExport }.ForEach{ - # Dot sourcing the Tasks via their exported aliases + # Dot-sourcing the Tasks via their exported aliases. . (Get-Alias $_.Key) } } } catch { - Write-Host -ForegroundColor Red -Object "Could not load tasks for module $Module." - Write-Error $_ + Write-Host -Object "Could not load tasks for module $module." -ForegroundColor Red + + Write-Error -Message $_ } } } - # Loading Build Tasks defined in the .build/ folder (will override the ones imported above if same task name) - Get-ChildItem -Path ".build/" -Recurse -Include *.ps1 -ErrorAction Ignore | ForEach-Object { - "Importing file $($_.BaseName)" | Write-Verbose - . $_.FullName - } + # Loading Build Tasks defined in the .build/ folder (will override the ones imported above if same task name). + Get-ChildItem -Path '.build/' -Recurse -Include '*.ps1' -ErrorAction Ignore | + ForEach-Object { + "Importing file $($_.BaseName)" | Write-Verbose + + . $_.FullName + } - # Synopsis: Empty task, useful to test the bootstrap process + # Synopsis: Empty task, useful to test the bootstrap process. task noop { } - # Define default task sequence ("."), can be overridden in the $BuildInfo + # Define default task sequence ("."), can be overridden in the $BuildInfo. task . { - Write-Build Yellow "No sequence currently defined for the default task" + Write-Build -Object 'No sequence currently defined for the default task' -ForegroundColor Yellow } - # Load Invoke-Build task sequences/workflows from $BuildInfo - Write-Host -ForegroundColor DarkGray "Adding Workflow from configuration:" - foreach ($Workflow in $BuildInfo.BuildWorkflow.keys) + Write-Host -Object 'Adding Workflow from configuration:' -ForegroundColor DarkGray + + # Load Invoke-Build task sequences/workflows from $BuildInfo. + foreach ($workflow in $BuildInfo.BuildWorkflow.keys) { - Write-Verbose "Creating Build Workflow '$Workflow' with tasks $($BuildInfo.BuildWorkflow.($Workflow) -join ', ')" - $WorkflowItem = $BuildInfo.BuildWorkflow.($Workflow) - if ($WorkflowItem.Trim() -match '^\{(?[\w\W]*)\}$') + Write-Verbose -Message "Creating Build Workflow '$Workflow' with tasks $($BuildInfo.BuildWorkflow.($Workflow) -join ', ')." + + $workflowItem = $BuildInfo.BuildWorkflow.($workflow) + + if ($workflowItem.Trim() -match '^\{(?[\w\W]*)\}$') { - $WorkflowItem = [ScriptBlock]::Create($Matches['sb']) + $workflowItem = [ScriptBlock]::Create($Matches['sb']) } - Write-Host -ForegroundColor DarkGray " +-> $Workflow" - task $Workflow $WorkflowItem + + Write-Host -Object " +-> $workflow" -ForegroundColor DarkGray + + task $workflow $workflowItem } - Write-Host -ForeGroundColor magenta "[build] Executing requested workflow: $($Tasks -join ', ')" + Write-Host -Object "[build] Executing requested workflow: $($Tasks -join ', ')" -ForeGroundColor Magenta } finally { - Pop-Location -StackName BeforeBuild + Pop-Location -StackName 'BeforeBuild' } } -Begin +begin { - # Find build config if not specified - if (-not $BuildConfig) { - $config = Get-ChildItem -Path "$PSScriptRoot\*" -Include 'build.y*ml', 'build.psd1', 'build.json*' -ErrorAction:Ignore - if (-not $config -or ($config -is [array] -and $config.Length -le 0)) { - throw "No build configuration found. Specify path via -BuildConfig" + # Find build config if not specified. + if (-not $BuildConfig) + { + $config = Get-ChildItem -Path "$PSScriptRoot\*" -Include 'build.y*ml', 'build.psd1', 'build.json*' -ErrorAction Ignore + + if (-not $config -or ($config -is [System.Array] -and $config.Length -le 0)) + { + throw 'No build configuration found. Specify path via parameter BuildConfig.' } - elseif ($config -is [array]) { - if ($config.Length -gt 1) { - throw "More than one build configuration found. Specify which one to use via -BuildConfig" + elseif ($config -is [System.Array]) + { + if ($config.Length -gt 1) + { + throw 'More than one build configuration found. Specify which path to use via parameter BuildConfig.' } + $BuildConfig = $config[0] } - else { + else + { $BuildConfig = $config } } + # Bootstrapping the environment before using Invoke-Build as task runner - if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') { - Write-Host -foregroundColor Green "[pre-build] Starting Build Init" - Push-Location $PSScriptRoot -StackName BuildModule + Write-Host -Object "[pre-build] Starting Build Init" -ForegroundColor Green + + Push-Location $PSScriptRoot -StackName 'BuildModule' } if ($RequiredModulesDirectory -in @('CurrentUser', 'AllUsers')) { - # Installing modules instead of saving them - Write-Host -foregroundColor Green "[pre-build] Required Modules will be installed for $RequiredModulesDirectory, not saved." - # Tell Resolve-Dependency to use provided scope as the -PSDependTarget if not overridden in Build.psd1 + # Installing modules instead of saving them. + Write-Host -Object "[pre-build] Required Modules will be installed to the PowerShell module path that is used for $RequiredModulesDirectory." -ForegroundColor Green + + <# + The variable $PSDependTarget will be used below when building the splatting + variable before calling Resolve-Dependency.ps1, unless overridden in the + file Resolve-Dependency.psd1. + #> $PSDependTarget = $RequiredModulesDirectory } else { - if (-Not (Split-Path -IsAbsolute -Path $OutputDirectory)) + if (-not (Split-Path -IsAbsolute -Path $OutputDirectory)) { $OutputDirectory = Join-Path -Path $PSScriptRoot -ChildPath $OutputDirectory } - # Resolving the absolute path to save the required modules to - if (-Not (Split-Path -IsAbsolute -Path $RequiredModulesDirectory)) + # Resolving the absolute path to save the required modules to. + if (-not (Split-Path -IsAbsolute -Path $RequiredModulesDirectory)) { $RequiredModulesDirectory = Join-Path -Path $PSScriptRoot -ChildPath $RequiredModulesDirectory } - # Create the output/modules folder if not exists, or resolve the Absolute path otherwise - if (Resolve-Path $RequiredModulesDirectory -ErrorAction SilentlyContinue) + # Create the output/modules folder if not exists, or resolve the Absolute path otherwise. + if (Resolve-Path -Path $RequiredModulesDirectory -ErrorAction SilentlyContinue) { - Write-Debug "[pre-build] Required Modules path already exist at $RequiredModulesDirectory" - $RequiredModulesPath = Convert-Path $RequiredModulesDirectory + Write-Debug -Message "[pre-build] Required Modules path already exist at $RequiredModulesDirectory" + + $requiredModulesPath = Convert-Path -Path $RequiredModulesDirectory } else { - Write-Host -foregroundColor Green "[pre-build] Creating required modules directory $RequiredModulesDirectory." - $RequiredModulesPath = (New-Item -ItemType Directory -Force -Path $RequiredModulesDirectory).FullName - } + Write-Host -Object "[pre-build] Creating required modules directory $RequiredModulesDirectory." -ForegroundColor Green - # Prepending $RequiredModulesPath folder to PSModulePath to resolve from this folder FIRST - if ($RequiredModulesDirectory -notIn @('CurrentUser', 'AllUsers') -and - (($Env:PSModulePath -split [io.path]::PathSeparator) -notContains $RequiredModulesDirectory)) - { - Write-Host -foregroundColor Green "[pre-build] Prepending '$RequiredModulesDirectory' folder to PSModulePath" - $Env:PSModulePath = $RequiredModulesDirectory + [io.path]::PathSeparator + $Env:PSModulePath + $requiredModulesPath = (New-Item -ItemType Directory -Force -Path $RequiredModulesDirectory).FullName } - # Checking if the user should -ResolveDependency - if ((!(Get-Module -ListAvailable powershell-yaml) -or !(Get-Module -ListAvailable InvokeBuild) -or !(Get-Module -ListAvailable PSDepend)) -and !$ResolveDependency) + $powerShellModulePaths = $env:PSModulePath -split [System.IO.Path]::PathSeparator + + # Pre-pending $requiredModulesPath folder to PSModulePath to resolve from this folder FIRST. + if ($RequiredModulesDirectory -notin @('CurrentUser', 'AllUsers') -and + ($powerShellModulePaths -notcontains $RequiredModulesDirectory)) { - if ($AutoRestore -or !$PSBoundParameters.ContainsKey('Tasks') -or $Tasks -contains 'build') - { - Write-Host -ForegroundColor Yellow "[pre-build] Dependency missing, running './build.ps1 -ResolveDependency -Tasks noop' for you `r`n" - $ResolveDependency = $true - } - else - { - Write-Warning "Some required Modules are missing, make sure you first run with the '-ResolveDependency' parameter." - Write-Warning "Running 'build.ps1 -ResolveDependency -Tasks noop' will pull required modules without running the build task." - } + Write-Host -Object "[pre-build] Pre-pending '$RequiredModulesDirectory' folder to PSModulePath" -ForegroundColor Green + + $env:PSModulePath = $RequiredModulesDirectory + [System.IO.Path]::PathSeparator + $env:PSModulePath } - if ($BuiltModuleSubdirectory) + $powerShellYamlModule = Get-Module -Name 'powershell-yaml' -ListAvailable + $invokeBuildModule = Get-Module -Name 'InvokeBuild' -ListAvailable + $psDependModule = Get-Module -Name 'PSDepend' -ListAvailable + + # Checking if the user should -ResolveDependency. + if (-not ($powerShellYamlModule -and $invokeBuildModule -and $psDependModule) -and -not $ResolveDependency) { - if (-Not (Split-Path -IsAbsolute $BuiltModuleSubdirectory)) + if ($AutoRestore -or -not $PSBoundParameters.ContainsKey('Tasks') -or $Tasks -contains 'build') { - $BuildModuleOutput = Join-Path $OutputDirectory $BuiltModuleSubdirectory + Write-Host -Object "[pre-build] Dependency missing, running './build.ps1 -ResolveDependency -Tasks noop' for you `r`n" -ForegroundColor Yellow + + $ResolveDependency = $true } else { - $BuildModuleOutput = $BuiltModuleSubdirectory + Write-Warning -Message "Some required Modules are missing, make sure you first run with the '-ResolveDependency' parameter. Running 'build.ps1 -ResolveDependency -Tasks noop' will pull required modules without running the build task." } } - else - { - $BuildModuleOutput = $OutputDirectory - } - # Prepending $BuildModuleOutput folder to PSModulePath to resolve built module from this folder - if (($Env:PSModulePath -split [io.path]::PathSeparator) -notContains $BuildModuleOutput) - { - Write-Host -foregroundColor Green "[pre-build] Prepending '$BuildModuleOutput' folder to PSModulePath" - $Env:PSModulePath = $BuildModuleOutput + [io.path]::PathSeparator + $Env:PSModulePath - } - - # Tell Resolve-Dependency to use $RequiredModulesPath as -PSDependTarget if not overridden in Build.psd1 - $PSDependTarget = $RequiredModulesPath + <# + The variable $PSDependTarget will be used below when building the splatting + variable before calling Resolve-Dependency.ps1, unless overridden in the + file Resolve-Dependency.psd1. + #> + $PSDependTarget = $requiredModulesPath } if ($ResolveDependency) { - Write-Host -Object "[pre-build] Resolving dependencies." -foregroundColor Green - $ResolveDependencyParams = @{ } + Write-Host -Object "[pre-build] Resolving dependencies using preferred method." -ForegroundColor Green + + $resolveDependencyParams = @{ } - # If BuildConfig is a Yaml file, bootstrap powershell-yaml via ResolveDependency + # If BuildConfig is a Yaml file, bootstrap powershell-yaml via ResolveDependency. if ($BuildConfig -match '\.[yaml|yml]$') { - $ResolveDependencyParams.add('WithYaml', $True) + $resolveDependencyParams.Add('WithYaml', $true) } - $ResolveDependencyAvailableParams = (Get-Command -Name '.\Resolve-Dependency.ps1').parameters.keys - foreach ($CmdParameter in $ResolveDependencyAvailableParams) - { + $resolveDependencyAvailableParams = (Get-Command -Name '.\Resolve-Dependency.ps1').Parameters.Keys + foreach ($cmdParameter in $resolveDependencyAvailableParams) + { # The parameter has been explicitly used for calling the .build.ps1 - if ($MyInvocation.BoundParameters.ContainsKey($CmdParameter)) + if ($MyInvocation.BoundParameters.ContainsKey($cmdParameter)) { - $ParamValue = $MyInvocation.BoundParameters.ContainsKey($CmdParameter) - Write-Debug " adding $CmdParameter :: $ParamValue [from user-provided parameters to Build.ps1]" - $ResolveDependencyParams.Add($CmdParameter, $ParamValue) + $paramValue = $MyInvocation.BoundParameters.Item($cmdParameter) + + Write-Debug " adding $cmdParameter :: $paramValue [from user-provided parameters to Build.ps1]" + + $resolveDependencyParams.Add($cmdParameter, $paramValue) } # Use defaults parameter value from Build.ps1, if any else { - if ($ParamValue = Get-Variable -Name $CmdParameter -ValueOnly -ErrorAction Ignore) + $paramValue = Get-Variable -Name $cmdParameter -ValueOnly -ErrorAction Ignore + + if ($paramValue) { - Write-Debug " adding $CmdParameter :: $ParamValue [from default Build.ps1 variable]" - $ResolveDependencyParams.add($CmdParameter, $ParamValue) + Write-Debug " adding $cmdParameter :: $paramValue [from default Build.ps1 variable]" + + $resolveDependencyParams.Add($cmdParameter, $paramValue) } } } - Write-Host -foregroundColor Green "[pre-build] Starting bootstrap process." - .\Resolve-Dependency.ps1 @ResolveDependencyParams + Write-Host -Object "[pre-build] Starting bootstrap process." -ForegroundColor Green + + .\Resolve-Dependency.ps1 @resolveDependencyParams } - if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') { - Write-Verbose "Bootstrap completed. Handing back to InvokeBuild." + Write-Verbose -Message "Bootstrap completed. Handing back to InvokeBuild." + if ($PSBoundParameters.ContainsKey('ResolveDependency')) { - Write-Verbose "Dependency already resolved. Removing task" + Write-Verbose -Message "Dependency already resolved. Removing task." + $null = $PSBoundParameters.Remove('ResolveDependency') } - Write-Host -foregroundColor Green "[build] Starting build with InvokeBuild." + + Write-Host -Object "[build] Starting build with InvokeBuild." -ForegroundColor Green + Invoke-Build @PSBoundParameters -Task $Tasks -File $MyInvocation.MyCommand.Path - Pop-Location -StackName BuildModule + + Pop-Location -StackName 'BuildModule' + return } } diff --git a/build.yaml b/build.yaml index e1f4e8f..effd013 100644 --- a/build.yaml +++ b/build.yaml @@ -2,41 +2,18 @@ #################################################### # ModuleBuilder Configuration # #################################################### -# Path to the Module Manifest to build (where path will be resolved from) -# SourcePath: ./Sampler/Sampler.psd1 -# Output Directory where ModuleBuilder will build the Module, relative to module manifest -# OutputDirectory: ../output/Sampler CopyPaths: - en-US - DSCResources - Modules -Encoding: UTF8 # With BOM in WinPS, noBOM in PSCore. - -# Suffix to add to Root module PSM1 after merge (here, the Set-Alias exporting IB tasks) -# suffix: suffix.ps1 -# prefix: prefix.ps1 +Encoding: UTF8 VersionedOutputDirectory: true #################################################### -# ModuleBuilder Submodules Configuration # -#################################################### - -# NestedModule: -# HelperSubmodule: # This is the first submodule to build into the output -# Path: ./Sampler/Modules/HelperSubmodule/HelperSubmodule.psd1 -# # $ModuleVersionFolder is trimmed (remove -.*) and OutputDirectory expanded (the only one) -# OutputDirectory: ./output/Sampler/$ModuleVersionFolder/Modules/HelperSubmodule -# VersionedOutputDirectory: false -# # suffix: -# # prefix: - - -#################################################### -# Sampler Pipeline Configuration # +# Pipeline Configuration # #################################################### -# Defining 'Workflows' (suite of InvokeBuild tasks) to be run using their alias BuildWorkflow: - '.': # "." is the default Invoke-Build workflow. It is called when no -Tasks is specified to the build.ps1 + '.': - build - test @@ -44,8 +21,9 @@ BuildWorkflow: - Clean - Build_Module_ModuleBuilder - Build_NestedModules_ModuleBuilder - - Create_changelog_release_output - #- Generate_Conceptual_Help + - Create_Changelog_Release_Output + - Generate_Conceptual_Help + - Generate_Wiki_Content pack: - build @@ -54,73 +32,60 @@ BuildWorkflow: hqrmtest: - DscResource_Tests_Stop_On_Fail - # defining test task to be run when invoking `./build.ps1 -Tasks test` test: - Pester_Tests_Stop_On_Fail - - Pester_if_Code_Coverage_Under_Threshold - + - Pester_If_Code_Coverage_Under_Threshold merge: - Merge_CodeCoverage_Files publish: - - Publish_release_to_GitHub - - publish_module_to_gallery # runs if nuget is not available - + - publish_module_to_gallery + - Publish_Release_To_GitHub + - Publish_GitHub_Wiki_Content #################################################### -# PESTER Configuration # +# PESTER Configuration # #################################################### -Pester: #Passthru, OutputFile, CodeCoverageOutputFile not supported +Pester: OutputFormat: NUnitXML - # Will look at every *.ps1 & *.psm1 under ModulePath, excepts when $_.FullName -match (Join-Path $ProjectPath $ExcludeFromCodeCoverageItem) ExcludeFromCodeCoverage: - # - Template - # Default is to use the tests folder in the project folder or source folder (if present) - # can use it to prioritize: tests/QA, tests/Unit, tests/Integration Script: - # - tests/QA/module.tests.ps1 - # - tests/QA - tests/Unit - # - tests/Integration ExcludeTag: - helpQuality - FunctionalQuality - TestQuality Tag: - CodeCoverageThreshold: 79 # Set to 0 to bypass - # CodeCoverageOutputFile: JaCoCo_$OsShortName.xml - # CodeCoverageOutputFileEncoding: ascii - # CodeCoverageMergedOutputFile: JaCoCo_Merged.xml + CodeCoverageThreshold: 78 + +CodeCoverage: + # Filename of the file that will be outputted by the task Merge_CodeCoverage_Files. + CodeCoverageMergedOutputFile: JaCoCo_Merged.xml + # File pattern used to search for files under the ./output/testResults folder + # by task Merge_CodeCoverage_Files. + CodeCoverageFilePattern: Codecov*.xml + #CodeCoverageOutputFile: JaCoCo_Coverage.xml + #CodeCoverageOutputFileEncoding: ascii DscTest: + OutputFormat: NUnitXML ExcludeTag: - - "Common Tests - New Error-Level Script Analyzer Rules" + - 'Common Tests - New Error-Level Script Analyzer Rules' Tag: ExcludeSourceFile: - output ExcludeModuleFile: - # - Templates - # - Modules/DscResource.Common - - -Resolve-Dependency: #Parameters for Resolve-Dependency - #PSDependTarget: ./output/modules - #Proxy: '' - #ProxyCredential: - Gallery: '' - #MinimumPSDependVersion = '0.3.0' - AllowPrerelease: false - Verbose: false + MainGitBranch: master ModuleBuildTasks: Sampler: - - '*.build.Sampler.ib.tasks' # this means: import (dot source) all aliases ending with .ib.tasks exported by sampler module + - '*.build.Sampler.ib.tasks' + Sampler.GitHubTasks: + - '*.ib.tasks' DscResource.DocGenerator: - 'Task.*' - -# Invoke-Build Header to be used to 'decorate' the terminal output of the tasks. TaskHeader: | param($Path) "" @@ -131,6 +96,7 @@ TaskHeader: | Write-Build DarkGray " $Path" Write-Build DarkGray " $($Task.InvocationInfo.ScriptName):$($Task.InvocationInfo.ScriptLineNumber)" "" + GitHubConfig: GitHubFilesToAdd: - 'CHANGELOG.md' @@ -138,3 +104,15 @@ GitHubConfig: GitHubConfigUserEmail: dsccommunity@outlook.com UpdateChangelogOnPrerelease: false +#################################################### +# DscResource.DocGenerator Configuration # +#################################################### +DscResource.DocGenerator: + Generate_Conceptual_Help: + MarkdownCodeRegularExpression: + - '\`(.+?)\`' # Match inline code-block + - '\\(\\)' # Match escaped backslash + - '\[[^\[]+\]\((.+?)\)' # Match markdown URL + - '_(.+?)_' # Match Italic (underscore) + - '\*\*(.+?)\*\*' # Match bold + - '\*(.+?)\*' # Match Italic (asterisk) diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..9d8b22a --- /dev/null +++ b/codecov.yml @@ -0,0 +1,28 @@ +codecov: + require_ci_to_pass: no + # master should be the baseline for reporting + branch: master + +comment: + layout: "reach, diff, flags, files" + behavior: default + +coverage: + range: 50..80 + round: down + precision: 0 + + status: + project: + default: + # Set the overall project code coverage requirement to 70% + target: 70 + patch: + default: + # Set the pull request requirement to not regress overall coverage by more than 5% + # and let codecov.io set the goal for the code changed in the patch. + target: auto + threshold: 5 + +fixes: + - '^\d+\.\d+\.\d+::source' # move path "X.Y.Z" => "source" diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index e6baf1e..0000000 --- a/gulpfile.js +++ /dev/null @@ -1,24 +0,0 @@ -var gulp = require("gulp"); -var concat = require("gulp-concat"); -var through2 = require("through2"); -var markdownlint = require("markdownlint"); - -gulp.task("test-mdsyntax", function task() { - return gulp.src("Modules/OfficeOnlineServerDsc/DSCResources/**/*.md", { "read": false }) - .pipe(through2.obj(function obj(file, enc, next) { - markdownlint( - { - "files": [ file.path ], - "config": require("./.markdownlint.json") - }, - function callback(err, result) { - var resultString = (result || "").toString(); - if (resultString) { - file.contents = new Buffer(resultString); - } - next(err, file); - }); - })) - .pipe(concat("markdownissues.txt", { newLine: "\r\n" })) - .pipe(gulp.dest(".")); -}); diff --git a/package.json b/package.json deleted file mode 100644 index 7dd10a8..0000000 --- a/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "officeonlinedsc", - "version": "1.4.0", - "description": "The OfficeOnlineDsc PowerShell module provides DSC resources that can be used to deploy and manage a Office Web Apps 2013 and Office Online Server 2016", - "main": "gulpfile.js", - "dependencies": { - "gulp": "^3.9.1", - "through2": "^2.0.1", - "markdownlint": "^0.2.0" - }, - "devDependencies": { - "gulp-concat": "^2.6.0" - }, - "scripts": { - "test": "powershell -File .vscode\\RunPesterTests.ps1" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/PowerShell/OfficeOnlineDsc.git" - }, - "author": "Microsoft Corporation", - "license": "MIT", - "bugs": { - "url": "https://github.com/PowerShell/OfficeOnlineDsc/issues" - }, - "homepage": "https://github.com/PowerShell/OfficeOnlineDsc#readme" -} diff --git a/src/WikiSource/Home.md b/src/WikiSource/Home.md new file mode 100644 index 0000000..c444de0 --- /dev/null +++ b/src/WikiSource/Home.md @@ -0,0 +1,62 @@ +# Welcome to the OfficeOnlineServerDsc wiki + +*OfficeOnlineServerDsc v#.#.#* + +Here you will find all the information you need to make use of the OfficeOnlineServerDsc +DSC resources in the latest release. This includes details of the resources +that are available, current capabilities, known issues, and information to +help plan a DSC based implementation of OfficeOnlineServerDsc. + +Please leave comments, feature requests, and bug reports for this module in +the [issues section](https://github.com/dsccommunity/OfficeOnlineServerDsc/issues) +for this repository. + +## Deprecated resources + +The documentation, examples, unit test, and integration tests have been removed +for these deprecated resources. These resources will be removed +in a future release. + +*No resources are currently deprecated.* + +## Getting started + +To get started either: + +- Install from the PowerShell Gallery using PowerShellGet by running the + following command: + +```powershell +Install-Module -Name OfficeOnlineServerDsc -Repository PSGallery +``` + +- Download OfficeOnlineServerDsc from the [PowerShell Gallery](https://www.powershellgallery.com/packages/OfficeOnlineServerDsc) + and then unzip it to one of your PowerShell modules folders (such as + `$env:ProgramFiles\WindowsPowerShell\Modules`). + +To confirm installation, run the below command and ensure you see the OfficeOnlineServerDsc +DSC resources available: + +```powershell +Get-DscResource -Module OfficeOnlineServerDsc +``` + +### Powershell + +It is recommended to use Windows Management Framework (PowerShell) version 5.1. + +To run PowerShell DSC you need to have PowerShell 4.0 or higher (which is included +in Windows Management Framework 4.0 or higher). This version of PowerShell is +shipped with Windows Server 2012 R2, and Windows 8.1 or higher. To use DSC +on earlier versions of Windows install the Windows Management Framework 4.0. + +These DSC resources might not work with PowerShell 7.x. + +### Supported Office Online Server versions** + +OfficeOnlineServerDsc currently supports Office Online Server 2016 and +Office Web Apps 2013 on any OS supported by either product. + +## Change log + +A full list of changes in each version can be found in the [change log](https://github.com/dsccommunity/SqlServerDsc/blob/main/CHANGELOG.md). diff --git a/src/build.psd1 b/src/build.psd1 deleted file mode 100644 index 7a542ac..0000000 --- a/src/build.psd1 +++ /dev/null @@ -1,6 +0,0 @@ -@{ - Path = 'OfficeOnlineServerDsc.psd1' #or build breaks on Linux -} -# Waiting for ModuleBuilder to do away with this file -# when all parameters are provided to the function - diff --git a/tests/TestHarness.psm1 b/tests/TestHarness.psm1 deleted file mode 100644 index 7860020..0000000 --- a/tests/TestHarness.psm1 +++ /dev/null @@ -1,96 +0,0 @@ -function Invoke-TestHarness -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $false)] - [System.String] - $TestResultsFile, - - [Parameter(Mandatory = $false)] - [System.String] - $DscTestsPath, - - [Parameter(Mandatory = $false)] - [Switch] - $IgnoreCodeCoverage - ) - - Write-Verbose -Message 'Commencing all OfficeOnlineServerDsc tests' - - $repoDir = Join-Path -Path $PSScriptRoot -ChildPath '..\' -Resolve - - $testCoverageFiles = @() - if ($IgnoreCodeCoverage.IsPresent -eq $false) - { - Get-ChildItem -Path "$repoDir\modules\OfficeOnlineServerDsc\DSCResources\**\*.psm1" -Recurse | ForEach-Object { - if ($_.FullName -notlike '*\DSCResource.Tests\*') - { - $testCoverageFiles += $_.FullName - } - } - } - - $testResultSettings = @{ } - if ([String]::IsNullOrEmpty($TestResultsFile) -eq $false) - { - $testResultSettings.Add('OutputFormat', 'NUnitXml' ) - $testResultSettings.Add('OutputFile', $TestResultsFile) - } - - Import-Module -Name "$repoDir\modules\OfficeOnlineServerDsc\OfficeOnlineServerDsc.psd1" - $testsToRun = @() - - # Run Unit Tests - $versionsPath = Join-Path -Path $repoDir -ChildPath "\Tests\Unit\Stubs\" - $versionsToTest = (Get-ChildItem -Path $versionsPath).Name - # Import the first stub found so that there is a base module loaded before the tests start - $firstVersion = $versionsToTest | Select-Object -First 1 - $firstStub = Join-Path -Path $repoDir ` - -ChildPath "\Tests\Unit\Stubs\$firstVersion\OfficeWebApps.psm1" - Import-Module $firstStub -WarningAction SilentlyContinue - - $versionsToTest | ForEach-Object -Process { - $stubPath = Join-Path -Path $repoDir ` - -ChildPath "\Tests\Unit\Stubs\$_\OfficeWebApps.psm1" - $testsToRun += @(@{ - 'Path' = (Join-Path -Path $repoDir -ChildPath "\Tests\Unit") - 'Parameters' = @{ - 'WACCmdletModule' = $stubPath - } - }) - } - - # Integration Tests (not run in appveyor due to time/reboots needed to install OfficeOnlineServer) - #$integrationTestsPath = Join-Path -Path $repoDir -ChildPath 'Tests\Integration' - #$testsToRun += @( (Get-ChildItem -Path $integrationTestsPath -Filter '*.Tests.ps1').FullName ) - - # DSC Common Tests - if ($PSBoundParameters.ContainsKey('DscTestsPath') -eq $true) - { - $getChildItemParameters = @{ - Path = $DscTestsPath - Recurse = $true - Filter = '*.Tests.ps1' - } - - # Get all tests '*.Tests.ps1'. - $commonTestFiles = Get-ChildItem @getChildItemParameters - - # Remove DscResource.Tests unit and integration tests. - $commonTestFiles = $commonTestFiles | Where-Object -FilterScript { - $_.FullName -notmatch 'DSCResource.Tests\\Tests' - } - - $testsToRun += @( $commonTestFiles.FullName ) - } - - if ($IgnoreCodeCoverage.IsPresent -eq $false) - { - $testResultSettings.Add('CodeCoverage', $testCoverageFiles) - } - - $results = Invoke-Pester -Script $testsToRun -PassThru @testResultSettings - - return $results -} diff --git a/tests/Unit/OfficeOnlineserverDSC/MSFT_OfficeOnlineServerProductUpdate.tests.ps1 b/tests/Unit/OfficeOnlineserverDSC/MSFT_OfficeOnlineServerProductUpdate.tests.ps1 new file mode 100644 index 0000000..e5326e4 --- /dev/null +++ b/tests/Unit/OfficeOnlineserverDSC/MSFT_OfficeOnlineServerProductUpdate.tests.ps1 @@ -0,0 +1,2322 @@ +[CmdletBinding()] +Param +( + [String] + $WACCmdletModule = (Join-Path $PSScriptRoot "..\Stubs\15.0.4569.1506\OfficeWebApps.psm1" -Resolve) +) + +$script:DSCModuleName = 'OfficeOnlineServerDsc' +$script:DSCResourceName = 'MSFT_OfficeOnlineServerProductUpdate' +$global:CurrentWACCmdletModule = $WACCmdletModule + +function Invoke-TestSetup +{ + try + { + Import-Module -Name DscResource.Test -Force -ErrorAction 'Stop' + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' + } + + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' +} + +function Invoke-TestCleanup +{ + Restore-TestEnvironment -TestEnvironment $script:testEnvironment +} + +Invoke-TestSetup + +try +{ + InModuleScope $Script:DSCResourceName { + Describe "OfficeOnlineServerInstall [WAC server version $((Get-Item $Global:CurrentWACCmdletModule).Directory.BaseName)]" { + + Remove-Module -Name "OfficeWebApps" -Force -ErrorAction SilentlyContinue + Import-Module $Global:CurrentWACCmdletModule -WarningAction SilentlyContinue + + Context "CU file does not exist" { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup_not_exist.exe" + Servers = @("OOS1") + } + + Mock -CommandName Test-Path -MockWith { + return $false + } + + Mock -CommandName Test-Path -MockWith { + return $false + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + $returnval = "" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + return "" + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + It "Throw exception in Get method" { + { Get-TargetResource @testParams } | Should Throw "Setup file cannot be found:" + } + + It "Throw exception in Test method" { + { Test-TargetResource @testParams } | Should Throw "Setup file cannot be found:" + } + + It "Throw exception in Set method" { + { Set-TargetResource @testParams } | Should Throw "Setup file cannot be found:" + } + } + + Context "OfficeVersion.inc file does not exist" { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup_not_exist.exe" + Servers = @("OOS1") + } + + $env:COMPUTERNAME = "OOS1" + + Mock -CommandName Test-Path -MockWith { + return $true + } + + Mock -CommandName Test-Path -MockWith { + return $false + } -ParameterFilter { $Path -like "*OfficeVersion.inc" } + + Mock -CommandName Test-Path -MockWith { + return $false + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + $returnval = "" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + return "" + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-ItemProperty -MockWith { + return @{ + VersionInfo = @{ + FileVersion = "16.0.10354.20001" + } + } + } + + It "Throw exception in Get method" { + { Get-TargetResource @testParams } | Should Throw "Cannot find file " + } + + It "Throw exception in Test method" { + { Test-TargetResource @testParams } | Should Throw "Cannot find file " + } + + It "Throw exception in Set method" { + { Set-TargetResource @testParams } | Should Throw "Cannot find file " + } + } + + Context "Incorrect info in OfficeVersion.inc file" { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup_not_exist.exe" + Servers = @("OOS1") + } + $env:COMPUTERNAME = "OOS1" + + Mock -CommandName Test-Path -MockWith { + return $true + } + + Mock -CommandName Test-Path -MockWith { + return $false + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + $returnval = "" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + return "" + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-ItemProperty -MockWith { + return @{ + VersionInfo = @{ + FileVersion = "16.0.10354.20001" + } + } + } + + Mock -CommandName Get-Content -MockWith { + $returnval = @" +# Version Numbers Makefile Include + +# While the actual version of all assemblies will be 16.0.0.0, and all references to +# the filesystem will be web server extensions\16, and all references to the registry +# likewise '16.0', the OM of SharePoint will remain compatibilityLevel == 15 (the +# 2013 user experience). This necessitates a constant for the current 'maximum' +# compatibilityLevel, and other code will still need to refer to the actual product +# version as a separate entity. +SERVER_COMPAT_RMJ = 15 +WSS_RMJ = 4 +ERROR = 16 +RMM = 0 +RUP = 10353 +RPR = 20001 +ISBUILDLABMACHINE = 0 +"@ + return $returnval + } + + It "Throw exception in Get method" { + { Get-TargetResource @testParams } | Should Throw "Cannot read Version information from file" + } + + It "Throw exception in Test method" { + { Test-TargetResource @testParams } | Should Throw "Cannot read Version information from file" + } + } + + ## Servers not contain current server -> SET Method + Context "Current server not in Servers variable" { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup.exe" + Servers = @("OOS1") + } + + $env:COMPUTERNAME = "SERVER_NOT_IN_LIST" + + Mock -CommandName Test-Path -MockWith { + return $true + } + + Mock -CommandName Test-Path -MockWith { + return $false + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + $returnval = "" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + return "" + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + It "Throw exception in Get method" { + { Get-TargetResource @testParams } | Should Throw "Parameter Servers should contain the current server name" + } + + It "Throw exception in Test method" { + { Test-TargetResource @testParams } | Should Throw "Parameter Servers should contain the current server name" + } + + It "Throw exception in Set method" { + { Set-TargetResource @testParams } | Should Throw "Parameter Servers should contain the current server name" + } + } + + Context "Ensure = Absent" { + $testParams = @{ + Ensure = "Absent" + SetupFile = "C:\InstallFiles\setup.exe" + Servers = @("OOS1") + } + + It "Throw exception in Get method" { + { Get-TargetResource @testParams } | Should Throw "Office Online Server does not support uninstalling updates." + } + + It "Throw exception in Set method" { + { Set-TargetResource @testParams } | Should Throw "Office Online Server does not support uninstalling updates." + } + } + + Context "Unknown exit code during install" { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup.exe" + Servers = @("OOS1") + } + + $env:COMPUTERNAME = "OOS1" + + Mock -CommandName Invoke-Command -MockWith { + return @{ + Machines = "OOS1" + MasterMachine = $testParams.Servers + Roles = "All" + Config = @" +{ + "FarmOU": "", + "InternalURL": "https://oos.portal.politie.local/", + "ExternalURL": null, + "AllowHTTP": false, + "AllowOutboundHttp": false, + "SSLOffloaded": false, + "CertificateName": "*.portal.politie.local", + "S2SCertificateName": "", + "EditingEnabled": true, + "LogLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Data\\Logs\\ULS", + "LogRetentionInDays": 7, + "LogVerbosity": "", + "Proxy": null, + "CacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\d", + "MaxMemoryCacheSizeInMB": 75, + "DocumentInfoCacheSize": 5000, + "CacheSizeInGB": 15, + "ClipartEnabled": false, + "TranslationEnabled": false, + "MaxTranslationCharacterCount": 125000, + "TranslationServiceAppId": "", + "TranslationServiceAddress": "", + "RenderingLocalCacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\waccache", + "RecycleActiveProcessCount": 5, + "AllowCEIP": false, + "ExcelRequestDurationMax": 300, + "ExcelSessionTimeout": 450, + "ExcelWorkbookSizeMax": 30, + "ExcelPrivateBytesMax": -1, + "ExcelConnectionLifetime": 1800, + "ExcelExternalDataCacheLifetime": 300, + "ExcelAllowExternalData": true, + "ExcelUseEffectiveUserName": false, + "ExcelWarnOnDataRefresh": true, + "ExcelUdfsAllowed": false, + "ExcelMemoryCacheThreshold": 85, + "ExcelUnusedObjectAgeMax": -1, + "ExcelCachingUnusedFiles": true, + "ExcelAbortOnRefreshOnOpenFail": true, + "ExcelAutomaticVolatileFunctionCacheLifeTime": 300, + "ExcelConcurrentDataRequestsPerSessionMax": 5, + "ExcelDefaultWorkbookCalcMode": 0, + "ExcelRestExternalDataEnabled": true, + "ExcelChartAndImageSizeMax": 1, + "OpenFromUrlEnabled": false, + "OpenFromUncEnabled": true, + "OpenFromUrlThrottlingEnabled": true, + "PicturePasteDisabled": true, + "RemovePersonalInformationFromLogs": false, + "AllowHttpSecureStoreConnections": false +} +"@ + NewMaster = "OOS1" + Name = "OOS1" + } + } + + Mock -CommandName Remove-OfficeWebAppsMachine -MockWith { } + + Mock -CommandName Test-Path -MockWith { + return $true + } + + Mock -CommandName Test-Path -MockWith { + return $false + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + return $null + } + + Mock -CommandName Get-Item -MockWith { + $returnval = "" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + return "" + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-ItemProperty -MockWith { + return @{ + VersionInfo = @{ + FileVersion = "16.0.10354.20001" + } + } + } + + Mock -CommandName Get-Content -MockWith { + $returnval = @" +# Version Numbers Makefile Include + +# While the actual version of all assemblies will be 16.0.0.0, and all references to +# the filesystem will be web server extensions\16, and all references to the registry +# likewise '16.0', the OM of SharePoint will remain compatibilityLevel == 15 (the +# 2013 user experience). This necessitates a constant for the current 'maximum' +# compatibilityLevel, and other code will still need to refer to the actual product +# version as a separate entity. +SERVER_COMPAT_RMJ = 15 +WSS_RMJ = 4 +RMJ = 16 +RMM = 0 +RUP = 10353 +RPR = 20001 +ISBUILDLABMACHINE = 0 +"@ + return $returnval + } + + Mock -CommandName Start-Process -MockWith { + return @{ + ExitCode = 9999 + } + } + + Mock -CommandName New-Item -MockWith { } + Mock -CommandName New-ItemProperty -MockWith { } + + $global:DSCMachineStatus = 0 + It "Starts the install from the set method" { + { Set-TargetResource @testParams } | Should Throw "Office Online Server update install failed, exit code was" + } + } + + Context "CU is not installed, but should be - Single server" { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup.exe" + Servers = @("OOS1") + } + + $env:COMPUTERNAME = "OOS1" + + Mock -CommandName Invoke-Command -MockWith { + return @{ + Machines = "OOS1" + MasterMachine = $testParams.Servers + Roles = "All" + Config = @" +{ + "FarmOU": "", + "InternalURL": "https://oos.portal.politie.local/", + "ExternalURL": null, + "AllowHTTP": false, + "AllowOutboundHttp": false, + "SSLOffloaded": false, + "CertificateName": "*.portal.politie.local", + "S2SCertificateName": "", + "EditingEnabled": true, + "LogLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Data\\Logs\\ULS", + "LogRetentionInDays": 7, + "LogVerbosity": "", + "Proxy": null, + "CacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\d", + "MaxMemoryCacheSizeInMB": 75, + "DocumentInfoCacheSize": 5000, + "CacheSizeInGB": 15, + "ClipartEnabled": false, + "TranslationEnabled": false, + "MaxTranslationCharacterCount": 125000, + "TranslationServiceAppId": "", + "TranslationServiceAddress": "", + "RenderingLocalCacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\waccache", + "RecycleActiveProcessCount": 5, + "AllowCEIP": false, + "ExcelRequestDurationMax": 300, + "ExcelSessionTimeout": 450, + "ExcelWorkbookSizeMax": 30, + "ExcelPrivateBytesMax": -1, + "ExcelConnectionLifetime": 1800, + "ExcelExternalDataCacheLifetime": 300, + "ExcelAllowExternalData": true, + "ExcelUseEffectiveUserName": false, + "ExcelWarnOnDataRefresh": true, + "ExcelUdfsAllowed": false, + "ExcelMemoryCacheThreshold": 85, + "ExcelUnusedObjectAgeMax": -1, + "ExcelCachingUnusedFiles": true, + "ExcelAbortOnRefreshOnOpenFail": true, + "ExcelAutomaticVolatileFunctionCacheLifeTime": 300, + "ExcelConcurrentDataRequestsPerSessionMax": 5, + "ExcelDefaultWorkbookCalcMode": 0, + "ExcelRestExternalDataEnabled": true, + "ExcelChartAndImageSizeMax": 1, + "OpenFromUrlEnabled": false, + "OpenFromUncEnabled": true, + "OpenFromUrlThrottlingEnabled": true, + "PicturePasteDisabled": true, + "RemovePersonalInformationFromLogs": false, + "AllowHttpSecureStoreConnections": false +} +"@ + NewMaster = "OOS1" + Name = "OOS1" + } + } + + Mock -CommandName Remove-OfficeWebAppsMachine -MockWith { } + + Mock -CommandName Test-Path -MockWith { + return $true + } + + Mock -CommandName Test-Path -MockWith { + return $false + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + return $null + } + + Mock -CommandName Get-Item -MockWith { + $returnval = "" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + return "" + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-ItemProperty -MockWith { + return @{ + VersionInfo = @{ + FileVersion = "16.0.10354.20001" + } + } + } + + Mock -CommandName Get-Content -MockWith { + $returnval = @" +# Version Numbers Makefile Include + +# While the actual version of all assemblies will be 16.0.0.0, and all references to +# the filesystem will be web server extensions\16, and all references to the registry +# likewise '16.0', the OM of SharePoint will remain compatibilityLevel == 15 (the +# 2013 user experience). This necessitates a constant for the current 'maximum' +# compatibilityLevel, and other code will still need to refer to the actual product +# version as a separate entity. +SERVER_COMPAT_RMJ = 15 +WSS_RMJ = 4 +RMJ = 16 +RMM = 0 +RUP = 10353 +RPR = 20001 +ISBUILDLABMACHINE = 0 +"@ + return $returnval + } + + Mock -CommandName Start-Process -MockWith { + return @{ + ExitCode = 0 + } + } + + Mock -CommandName New-Item -MockWith { } + Mock -CommandName New-ItemProperty -MockWith { } + + It "Returns that it is not installed from the get method" { + (Get-TargetResource @testParams).Ensure | Should Be "Absent" + } + + It "Returns false from the test method" { + Test-TargetResource @testParams | Should Be $false + } + + $global:DSCMachineStatus = 0 + It "Starts the install from the set method" { + Set-TargetResource @testParams + Assert-MockCalled Start-Process + $global:DSCMachineStatus | Should Be 1 + } + } + + Context "CU is not installed, but should be - Master server, last server in farm" { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup.exe" + Servers = @("OOS1", "OOS2") + } + + $env:COMPUTERNAME = "OOS1" + + $global:srvCounter = 1 + Mock -CommandName Invoke-Command -MockWith { + $returnval = @{ + Roles = "All" + Config = @" +{ + "FarmOU": "", + "InternalURL": "https://oos.portal.politie.local/", + "ExternalURL": null, + "AllowHTTP": false, + "AllowOutboundHttp": false, + "SSLOffloaded": false, + "CertificateName": "*.portal.politie.local", + "S2SCertificateName": "", + "EditingEnabled": true, + "LogLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Data\\Logs\\ULS", + "LogRetentionInDays": 7, + "LogVerbosity": "", + "Proxy": null, + "CacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\d", + "MaxMemoryCacheSizeInMB": 75, + "DocumentInfoCacheSize": 5000, + "CacheSizeInGB": 15, + "ClipartEnabled": false, + "TranslationEnabled": false, + "MaxTranslationCharacterCount": 125000, + "TranslationServiceAppId": "", + "TranslationServiceAddress": "", + "RenderingLocalCacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\waccache", + "RecycleActiveProcessCount": 5, + "AllowCEIP": false, + "ExcelRequestDurationMax": 300, + "ExcelSessionTimeout": 450, + "ExcelWorkbookSizeMax": 30, + "ExcelPrivateBytesMax": -1, + "ExcelConnectionLifetime": 1800, + "ExcelExternalDataCacheLifetime": 300, + "ExcelAllowExternalData": true, + "ExcelUseEffectiveUserName": false, + "ExcelWarnOnDataRefresh": true, + "ExcelUdfsAllowed": false, + "ExcelMemoryCacheThreshold": 85, + "ExcelUnusedObjectAgeMax": -1, + "ExcelCachingUnusedFiles": true, + "ExcelAbortOnRefreshOnOpenFail": true, + "ExcelAutomaticVolatileFunctionCacheLifeTime": 300, + "ExcelConcurrentDataRequestsPerSessionMax": 5, + "ExcelDefaultWorkbookCalcMode": 0, + "ExcelRestExternalDataEnabled": true, + "ExcelChartAndImageSizeMax": 1, + "OpenFromUrlEnabled": false, + "OpenFromUncEnabled": true, + "OpenFromUrlThrottlingEnabled": true, + "PicturePasteDisabled": true, + "RemovePersonalInformationFromLogs": false, + "AllowHttpSecureStoreConnections": false +} +"@ + NewMaster = "OOS2" + } + + if ($global:srvCounter -eq 1) + { + $returnval.Name = "OOS1" + $returnval.Machines = @("OOS1") + $returnval.MasterMachine = "OOS1" + $returnval.Version = "16.0.10353.20001" + } + else + { + $returnval.Name = "OOS2" + $returnval.Machines = @("OOS2") + $returnval.MasterMachine = "OOS2" + $returnval.Version = "16.0.10354.20001" + } + + $global:srvCounter++ + + return $returnval + } + + Mock -CommandName Remove-OfficeWebAppsMachine -MockWith { } + + Mock -CommandName Test-Path -MockWith { + return $true + } + + Mock -CommandName Test-Path -MockWith { + return $false + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + return $null + } + + Mock -CommandName Get-Item -MockWith { + $returnval = "" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + return "" + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-ItemProperty -MockWith { + return @{ + VersionInfo = @{ + FileVersion = "16.0.10354.20001" + } + } + } + + Mock -CommandName Get-Content -MockWith { + $returnval = @" +# Version Numbers Makefile Include + +# While the actual version of all assemblies will be 16.0.0.0, and all references to +# the filesystem will be web server extensions\16, and all references to the registry +# likewise '16.0', the OM of SharePoint will remain compatibilityLevel == 15 (the +# 2013 user experience). This necessitates a constant for the current 'maximum' +# compatibilityLevel, and other code will still need to refer to the actual product +# version as a separate entity. +SERVER_COMPAT_RMJ = 15 +WSS_RMJ = 4 +RMJ = 16 +RMM = 0 +RUP = 10353 +RPR = 20001 +ISBUILDLABMACHINE = 0 +"@ + return $returnval + } + + Mock -CommandName Start-Process -MockWith { + return @{ + ExitCode = 0 + } + } + + Mock -CommandName New-Item -MockWith { } + Mock -CommandName New-ItemProperty -MockWith { } + + It "Returns that it is not installed from the get method" { + (Get-TargetResource @testParams).Ensure | Should Be "Absent" + } + + It "Returns false from the test method" { + Test-TargetResource @testParams | Should Be $false + } + + $global:DSCMachineStatus = 0 + It "Starts the install from the set method" { + Set-TargetResource @testParams + Assert-MockCalled Start-Process + $global:DSCMachineStatus | Should Be 1 + } + } + + Context "CU is not installed, but should be - Master server, not last server in farm" { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup.exe" + Servers = @("OOS1", "OOS2") + } + + $env:COMPUTERNAME = "OOS1" + + $global:srvCounter = 1 + Mock -CommandName Invoke-Command -MockWith { + $returnval = @{ + Roles = "All" + Config = @" +{ + "FarmOU": "", + "InternalURL": "https://oos.portal.politie.local/", + "ExternalURL": null, + "AllowHTTP": false, + "AllowOutboundHttp": false, + "SSLOffloaded": false, + "CertificateName": "*.portal.politie.local", + "S2SCertificateName": "", + "EditingEnabled": true, + "LogLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Data\\Logs\\ULS", + "LogRetentionInDays": 7, + "LogVerbosity": "", + "Proxy": null, + "CacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\d", + "MaxMemoryCacheSizeInMB": 75, + "DocumentInfoCacheSize": 5000, + "CacheSizeInGB": 15, + "ClipartEnabled": false, + "TranslationEnabled": false, + "MaxTranslationCharacterCount": 125000, + "TranslationServiceAppId": "", + "TranslationServiceAddress": "", + "RenderingLocalCacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\waccache", + "RecycleActiveProcessCount": 5, + "AllowCEIP": false, + "ExcelRequestDurationMax": 300, + "ExcelSessionTimeout": 450, + "ExcelWorkbookSizeMax": 30, + "ExcelPrivateBytesMax": -1, + "ExcelConnectionLifetime": 1800, + "ExcelExternalDataCacheLifetime": 300, + "ExcelAllowExternalData": true, + "ExcelUseEffectiveUserName": false, + "ExcelWarnOnDataRefresh": true, + "ExcelUdfsAllowed": false, + "ExcelMemoryCacheThreshold": 85, + "ExcelUnusedObjectAgeMax": -1, + "ExcelCachingUnusedFiles": true, + "ExcelAbortOnRefreshOnOpenFail": true, + "ExcelAutomaticVolatileFunctionCacheLifeTime": 300, + "ExcelConcurrentDataRequestsPerSessionMax": 5, + "ExcelDefaultWorkbookCalcMode": 0, + "ExcelRestExternalDataEnabled": true, + "ExcelChartAndImageSizeMax": 1, + "OpenFromUrlEnabled": false, + "OpenFromUncEnabled": true, + "OpenFromUrlThrottlingEnabled": true, + "PicturePasteDisabled": true, + "RemovePersonalInformationFromLogs": false, + "AllowHttpSecureStoreConnections": false +} +"@ + NewMaster = "OOS2" + Machines = @("OOS1", "OOS2") + MasterMachine = "OOS1" + } + + if ($global:srvCounter -eq 1) + { + $returnval.Name = "OOS1" + $returnval.Version = "16.0.10353.20001" + } + else + { + $returnval.Name = "OOS2" + $returnval.Version = "16.0.10353.20001" + } + + $global:srvCounter++ + + return $returnval + } + + Mock -CommandName Remove-OfficeWebAppsMachine -MockWith { } + + Mock -CommandName Test-Path -MockWith { + return $true + } + + Mock -CommandName Test-Path -MockWith { + return $false + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + return $null + } + + Mock -CommandName Get-Item -MockWith { + $returnval = "" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + return "" + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-ItemProperty -MockWith { + return @{ + VersionInfo = @{ + FileVersion = "16.0.10354.20001" + } + } + } + + Mock -CommandName Get-Content -MockWith { + $returnval = @" +# Version Numbers Makefile Include + +# While the actual version of all assemblies will be 16.0.0.0, and all references to +# the filesystem will be web server extensions\16, and all references to the registry +# likewise '16.0', the OM of SharePoint will remain compatibilityLevel == 15 (the +# 2013 user experience). This necessitates a constant for the current 'maximum' +# compatibilityLevel, and other code will still need to refer to the actual product +# version as a separate entity. +SERVER_COMPAT_RMJ = 15 +WSS_RMJ = 4 +RMJ = 16 +RMM = 0 +RUP = 10353 +RPR = 20001 +ISBUILDLABMACHINE = 0 +"@ + return $returnval + } + + Mock -CommandName Start-Process -MockWith { + return @{ + ExitCode = 0 + } + } + + Mock -CommandName New-Item -MockWith { } + Mock -CommandName New-ItemProperty -MockWith { } + + $global:SleepCounter = 0 + Mock -CommandName Start-Sleep -MockWith { + $global:SleepCounter++ + } + + Mock -CommandName Get-OfficeWebAppsFarm -MockWith { + if ($global:SleepCounter -lt 25) + { + return @{ + Machines = @( + @{ + MachineName = "OOS1" + }, + @{ + MachineName = "OOS2" + } + ) + } + } + else + { + return @{ + Machines = @( + @{ + MachineName = "OOS1" + } + ) + } + } + } + + + + + It "Returns that it is not installed from the get method" { + (Get-TargetResource @testParams).Ensure | Should Be "Absent" + } + + It "Returns false from the test method" { + Test-TargetResource @testParams | Should Be $false + } + + $global:DSCMachineStatus = 0 + It "Starts the install from the set method" { + Set-TargetResource @testParams + Assert-MockCalled Start-Process + $global:SleepCounter | Should Be 25 # Looped through Sleep code 25 times + $global:DSCMachineStatus | Should Be 1 + } + } + + Context "CU is not installed, but should be - Master server, resume install as master server. Create new farm." { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup.exe" + Servers = @("OOS1") + } + + $env:COMPUTERNAME = "OOS1" + + $global:srvCounter = 1 + Mock -CommandName Invoke-Command -MockWith { + $returnval = @{ + Roles = "All" + Config = @" +{ + "FarmOU": "", + "InternalURL": "https://oos.portal.politie.local/", + "ExternalURL": null, + "AllowHTTP": false, + "AllowOutboundHttp": false, + "SSLOffloaded": false, + "CertificateName": "*.portal.politie.local", + "S2SCertificateName": "", + "EditingEnabled": true, + "LogLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Data\\Logs\\ULS", + "LogRetentionInDays": 7, + "LogVerbosity": "", + "Proxy": null, + "CacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\d", + "MaxMemoryCacheSizeInMB": 75, + "DocumentInfoCacheSize": 5000, + "CacheSizeInGB": 15, + "ClipartEnabled": false, + "TranslationEnabled": false, + "MaxTranslationCharacterCount": 125000, + "TranslationServiceAppId": "", + "TranslationServiceAddress": "", + "RenderingLocalCacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\waccache", + "RecycleActiveProcessCount": 5, + "AllowCEIP": false, + "ExcelRequestDurationMax": 300, + "ExcelSessionTimeout": 450, + "ExcelWorkbookSizeMax": 30, + "ExcelPrivateBytesMax": -1, + "ExcelConnectionLifetime": 1800, + "ExcelExternalDataCacheLifetime": 300, + "ExcelAllowExternalData": true, + "ExcelUseEffectiveUserName": false, + "ExcelWarnOnDataRefresh": true, + "ExcelUdfsAllowed": false, + "ExcelMemoryCacheThreshold": 85, + "ExcelUnusedObjectAgeMax": -1, + "ExcelCachingUnusedFiles": true, + "ExcelAbortOnRefreshOnOpenFail": true, + "ExcelAutomaticVolatileFunctionCacheLifeTime": 300, + "ExcelConcurrentDataRequestsPerSessionMax": 5, + "ExcelDefaultWorkbookCalcMode": 0, + "ExcelRestExternalDataEnabled": true, + "ExcelChartAndImageSizeMax": 1, + "OpenFromUrlEnabled": false, + "OpenFromUncEnabled": true, + "OpenFromUrlThrottlingEnabled": true, + "PicturePasteDisabled": true, + "RemovePersonalInformationFromLogs": false, + "AllowHttpSecureStoreConnections": false +} +"@ + NewMaster = "OOS1" + } + + if ($global:srvCounter -eq 1) + { + $returnval.Name = "OOS1" + $returnval.Version = "16.0.10354.20001" + } + + $global:srvCounter++ + + return $returnval + } + + Mock -CommandName New-OfficeWebAppsFarm -MockWith { } + + Mock -CommandName Test-Path -MockWith { + return $true + } + + Mock -CommandName Test-Path -MockWith { + return $true + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + return $null + } + + Mock -CommandName Get-Item -MockWith { + $returnval = "" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true)] + [System.String] + $Input + ) + + if ($Input -eq "State") + { + return "Patching" + } + else + { + return @" +{ + "FarmOU": "", + "InternalURL": "https://oos.portal.politie.local/", + "ExternalURL": null +} +"@ + } + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-ItemProperty -MockWith { + return @{ + VersionInfo = @{ + FileVersion = "16.0.10354.20001" + } + } + } + + Mock -CommandName Get-Content -MockWith { + $returnval = @" +# Version Numbers Makefile Include + +# While the actual version of all assemblies will be 16.0.0.0, and all references to +# the filesystem will be web server extensions\16, and all references to the registry +# likewise '16.0', the OM of SharePoint will remain compatibilityLevel == 15 (the +# 2013 user experience). This necessitates a constant for the current 'maximum' +# compatibilityLevel, and other code will still need to refer to the actual product +# version as a separate entity. +SERVER_COMPAT_RMJ = 15 +WSS_RMJ = 4 +RMJ = 16 +RMM = 0 +RUP = 10354 +RPR = 20001 +ISBUILDLABMACHINE = 0 +"@ + return $returnval + } + + Mock -CommandName Remove-Item -MockWith { } + + It "Returns that it is not installed from the get method" { + (Get-TargetResource @testParams).Ensure | Should Be "Present" + } + + It "Returns false from the test method" { + Test-TargetResource @testParams | Should Be $false + } + + $global:DSCMachineStatus = 0 + It "Starts the install from the set method" { + Set-TargetResource @testParams + Assert-MockCalled New-OfficeWebAppsFarm + } + } + + Context "CU is not installed, but should be - Master server, resume install and join other farm" { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup.exe" + Servers = @("OOS1", "OOS2") + } + + $env:COMPUTERNAME = "OOS1" + + $global:srvCounter = 1 + Mock -CommandName Invoke-Command -MockWith { + $returnval = @{ + Roles = "All" + Config = @" +{ + "FarmOU": "", + "InternalURL": "https://oos.portal.politie.local/", + "ExternalURL": null, + "AllowHTTP": false, + "AllowOutboundHttp": false, + "SSLOffloaded": false, + "CertificateName": "*.portal.politie.local", + "S2SCertificateName": "", + "EditingEnabled": true, + "LogLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Data\\Logs\\ULS", + "LogRetentionInDays": 7, + "LogVerbosity": "", + "Proxy": null, + "CacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\d", + "MaxMemoryCacheSizeInMB": 75, + "DocumentInfoCacheSize": 5000, + "CacheSizeInGB": 15, + "ClipartEnabled": false, + "TranslationEnabled": false, + "MaxTranslationCharacterCount": 125000, + "TranslationServiceAppId": "", + "TranslationServiceAddress": "", + "RenderingLocalCacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\waccache", + "RecycleActiveProcessCount": 5, + "AllowCEIP": false, + "ExcelRequestDurationMax": 300, + "ExcelSessionTimeout": 450, + "ExcelWorkbookSizeMax": 30, + "ExcelPrivateBytesMax": -1, + "ExcelConnectionLifetime": 1800, + "ExcelExternalDataCacheLifetime": 300, + "ExcelAllowExternalData": true, + "ExcelUseEffectiveUserName": false, + "ExcelWarnOnDataRefresh": true, + "ExcelUdfsAllowed": false, + "ExcelMemoryCacheThreshold": 85, + "ExcelUnusedObjectAgeMax": -1, + "ExcelCachingUnusedFiles": true, + "ExcelAbortOnRefreshOnOpenFail": true, + "ExcelAutomaticVolatileFunctionCacheLifeTime": 300, + "ExcelConcurrentDataRequestsPerSessionMax": 5, + "ExcelDefaultWorkbookCalcMode": 0, + "ExcelRestExternalDataEnabled": true, + "ExcelChartAndImageSizeMax": 1, + "OpenFromUrlEnabled": false, + "OpenFromUncEnabled": true, + "OpenFromUrlThrottlingEnabled": true, + "PicturePasteDisabled": true, + "RemovePersonalInformationFromLogs": false, + "AllowHttpSecureStoreConnections": false +} +"@ + NewMaster = "OOS2" + } + + if ($global:srvCounter -eq 1) + { + $returnval.Name = "OOS1" + $returnval.Version = "16.0.10354.20001" + } + else + { + $returnval.Name = "OOS2" + $returnval.Version = "16.0.10354.20001" + $returnval.Machines = @("OOS2") + $returnval.MasterMachine = "OOS2" + } + + $global:srvCounter++ + + return $returnval + } + + Mock -CommandName Test-Path -MockWith { + return $true + } + + Mock -CommandName Test-Path -MockWith { + return $true + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + return $null + } + + Mock -CommandName Get-Item -MockWith { + $returnval = "test" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true)] + [System.String] + $Input + ) + + if ($Input -eq "State") + { + return "Patching" + } + else + { + return "All" + } + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-ItemProperty -MockWith { + return @{ + VersionInfo = @{ + FileVersion = "16.0.10354.20001" + } + } + } + + Mock -CommandName Get-Content -MockWith { + $returnval = @" +# Version Numbers Makefile Include + +# While the actual version of all assemblies will be 16.0.0.0, and all references to +# the filesystem will be web server extensions\16, and all references to the registry +# likewise '16.0', the OM of SharePoint will remain compatibilityLevel == 15 (the +# 2013 user experience). This necessitates a constant for the current 'maximum' +# compatibilityLevel, and other code will still need to refer to the actual product +# version as a separate entity. +SERVER_COMPAT_RMJ = 15 +WSS_RMJ = 4 +RMJ = 16 +RMM = 0 +RUP = 10354 +RPR = 20001 +ISBUILDLABMACHINE = 0 +"@ + return $returnval + } + + Mock -CommandName New-OfficeWebAppsMachine -MockWith { } + + Mock -CommandName Remove-Item -MockWith { } + + Mock -CommandName Get-OfficeWebAppsFarm -MockWith { + if ($global:SleepCounter -lt 25) + { + return @{ + Machines = @( + @{ + MachineName = "OOS1" + }, + @{ + MachineName = "OOS2" + } + ) + } + } + else + { + return @{ + Machines = @( + @{ + MachineName = "OOS1" + } + ) + } + } + } + + It "Returns that it is not installed from the get method" { + (Get-TargetResource @testParams).Ensure | Should Be "Present" + } + + It "Returns false from the test method" { + Test-TargetResource @testParams | Should Be $false + } + + $global:DSCMachineStatus = 0 + It "Starts the install from the set method" { + Set-TargetResource @testParams + Assert-MockCalled New-OfficeWebAppsMachine + Assert-MockCalled Remove-Item + } + } + + Context "CU is not installed, but should be - Not Master server, first patched server, create new farm" { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup.exe" + Servers = @("OOS1", "OOS2") + } + + $env:COMPUTERNAME = "OOS2" + + $global:srvCounter = 1 + Mock -CommandName Invoke-Command -MockWith { + $returnval = @{ + Roles = "All" + Config = @" +{ + "FarmOU": "", + "InternalURL": "https://oos.portal.politie.local/", + "ExternalURL": null, + "AllowHTTP": false, + "AllowOutboundHttp": false, + "SSLOffloaded": false, + "CertificateName": "*.portal.politie.local", + "S2SCertificateName": "", + "EditingEnabled": true, + "LogLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Data\\Logs\\ULS", + "LogRetentionInDays": 7, + "LogVerbosity": "", + "Proxy": null, + "CacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\d", + "MaxMemoryCacheSizeInMB": 75, + "DocumentInfoCacheSize": 5000, + "CacheSizeInGB": 15, + "ClipartEnabled": false, + "TranslationEnabled": false, + "MaxTranslationCharacterCount": 125000, + "TranslationServiceAppId": "", + "TranslationServiceAddress": "", + "RenderingLocalCacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\waccache", + "RecycleActiveProcessCount": 5, + "AllowCEIP": false, + "ExcelRequestDurationMax": 300, + "ExcelSessionTimeout": 450, + "ExcelWorkbookSizeMax": 30, + "ExcelPrivateBytesMax": -1, + "ExcelConnectionLifetime": 1800, + "ExcelExternalDataCacheLifetime": 300, + "ExcelAllowExternalData": true, + "ExcelUseEffectiveUserName": false, + "ExcelWarnOnDataRefresh": true, + "ExcelUdfsAllowed": false, + "ExcelMemoryCacheThreshold": 85, + "ExcelUnusedObjectAgeMax": -1, + "ExcelCachingUnusedFiles": true, + "ExcelAbortOnRefreshOnOpenFail": true, + "ExcelAutomaticVolatileFunctionCacheLifeTime": 300, + "ExcelConcurrentDataRequestsPerSessionMax": 5, + "ExcelDefaultWorkbookCalcMode": 0, + "ExcelRestExternalDataEnabled": true, + "ExcelChartAndImageSizeMax": 1, + "OpenFromUrlEnabled": false, + "OpenFromUncEnabled": true, + "OpenFromUrlThrottlingEnabled": true, + "PicturePasteDisabled": true, + "RemovePersonalInformationFromLogs": false, + "AllowHttpSecureStoreConnections": false +} +"@ + NewMaster = $null + Machines = @("OOS1", "OOS2") + MasterMachine = "OOS1" + } + + if ($global:srvCounter -eq 1) + { + $returnval.Name = "OOS1" + $returnval.Version = "16.0.10353.20001" + } + else + { + $returnval.Name = "OOS2" + $returnval.Version = "16.0.10353.20001" + } + + $global:srvCounter++ + + return $returnval + } + + Mock -CommandName Remove-OfficeWebAppsMachine -MockWith { } + + Mock -CommandName Test-Path -MockWith { + return $true + } + + Mock -CommandName Test-Path -MockWith { + return $false + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + return $null + } + + Mock -CommandName Get-Item -MockWith { + $returnval = "" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + return "" + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-ItemProperty -MockWith { + return @{ + VersionInfo = @{ + FileVersion = "16.0.10354.20001" + } + } + } + + Mock -CommandName Get-Content -MockWith { + $returnval = @" +# Version Numbers Makefile Include + +# While the actual version of all assemblies will be 16.0.0.0, and all references to +# the filesystem will be web server extensions\16, and all references to the registry +# likewise '16.0', the OM of SharePoint will remain compatibilityLevel == 15 (the +# 2013 user experience). This necessitates a constant for the current 'maximum' +# compatibilityLevel, and other code will still need to refer to the actual product +# version as a separate entity. +SERVER_COMPAT_RMJ = 15 +WSS_RMJ = 4 +RMJ = 16 +RMM = 0 +RUP = 10353 +RPR = 20001 +ISBUILDLABMACHINE = 0 +"@ + return $returnval + } + + Mock -CommandName Start-Process -MockWith { + return @{ + ExitCode = 0 + } + } + + Mock -CommandName New-Item -MockWith { } + Mock -CommandName New-ItemProperty -MockWith { } + + It "Returns that it is not installed from the get method" { + (Get-TargetResource @testParams).Ensure | Should Be "Absent" + } + + It "Returns false from the test method" { + Test-TargetResource @testParams | Should Be $false + } + + $global:DSCMachineStatus = 0 + It "Starts the install from the set method" { + Set-TargetResource @testParams + Assert-MockCalled Start-Process + $global:DSCMachineStatus | Should Be 1 + } + } + + ### CU Is not installed: Not Master server, not first patched server -> Install and join other master server + Context "CU is not installed, but should be - Not Master server, second patched server, join new farm" { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup.exe" + Servers = @("OOS1", "OOS2", "OOS3") + } + + $env:COMPUTERNAME = "OOS2" + + $global:srvCounter = 1 + Mock -CommandName Invoke-Command -MockWith { + $returnval = @{ + Roles = "All" + Config = @" +{ + "FarmOU": "", + "InternalURL": "https://oos.portal.politie.local/", + "ExternalURL": null, + "AllowHTTP": false, + "AllowOutboundHttp": false, + "SSLOffloaded": false, + "CertificateName": "*.portal.politie.local", + "S2SCertificateName": "", + "EditingEnabled": true, + "LogLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Data\\Logs\\ULS", + "LogRetentionInDays": 7, + "LogVerbosity": "", + "Proxy": null, + "CacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\d", + "MaxMemoryCacheSizeInMB": 75, + "DocumentInfoCacheSize": 5000, + "CacheSizeInGB": 15, + "ClipartEnabled": false, + "TranslationEnabled": false, + "MaxTranslationCharacterCount": 125000, + "TranslationServiceAppId": "", + "TranslationServiceAddress": "", + "RenderingLocalCacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\waccache", + "RecycleActiveProcessCount": 5, + "AllowCEIP": false, + "ExcelRequestDurationMax": 300, + "ExcelSessionTimeout": 450, + "ExcelWorkbookSizeMax": 30, + "ExcelPrivateBytesMax": -1, + "ExcelConnectionLifetime": 1800, + "ExcelExternalDataCacheLifetime": 300, + "ExcelAllowExternalData": true, + "ExcelUseEffectiveUserName": false, + "ExcelWarnOnDataRefresh": true, + "ExcelUdfsAllowed": false, + "ExcelMemoryCacheThreshold": 85, + "ExcelUnusedObjectAgeMax": -1, + "ExcelCachingUnusedFiles": true, + "ExcelAbortOnRefreshOnOpenFail": true, + "ExcelAutomaticVolatileFunctionCacheLifeTime": 300, + "ExcelConcurrentDataRequestsPerSessionMax": 5, + "ExcelDefaultWorkbookCalcMode": 0, + "ExcelRestExternalDataEnabled": true, + "ExcelChartAndImageSizeMax": 1, + "OpenFromUrlEnabled": false, + "OpenFromUncEnabled": true, + "OpenFromUrlThrottlingEnabled": true, + "PicturePasteDisabled": true, + "RemovePersonalInformationFromLogs": false, + "AllowHttpSecureStoreConnections": false +} +"@ + } + + switch ($global:srvCounter) + { + 1 + { + $returnval.Name = "OOS1" + $returnval.Version = "16.0.10353.20001" + $returnval.Machines = @("OOS1", "OOS2") + $returnval.MasterMachine = "OOS1" + $returnval.NewMaster = "OOS1" + } + 2 + { + $returnval.Name = "OOS2" + $returnval.Version = "16.0.10353.20001" + $returnval.Machines = @("OOS1", "OOS2") + $returnval.MasterMachine = "OOS1" + $returnval.NewMaster = "OOS1" + } + 3 + { + $returnval.Name = "OOS3" + $returnval.Version = "16.0.10354.20001" + $returnval.Machines = @("OOS3") + $returnval.MasterMachine = "OOS3" + $returnval.NewMaster = "OOS3" + } + } + + $global:srvCounter++ + + return $returnval + } + + Mock -CommandName Remove-OfficeWebAppsMachine -MockWith { } + + Mock -CommandName Test-Path -MockWith { + return $true + } + + Mock -CommandName Test-Path -MockWith { + return $false + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + return $null + } + + Mock -CommandName Get-Item -MockWith { + $returnval = "" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + return "" + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-ItemProperty -MockWith { + return @{ + VersionInfo = @{ + FileVersion = "16.0.10354.20001" + } + } + } + + Mock -CommandName Get-Content -MockWith { + $returnval = @" +# Version Numbers Makefile Include + +# While the actual version of all assemblies will be 16.0.0.0, and all references to +# the filesystem will be web server extensions\16, and all references to the registry +# likewise '16.0', the OM of SharePoint will remain compatibilityLevel == 15 (the +# 2013 user experience). This necessitates a constant for the current 'maximum' +# compatibilityLevel, and other code will still need to refer to the actual product +# version as a separate entity. +SERVER_COMPAT_RMJ = 15 +WSS_RMJ = 4 +RMJ = 16 +RMM = 0 +RUP = 10353 +RPR = 20001 +ISBUILDLABMACHINE = 0 +"@ + return $returnval + } + + Mock -CommandName Start-Process -MockWith { + return @{ + ExitCode = 0 + } + } + + Mock -CommandName New-Item -MockWith { } + Mock -CommandName New-ItemProperty -MockWith { } + + It "Returns that it is not installed from the get method" { + (Get-TargetResource @testParams).Ensure | Should Be "Absent" + } + + It "Returns false from the test method" { + Test-TargetResource @testParams | Should Be $false + } + + $global:DSCMachineStatus = 0 + It "Starts the install from the set method" { + Set-TargetResource @testParams + Assert-MockCalled Start-Process + $global:DSCMachineStatus | Should Be 1 + } + } + + Context "CU is not installed, but should be - Not Master server, resume install as master server. Create new farm." { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup.exe" + Servers = @("OOS1", "OOS2", "OOS3") + } + + $env:COMPUTERNAME = "OOS3" + + $global:srvCounter = 1 + Mock -CommandName Invoke-Command -MockWith { + $returnval = @{ + Roles = "All" + Config = @" +{ + "FarmOU": "", + "InternalURL": "https://oos.portal.politie.local/", + "ExternalURL": null, + "AllowHTTP": false, + "AllowOutboundHttp": false, + "SSLOffloaded": false, + "CertificateName": "*.portal.politie.local", + "S2SCertificateName": "", + "EditingEnabled": true, + "LogLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Data\\Logs\\ULS", + "LogRetentionInDays": 7, + "LogVerbosity": "", + "Proxy": null, + "CacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\d", + "MaxMemoryCacheSizeInMB": 75, + "DocumentInfoCacheSize": 5000, + "CacheSizeInGB": 15, + "ClipartEnabled": false, + "TranslationEnabled": false, + "MaxTranslationCharacterCount": 125000, + "TranslationServiceAppId": "", + "TranslationServiceAddress": "", + "RenderingLocalCacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\waccache", + "RecycleActiveProcessCount": 5, + "AllowCEIP": false, + "ExcelRequestDurationMax": 300, + "ExcelSessionTimeout": 450, + "ExcelWorkbookSizeMax": 30, + "ExcelPrivateBytesMax": -1, + "ExcelConnectionLifetime": 1800, + "ExcelExternalDataCacheLifetime": 300, + "ExcelAllowExternalData": true, + "ExcelUseEffectiveUserName": false, + "ExcelWarnOnDataRefresh": true, + "ExcelUdfsAllowed": false, + "ExcelMemoryCacheThreshold": 85, + "ExcelUnusedObjectAgeMax": -1, + "ExcelCachingUnusedFiles": true, + "ExcelAbortOnRefreshOnOpenFail": true, + "ExcelAutomaticVolatileFunctionCacheLifeTime": 300, + "ExcelConcurrentDataRequestsPerSessionMax": 5, + "ExcelDefaultWorkbookCalcMode": 0, + "ExcelRestExternalDataEnabled": true, + "ExcelChartAndImageSizeMax": 1, + "OpenFromUrlEnabled": false, + "OpenFromUncEnabled": true, + "OpenFromUrlThrottlingEnabled": true, + "PicturePasteDisabled": true, + "RemovePersonalInformationFromLogs": false, + "AllowHttpSecureStoreConnections": false +} +"@ + } + + switch ($global:srvCounter) + { + 1 + { + $returnval.Name = "OOS1" + $returnval.Version = "16.0.10353.20001" + $returnval.Machines = @("OOS1", "OOS2") + $returnval.MasterMachine = "OOS1" + $returnval.NewMaster = "OOS1" + } + 2 + { + $returnval.Name = "OOS2" + $returnval.Version = "16.0.10353.20001" + $returnval.Machines = @("OOS1", "OOS2") + $returnval.MasterMachine = "OOS1" + $returnval.NewMaster = "OOS1" + } + 3 + { + $returnval.Name = "OOS3" + $returnval.Version = "16.0.10354.20001" + $returnval.Machines = $null + $returnval.MasterMachine = $null + $returnval.NewMaster = "OOS3" + } + } + + $global:srvCounter++ + + return $returnval + } + + Mock -CommandName New-OfficeWebAppsFarm -MockWith { } + + Mock -CommandName Test-Path -MockWith { + return $true + } + + Mock -CommandName Test-Path -MockWith { + return $true + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + return $null + } + + Mock -CommandName Get-Item -MockWith { + $returnval = "" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true)] + [System.String] + $Input + ) + + if ($Input -eq "State") + { + return "Patching" + } + else + { + return @" +{ + "FarmOU": "", + "InternalURL": "https://oos.portal.politie.local/", + "ExternalURL": null +} +"@ + } + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-ItemProperty -MockWith { + return @{ + VersionInfo = @{ + FileVersion = "16.0.10354.20001" + } + } + } + + Mock -CommandName Get-Content -MockWith { + $returnval = @" +# Version Numbers Makefile Include + +# While the actual version of all assemblies will be 16.0.0.0, and all references to +# the filesystem will be web server extensions\16, and all references to the registry +# likewise '16.0', the OM of SharePoint will remain compatibilityLevel == 15 (the +# 2013 user experience). This necessitates a constant for the current 'maximum' +# compatibilityLevel, and other code will still need to refer to the actual product +# version as a separate entity. +SERVER_COMPAT_RMJ = 15 +WSS_RMJ = 4 +RMJ = 16 +RMM = 0 +RUP = 10354 +RPR = 20001 +ISBUILDLABMACHINE = 0 +"@ + return $returnval + } + + Mock -CommandName Remove-Item -MockWith { } + + It "Returns that it is not installed from the get method" { + (Get-TargetResource @testParams).Ensure | Should Be "Present" + } + + It "Returns false from the test method" { + Test-TargetResource @testParams | Should Be $false + } + + $global:DSCMachineStatus = 0 + It "Starts the install from the set method" { + Set-TargetResource @testParams + Assert-MockCalled New-OfficeWebAppsFarm + } + } + + Context "CU is not installed, but should be - Not Master server, resume install and join other farm" { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup.exe" + Servers = @("OOS1", "OOS2", "OOS3") + } + + $env:COMPUTERNAME = "OOS2" + + $global:srvCounter = 1 + Mock -CommandName Invoke-Command -MockWith { + $returnval = @{ + Roles = "All" + Config = @" +{ + "FarmOU": "", + "InternalURL": "https://oos.portal.politie.local/", + "ExternalURL": null, + "AllowHTTP": false, + "AllowOutboundHttp": false, + "SSLOffloaded": false, + "CertificateName": "*.portal.politie.local", + "S2SCertificateName": "", + "EditingEnabled": true, + "LogLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Data\\Logs\\ULS", + "LogRetentionInDays": 7, + "LogVerbosity": "", + "Proxy": null, + "CacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\d", + "MaxMemoryCacheSizeInMB": 75, + "DocumentInfoCacheSize": 5000, + "CacheSizeInGB": 15, + "ClipartEnabled": false, + "TranslationEnabled": false, + "MaxTranslationCharacterCount": 125000, + "TranslationServiceAppId": "", + "TranslationServiceAddress": "", + "RenderingLocalCacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\waccache", + "RecycleActiveProcessCount": 5, + "AllowCEIP": false, + "ExcelRequestDurationMax": 300, + "ExcelSessionTimeout": 450, + "ExcelWorkbookSizeMax": 30, + "ExcelPrivateBytesMax": -1, + "ExcelConnectionLifetime": 1800, + "ExcelExternalDataCacheLifetime": 300, + "ExcelAllowExternalData": true, + "ExcelUseEffectiveUserName": false, + "ExcelWarnOnDataRefresh": true, + "ExcelUdfsAllowed": false, + "ExcelMemoryCacheThreshold": 85, + "ExcelUnusedObjectAgeMax": -1, + "ExcelCachingUnusedFiles": true, + "ExcelAbortOnRefreshOnOpenFail": true, + "ExcelAutomaticVolatileFunctionCacheLifeTime": 300, + "ExcelConcurrentDataRequestsPerSessionMax": 5, + "ExcelDefaultWorkbookCalcMode": 0, + "ExcelRestExternalDataEnabled": true, + "ExcelChartAndImageSizeMax": 1, + "OpenFromUrlEnabled": false, + "OpenFromUncEnabled": true, + "OpenFromUrlThrottlingEnabled": true, + "PicturePasteDisabled": true, + "RemovePersonalInformationFromLogs": false, + "AllowHttpSecureStoreConnections": false +} +"@ + } + + switch ($global:srvCounter) + { + 1 + { + $returnval.Name = "OOS1" + $returnval.Version = "16.0.10353.20001" + $returnval.Machines = @("OOS1", "OOS2") + $returnval.MasterMachine = "OOS1" + $returnval.NewMaster = "OOS1" + } + 2 + { + $returnval.Name = "OOS2" + $returnval.Version = "16.0.10354.20001" + $returnval.Machines = $null + $returnval.MasterMachine = $null + $returnval.NewMaster = "OOS3" + } + 3 + { + $returnval.Name = "OOS3" + $returnval.Version = "16.0.10354.20001" + $returnval.Machines = @("OOS3") + $returnval.MasterMachine = "OOS3" + $returnval.NewMaster = "OOS3" + } + } + + $global:srvCounter++ + + return $returnval + } + + Mock -CommandName Test-Path -MockWith { + return $true + } + + Mock -CommandName Test-Path -MockWith { + return $true + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + return $null + } + + Mock -CommandName Get-Item -MockWith { + $returnval = "test" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true)] + [System.String] + $Input + ) + + if ($Input -eq "State") + { + return "Patching" + } + else + { + return "All" + } + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-ItemProperty -MockWith { + return @{ + VersionInfo = @{ + FileVersion = "16.0.10354.20001" + } + } + } + + Mock -CommandName Get-Content -MockWith { + $returnval = @" +# Version Numbers Makefile Include + +# While the actual version of all assemblies will be 16.0.0.0, and all references to +# the filesystem will be web server extensions\16, and all references to the registry +# likewise '16.0', the OM of SharePoint will remain compatibilityLevel == 15 (the +# 2013 user experience). This necessitates a constant for the current 'maximum' +# compatibilityLevel, and other code will still need to refer to the actual product +# version as a separate entity. +SERVER_COMPAT_RMJ = 15 +WSS_RMJ = 4 +RMJ = 16 +RMM = 0 +RUP = 10354 +RPR = 20001 +ISBUILDLABMACHINE = 0 +"@ + return $returnval + } + + Mock -CommandName New-OfficeWebAppsMachine -MockWith { } + + Mock -CommandName Remove-Item -MockWith { } + + It "Returns that it is not installed from the get method" { + (Get-TargetResource @testParams).Ensure | Should Be "Present" + } + + It "Returns false from the test method" { + Test-TargetResource @testParams | Should Be $false + } + + $global:DSCMachineStatus = 0 + It "Starts the install from the set method" { + Set-TargetResource @testParams + Assert-MockCalled New-OfficeWebAppsMachine + Assert-MockCalled Remove-Item + } + } + + ### CU Is not installed: No role known -> New server, just install patch + Context "CU is not installed, but should be - New server, no role known, just install patch" { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup.exe" + Servers = @("OOS1", "OOS2") + } + + $env:COMPUTERNAME = "OOS1" + + $global:srvCounter = 1 + Mock -CommandName Invoke-Command -MockWith { + $returnval = @{ + Roles = $null + Config = $null + } + + switch ($global:srvCounter) + { + 1 + { + $returnval.Name = "OOS1" + $returnval.Version = "16.0.10354.20001" + $returnval.Machines = $null + $returnval.MasterMachine = $null + $returnval.NewMaster = "OOS1" + } + 2 + { + $returnval.Name = "OOS2" + $returnval.Version = "16.0.10354.20001" + $returnval.Machines = $null + $returnval.MasterMachine = $null + $returnval.NewMaster = "OOS1" + } + } + + $global:srvCounter++ + + return $returnval + } + + Mock -CommandName Test-Path -MockWith { + return $true + } + + Mock -CommandName Test-Path -MockWith { + return $false + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + return $null + } + + Mock -CommandName Get-Item -MockWith { + $returnval = "test" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true)] + [System.String] + $Input + ) + + return $null + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-ItemProperty -MockWith { + return @{ + VersionInfo = @{ + FileVersion = "16.0.10354.20001" + } + } + } + + Mock -CommandName Get-Content -MockWith { + $returnval = @" +# Version Numbers Makefile Include + +# While the actual version of all assemblies will be 16.0.0.0, and all references to +# the filesystem will be web server extensions\16, and all references to the registry +# likewise '16.0', the OM of SharePoint will remain compatibilityLevel == 15 (the +# 2013 user experience). This necessitates a constant for the current 'maximum' +# compatibilityLevel, and other code will still need to refer to the actual product +# version as a separate entity. +SERVER_COMPAT_RMJ = 15 +WSS_RMJ = 4 +RMJ = 16 +RMM = 0 +RUP = 10353 +RPR = 20001 +ISBUILDLABMACHINE = 0 +"@ + return $returnval + } + + Mock -CommandName Start-Process -MockWith { + return @{ + ExitCode = 0 + } + } + + It "Returns that it is not installed from the get method" { + (Get-TargetResource @testParams).Ensure | Should Be "Absent" + } + + It "Returns false from the test method" { + Test-TargetResource @testParams | Should Be $false + } + + $global:DSCMachineStatus = 0 + It "Starts the install from the set method" { + Set-TargetResource @testParams + Assert-MockCalled Start-Process + $global:DSCMachineStatus | Should Be 1 + } + } + + ### CU Is installed + Context "CU is installed and should be" { + $testParams = @{ + Ensure = "Present" + SetupFile = "C:\InstallFiles\setup.exe" + Servers = @("OOS1", "OOS2") + } + + $env:COMPUTERNAME = "OOS1" + + $global:srvCounter = 1 + Mock -CommandName Invoke-Command -MockWith { + $returnval = @{ + Roles = "All" + Config = @" +{ + "FarmOU": "", + "InternalURL": "https://oos.portal.politie.local/", + "ExternalURL": null, + "AllowHTTP": false, + "AllowOutboundHttp": false, + "SSLOffloaded": false, + "CertificateName": "*.portal.politie.local", + "S2SCertificateName": "", + "EditingEnabled": true, + "LogLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Data\\Logs\\ULS", + "LogRetentionInDays": 7, + "LogVerbosity": "", + "Proxy": null, + "CacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\d", + "MaxMemoryCacheSizeInMB": 75, + "DocumentInfoCacheSize": 5000, + "CacheSizeInGB": 15, + "ClipartEnabled": false, + "TranslationEnabled": false, + "MaxTranslationCharacterCount": 125000, + "TranslationServiceAppId": "", + "TranslationServiceAddress": "", + "RenderingLocalCacheLocation": "C:\\ProgramData\\Microsoft\\OfficeWebApps\\Working\\waccache", + "RecycleActiveProcessCount": 5, + "AllowCEIP": false, + "ExcelRequestDurationMax": 300, + "ExcelSessionTimeout": 450, + "ExcelWorkbookSizeMax": 30, + "ExcelPrivateBytesMax": -1, + "ExcelConnectionLifetime": 1800, + "ExcelExternalDataCacheLifetime": 300, + "ExcelAllowExternalData": true, + "ExcelUseEffectiveUserName": false, + "ExcelWarnOnDataRefresh": true, + "ExcelUdfsAllowed": false, + "ExcelMemoryCacheThreshold": 85, + "ExcelUnusedObjectAgeMax": -1, + "ExcelCachingUnusedFiles": true, + "ExcelAbortOnRefreshOnOpenFail": true, + "ExcelAutomaticVolatileFunctionCacheLifeTime": 300, + "ExcelConcurrentDataRequestsPerSessionMax": 5, + "ExcelDefaultWorkbookCalcMode": 0, + "ExcelRestExternalDataEnabled": true, + "ExcelChartAndImageSizeMax": 1, + "OpenFromUrlEnabled": false, + "OpenFromUncEnabled": true, + "OpenFromUrlThrottlingEnabled": true, + "PicturePasteDisabled": true, + "RemovePersonalInformationFromLogs": false, + "AllowHttpSecureStoreConnections": false +} +"@ + } + + switch ($global:srvCounter) + { + 1 + { + $returnval.Name = "OOS1" + $returnval.Version = "16.0.10354.20001" + $returnval.Machines = @("OOS1", "OOS2") + $returnval.MasterMachine = "OOS1" + $returnval.NewMaster = "OOS1" + } + 2 + { + $returnval.Name = "OOS2" + $returnval.Version = "16.0.10354.20001" + $returnval.Machines = @("OOS1", "OOS2") + $returnval.MasterMachine = "OOS1" + $returnval.NewMaster = "OOS1" + } + } + + $global:srvCounter++ + + return $returnval + } + + Mock -CommandName Test-Path -MockWith { + return $true + } + + Mock -CommandName Test-Path -MockWith { + return $false + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-Item -MockWith { + return $null + } + + Mock -CommandName Get-Item -MockWith { + $returnval = "test" + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name GetValue ` + -Value { + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true)] + [System.String] + $Input + ) + + return $null + } -PassThru -Force + + return $returnval + } -ParameterFilter { $Path -eq "HKLM:\SOFTWARE\OOSDsc" } + + Mock -CommandName Get-ItemProperty -MockWith { + return @{ + VersionInfo = @{ + FileVersion = "16.0.10354.20001" + } + } + } + + Mock -CommandName Get-Content -MockWith { + $returnval = @" +# Version Numbers Makefile Include + +# While the actual version of all assemblies will be 16.0.0.0, and all references to +# the filesystem will be web server extensions\16, and all references to the registry +# likewise '16.0', the OM of SharePoint will remain compatibilityLevel == 15 (the +# 2013 user experience). This necessitates a constant for the current 'maximum' +# compatibilityLevel, and other code will still need to refer to the actual product +# version as a separate entity. +SERVER_COMPAT_RMJ = 15 +WSS_RMJ = 4 +RMJ = 16 +RMM = 0 +RUP = 10354 +RPR = 20001 +ISBUILDLABMACHINE = 0 +"@ + return $returnval + } + + Mock -CommandName New-OfficeWebAppsMachine -MockWith { } + + Mock -CommandName Remove-Item -MockWith { } + + It "Returns that it is not installed from the get method" { + (Get-TargetResource @testParams).Ensure | Should Be "Present" + } + + It "Returns false from the test method" { + Test-TargetResource @testParams | Should Be $true + } + } + } + } +} +finally +{ + Invoke-TestCleanup +}