Import-VcCmApp.ps1

Function Import-VcCmApp {
    <#
    .SYNOPSIS
        Creates Visual C++ Redistributable applications in a ConfigMgr site.

    .DESCRIPTION
        Creates an application in a Configuration Manager site for each Visual C++ Redistributable and includes setting whether the Redistributable can run on 32-bit or 64-bit Windows and the Uninstall key for detecting whether the Redistributable is installed.

        Use Get-VcList and Get-VcRedist to download the Redistributable and create the array of Redistributables for importing into ConfigMgr.
    
    .NOTES
        Name: Import-VcCmApp
        Author: Aaron Parker
        Twitter: @stealthpuppy

    .LINK
        https://stealthpuppy.com

    .PARAMETER VcList
        An array containing details of the Visual C++ Redistributables from Get-VcList.

    .PARAMETER Path
        A folder containing the downloaded Visual C++ Redistributables.

    .PARAMETER Release
        Specifies the release (or version) of the redistributables to download or install.

    .PARAMETER Architecture
        Specifies the processor architecture to download or install.

    .PARAMETER SMSSiteCode
        Specify the Site Code for ConfigMgr app creation.

    .PARAMETER CMPath
        Specify a UNC path where the Visual C++ Redistributables will be distributed from

    .EXAMPLE

#>

    [CmdletBinding(SupportsShouldProcess = $True)]
    [OutputType([Array])]
    Param (
        [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $False, `
                HelpMessage = "An array containing details of the Visual C++ Redistributables from Get-VcList.")]
        [ValidateNotNull()]
        [array]$VcList,

        [Parameter(Mandatory = $True, Position = 1, HelpMessage = "A folder containing the downloaded Visual C++ Redistributables.")]
        [ValidateScript( {If (Test-Path $_ -PathType 'Container') { $True } Else { Throw "Cannot find path $_" } })]
        [string]$Path,

        [Parameter(Mandatory = $False, HelpMessage = "Specify the version of the Redistributables to install.")]
        [ValidateSet('2005', '2008', '2010', '2012', '2013', '2015', '2017')]
        [string[]]$Release = @("2008", "2010", "2012", "2013", "2015", "2017"),

        [Parameter(Mandatory = $False, HelpMessage = "Specify the processor architecture/s to install.")]
        [ValidateSet('x86', 'x64')]
        [string[]]$Architecture = @("x86", "x64"),

        [Parameter(Mandatory = $True, HelpMessage = "Specify ConfigMgr Site Code.")]
        [ValidateScript( { If ($_ -match "^[a-zA-Z0-9]{3}$") { $True } Else { Throw "$_ is not a valid ConfigMgr site code." } })]
        [string]$SMSSiteCode,

        [Parameter(Mandatory = $True, HelpMessage = "Specify the ConfigMgr UNC path.")]
        [string]$CMPath,

        [Parameter()]$Publisher = "Microsoft",
        [Parameter()]$Language = "en-US",
        [Parameter()]$CMAppFolder = "VcRedist"
    )
    Begin {        
        # CMPath will be the network location for copying the Visual C++ Redistributables to
        Set-Location -Path $Path
        If (!([bool]([System.Uri]$CMPath).IsUnc)) { Throw "$CMPath must be a valid UNC path." }
        If (Test-Path $CMPath) {
            # Copy VcRedists to the network location. Use robocopy for robustness
            If ($PSCmdlet.ShouldProcess("$($Path) to $($CMPath)", "Copy")) {
                Robocopy.exe *.exe $Path $CMPath /S /XJ /R:1 /W:1 /NP /NJH /NJS /NFL /NDL
            }
        } Else {
            Write-Warning "Unable to confirm $CMPath exists. Please check that $CMPath is valid."
        }
        
        # If the ConfigMgr console is installed, load the PowerShell module; Requires PowerShell module to be installed
        If (Test-Path $env:SMS_ADMIN_UI_PATH) {
            Try {            
                # Import the ConfigurationManager.psd1 module
                Import-Module "$($env:SMS_ADMIN_UI_PATH)\..\ConfigurationManager.psd1" | Out-Null
            }
            Catch {
                Throw "Could not load ConfigMgr Module. Please make sure that the ConfigMgr Console is installed."
                Break
            }
        }
        Else {
            Throw "Cannot find environment variable SMS_ADMIN_UI_PATH. Is the ConfigMgr Console and PowerShell module installed?"
            Break
        }

        # Create the folder for importing the Redistributables into
        If ($CMAppFolder) {
            If ($PSCmdlet.ShouldProcess("$($SMSSiteCode):\Application\$($CMAppFolder)", "Creating")) {
                New-Item -Path "$($SMSSiteCode):\Application\$($CMAppFolder)" -ErrorAction SilentlyContinue
            }
        }
        If (Test-Path "$($SMSSiteCode):\Application\$($CMAppFolder)") {
            Write-Verbose "Successfully created folder $($SMSSiteCode):\Application\$($CMAppFolder)"
            $CMAppFolder = "$($SMSSiteCode):\Application\$($CMAppFolder)"
        } Else {
            Write-Verbose "Defaulting to folder: $($SMSSiteCode):\Application"
            $CMAppFolder = "$($SMSSiteCode):\Application"
        }

        # Output variable
        $Output = @()
    }
    Process {
        # Filter release and architecture if specified
        If ($PSBoundParameters.ContainsKey('Release')) {
            Write-Verbose "Filtering releases for platform."
            $VcList = $VcList | Where-Object { $_.Release -eq $Release }
        }
        If ($PSBoundParameters.ContainsKey('Architecture')) {
            Write-Verbose "Filtering releases for architecture."
            $VcList = $VcList | Where-Object { $_.Architecture -eq $Architecture }
        }

        ForEach ($Vc in $VcList) {
            Write-Verbose "Importing app: [$($Vc.Name)][$($Vc.Release)][$($Vc.Architecture)]"

            # Import as an application into ConfigMgr
            If ($PSCmdlet.ShouldProcess("$($Vc.Name) in $CMPath", "Import ConfigMgr app")) {

                # Configure parameters and change to the SMS Site drive
                Set-Location -Path $Path
                $Target = "$($(Get-Item -Path $CMPath).FullName)\$($Vc.Release)\$($Vc.Architecture)\$($Vc.ShortName)"
                $Filename = Split-Path -Path $Vc.Download -Leaf
                
                # Change to the SMS Application folder before importing the applications
                Set-Location $CMAppFolder -ErrorVariable ConnectionError

                # Create the ConfigMgr application with properties from the XML file
                If ($pscmdlet.ShouldProcess($Vc.Name + " $($Vc.Architecture)", "Creating ConfigMgr application")) {
                    $App = New-CMApplication -Name "$($Vc.Name) $($Vc.Architecture)" `
                        -Description "$($Publisher) $($Vc.Name) $($Vc.Architecture) imported by $($MyInvocation.MyCommand)" `
                        -SoftwareVersion "$($Vc.Release) $($Vc.Architecture)" `
                        -LinkText $Vc.URL `
                        -Publisher $Publisher `
                        -Keyword "Visual C++ Redistributable" `
                        -ErrorVariable CMAppError
                    $Output += $App
                    $App | Move-CMObject -FolderPath $CMAppFolder -ErrorAction SilentlyContinue | Out-Null
                }

                # Add a deployment type to the application
                If ($pscmdlet.ShouldProcess($Vc.Name + " $($Vc.Architecture)", "Adding deployment type")) {
                    $App | Add-CMScriptDeploymentType `
                        -InstallCommand "$Filename $($Vc.Install)" `
                        -ContentLocation $Target `
                        -ProductCode $Vc.ProductCode `
                        -SourceUpdateProductCode $Vc.ProductCode `
                        -DeploymentTypeName ("SCRIPT_" + $Vc.Name) `
                        -UserInteractionMode Hidden `
                        -UninstallCommand "msiexec /x $($Vc.ProductCode) /qn-" `
                        -LogonRequirementType WhetherOrNotUserLoggedOn `
                        -InstallationBehaviorType InstallForSystem `
                        -Comment "Generated by $($MyInvocation.MyCommand)" `
                        -ErrorVariable CMDtError | Out-Null
                }
            }
        }
    }
    End {
        Set-Location -Path $Path

        # Output array of applications created in ConfigMgr
        $Output
    }
}