MSI/MSI.psm1

#Requires -Version 2.0

# Copyright (C) Microsoft Corporation. All rights reserved.
#
# THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
# KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
# PARTICULAR PURPOSE.

$script:loc = data {
    convertfrom-stringdata @'
        AdvtCaption = Confirm install
        AdvtDescription = Installing advertised features for product, {0}.
        AdvtWarning = Are you sure you want to install advertised features for product, {0}?
'@

}
import-localizeddata -bind loc -filename Resources.psd1 -ea SilentlyContinue

function Get-MSIComponentState
{
# .ExternalHelp Microsoft.Tools.WindowsInstaller.PowerShell.dll-Help.xml

    [CmdletBinding(DefaultParameterSetName = "Product")]
    param
    (
        [Parameter(ParameterSetName = "Product", Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [Microsoft.Deployment.WindowsInstaller.ProductInstallation[]] $Product,

        [Parameter(ParameterSetName = "ProductCode", Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Microsoft.Tools.WindowsInstaller.PowerShell.ValidateGuid()]
        [string[]] $ProductCode,

        [Parameter(ParameterSetName = "ProductCode", ValueFromPipelineByPropertyName = $true)]
        [Alias("Context", "InstallContext")]
        [Microsoft.Deployment.WindowsInstaller.UserContexts] $UserContext = "All",

        [Parameter(ParameterSetName = "ProductCode", ValueFromPipelineByPropertyName = $true)]
        [Microsoft.Tools.WindowsInstaller.PowerShell.Sid()]
        [string] $UserSid
    )

    process
    {
        if ($PSCmdlet.ParameterSetName -eq "ProductCode")
        {
            $Product = @(,(get-msiproductinfo @PSBoundParameters))
        }

        $Product | foreach-object {

            [string] $productCode = $_.ProductCode

            # Get the state of every authored component in the product and applied patches.
            $_ | get-msitable -table Component | foreach-object {

                # Attach authored information from the product package to the output object.
                $_ | get-msicomponentinfo -productcode $productCode `
                   | foreach-object { $_.PSTypeNames.Insert(0, $_.PSTypeNames[0] + "#State"); $_ } `
                   | add-member -type NoteProperty -name Component -value $_.Component -passthru
            }
        }
    }
}

function Get-MSISharedComponentInfo
{
# .ExternalHelp Microsoft.Tools.WindowsInstaller.PowerShell.dll-Help.xml

    [CmdletBinding()]
    param
    (
        [Parameter(Position = 0)]
        [ValidateNotNullOrEmpty()]
        [Microsoft.Tools.WindowsInstaller.PowerShell.ValidateGuid()]
        [string[]] $ComponentCode,
        
        [Parameter(Position = 1)]
        [ValidateRange(2, 2147483647)]
        [int] $Count = 2
    )
    
    end
    {
        $getcomponents = { get-msicomponentinfo }
        if ($ComponentCode)
        {
            $getcomponents = { get-msicomponentinfo -componentcode $ComponentCode }
        }
        & $getcomponents | group-object -property ComponentCode | where-object { $_.Count -ge $Count } `
            | select-object -expand Group
    }
}

function Install-MSIAdvertisedFeature
{
# .ExternalHelp Microsoft.Tools.WindowsInstaller.PowerShell.dll-Help.xml

    [CmdletBinding(DefaultParameterSetName = "ProductCode", ConfirmImpact = "Medium", SupportsShouldProcess = $true)]
    param
    (
        [Parameter(ParameterSetName = "ProductCode", ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [Microsoft.Tools.WindowsInstaller.PowerShell.ValidateGuid()]
        [string[]] $ProductCode,

        [Parameter(ParameterSetName = "Product", Mandatory = $true, ValueFromPipeline = $true)]
        [Microsoft.Deployment.WindowsInstaller.ProductInstallation[]] $Product,

        [Parameter(Position = 0, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string[]] $FeatureName,

        [Parameter()]
        [switch] $Force,

        [Parameter(ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true)]
        [string[]] $Properties
    )

    begin
    {
        $YesToAll = $false
        $NoToAll = $false
    }

    process
    {
        # Get only specified or all products given a ProductCode.
        if ($PSBoundParameters.ContainsKey("ProductCode"))
        {
            $Product = get-msiproductinfo -productcode $ProductCode
        }
        elseif ($PSCmdlet.ParameterSetName -eq "ProductCode")
        {
            $Product = get-msiproductinfo
        }

        # Get only specified or all features for each selected product.
        if ($PSBoundParameters.ContainsKey("FeatureName"))
        {
            $features = $Product | foreach-object {
                get-msifeatureinfo -productcode $_.ProductCode -featurename $FeatureName
            }
        }
        else
        {
            $features = $Product | get-msifeatureinfo
        }

        # Construct and splat the necessary parameters to re-install each product.
        $features | where-object { $_.State -eq "Advertised" } | group-object ProductCode | foreach-object {
            $params = @{
                "ProductCode" = $_.Name;
                "Properties" = "$Properties ADDLOCAL=$(($_.Group | select-object -expand Name) -join ',')"
            }

            # Get the advertised product name from the existing collection so we don't cause a reentrance issue.
            $name = $_.Name
            $name = $Product | where-object { $_.ProductCode -eq $name } | select-object -expand AdvertisedProductName

            $description = $script:loc.AdvtDescription -f $name
            $warning = $script:loc.AdvtWarning -f $name

            if ($PSCmdlet.ShouldProcess($description, $warning, $script:loc.AdvtCaption))
            {
                if ($Force -or $PSCmdlet.ShouldContinue($null, $null, [ref] $YesToAll, [ref] $NoToAll))
                {
                    install-msiproduct @params
                }
            }
        }
    }
}

# Update the usage information for this module if installed.
[Microsoft.Tools.WindowsInstaller.PowerShell.Module]::Use()