DSCResources/Grani_WebPI/Grani_WebPI.psm1
#region Initialize $Script:WebPlatformInstaller = [ordered]@{} $WebPlatformInstaller.RequiredAssemblies = 'C:\Program Files\Microsoft\Web Platform Installer\Microsoft.Web.PlatformInstaller.dll' $WebPlatformInstaller.Requiredexe = 'C:\Program Files\Microsoft\Web Platform Installer\WebpiCmd-x64.exe' function New-WebPlatformInstaller { [OutputType([Void])] [CmdletBinding()] param() if (-not(Test-Path $WebPlatformInstaller.RequiredAssemblies)) { throw New-Object System.IO.FileNotFoundException ("Unable to find the specified file.", $WebPlatformInstaller.RequiredAssemblies) } if (-not(Test-Path $WebPlatformInstaller.Requiredexe)) { throw New-Object System.IO.FileNotFoundException ("Unable to find the specified file.", $WebPlatformInstaller.Requiredexe) } try { [reflection.assembly]::LoadWithPartialName("Microsoft.Web.PlatformInstaller") > $null Add-Type -Path $WebPlatformInstaller.RequiredAssemblies } catch { } } New-WebPlatformInstaller #endregion #region Resource function Set-TargetResource { [OutputType([System.String])] [CmdletBinding()] param ( [parameter(Mandatory = $true)] [System.String]$Name ) Install-WebPlatformInstallerProgram -ProductId $Name if ($?) { Write-Verbose "Installing WebPI Package '$Name' complete."; return; } } function Get-TargetResource { [OutputType([HashTable])] [CmdletBinding()] param ( [parameter(Mandatory = $true)] [System.String]$Name ) $result = Get-WebPlatformInstallerProduct -ProductId $Name -Installed return @{ Name = $result.ProductId } } function Test-TargetResource { [OutputType([System.Boolean])] [CmdletBinding()] param ( [parameter(Mandatory = $true)] [System.String]$Name ) [bool]$result = (Test-WebPlatformInstallerProductIsInstalled -ProductId $Name) -eq $true return $result } #endregion #region Constructor function New-WebPlatformInstallerInstallManager { [OutputType([Void])] [CmdletBinding()] param() $WebPlatformInstaller.installManager = New-Object Microsoft.Web.PlatformInstaller.InstallManager } function New-WebPlatformInstallerProductManager { [OutputType([Void])] [CmdletBinding()] param() $productManager = New-Object Microsoft.Web.PlatformInstaller.ProductManager $productManager.Load() $WebPlatformInstaller.productManager = $productManager Write-Verbose "Remove Blank Keywords Products" $WebPlatformInstaller.productManagerProducts = $WebPlatformInstaller.productManager.Products | where Keywords $WebPlatformInstaller.productManagerProductsBlankKeyword = $WebPlatformInstaller.productManager.Products | where {$_.Keywords.Name -eq $null} } #endregion #region Product function Get-WebPlatformInstallerProduct { <# .Synopsis Get WebPlatformInstaller Packages. .DESCRIPTION This function will return Product information for WebPlatform Installer. You can select 2 mode. 1. -ProductId will give you availability to filter package. 2. Omit -ProductId will return all packages. Make sure No keyword items and IIS Components (Windows Feature) will never checked. .EXAMPLE Get-WebPlatformInstallerProduct # Returns All Product information .EXAMPLE Get-WebPlatformInstallerProduct -Installed # Returns All Installed Product information .EXAMPLE Get-WebPlatformInstallerProduct -Available # Returns All Available Product information .EXAMPLE Get-WebPlatformInstallerProduct -ProductId WDeploy # Returns WDeploy Product information .EXAMPLE Get-WebPlatformInstallerProduct -ProductId WDeploy -Installed # Returns WDeploy Product information if installed .EXAMPLE Get-WebPlatformInstallerProduct -ProductId WDeploy -Available # Returns WDeploy Product information if available #> [OutputType([Microsoft.Web.PlatformInstaller.Product[]])] [CmdletBinding(DefaultParameterSetName = "Any")] param ( [parameter(Mandatory = $false, Position = 0, ValueFromPipelineByPropertyName = 1, ValueFromPipeline = 1)] [string[]]$ProductId, [parameter(Mandatory = $false, Position = 1, ValueFromPipelineByPropertyName = 1, ParameterSetName = "Installed")] [switch]$Installed, [parameter(Mandatory = $false, Position = 1, ValueFromPipelineByPropertyName = 1, ParameterSetName = "Available")] [switch]$Available, [switch]$Force ) begin { if (($null -eq $WebPlatformInstaller.productManagerProducts) -or $Force) { New-WebPlatformInstallerProductManager } # Initialize if ($PSBoundParameters.ContainsKey('ProductId')) { $result = $null $private:productManagerDic = New-Object 'System.Collections.Generic.Dictionary[[string], [Microsoft.Web.PlatformInstaller.Product]]' ([StringComparer]::OrdinalIgnoreCase) $private:productManagerList = New-Object 'System.Collections.Generic.List[Microsoft.Web.PlatformInstaller.Product]' $WebPlatformInstaller.productManagerProducts | % {$productManagerDic.Add($_.ProductId, $_)} } } process { if (-not $PSBoundParameters.ContainsKey('ProductId')) { Write-Verbose ("Searching All Products.") switch ($true) { $Installed { return $WebPlatformInstaller.productManagerProducts | where {$_.IsInstalled($false) } | sort ProductId } $Available { return $WebPlatformInstaller.productManagerProducts | where {-not $_.IsInstalled($false) } | sort ProductId } Default { return $WebPlatformInstaller.productManagerProducts | sort ProductId } } } foreach ($id in $ProductId) { # Search product by ProductId Write-Verbose ("Searching ProductId : '{0}'" -f $id) $isSuccess = $productManagerDic.TryGetValue($id, [ref]$result) # Success if ($isSuccess) { $productManagerList.Add($result); continue; } # Skip if ($id -in $WebPlatformInstaller.productManagerProductsBlankKeyword.ProductId) { [Console]::WriteLine("ProductId '{0}' will skip as it is not supported." -f $id); continue; } # Fail throw New-Object System.InvalidOperationException ("WebPlatform Installation could not found package '{0}' as valid ProductId. Please select from '{1}'" -f $id, (($WebPlatformInstaller.productManagerProducts.ProductId | sort) -join "', '")) } switch ($true) { $Installed { return $productManagerList | where {$_.IsInstalled($false) } | sort ProductId } $Available { return $productManagerList | where {-not $_.IsInstalled($false) } | sort ProductId } Default { return $productManagerList | sort ProductId } } } } #endregion #region Install function Install-WebPlatformInstallerProgram { <# .Synopsis Install target Package. .DESCRIPTION This function will install desired Package. If Package is already installed, then skip it. .EXAMPLE Install-WebPlatformInstallerProgram -ProductId WDeploy # Install WDeploy #> [OutputType([void])] [CmdletBinding()] param ( [parameter(Mandatory = $false, Position = 0, ValueFromPipelineByPropertyName = 1, ValueFromPipeline = 1)] [string[]]$ProductId, [parameter(Mandatory = $false, Position = 0, ValueFromPipelineByPropertyName = 1)] [ValidateSet('en', 'fr', 'es', 'de', 'it', 'ja', 'ko', 'ru', 'zh-cn', 'zh-tw', 'cs', 'pl', 'tr', 'pt-br', 'he', 'zh-hk', 'pt-pt')] [string]$LanguageCode = 'en' ) process { Write-Verbose "Checking Product is already installed." $ProductId ` | % { if (Test-WebPlatformInstallerProductIsInstalled -ProductId $_) { [Console]::WriteLine("Package '{0}' already installed. Skip installation." -f $_); return; } $productIdList.Add($_) } } end { if (($productIdList | measure).count -eq 0) { return; } try { # Prerequisites Write-Verbose "Get Product" [Microsoft.Web.PlatformInstaller.Product[]]$product = Get-WebPlatformInstallerProduct -ProductId $productIdList if ($null -eq $product) { throw New-Object System.NullReferenceException } # Install # InstallByNET -LanguageCode $LanguageCode -product $product InstallByWebPICmd -Name $ProductId } catch { throw $_ } finally { if ($null -ne $WebPlatformInstaller.installManager) { $WebPlatformInstaller.installManager.Dispose() } } } begin { # Initialize if ($null -eq $WebPlatformInstaller.productManager) { New-WebPlatformInstallerProductManager } $productIdList = New-Object 'System.Collections.Generic.List[string]' function ShowInstallerContextStatus { if ($null -ne $WebPlatformInstaller.installManager.InstallerContexts) { $WebPlatformInstaller.installManager.InstallerContexts | Out-String -Stream | Write-Verbose } } function WatchInstallationStatus { [OutputType([bool])] [CmdletBinding()] param ( [parameter(Mandatory = $false, Position = 0, ValueFromPipelineByPropertyName = 1)] [string]$ProductId, [parameter(Mandatory = $false, Position = 0, ValueFromPipelineByPropertyName = 1)] [Microsoft.Web.PlatformInstaller.InstallationState]$PreStatus, [parameter(Mandatory = $false, Position = 0, ValueFromPipelineByPropertyName = 1)] [Microsoft.Web.PlatformInstaller.InstallationState]$PostStatus ) # Skip if ($postStatus -eq $preStatus) { Write-Verbose "Installation not begin" return $false } # Monitor ShowInstallerContextStatus while ($postStatus -ne [Microsoft.Web.PlatformInstaller.InstallationState]::InstallCompleted) { Start-Sleep -Milliseconds 100 $postStatus = $WebPlatformInstaller.installManager.InstallerContexts.InstallationState } ShowInstallerContextStatus $logfiles = $WebPlatformInstaller.installManager.InstallerContexts.Installer.LogFiles $latestLog = ($logfiles | select -Last 1) [Console]::WriteLine(("'{0}' Installation completed. Check Log file at '{1}'" -f ($ProductId -join "', '"), $latestLog)) Write-Verbose ("Latest Log file is '{0}'." -f (Get-Content -Path $latestLog -Encoding UTF8 -Raw)) return $true } function InstallByNET { [OutputType([void])] [CmdletBinding()] param ( [parameter(Mandatory = $false, Position = 0, ValueFromPipelineByPropertyName = 1)] [string]$LanguageCode, [parameter(Mandatory = $false, Position = 0, ValueFromPipelineByPropertyName = 1)] [Microsoft.Web.PlatformInstaller.Product[]]$product ) # Initialize New-WebPlatformInstallerInstallManager $installer = New-Object 'System.Collections.Generic.List[Microsoft.Web.PlatformInstaller.Installer]' # Get Language [Microsoft.Web.PlatformInstaller.Language]$language = $WebPlatformInstaller.productManager.GetLanguage($LanguageCode) $product ` | % { Write-Verbose "Get Installer" $x = $_.GetInstaller($language) if ($null -eq $x.InstallerFile) { [Console]::WriteLine("Package '{0}' detected as no Installer to install. Skip Installation." -f $_.ProductId); return; } $installer.Add($x) $WebPlatformInstaller.InstallManager.Load($installer) Write-Verbose "Donwload Installer" ShowInstallerContextStatus $failureReason = $null $success = $WebPlatformInstaller.InstallManager.InstallerContexts | % { $WebPlatformInstaller.installManager.DownloadInstallerFile($_, [ref]$failureReason) } if ((-not $success) -and $failureReason) { throw New-Object System.InvalidOperationException ("Donwloading '{0}' Failed Exception!! Reason : {1}" -f ($ProductId -join "' ,'"), $failureReason ) } Write-Verbose "Show Donwloaded Installer Status" ShowInstallerContextStatus # Get Status [Microsoft.Web.PlatformInstaller.InstallationState]$preStatus = $WebPlatformInstaller.installManager.InstallerContexts.InstallationState Write-Verbose "Start Installation with StartInstallation()" $WebPlatformInstaller.installManager.StartInstallation() if (WatchInstallationStatus -ProductId $_.ProductId -PreStatus $preStatus -PostStatus $WebPlatformInstaller.installManager.InstallerContexts.InstallationState) { return; } Write-Verbose "Start Installation with StartApplicationInstallation()" $WebPlatformInstaller.installManager.StartApplicationInstallation() if (WatchInstallationStatus -ProductId $_.ProductId -PreStatus $preStatus -PostStatus $WebPlatformInstaller.installManager.InstallerContexts.InstallationState) { return; } Write-Verbose "Start Installation with StartSynchronousInstallation()" $installResult = $WebPlatformInstaller.installManager.StartSynchronousInstallation() if (WatchInstallationStatus -ProductId $_.ProductId -PreStatus $preStatus -PostStatus $WebPlatformInstaller.installManager.InstallerContexts.InstallationState) { return; } } } function InstallByWebPICmd { [OutputType([Void])] [CmdletBinding()] param ( [parameter(Mandatory = $true)] [System.String[]]$Name ) end { foreach ($x in $Name) { Write-Verbose ("Installing package '{0}'" -f $x) [string]$arguments = @( "/Install", "/Products:$x", "/AcceptEula", "/SuppressReboot" ) Invoke-WebPICmd -Arguments $arguments } } begin { Write-Verbose "Start Installation with WebPICmd" function Invoke-WebPICmd { [OutputType([System.String])] [CmdletBinding()] param ( [parameter(Mandatory = $true)] [System.String]$Arguments ) $fileName = "$env:ProgramFiles\Microsoft\Web Platform Installer\WebpiCmd-x64.exe" if (!(Test-Path -Path $fileName)) { throw New-Object System.InvalidOperationException ("Web Platform Installer not installed exception!") } try { $psi = New-Object System.Diagnostics.ProcessStartInfo $psi.CreateNoWindow = $true $psi.UseShellExecute = $false $psi.RedirectStandardOutput = $true $psi.RedirectStandardError = $true $psi.FileName = $fileName $psi.Arguments = $Arguments $process = New-Object System.Diagnostics.Process $process.StartInfo = $psi $process.Start() > $null $output = $process.StandardOutput.ReadToEnd() $process.StandardOutput.ReadLine() $process.WaitForExit() return $output } catch { $outputError = $process.StandardError.ReadToEnd() throw $_ + $outputError } finally { if ($null -ne $psi) { $psi = $null} if ($null -ne $process) { $process.Dispose() } } } } } } } function Test-WebPlatformInstallerProductIsInstalled { <# .Synopsis Test target Package is already installed or not. .DESCRIPTION This function will check desired Package is already installed or not yet by Boolean. $true : Means already installed. $false : Means not yet installed. Pass ProductId which you want to check. .EXAMPLE Test-WebPlatformInstallerProductIsInstalled -ProductId WDeploy # Check WDeploy is installed or not. #> [OutputType([bool])] [CmdletBinding(DefaultParameterSetName = "Any")] param ( [parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = 1, ValueFromPipeline = 1)] [string[]]$ProductId ) # Not use Cached Value $result = Get-WebPlatformInstallerProduct -ProductId $ProductId | % {$_.IsInstalled($false)} if ($null -ne $result) { Write-Verbose $result } return $result } #endregion Export-ModuleMember -Function *-TargetResource |