CMBuilder.psm1

#Region '.\private\Application.ps1' 0
<#
 
    .SYNOPSIS
 
        Creates an SCCM Application
 
    .DESCRIPTION
 
        With a provided (required) configuration file, we can automatically create applications in SCCM, superseeding previous versions
 
#>


function Application {
    [CmdletBinding()]

    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [System.String]
        $Folder
    )

    Process {
        Write-Host '[CMBuilder-Application] Creating Application'

        $NewCMApplication = @{
            Name                     = ('{0} {1} {2}' -f $Configuration.Publisher, $Configuration.Name, $Script:Version)
            LocalizedApplicationName = if ($Configuration.LocalizedApplicationName) {
                $Configuration.LocalizedApplicationName
            } else {
                ('{0} {1}' -f $Configuration.Publisher, $Configuration.Name)
            }
            SoftwareVersion          = $Script:Version
            Publisher                = $Configuration.Publisher
            Description              = ('Created by CMBuilder -- {0}' -f $Configuration.Description)
            ReleaseDate              = (Get-Date)
            AutoInstall              = $true # Allow install from Task Sequence
        }

        if ($Configuration.AppCatalog) {
            $NewCMApplication.Add('AppCatalog', $Configuration.AppCatalog)
        }

        if ($Configuration.DefaultLanguageId) {
            $NewCMApplication.Add('DefaultLanguageId', $Configuration.DefaultLanguageId)
        }

        if ($Configuration.DisplaySupersedenceInApplicationCatalog) {
            $NewCMApplication.Add('DisplaySupersedenceInApplicationCatalog', $Configuration.DisplaySupersedenceInApplicationCatalog)
        }

        if ($Configuration.IconLocationFile) {
            $NewCMApplication.Add('IconLocationFile', $Configuration.IconLocationFile)
        }

        if ($Configuration.IsFeatured) {
            $NewCMApplication.Add('IsFeatured', $Configuration.IsFeatured)
        }

        if ($Configuration.Keyword) {
            $NewCMApplication.Add('Keyword', $Configuration.Keyword)
        }

        if ($Configuration.LinkText) {
            $NewCMApplication.Add('LinkText', $Configuration.LinkText)
        }

        if ($Configuration.LocalizedDescription) {
            $NewCMApplication.Add('LocalizedDescription', $Configuration.LocalizedDescription)
        }

        if ($Configuration.LocalizedName) {
            $NewCMApplication.Add('LocalizedName', $Configuration.LocalizedName)
        }

        if ($Configuration.OptionalReference) {
            $NewCMApplication.Add('OptionalReference', $Configuration.OptionalReference)
        }

        if ($Configuration.Owner) {
            $NewCMApplication.Add('Owner', $Configuration.Owner)
        }

        if ($Configuration.PrivacyUrl) {
            $NewCMApplication.Add('PrivacyUrl', $Configuration.PrivacyUrl)
        }

        if ($Configuration.SupportContact) {
            $NewCMApplication.Add('SupportContact', $Configuration.SupportContact)
        }

        if ($Configuration.UserDocumentation) {
            $NewCMApplication.Add('UserDocumentation', $Configuration.UserDocumentation)
        }

        Write-Host ('[CMBuilder-Application] New-CMApplication: {0}' -f ($NewCMApplication | Out-String))
        $CMApplication = New-CMApplication @NewCMApplication
        Move-CMObject -FolderPath $Folder -InputObject $CMApplication

        Write-Output $CMApplication
    }
}
#EndRegion '.\private\Application.ps1' 102
#Region '.\private\ApplicationGroup.ps1' 0
function ApplicationGroup {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        $NewCMApplication,

        [Parameter(Mandatory = $true, Position = 1)]
        $OldCMApplication
    )

    # Get appgroups that contain the old application
    $ApplicationGroups = Get-CMApplicationGroup | Where-Object SDMPackageXML -Match ($OldCMApplication.ModelName -split '/')[1]

    :ApplicationGroup foreach ($Group in $ApplicationGroups) {
        Write-Host ('[ApplicationGroup] Processing Application Group: {0}' -f $Group.LocalizedDisplayName)

        $SDKObject = $Group | ConvertTo-CMApplicationGroup

        Write-Host '[ApplicationGroup] Get names of applications currently in the group'

        [System.Collections.ArrayList] $ApplicationNames = foreach ($Application in $SDKObject.GroupItems) {
            $AppNameQuery = "SELECT LocalizedDisplayName FROM SMS_Application WHERE ModelName = '$($Application.ObjectId.Scope)/$($Application.ObjectId.Name)'"

            Write-Output (Invoke-CMWmiQuery -Query $AppNameQuery).LocalizedDisplayName | Select-Object -Last 1
        }

        # Create an array with the updated application
        $NewList = $ApplicationNames.Replace($OldCMApplication.LocalizedDisplayName, $NewCMApplication.LocalizedDisplayName)

        Write-Host '[ApplicationGroup] Updating Application Group'
        if (-not (Get-CMObjectLockDetails -InputObject $Group).LockState) {
            Set-CMApplicationGroup -Name $Group.LocalizedDisplayName -RemoveApplication $ApplicationNames -AddApplication $NewList
        } else {
            Write-Host ('[ApplicationGroup] CMObject is locked. Skipping.')
            continue ApplicationGroup
        }

        if (Get-Variable -Name 'ModifiedApplicationGroups' -Scope 'Global' -ErrorAction SilentlyContinue) {
            Write-Host '[ApplicationGroup] ModifiedApplicationGroups variable exists. Adding some info...'
            $ModifiedApplicationGroups.Add([PSCustomObject] @{
                GroupName = $Group.LocalizedDisplayName
            }) | Out-Null
        }
    }
}
#EndRegion '.\private\ApplicationGroup.ps1' 46
#Region '.\private\Collection.ps1' 0
<#
.SYNOPSIS
    Creates a device collection.
.DESCRIPTION
    Create a device collection on the ConfigMgr Site.
     
#>


function Collection {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        $CollectionName,

        [Parameter(Mandatory = $true, Position = 1)]
        $LimitingCollectionName,

        [Parameter(Mandatory = $true, Position = 2)]
        $SitePath
    )

    Process {

        $NewColl = @{
            LimitingCollectionName = $LimitingCollectionName
            Name                   = $CollectionName
            Comment                = 'Collection created by CMBuilder'
            RefreshSchedule        = New-CMSchedule -Start (Get-Date) -DayOfWeek Sunday -RecurCount 1
        }
        Write-Host ('[Collection] Creating collection with args: {0}' -f ($NewColl | Out-String))
        $Collection = New-CMDeviceCollection @NewColl

        $FolderPath = ('{0}:\DeviceCollection\{1}' -f $SiteCode, $SitePath)

        Write-Host ('[Collection] Moving to: {0}' -f $FolderPath)
        Move-CMObject -FolderPath $FolderPath -InputObject $Collection

        $Collection

    }
}
#EndRegion '.\private\Collection.ps1' 42
#Region '.\private\Deploy.ps1' 0
function Deploy {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        $NewCMApplication,

        [Parameter(Position = 1)]
        $OldCMApplication
    )

    Process {

        if (($OldCMApplication) -and ($CopyDeployment)) {
            # Filter out disabled and collections defined in the Configuration
            $CurrentDeployments = Get-CMApplicationDeployment -InputObject $OldCMApplication | Where-Object { ($_.Enabled -eq $true) -and ($_.CollectionName -notin $Configuration.Deploy.CollectionName) }

            if ($CurrentDeployments) {
                Write-Host ('[CMBuilder-Deploy.Copy] Copying previous deployment settings from: {0}' -f ($CurrentDeployments -join '; ') )
                Deploy.Copy -CMApplication $NewCMApplication -CurrentDeployments $CurrentDeployments
            }
        }

        if ($Configuration.Deploy) {
            Write-Host '[CMBuilder-Deploy.Configuration] Deploying to specified collections.'
            Deploy.Configuration -CMApplication $NewCMApplication
        }

    }
}
#EndRegion '.\private\Deploy.ps1' 30
#Region '.\private\deploy\Deploy.Configuration.ps1' 0
function Deploy.Configuration {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        $CMApplication
    )

    Process {

        foreach ($Deployment in $Configuration.Deploy) {
            if (-not ($Collection = Get-CMDeviceCollection -Name $Deployment.CollectionName )) {
                Write-Verbose ('[CMBuilder-Deploy.Configuration] Creating collection: {0}' -f $Deployment.CollectionName)
                $Collection = Collection -CollectionName $Deployment.CollectionName -LimitingCollectionName $Deployment.LimitingCollectionName -SitePath $CollectionPath
            }

            Write-Host ('[CMBuilder-Deploy.Configuration] Deploying to collection: {0}' -f $Collection.Name)

            $NewCMApplicationDeployment = @{
                InputObject   = $CMApplication
                Collection    = $Collection
                DeployAction  = if ($Deployment.DeployAction) { $Deployment.DeployAction } else { 'Install' }
                DeployPurpose = if ($Deployment.DeployPurpose) { $Deployment.DeployPurpose } else { 'Required' }
                TimeBaseOn    = 'LocalTime'
            }

            if ($Deployment.AppCatalog) {
                $NewCMApplicationDeployment.Add('AppCatalog', $Deployment.AppCatalog)
            }

            <#
                Applies to version 2002 and later. Use this parameter to configure the repair application option when creating a deployment for an application
            #>

            if ($Deployment.AllowRepairApp) {
                $NewCMApplicationDeployment.Add('AllowRepairApp', $Deployment.AllowRepairApp)
            }

            <#
                If you set this parameter to $true, an administrator must approve a request for this application on the device.
            #>

            if ($Deployment.ApprovalRequired) {
                $NewCMApplicationDeployment.Add('ApprovalRequired', $Deployment.ApprovalRequired)
            }

            <#
                Starting in version 2107, set this parameter to $true to enable the application deployment setting for install behaviors.
 
                Then use the Add-CMDeploymentTypeInstallBehavior cmdlet to add an executable file to check isn't running for the install to succeed.
            #>

            if ($Deployment.AutoCloseExecutable) {
                $NewCMApplicationDeployment.Add('AutoCloseExecutable', $Deployment.AutoCloseExecutable)
            }

            <#
                Specify a DateTime object for when this deployment is available. To get this object, use the Get-Date built-in cmdlet.
 
                Use DeadlineDateTime to specify the deployment assignment, or deadline
            #>

            if ($NewCMApplicationDeployment.DeployPurpose -eq 'Required') {
                if ($Deployment.ImmediateDeployment) {
                    # Become available right now
                    $NewCMApplicationDeployment.Add('AvailableDateTime', (Get-Date))
                } else {
                    # Become available at 8AM Monday morning
                    $NewCMApplicationDeployment.Add('AvailableDateTime', (Get-Date -Hour 8 -Minute 0 -Second 0).AddDays((8 - ((Get-Date).DayOfWeek.value__)) % 7))
                }
            }

            <#
                Specify an optional comment for this deployment.
            #>

            if ($Deployment.Comment) {
                $NewCMApplicationDeployment.Add('Comment', $Deployment.Comment)
            }

            <#
                Specify a DateTime object for when this deployment is assigned, also known as the deadline. To get this object, use the Get-Date built-in cmdlet.
 
                Use -AvailableDateTime to specify when the deployment is available
            #>

            if ($NewCMApplicationDeployment.DeployPurpose -eq 'Required') {
                if ($Deployment.ImmediateDeployment) {
                    # 1 week deadline
                    $NewCMApplicationDeployment.Add('DeadlineDateTime', (Get-Date).AddDays(7))
                } else {
                    # 8AM next Monday morning
                    $NewCMApplicationDeployment.Add('DeadlineDateTime', (Get-Date -Hour 8 -Minute 0 -Second 0).AddDays(((8 - ((Get-Date).DayOfWeek.value__)) % 7) + 7))
                }
            }

            <#
                Set this parameter to $true to enable delayed enforcement.
            #>

            if ($NewCMApplicationDeployment.DeployPurpose -eq 'Required') {
                if (-not ($Deployment.ImmediateDeployment)) {
                    $NewCMApplicationDeployment.Add('EnableSoftDeadline', $true)
                }
            }

            <#
                Specifies the percentage of failed application installation that causes an alert. Specify an integer from 1 through 100. To enable this alert, set the CreatAlertBaseOnPercentFailure parameter to $true.
            #>

            if ($Deployment.FailParameterValue) {
                $NewCMApplicationDeployment.Add('FailParameterValue', $Deployment.FailParameterValue)
            }

            <#
                Indicates whether to create an Operations Manager alert if a client fails to install the application.
            #>

            if ($Deployment.GenerateScomAlertOnFailure) {
                $NewCMApplicationDeployment.Add('GenerateScomAlertOnFailure', $Deployment.GenerateScomAlertOnFailure)
            }

            <#
                Indicates whether the deployment takes place even if scheduled outside of a maintenance window.
                A maintenance window is a specified period of time used for computer maintenance and updates.
                If this value is $true, Configuration Manager deploys the application even if the scheduled time falls outside the maintenance window.
                If this value is $false, Configuration Manager doesn't deploy the application outside the window. It waits until it can deploy in an available window.
            #>

            if ($Deployment.OverrideServiceWindow) {
                $NewCMApplicationDeployment.Add('OverrideServiceWindow', $Deployment.OverrideServiceWindow)
            }

            <#
                Indicates whether to enable write filters for embedded devices. For a value of $true, the device commits changes during a maintenance window.
                This action requires a restart. For a value of $false, the device saves changes in an overlay and commits them later.
            #>

            if ($Deployment.PersistOnWriteFilterDevice) {
                $NewCMApplicationDeployment.Add('PersistOnWriteFilterDevice', $Deployment.PersistOnWriteFilterDevice)
            }

            <#
                When you set CreateAlertBaseOnPercentSuccess to $true, use this parameter to specify a DateTime object.
                Configuration Manager creates a deployment alert when the threshold is lower than the SuccessParameterValue after this date.
            #>

            if ($Deployment.PostponeDateTime) {
                $NewCMApplicationDeployment.Add('PostponeDateTime', $Deployment.PostponeDateTime)
            }

            <#
                Indicates whether to pre-deploy the application to the primary device of the user.
            #>

            if ($Deployment.PreDeploy) {
                $NewCMApplicationDeployment.Add('PreDeploy', $Deployment.PreDeploy)
            }

            <#
                Indicates whether a computer restarts outside a maintenance window. A maintenance window is a specified period of time used for computer maintenance and updates.
                If this value is $true, any required restart takes place without regard to maintenance windows.
                If this value is $false, the computer doesn't restart outside a maintenance window.
            #>

            if ($Deployment.RebootOutsideServiceWindow) {
                $NewCMApplicationDeployment.Add('RebootOutsideServiceWindow', $Deployment.RebootOutsideServiceWindow)
            }

            <#
                When required software is available on the client, set this parameter to $true to replace the default toast notifications with a dialog window.
                It's false by default.
            #>

            if ($Deployment.ReplaceToastNotificationWithDialog) {
                $NewCMApplicationDeployment.Add('ReplaceToastNotificationWithDialog', $Deployment.ReplaceToastNotificationWithDialog)
            }

            <#
                Indicates whether to send a wake-up packet to computers before the deployment begins.
                If this value is $true, Configuration Manager attempts to wake a computer from sleep.
                If this value is $false, it doesn't wake computers from sleep. For computers to wake, you must first configure Wake On LAN.
            #>

            if ($Deployment.SendWakeupPacket) {
                $NewCMApplicationDeployment.Add('SendWakeupPacket', $Deployment.SendWakeupPacket)
            }

            <#
                Specifies the percentage of successful application installation that causes an alert. Specify an integer from 0 through 99.
                To enable this alert, set the CreateAlertBaseOnPercentSuccess parameter as $true.
            #>

            if ($Deployment.SuccessParameterValue) {
                $NewCMApplicationDeployment.Add('SuccessParameterValue', $Deployment.SuccessParameterValue)
            }

            <#
                For an available deployment, use this parameter to specify the installation deadline to upgrade users or devices that have the superseded application installed.
                Use DeadlineDateTime to specify a specific time, otherwise it's as soon as possible after the AvailableDateTime.
            #>

            if ($Deployment.UpdateSupersedence) {
                $NewCMApplicationDeployment.Add('UpdateSupersedence', $Deployment.UpdateSupersedence)
            }

            <#
                Indicates whether to allow clients to download content over a metered internet connection after the deadline, which may incur extra expense.
            #>

            if ($Deployment.UseMeteredNetwork) {
                $NewCMApplicationDeployment.Add('UseMeteredNetwork', $Deployment.UseMeteredNetwork)
            }

            <#
                Specifies the type of user notification.
 
                DisplayAll: Display in Software Center and show all notifications.
                DisplaySoftwareCenterOnly: Display in Software Center, and only show notifications of computer restarts.
                HideAll: Hide in Software Center and all notifications
            #>

            if ($Deployment.UserNotification) {
                $NewCMApplicationDeployment.Add('UserNotification', $Deployment.UserNotification)
            }

            Write-Host ('[CMBuilder-Deploy.Configuration] New-CMApplicationDeployment: {0}' -f ($NewCMApplicationDeployment | Out-String))
            New-CMApplicationDeployment @NewCMApplicationDeployment

            if (Get-Variable -Name 'NewDeployments' -Scope 'Global' -ErrorAction SilentlyContinue) {
                Write-Host '[CMBuilder-Deploy.Configuration] NewDeployments variable exists. Adding some info...'
                $NewDeployments.Add([PSCustomObject] @{
                    CollectionName = $Collection.Name
                    Action         = $NewCMApplicationDeployment.DeployPurpose
                }) | Out-Null
            }
        }
    }
}
#EndRegion '.\private\deploy\Deploy.Configuration.ps1' 219
#Region '.\private\deploy\Deploy.Copy.ps1' 0
function Deploy.Copy {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        $CMApplication,

        [Parameter(Mandatory = $true, Position = 2)]
        $CurrentDeployments
    )

    Process {

        foreach ($Deployment in $CurrentDeployments) {
            $NewCMApplicationDeployment = @{
                InputObject                = $CMApplication
                ApprovalRequired           = $Deployment.RequireApproval
                CollectionID               = $Deployment.TargetCollectionID
                Comment                    = ('Deployment created by CMBuilder')
                DeployAction               = $Deployment.AssignmentName.Split('_')[-1]
                EnableMomAlert             = $Deployment.DisableMomAlerts
                EnableSoftDeadline         = $Deployment.SoftDeadlineEnabled
                GenerateScomAlertOnFailure = $Deployment.RaiseMomAlertsOnFailure
                OverrideServiceWindow      = $Deployment.OverrideServiceWindows
                PersistOnWriteFilterDevice = $Deployment.PersistOnWriteFilterDevices
                RebootOutsideServiceWindow = $Deployment.RebootOutsideOfServiceWindows
                SendWakeupPacket           = $Deployment.WoLEnabled
                TimeBaseOn                 = if ($Deployment.UseGMTTimes) { 'UTC' } else { 'LocalTime' }
                UpdateSupersedence         = $Deployment.UpdateSupersedence
                UserNotification           = if (($Deployment.NotifyUser -eq $true) -and ($Deployment.UserUIExperience -eq $true)) {
                                                    'DisplayAll'
                                                } elseif (($Deployment.NotifyUser -eq $false) -and ($Deployment.UserUIExperience -eq $true)) {
                                                    'DisplaySoftwareCenterOnly'
                                                } elseif (($Deployment.NotifyUser -eq $false) -and ($Deployment.UserUIExperience -eq $false)) {
                                                    'HideAll'
                                                }
            }

            switch ($Deployment.OfferTypeID) {
                0 { $NewCMApplicationDeployment.Add('DeployPurpose', 'Required') }
                2 { $NewCMApplicationDeployment.Add('DeployPurpose', 'Available')}
                3 { $NewCMApplicationDeployment.Add('Simulation', $true) }
                Default { Write-Error ('[CMBuilder-Deploy.Copy] Failed to determine DeployPurpose. Got OfferTypeID of {0}' -f $Deployment.OfferTypeID) }
            }

            if ($NewCMApplicationDeployment.DeployPurpose -eq 'Required') {
                $NewCMApplicationDeployment.Add('AvailableDateTime', (Get-Date -Hour 8 -Minute 0 -Second 0).AddDays((8 - ((Get-Date).DayOfWeek.value__)) % 7))
                $NewCMApplicationDeployment.Add('DeadlineDateTime', (Get-Date -Hour 8 -Minute 0 -Second 0).AddDays(((8 - ((Get-Date).DayOfWeek.value__)) % 7) + 7))
            }

            <#
                OfferFlags are stored as an UINT32 in the SMS_ApplicationAssignment WMI Class. This value is incremented by the value of each OfferFlag turned on.
                By enumerating over the available flags we find by name which are turned on.
            #>

            $OfferFlags = [enum]::GetValues([Microsoft.ConfigurationManagement.ManagementProvider.OfferFlags]) | Where-Object { $_.value__ -band $Deployment.OfferFlags }

            Write-Output ('[CMBuilder-Deploy.Copy] The following OfferFlags were enumerated: {0}' -f ($OfferFlags -join ' '))

            switch ($OfferFlags) {
                'Predeploy' { $NewCMApplicationDeployment.Add('PreDeploy', $true) }
                #'OnDemand' { }
                'EnableProcessTermination' { $NewCMApplicationDeployment.Add('AutoCloseExecutable', $true) }
                'AllowUsersToRepairApp' { $NewCMApplicationDeployment.Add('AllowRepairApp', $true) }
                #'RelativeSchedule' { }
                'HighImpactDeployment' { $NewCMApplicationDeployment.Add('ReplaceToastNotificationWithDialog', $true) }
                #'ImplicitUninstall' { }
                Default { }
            }

            Write-Host ('[CMBuilder-Deploy.Copy] New-CMApplicationDeployment: {0}' -f ($NewCMApplicationDeployment | Out-String))
            New-CMApplicationDeployment @NewCMApplicationDeployment

            if (Get-Variable -Name 'NewDeployments' -Scope 'Global' -ErrorAction SilentlyContinue) {
                Write-Host '[CMBuilder-Deploy.Copy] NewDeployments variable exists. Adding some info...'
                $NewDeployments.Add([PSCustomObject] @{
                    CollectionName = $Deployment.CollectionName
                    Action         = $NewCMApplicationDeployment.DeployPurpose
                }) | Out-Null
            }

        }
    }
}
#EndRegion '.\private\deploy\Deploy.Copy.ps1' 83
#Region '.\private\DeploymentType.ps1' 0
<#
 
    .SYNOPSIS
 
        Create an SCCM Application
 
    .DESCRIPTION
 
        With a provided (required) configuration file, we can automatically create applications in SCCM, superseeding previous versions
 
#>


function DeploymentType {
    [CmdletBinding()]

    param (
        [Parameter(Mandatory = $true, Position = 0)]
        $CMApplication
    )

    Process {
        foreach ($DeploymentType in $Configuration.DeploymentTypes) {
            Write-Host '[CMBuilder-Deployment Type] Creating Deployment Type'

            $AddCMScriptDeploymentType = @{
                DeploymentTypeName       = $DeploymentType.Name
                InputObject              = $CMApplication
                Force                    = $true
                InstallCommand           = $DeploymentType.InstallCommand
                InstallationBehaviorType = if ($DeploymentType.InstallationBehaviorType) {
                    $DeploymentType.InstallationBehaviorType
                } else {
                    'InstallForSystem'
                }
                LogonRequirementType     = if ($DeploymentType.LogonRequirementType) {
                    $DeploymentType.LogonRequirementType
                } else {
                    'WhetherOrNotUserLoggedOn'
                }
                UserInteractionMode      = if ($DeploymentType.UserInteractionMode) {
                    $DeploymentType.UserInteractionMode
                } else {
                    'Hidden'
                }
            }

            if ($DeploymentType.Detection) {
                $AddCMScriptDeploymentType.Add('AddDetectionClause', (Detection $DeploymentType.Detection))
            } elseif ($DeploymentType.DetectionScript) {
                $AddCMScriptDeploymentType.Add('ScriptFile', $DeploymentType.DetectionScript)

                switch -regex ($DeploymentType.DetectionScript) {
                    '(?i)(\.ps1$)' { $AddCMScriptDeploymentType.Add('ScriptLanguage', 'PowerShell'); break}
                    '(?i)(\.vbs$)' { $AddCMScriptDeploymentType.Add('ScriptLanguage', 'VBScript'); break}
                     '(?i)(\.js$)' { $AddCMScriptDeploymentType.Add('ScriptLanguage', 'JavaScript'); break}
                           default { Throw 'Unable to match detection script file to language. '}
                }

            } else {
                Throw 'DeploymentType does not contain Detection or ScriptDetection! Failed to create the DeploymentType!'
            }

            if ($DeploymentType.ContentLocation) {
                $AddCMScriptDeploymentType.Add('ContentLocation', $DeploymentType.ContentLocation)
            } else {   
                $AddCMScriptDeploymentType.Add('ContentLocation', (Join-Path $ContentShare ('{0}\{1}\{2}' -f $Configuration.Publisher, $Configuration.Name, $Script:Version)))
            }

            if ($DeploymentType.CacheContent) {
                $AddCMScriptDeploymentType.Add('CacheContent', $DeploymentType.CacheContent)
            }

            if ($DeploymentType.ContentFallback) {
                $AddCMScriptDeploymentType.Add('ContentFallback', $DeploymentType.ContentFallback)
            }

            if ($DeploymentType.DetectionClauseConnector) {
                $AddCMScriptDeploymentType.Add('DetectionClauseConnector', $DeploymentType.DetectionClauseConnector)
            }

            if ($DeploymentType.EnableBranchCache) {
                $AddCMScriptDeploymentType.Add('EnableBranchCache', $DeploymentType.EnableBranchCache)
            }

            if ($DeploymentType.EstimatedRuntimeMins) {
                $AddCMScriptDeploymentType.Add('EstimatedRuntimeMins', $DeploymentType.EstimatedRuntimeMins)
            }

            if ($DeploymentType.Force32Bit) {
                $AddCMScriptDeploymentType.Add('Force32Bit', $DeploymentType.Force32Bit)
            }

            if ($DeploymentType.GroupDetectionClauses) {
                $AddCMScriptDeploymentType.Add('GroupDetectionClauses', $DeploymentType.GroupDetectionClauses)
            }

            if ($DeploymentType.InstallWorkingDirectory) {
                $AddCMScriptDeploymentType.Add('InstallWorkingDirectory', $DeploymentType.InstallWorkingDirectory)
            }

            if ($DeploymentType.LogonRequirementType) {
                $AddCMScriptDeploymentType.Add('LogonRequirementType', $DeploymentType.LogonRequirementType)
            }

            if ($DeploymentType.MaximumRuntimeMins) {
                $AddCMScriptDeploymentType.Add('MaximumRuntimeMins', $DeploymentType.MaximumRuntimeMins)
            }

            if ($DeploymentType.RebootBehavior) {
                $AddCMScriptDeploymentType.Add('RebootBehavior', $DeploymentType.RebootBehavior)
            }

            if ($DeploymentType.RepairCommand) {
                $AddCMScriptDeploymentType.Add('RepairCommand', $DeploymentType.RepairCommand)
            }

            if ($DeploymentType.RepairWorkingDirectory) {
                $AddCMScriptDeploymentType.Add('RepairWorkingDirectory', $DeploymentType.RepairWorkingDirectory)
            }

            if ($DeploymentType.RequireUserInteraction) {
                $AddCMScriptDeploymentType.Add('RequireUserInteraction', $DeploymentType.RequireUserInteraction)
            }

            if ($DeploymentType.SlowNetworkDeploymentMode) {
                $AddCMScriptDeploymentType.Add('SlowNetworkDeploymentMode', $DeploymentType.SlowNetworkDeploymentMode)
            }

            if ($DeploymentType.SourceUpdateProductCode) {
                $AddCMScriptDeploymentType.Add('SourceUpdateProductCode', $DeploymentType.SourceUpdateProductCode)
            }

            if ($DeploymentType.UninstallContentLocation) {
                $AddCMScriptDeploymentType.Add('UninstallContentLocation', $DeploymentType.UninstallContentLocation)
            }

            if ($DeploymentType.UninstallOption) {
                $AddCMScriptDeploymentType.Add('UninstallOption', $DeploymentType.UninstallOption)
            }

            if ($DeploymentType.UninstallWorkingDirectory) {
                $AddCMScriptDeploymentType.Add('UninstallWorkingDirectory', $DeploymentType.UninstallWorkingDirectory)
            }

            if ($DeploymentType.AddRequirement) {
                $AddCMScriptDeploymentType.Add('AddRequirement', (Requirement $DeploymentType.AddRequirement))
            }

            if ($DeploymentType.RemoveLanguage) {
                $AddCMScriptDeploymentType.Add('RemoveLanguage', $DeploymentType.RemoveLanguage)
            }

            if ($DeploymentType.RemoveRequirement) {
                $AddCMScriptDeploymentType.Add('RemoveRequirement', $DeploymentType.RemoveRequirement)
            }

            if ($DeploymentType.AddLanguage) {
                $AddCMScriptDeploymentType.Add('AddLanguage', $DeploymentType.AddLanguage)
            }

            if ($DeploymentType.Comment) {
                $AddCMScriptDeploymentType.Add('Comment', $DeploymentType.Comment)
            }

            if ($DeploymentType.UninstallCommand) {
                $AddCMScriptDeploymentType.Add('UninstallCommand', $DeploymentType.UninstallCommand)
            }

            Write-Host ('[CMBuilder-Deployment Type] Add-CMScriptDeploymentType: {0}' -f ($AddCMScriptDeploymentType | Out-String))
            $NewDeploymentType = Add-CMScriptDeploymentType @AddCMScriptDeploymentType

            if ($DeploymentType.InstallBehavior) {
                InstallBehavior -CMApplication $CMApplication -DeploymentType $NewDeploymentType -InstallBehaviorRules $DeploymentType.InstallBehavior
            }
        }
    }
}
#EndRegion '.\private\DeploymentType.ps1' 178
#Region '.\private\Detection.ps1' 0
function Detection {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        $Detection
    )

    Process {
        foreach ($Detect in $Detection) {
            & "Detection.$($Detect.Type)" -Detection $Detect
        }
    }
}
#EndRegion '.\private\Detection.ps1' 15
#Region '.\private\detection\Detection.Directory.ps1' 0
function Detection.Directory {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Detection
    )

    Process {
        $NewCMDetectionClauseDirectory = @{
            DirectoryName = $Detection.DirectoryName
            Path          = $Detection.Path
        }

        if ($Detection.PropertyType) {
            Write-Host '[CMBuilder-Detection.Directory] Detecting specific value'
            if ($Detection.PropertyType) {
                $NewCMDetectionClauseDirectory.Add('PropertyType', $Detection.PropertyType)
            } else {
                $NewCMDetectionClauseDirectory.Add('PropertyType', 'DateCreated')
            }

            $NewCMDetectionClauseDirectory.Add('ExpectedValue', $Detection.ExpectedValue)
        } else {
            Write-Host '[CMBuilder-Detection.Directory] Detecting Existence'
            $NewCMDetectionClauseDirectory.Add('Existence', $true)
        }

        Write-Host ('[CMBuilder-Detection.Directory] New-CMDetectionClauseDirectory: {0}' -f ($NewCMDetectionClauseDirectory | Out-String))
        $CMDetectionClauseDirectory = New-CMDetectionClauseDirectory @NewCMDetectionClauseDirectory

        Write-Output $CMDetectionClauseDirectory
    }
}
#EndRegion '.\private\detection\Detection.Directory.ps1' 36
#Region '.\private\detection\Detection.File.ps1' 0
function Detection.File {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Detection
    )

    Process {
        $NewCMDetectionClauseFile = @{
            FileName = $Detection.FileName
            Path     = $Detection.Path
        }

        if ($Detection.PropertyType) {
            Write-Host '[CMBuilder-Detection.File] Detecting specific value'

            $NewCMDetectionClauseFile.Add('PropertyType', $Detection.PropertyType)
            $NewCMDetectionClauseFile.Add('ExpectedValue', $Detection.ExpectedValue)
            $NewCMDetectionClauseFile.Add('Value', $true)

            if ($Detection.ExpressionOperator) {
                $NewCMDetectionClauseFile.Add('ExpressionOperator', $Detection.ExpressionOperator)
            } else {
                $NewCMDetectionClauseFile.Add('ExpressionOperator', 'IsEquals')
            }
        } else {
            Write-Host '[CMBuilder-Detection.File] Detecting Existence'
            $NewCMDetectionClauseFile.Add('Existence', $true)
        }

        if ($Detection.Is32BitApplicationOn64BitSystem) {
            $NewCMDetectionClauseFile.Add('Is64Bit', $false)
        } else {
            $NewCMDetectionClauseFile.Add('Is64Bit', $true)
        }

        Write-Host ('[CMBuilder-Detection.File] New-CMDetectionClauseFile: {0}' -f ($NewCMDetectionClauseFile | Out-String))
        $CMDetectionClauseFile = New-CMDetectionClauseFile @NewCMDetectionClauseFile

        Write-Output $CMDetectionClauseFile
    }
}
#EndRegion '.\private\detection\Detection.File.ps1' 45
#Region '.\private\detection\Detection.RegistryKey.ps1' 0
function Detection.RegistryKey {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Detection
    )

    Process {
        $NewCMDetectionClauseRegistryKey = @{
            Hive      = $Detection.Hive
            KeyName   = $Detection.KeyName
            Existence = $true
        }

        if ($Detection.Is32BitApplicationOn64BitSystem) {
            $NewCMDetectionClauseRegistryKey.Add('Is64Bit', $false)
        } else {
            $NewCMDetectionClauseRegistryKey.Add('Is64Bit', $true)
        }

        Write-Host ('[CMBuilder-Detection.RegistryKey] New-CMDetectionClauseRegistryKey: {0}' -f ($NewCMDetectionClauseRegistryKey | Out-String))
        $CMDetectionClauseRegistryKey = New-CMDetectionClauseRegistryKey @NewCMDetectionClauseRegistryKey

        Write-Output $CMDetectionClauseRegistryKey
    }
}
#EndRegion '.\private\detection\Detection.RegistryKey.ps1' 29
#Region '.\private\detection\Detection.RegistryKeyValue.ps1' 0
function Detection.RegistryKeyValue {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Detection
    )

    Process {
        $NewCMDetectionClauseRegistryKeyValue = @{
            Hive         = $Detection.Hive
            KeyName      = $Detection.KeyName
            ValueName    = $Detection.ValueName
            PropertyType = $Detection.PropertyType
        }

        if ($Detection.ExpectedValue) {
            Write-Host '[CMBuilder-Detection.RegistryKeyValue] Detecting specific value'
            $NewCMDetectionClauseRegistryKeyValue.Add('Value', $true)
            $NewCMDetectionClauseRegistryKeyValue.Add('ExpectedValue', $Detection.ExpectedValue)
            if ($Detection.ExpressionOperator) {
                $NewCMDetectionClauseRegistryKeyValue.Add('ExpressionOperator', $Detection.ExpressionOperator)
            } else {
                $NewCMDetectionClauseRegistryKeyValue.Add('ExpressionOperator', 'IsEquals')
            }
        } else {
            Write-Host '[CMBuilder-Detection.RegistryKeyValue] Detecting Existence'
            $NewCMDetectionClauseRegistryKeyValue.Add('Existence', $Detection.Existence)
        }

        if ($Detection.Is32BitApplicationOn64BitSystem) {
            $NewCMDetectionClauseRegistryKeyValue.Add('Is64Bit', $false)
        } else {
            $NewCMDetectionClauseRegistryKeyValue.Add('Is64Bit', $true)
        }

        Write-Host ('[CMBuilder-Detection.RegistryKeyValue] New-CMDetectionClauseRegistryKeyValue: {0}' -f ($NewCMDetectionClauseRegistryKeyValue | Out-String))
        $CMDetectionClauseRegistryKeyValue = New-CMDetectionClauseRegistryKeyValue @NewCMDetectionClauseRegistryKeyValue

        Write-Output $CMDetectionClauseRegistryKeyValue
    }
}
#EndRegion '.\private\detection\Detection.RegistryKeyValue.ps1' 44
#Region '.\private\detection\Detection.WindowsInstaller.ps1' 0
function Detection.WindowsInstaller {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Detection
    )

    Process {
        $NewCMDetectionClauseWindowsInstaller = @{}

        if ($ProductCode) {
            $NewCMDetectionClauseWindowsInstaller.Add('ProductCode', $ProductCode)
        } elseif ($Detection.ProductCode) {
            $NewCMDetectionClauseWindowsInstaller.Add('ProductCode', $Detection.ProductCode)
        } else {
            Throw '[CMBuilder-Detection.WindowsInstaller] Failed to find the product code'
        }

        if ($Detection.PropertyType) {
            Write-Host '[CMBuilder-Detection.WindowsInstaller] Detecting specific value'

            $NewCMDetectionClauseWindowsInstaller.Add('Value', $true)
            $NewCMDetectionClauseWindowsInstaller.Add('ExpectedValue', $Detection.ExpectedValue)

            if ($Detection.PropertyType) {
                $NewCMDetectionClauseWindowsInstaller.Add('PropertyType', $Detection.PropertyType)
            } else {
                $NewCMDetectionClauseWindowsInstaller.Add('PropertyType', 'ProductVersion') # Doesn't really matter here, right now it only accepts this single value.
            }

            if ($Detection.ExpressionOperator) {
                $NewCMDetectionClauseWindowsInstaller.Add('ExpressionOperator', $Detection.ExpressionOperator)
            } else {
                $NewCMDetectionClauseWindowsInstaller.Add('ExpressionOperator', 'IsEquals')
            }
        } else {
            Write-Host '[CMBuilder-Detection.WindowsInstaller] Detecting Existence'

            $NewCMDetectionClauseWindowsInstaller.Add('Existence', $Detection.Existence)
        }

        Write-Host ('[CMBuilder-Detection.WindowsInstaller] New-CMDetectionClauseWindowsInstaller: {0}' -f ($NewCMDetectionClauseWindowsInstaller | Out-String))
        $CMDetectionClauseWindowsInstaller = New-CMDetectionClauseWindowsInstaller @NewCMDetectionClauseWindowsInstaller

        Write-Output $CMDetectionClauseWindowsInstaller
    }
}
#EndRegion '.\private\detection\Detection.WindowsInstaller.ps1' 50
#Region '.\private\Distribute.ps1' 0
function Distribute {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [Microsoft.ConfigurationManagement.ManagementProvider.WqlQueryEngine.WqlResultObject]
        $CMApplication
    )

    foreach ($DistributionPoint in $DistributionPoints) {
        $StartCMContentDistribution = @{
            InputObject           = $CMApplication
            DistributionPointName = $DistributionPoint
        }

        Write-Host ('[CMBuilder-Distribute] Start-CMContentDistribution: {0}' -f ($StartCMContentDistribution | Out-String))
        Start-CMContentDistribution @StartCMContentDistribution
    }

    foreach ($DistributionPointGroup in $DistributionPointGroups) {
        $StartCMContentDistribution = @{
            InputObject                = $CMApplication
            DistributionPointGroupName = $DistributionPointGroup
        }

        Write-Host ('[CMBuilder-Distribute] Start-CMContentDistribution: {0}' -f ($StartCMContentDistribution | Out-String))

        Start-CMContentDistribution @StartCMContentDistribution
    }
}
#EndRegion '.\private\Distribute.ps1' 31
#Region '.\private\Folder.ps1' 0
function Folder {
    [CmdletBinding()]
    param (
        [Parameter (Mandatory = $true, Position = 0)]
        [System.String]
        [ValidateSet('Application', 'SoftwareMetering', 'Package')]
        $FolderType,

        [Parameter(Mandatory = $false, Position = 1)]
        [System.String]
        $RootOverride
    )

    Process {
        Write-Progress 'Ensuring Folder Exists'

        if ($RootOverride) {
            $FolderPath = '{0}:\{1}\{2}\{3}\{4}' -f $SiteCode, $FolderType, $RootOverride, $Configuration.Publisher, $Configuration.Name
        } else {
            $FolderPath = '{0}:\{1}\{2}\{3}' -f $SiteCode, $FolderType, $Configuration.Publisher, $Configuration.Name
        }

        Write-Host ('[CMBuilder-Folder] Expected folder path: {0}' -f $FolderPath)

        if (-not (Test-Path $FolderPath)) {
            $WorkingVar = $FolderPath # FolderPath needs to be returned complete

            # Loop until a valid parent is found
            $MissingParents = do {
                Write-Output $WorkingVar
                $WorkingVar = Split-Path $WorkingVar -Parent
            } While (-not (Test-Path $WorkingVar))

            [array]::Reverse($MissingParents)

            # Create the missing folders
            foreach ($Parent in $MissingParents) {
                Write-Host ('[CMPackaer-Folder] Creating Missing Folders: {0}' -f $Parent)
                New-Item -Path $Parent -ItemType Directory -Force
            }
        }

        Write-Output $FolderPath
    }
}
#EndRegion '.\private\Folder.ps1' 46
#Region '.\private\InstallBehavior.ps1' 0
function InstallBehavior {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        $CMApplication,

        [Parameter(Mandatory = $true, Position = 1)]
        $DeploymentType,

        [Parameter(Mandatory = $true, Position = 2)]
        $InstallBehaviorRules
    )

    process {
        Write-Host '[CMBuilder-Install Behavior] Adding Install Behavior'

        $Application = Get-CMApplication -Name $CMApplication.LocalizedDisplayName | ConvertTo-CMApplication
        $DeploymentTypeIndex = $Application.DeploymentTypes.Title.IndexOf($DeploymentType.DeploymentTypeName)

        foreach ($Process in $InstallBehaviorRules) {
            Write-Host ('[CMBuilder-Install Behavior] Adding "{0}" to DeploymentType "{1}"' -f $Process.DisplayName, $DeploymentType.DeploymentTypeName)

            # Create the process detection
            $ProcessInfo = [Microsoft.ConfigurationManagement.ApplicationManagement.ProcessInformation]::new()
            $ProcessInfo.DisplayInfo.Add(@{'DisplayName' = $Process.DisplayName; Language = $NULL })
            $ProcessInfo.Name = $Process.Executable

            # Add the process to the DeploymentType
            $Application.DeploymentTypes[$DeploymentTypeIndex].Installer.InstallProcessDetection.ProcessList.Add($ProcessInfo)
        }

        Write-Host '[CMBuilder-Install Behavior] Commiting Changes'
        $Application = $Application | ConvertFrom-CMApplication
        $Application.Put()
    }
}
#EndRegion '.\private\InstallBehavior.ps1' 37
#Region '.\private\Metering.ps1' 0
function Metering {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [System.String]
        $Folder
    )

    process {
        foreach ($Meter in $Configuration.SoftwareMetering) {
            if ($Rule = Get-CMSoftwareMeteringRule -ProductName $Meter.ProductName) {
                Write-Host ('[Metering] Software Metering rule exists: {0}' -f ($Rule.ProductName -join ', '))
            } else {
                Write-Host '[Metering] Software Metering rule does not exist yet'
                $NewCMSoftwareMeteringRule = @{
                    ProductName      = $Meter.ProductName
                    SiteCode         = $SiteCode
                    FileName         = $Meter.FileName
                    OriginalFileName = $Meter.FileName
                    FileVersion      = if ($Meter.FileVersion) { $Meter.FileVersion } else { '*' }
                }

                Write-Host ('[Metering] New-CMSoftwareMeteringRule: {0}' -f ($NewCMSoftwareMeteringRule | Out-String))

                $Rule = New-CMSoftwareMeteringRule @NewCMSoftwareMeteringRule
                Move-CMObject -FolderPath $Folder -InputObject $Rule
            }
        }
    }
}
#EndRegion '.\private\Metering.ps1' 31
#Region '.\private\PkgSrc.ps1' 0
function PkgSrc {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [IO.DirectoryInfo]
        $SourceDirectory,

        # Root folder where content will be stored.
        [Parameter(Mandatory = $true)]
        [IO.DirectoryInfo]
        $ContentRoot
    )

    Write-Host ('[Move-ToPkgSrc] Current Location: {0}' -f (Get-Location))
    Write-Host ('[Move-ToPkgSrc] Content Root: {0}' -f $ContentRoot)
    $ContentSubDir = '{0}\{1}\{2}' -f $Configuration.Publisher, $Configuration.Name, $Version

    Write-Host ('[Move-ToPkgSrc] Subdir: {0}' -f $ContentSubDir)
    [IO.DirectoryInfo] $ContentDirectory = Join-Path $ContentRoot $ContentSubDir
    
    Write-Host ('[Move-ToPkgSrc] PkgSrc directory: [{0}]' -f $ContentDirectory.FullName)

    if (-not ($ContentDirectory.Exists)) {
        Write-Host '[Move-ToPkgSrc] PkgSrc directory does not exist. Creating...'
        $ContentDirectory = New-Item -Path $ContentDirectory.FullName -ItemType Directory -Force -Verbose
    }

    if ($Configuration.WimDeployment) {
        try {
            $SourceDirectory = Wim -Directory $SourceDirectory
        } catch {
            Write-Warning "Failed to create the Application WIM: $_"
            continue Project
        }
    } else {
        Write-Host '[Move-ToPkgSrc] Checking for a template directory'
        if (($Configuration.TemplateDirectory) -and (Test-Path $Configuration.TemplateDirectory)) {
            Write-Host '[Move-ToPkgSrc] Template directory exists! Copying...'
            Copy-Item -Path "$($Configuration.TemplateDirectory)\*" -Destination $ContentDirectory.FullName -Recurse -Force -PassThru | Out-Null
        }
    }

    Write-Host ('[Move-ToPkgSrc] Copying Source Directory: {0}' -f $SourceDirectory.FullName)
    Copy-Item -Path "$($SourceDirectory.FullName)\*" -Destination $ContentDirectory.FullName -Recurse -Force -PassThru | Out-Null

    if ($Configuration.WimDeployment) {
        Write-Host '[Move-ToPkgSrc] Cleaning up Source Directory after copy...'
        Remove-Item $SourceDirectory.FullName -Recurse -Force
    }
}
#EndRegion '.\private\PkgSrc.ps1' 51
#Region '.\private\Requirement.ps1' 0
function Requirement {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        $AddRequirement
    )

    Process {
        foreach ($Requirement in $AddRequirement) {
            & "Requirement.$($Requirement.Type)" -Requirement $Requirement
        }
    }
}
#EndRegion '.\private\Requirement.ps1' 15
#Region '.\private\requirement\Requirement.ActiveDirectorySiteValue.ps1' 0
function Requirement.ActiveDirectorySiteValue {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $NewCMRequirementRuleActiveDirectorySiteValue = @{
            InputObject  = (Get-CMGlobalCondition -Name 'Active Directory site')
            Site         = $Requirement.Site
            RuleOperator = $Requirement.RuleOperator
        }

        if ($Requirement.DisableWildcardHandling) {
            $NewCMRequirementRuleActiveDirectorySiteValue.Add('DisableWildcardHandling', $true)
        }

        if ($Requirement.ForceWildcardHandling) {
            $NewCMRequirementRuleActiveDirectorySiteValue.Add('ForceWildcardHandling', $true)
        }

        Write-Host ('[CMBuilder-Requirement.ActiveDirectorySiteValue] New-CMRequirementRuleActiveDirectorySiteValue: {0}' -f ($NewCMRequirementRuleActiveDirectorySiteValue | Out-String))
        $CMRequirementRuleActiveDirectorySiteValue = New-CMRequirementRuleActiveDirectorySiteValue @NewCMRequirementRuleActiveDirectorySiteValue

        Write-Output $CMRequirementRuleActiveDirectorySiteValue
    }
}
#EndRegion '.\private\requirement\Requirement.ActiveDirectorySiteValue.ps1' 31
#Region '.\private\requirement\Requirement.BooleanValue.ps1' 0
function Requirement.BooleanValue {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $NewCMRequirementRuleBooleanValue = @{
            InputObject = (Get-CMGlobalCondition -Name $Requirement.GlobalCondition)
            Value       = $Requirement.Value
        }

        Write-Host ('[CMBuilder-Requirement.BooleanValue] New-CMRequirementRuleBooleanValue: {0}' -f ($NewCMRequirementRuleBooleanValue | Out-String))
        $CMRequirementRuleBooleanValue = New-CMRequirementRuleBooleanValue @NewCMRequirementRuleBooleanValue

        Write-Output $CMRequirementRuleBooleanValue
    }

}
#EndRegion '.\private\requirement\Requirement.BooleanValue.ps1' 23
#Region '.\private\requirement\Requirement.CMSiteValue.ps1' 0
function Requirement.CMSiteValue {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $NewCMRequirementRuleCMSiteValue = @{
            InputObject  = (Get-CMGlobalCondition -Name 'Configuration Manager site')
            Site         = $Requirement.Site
            RuleOperator = $Requirement.RuleOperator
        }

        if ($Requirement.DisableWildcardHandling) {
            $NewCMRequirementRuleCMSiteValue.Add('DisableWildcardHandling', $true)
        }

        if ($Requirement.ForceWildcardHandling) {
            $NewCMRequirementRuleCMSiteValue.Add('ForceWildcardHandling', $true)
        }

        Write-Host ('[CMBuilder-Requirement.CMSiteValue] New-CMRequirementRuleCMSiteValue: {0}' -f ($NewCMRequirementRuleCMSiteValue | Out-String))
        $CMRequirementRuleCMSiteValue = New-CMRequirementRuleCMSiteValue @NewCMRequirementRuleCMSiteValue

        Write-Output $CMRequirementRuleCMSiteValue
    }
}
#EndRegion '.\private\requirement\Requirement.CMSiteValue.ps1' 31
#Region '.\private\requirement\Requirement.CommonValue.ps1' 0
function Requirement.CommonValue {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $NewCMRequirementRuleCommonValue = @{
            InputObject  = (Get-CMGlobalCondition -Name $Requirement.GlobalCondition)
            Value1       = $Requirement.Value1
            RuleOperator = $Requirement.RuleOperator
        }

        if ($Requirement.DisableWildcardHandling) {
            $NewCMRequirementRuleCommonValue.Add('DisableWildcardHandling', $true)
        }

        if ($Requirement.ForceWildcardHandling) {
            $NewCMRequirementRuleCommonValue.Add('ForceWildcardHandling', $true)
        }

        if ($Requirement.PropertyForAssembly) {
            $NewCMRequirementRuleCommonValue.Add('PropertyForAssembly', $Requirement.PropertyForAssembly)
        }

        if ($Requirement.PropertyForFileFolder) {
            $NewCMRequirementRuleCommonValue.Add('PropertyForFileFolder', $Requirement.PropertyForFileFolder)
        }

        if ($Requirement.Value2) {
            $NewCMRequirementRuleCommonValue.Add('Value2', $Requirement.Value2)
        }

        Write-Host ('[CMBuilder-Requirement.CommonValue] New-CMRequirementRuleCommonValue: {0}' -f ($NewCMRequirementRuleCommonValue | Out-String))
        $CMRequirementRuleCommonValue = New-CMRequirementRuleCommonValue @NewCMRequirementRuleCommonValue

        Write-Output $CMRequirementRuleCommonValue
    }
}
#EndRegion '.\private\requirement\Requirement.CommonValue.ps1' 43
#Region '.\private\requirement\Requirement.DeviceOwnershipValue.ps1' 0
function Requirement.DeviceOwnershipValue {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $NewCMRequirementRuleDeviceOwnershipValue = @{
            InputObject     = (Get-CMGlobalCondition -Name 'Ownership' | Where-Object CI_UniqueID -Match "Device_Ownership$($Requirement.DeviceType)`$")
            OwnershipOption = $Requirement.OwnershipOption
            RuleOperator    = $Requirement.RuleOperator
        }

        if ($Requirement.DisableWildcardHandling) {
            $NewCMRequirementRuleDeviceOwnershipValue.Add('DisableWildcardHandling', $true)
        }

        if ($Requirement.ForceWildcardHandling) {
            $NewCMRequirementRuleDeviceOwnershipValue.Add('ForceWildcardHandling', $true)
        }

        Write-Host ('[CMBuilder-Requirement.DeviceOwnershipValue] New-CMRequirementRuleDeviceOwnershipValue: {0}' -f ($NewCMRequirementRuleDeviceOwnershipValue | Out-String))
        $CMRequirementRuleDeviceOwnershipValue = New-CMRequirementRuleDeviceOwnershipValue @NewCMRequirementRuleDeviceOwnershipValue

        Write-Output $CMRequirementRuleDeviceOwnershipValue
    }
}
#EndRegion '.\private\requirement\Requirement.DeviceOwnershipValue.ps1' 31
#Region '.\private\requirement\Requirement.Existential.ps1' 0
function Requirement.Existential {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $NewCMRequirementRuleExistential = @{
            InputObject = (Get-CMGlobalCondition -Name $Requirement.GlobalCondition)
            Existential = $Requirement.Existential
        }

        if ($Requirement.DisableWildcardHandling) {
            $NewCMRequirementRuleExistential.Add('DisableWildcardHandling', $true)
        }

        if ($Requirement.ForceWildcardHandling) {
            $NewCMRequirementRuleExistential.Add('ForceWildcardHandling', $true)
        }

        Write-Host ('[CMBuilder-Requirement.Existential] New-CMRequirementRuleExistential: {0}' -f ($NewCMRequirementRuleExistential | Out-String))
        $CMRequirementRuleExistential = New-CMRequirementRuleExistential @NewCMRequirementRuleExistential

        Write-Output $CMRequirementRuleExistential
    }
}
#EndRegion '.\private\requirement\Requirement.Existential.ps1' 30
#Region '.\private\requirement\Requirement.Expression.ps1' 0
function Requirement.Expression {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $GlobalCondition = Get-CMGlobalCondition -AsDcmSdkObject -Name $Requirement.Name

        if (-not ($GlobalCondition)) {
            Throw ('[CMBuilder-Requirement.Expression] GlobalCondition {0} not found! This should match the name displayname in the admin console.' -f $Requirement.Name)
        }

        if ($GlobalCondition.Rules) {
            Write-Host ('[CMBuilder-Requirement.Expression] Adding rules from {0}' -f $GlobalCondition.Name)
            Write-Output $GlobalCondition.Rules

        } else {
            Write-Host ('[CMBuilder-Requirement.Expression] Rules not found in "{0}". Verify this is an expression!' -f $GlobalCondition.Name)
        }
    }
}
#EndRegion '.\private\requirement\Requirement.Expression.ps1' 26
#Region '.\private\requirement\Requirement.FileAttributeValue.ps1' 0
function Requirement.FileAttributeValue {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $NewCMRequirementRuleFileAttributeValue = @{
            InputObject = (Get-CMGlobalCondition -Name $Requirement.GlobalCondition)
        }

        if ($Requirement.DisableWildcardHandling) {
            $NewCMRequirementRuleFileAttributeValue.Add('DisableWildcardHandling', $true)
        }

        if ($Requirement.FileArchive) {
            $NewCMRequirementRuleFileAttributeValue.Add('FileArchive', $true)
        }

        if ($Requirement.FileCompressed) {
            $NewCMRequirementRuleFileAttributeValue.Add('FileCompressed', $true)
        }

        if ($Requirement.FileEncrypted) {
            $NewCMRequirementRuleFileAttributeValue.Add('FileEncrypted', $true)
        }

        if ($Requirement.FileHidden) {
            $NewCMRequirementRuleFileAttributeValue.Add('FileHidden', $true)
        }

        if ($Requirement.FileReadOnly) {
            $NewCMRequirementRuleFileAttributeValue.Add('FileReadOnly', $true)
        }

        if ($Requirement.FileSystem) {
            $NewCMRequirementRuleFileAttributeValue.Add('FileSystem', $true)
        }

        if ($Requirement.ForceWildcardHandling) {
            $NewCMRequirementRuleFileAttributeValue.Add('ForceWildcardHandling', $true)
        }

        Write-Host ('[CMBuilder-Requirement.FileAttributeValue] New-CMRequirementRuleFileAttributeValue: {0}' -f ($NewCMRequirementRuleFileAttributeValue | Out-String))
        $CMRequirementRuleFileAttributeValue = New-CMRequirementRuleFileAttributeValue @NewCMRequirementRuleFileAttributeValue

        Write-Output $CMRequirementRuleFileAttributeValue
    }
}
#EndRegion '.\private\requirement\Requirement.FileAttributeValue.ps1' 53
#Region '.\private\requirement\Requirement.FilePermissionValue.ps1' 0
function Requirement.FilePermissionValue {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $NewCMRequirementRuleFilePermissionValue = @{
            InputObject  = (Get-CMGlobalCondition -Name $Requirement.GlobalCondition)
            ControlEntry = foreach ($Entry in $Requirement.ControlEntry) {
                $NewCMFileSystemAccessControlEntry = @{
                    GroupOrUserName = $Entry.GroupOrUserName
                    AccessOption    = $Entry.AccessOption
                    Permission      = $Entry.Permission
                }
                New-CMFileSystemAccessControlEntry @NewCMFileSystemAccessControlEntry
            }
        }

        if ($Requirement.DisableWildcardHandling) {
            $NewCMRequirementRuleFilePermissionValue.Add('DisableWildcardHandling', $true)
        }

        if ($Requirement.Exclusive) {
            $NewCMRequirementRuleFilePermissionValue.Add('Exclusive', $true)
        }

        if ($Requirement.ForceWildcardHandling) {
            $NewCMRequirementRuleFilePermissionValue.Add('ForceWildcardHandling', $true)
        }

        Write-Host ('[CMBuilder-Requirement.FilePermissionValue] New-CMRequirementRuleFilePermissionValue: {0}' -f ($NewCMRequirementRuleFilePermissionValue | Out-String))
        $CMRequirementRuleFilePermissionValue = New-CMRequirementRuleFilePermissionValue @NewCMRequirementRuleFilePermissionValue

        Write-Output $CMRequirementRuleFilePermissionValue
    }
}
#EndRegion '.\private\requirement\Requirement.FilePermissionValue.ps1' 41
#Region '.\private\requirement\Requirement.FreeDiskSpaceValue.ps1' 0
function Requirement.FreeDiskSpaceValue {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $NewCMRequirementRuleFreeDiskSpaceValue = @{
            InputObject     = (Get-CMGlobalCondition -Name 'Disk Space')
            PartitionOption = $Requirement.PartitionOption
            RuleOperator    = $Requirement.RuleOperator
            Value1          = $Requirement.Value1
        }

        if ($Requirement.DisableWildcardHandling) {
            $NewCMRequirementRuleFreeDiskSpaceValue.Add('DisableWildcardHandling', $true)
        }

        if ($Requirement.DriverLetter) {
            $NewCMRequirementRuleFreeDiskSpaceValue.Add('DriverLetter', $Requirement.DriverLetter)
        }

        if ($Requirement.ForceWildcardHandling) {
            $NewCMRequirementRuleFreeDiskSpaceValue.Add('ForceWildcardHandling', $true)
        }

        if ($Requirement.PartitionOption) {
            $NewCMRequirementRuleFreeDiskSpaceValue.Add('PartitionOption', $Requirement.PartitionOption)
        }

        if ($Requirement.Value2) {
            $NewCMRequirementRuleFreeDiskSpaceValue.Add('Value2', $Requirement.Value2)
        }

        Write-Host ('[CMBuilder-Requirement.FreeDiskSpaceValue] New-CMRequirementRuleFreeDiskSpaceValue: {0}' -f ($NewCMRequirementRuleFreeDiskSpaceValue | Out-String))
        $CMRequirementRuleFreeDiskSpaceValue = New-CMRequirementRuleFreeDiskSpaceValue @NewCMRequirementRuleFreeDiskSpaceValue

        Write-Output $CMRequirementRuleFreeDiskSpaceValue
    }
}
#EndRegion '.\private\requirement\Requirement.FreeDiskSpaceValue.ps1' 44
#Region '.\private\requirement\Requirement.InputTypeValue.ps1' 0
function Requirement.InputTypeValue {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $NewCMRequirementRuleInputTypeValue = @{
            InputObject = (Get-CMGlobalCondition -Name 'Input Type')
            IsTouch     = $Requirement.IsTouch
        }

        Write-Host ('[CMBuilder-Requirement.InputTypeValue] New-CMRequirementRuleInputTypeValue: {0}' -f ($NewCMRequirementRuleInputTypeValue | Out-String))
        $CMRequirementRuleInputTypeValue = New-CMRequirementRuleInputTypeValue @NewCMRequirementRuleInputTypeValue

        Write-Output $CMRequirementRuleInputTypeValue
    }
}
#EndRegion '.\private\requirement\Requirement.InputTypeValue.ps1' 22
#Region '.\private\requirement\Requirement.OperatingSystemLanguageValue.ps1' 0
function Requirement.OperatingSystemLanguageValue {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $NewCMRequirementRuleOperatingSystemLanguageValue = @{
            InputObject  = (Get-CMGlobalCondition -Name 'Operating system language' | Where-Object PlatformType -eq $Requirement.PlatformType)
            RuleOperator = $Requirement.RuleOperator
            Culture      = foreach ($Culture in $Requirement.Culture) {
                [System.Globalization.CultureInfo]::GetCultures([System.Globalization.CultureTypes]::AllCultures) | Where-Object Name -eq $Culture
            }
        }

        if ($Requirement.DisableWildcardHandling) {
            $NewCMRequirementRuleOperatingSystemLanguageValue.Add('DisableWildcardHandling', $true)
        }

        if ($Requirement.ForceWildcardHandling) {
            $NewCMRequirementRuleOperatingSystemLanguageValue.Add('ForceWildcardHandling', $true)
        }

        if ($Requirement.IsMobile) {
            $NewCMRequirementRuleOperatingSystemLanguageValue.Add('IsMobile', $true)
        }

        Write-Host ('[CMBuilder-Requirement.OperatingSystemLanguageValue] New-CMRequirementRuleOperatingSystemLanguageValue: {0}' -f ($NewCMRequirementRuleOperatingSystemLanguageValue | Out-String))
        $CMRequirementRuleOperatingSystemLanguageValue = New-CMRequirementRuleOperatingSystemLanguageValue @NewCMRequirementRuleOperatingSystemLanguageValue

        Write-Output $CMRequirementRuleOperatingSystemLanguageValue
    }
}
#EndRegion '.\private\requirement\Requirement.OperatingSystemLanguageValue.ps1' 37
#Region '.\private\requirement\Requirement.OperatingSystemValue.ps1' 0
function Requirement.OperatingSystemValue {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $NewCMRequirementRuleOperatingSystemValue = @{
            InputObject  = (Get-CMGlobalCondition -Name 'Operating System' | Where-Object PlatformType -EQ $Requirement.PlatformType)
            RuleOperator = $Requirement.RuleOperator
        }

        if ($Requirement.DisableWildcardHandling) {
            $NewCMRequirementRuleOperatingSystemValue.Add('DisableWildcardHandling', $true)
        }

        if ($Requirement.ForceWildcardHandling) {
            $NewCMRequirementRuleOperatingSystemValue.Add('ForceWildcardHandling', $true)
        }

        if ($Requirement.Platform) {
            $NewCMRequirementRuleOperatingSystemValue.Add('Platform', $Requirement.Platform)
        }

        if ($Requirement.PlatformString) {
            $NewCMRequirementRuleOperatingSystemValue.Add('PlatformString', $Requirement.Platform)
        }

        if ($Requirement.SelectFullPlatform) {
            $NewCMRequirementRuleOperatingSystemValue.Add('SelectFullPlatform', $Requirement.Platform)
        }

        Write-Host ('[CMBuilder-Requirement.OperatingSystemValue] New-CMRequirementRuleOperatingSystemValue: {0}' -f ($NewCMRequirementRuleOperatingSystemValue | Out-String))
        $CMRequirementRuleOperatingSystemValue = New-CMRequirementRuleOperatingSystemValue @NewCMRequirementRuleOperatingSystemValue

        Write-Output $CMRequirementRuleOperatingSystemValue
    }
}
#EndRegion '.\private\requirement\Requirement.OperatingSystemValue.ps1' 42
#Region '.\private\requirement\Requirement.OUValue.ps1' 0
function Requirement.OUValue {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $NewCMRequirementRuleOUValue = @{
            InputObject        = (Get-CMGlobalCondition -Name 'Organizational unit (OU)')
            RuleOperator       = $Requirement.RuleOperator
            OrganizationalUnit = foreach ($OrganizationalUnit in $Requirement.OrganizationalUnit) {
                Write-Output ([hashtable] @{
                        OU             = $OrganizationalUnit.OU
                        IsIncludeSubOU = $OrganizationalUnit.IsIncludeSubOU
                    }
                )
            }
        }

        if ($Requirement.DisableWildcardHandling) {
            $NewCMRequirementRuleOUValue.Add('DisableWildcardHandling', $true)
        }

        if ($Requirement.ForceWildcardHandling) {
            $NewCMRequirementRuleOUValue.Add('ForceWildcardHandling', $true)
        }

        Write-Host ('[CMBuilder-Requirement.OUValue] New-CMRequirementRuleOUValue: {0}' -f ($NewCMRequirementRuleOUValue | Out-String))
        $CMRequirementRuleOUValue = New-CMRequirementRuleOUValue @NewCMRequirementRuleOUValue

        Write-Output $CMRequirementRuleOUValue
    }
}
#EndRegion '.\private\requirement\Requirement.OUValue.ps1' 37
#Region '.\private\requirement\Requirement.RegistryKeyPermissionValue.ps1' 0
function Requirement.RegistryKeyPermissionValue {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $NewCMRequirementRuleRegistryKeyPermissionValue = @{
            InputObject  = (Get-CMGlobalCondition -Name $Requirement.GlobalCondition)
            ControlEntry = foreach ($Entry in $Requirement.ControlEntry) {
                $NewCMRegistryAccessControlEntry = @{
                    GroupOrUserName = $Entry.GroupOrUserName
                    AccessOption    = $Entry.AccessOption
                    Permission      = $Entry.Permission
                }
                New-CMRegistryAccessControlEntry @NewCMRegistryAccessControlEntry
            }
        }

        if ($Requirement.DisableWildcardHandling) {
            $NewCMRequirementRuleRegistryKeyPermissionValue.Add('DisableWildcardHandling', $true)
        }

        if ($Requirement.Exclusive) {
            $NewCMRequirementRuleRegistryKeyPermissionValue.Add('Exclusive', $true)
        }

        if ($Requirement.ForceWildcardHandling) {
            $NewCMRequirementRuleRegistryKeyPermissionValue.Add('ForceWildcardHandling', $true)
        }

        Write-Host ('[CMBuilder-Requirement.RegistryKeyPermissionValue] New-NewCMRequirementRuleRegistryKeyPermissionValue: {0}' -f ($NewCMRequirementRuleRegistryKeyPermissionValue | Out-String))
        $CMRequirementRuleRegistryKeyPermissionValue = New-CMRequirementRuleRegistryKeyPermissionValue @NewCMRequirementRuleRegistryKeyPermissionValue

        Write-Output $CMRequirementRuleRegistryKeyPermissionValue
    }
}
#EndRegion '.\private\requirement\Requirement.RegistryKeyPermissionValue.ps1' 41
#Region '.\private\requirement\Requirement.ScreenResolutionValue.ps1' 0
function Requirement.ScreenResolutionValue {
    [CmdletBinding()]

    param (
        [Parameter(Position = 0, Mandatory = $true)]
        [PSCustomObject]
        $Requirement
    )

    Process {
        $NewCMRequirementRuleScreenResolutionValue = @{
            InputObject      = (Get-CMGlobalCondition -Name 'Screen resolution')
            ScreenResolution = $Requirement.ScreenResolution
        }

        if ($Requirement.DisableWildcardHandling) {
            $NewCMRequirementRuleScreenResolutionValue.Add('DisableWildcardHandling', $true)
        }

        if ($Requirement.ForceWildcardHandling) {
            $NewCMRequirementRuleScreenResolutionValue.Add('ForceWildcardHandling', $true)
        }

        Write-Host ('[CMBuilder-Requirement.ScreenResolutionValue] New-CMRequirementRuleScreenResolutionValue: {0}' -f ($NewCMRequirementRuleScreenResolutionValue | Out-String))
        $CMRequirementRuleScreenResolutionValue = New-CMRequirementRuleScreenResolutionValue @NewCMRequirementRuleScreenResolutionValue

        Write-Output $CMRequirementRuleScreenResolutionValue
    }
}
#EndRegion '.\private\requirement\Requirement.ScreenResolutionValue.ps1' 30
#Region '.\private\Supersede.ps1' 0
function Supersede {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        $NewCMApplication,

        [Parameter(Mandatory = $true, Position = 1)]
        $OldCMApplication
    )

    Process {
        if ($Configuration.DeploymentTypes.Count -gt 1) {
            foreach ($DeploymentType in $Configuration.DeploymentTypes) {
                $NewDeploymentType = Get-CMDeploymentType -InputObject $NewCMApplication -DeploymentTypeName $DeploymentType.Name
                $OldDeploymentType = Get-CMDeploymentType -InputObject $OldCMApplication -DeploymentTypeName $DeploymentType.Name

                if ($OldDeploymentType) {
                    $AddCMDeploymentTypeSupersedence = @{
                        SupersedingDeploymentType = $NewDeploymentType
                        SupersededDeploymentType  = $OldDeploymentType
                    }

                    if ($Configuration.SupersedeUninstall) {
                        Write-Host '[CMBuilder-Supersede] Supersede uninstall flag set'
                        $AddCMDeploymentTypeSupersedence.Add('IsUninstall', $true)
                    }

                    Write-Host ('[CMBuilder-Supersede] Add-CMDeploymentTypeSupersedence: {0}' -f ($AddCMDeploymentTypeSupersedence | Out-String))
                    Add-CMDeploymentTypeSupersedence @AddCMDeploymentTypeSupersedence

                } else {
                    Write-Host ('[CMBuilder-Supersede] Unable to find a Old DeploymentType to supersede. Possible new deploymenttype added.')
                }
            }
        } else {
            $NewDeploymentType = Get-CMDeploymentType -InputObject $NewCMApplication
            $OldDeploymentType = Get-CMDeploymentType -InputObject $OldCMApplication

            $AddCMDeploymentTypeSupersedence = @{
                SupersedingDeploymentType = $NewDeploymentType
                SupersededDeploymentType  = $OldDeploymentType
            }

            if ($Configuration.SupersedeUninstall) {
                Write-Host '[CMBuilder-Supersede] Supersede uninstall flag set'
                $AddCMDeploymentTypeSupersedence.Add('IsUninstall', $true)
            }

            Write-Host ('[CMBuilder-Supersede] Add-CMDeploymentTypeSupersedence: {0}' -f ($AddCMDeploymentTypeSupersedence | Out-String))
            Add-CMDeploymentTypeSupersedence @AddCMDeploymentTypeSupersedence
        }
    }
}
#EndRegion '.\private\Supersede.ps1' 54
#Region '.\private\TaskSequence.ps1' 0
function TaskSequence {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        $NewCMApplication,

        [Parameter(Mandatory = $true, Position = 1)]
        $OldCMApplication
    )

    # Get the task sequence package ids that reference the old application
    $ApplicationQuery = ('select PackageID from SMS_TaskSequenceAppReferencesInfo where RefAppModelName like "{0}"' -f $OldCMApplication.ModelName)
    $TaskSequenceIDs = (Invoke-CMWmiQuery -Query $ApplicationQuery).PackageID

    :TaskSequence foreach ($PackageID in $TaskSequenceIDs) {

        $TaskSequence = Get-CMTaskSequence -TaskSequencePackageId $PackageID -Fast

        Write-Host ('[TaskSequence] Processing Task Sequence: {0}' -f $TaskSequence.Name)
        if (-not ($TaskSequence.TsEnabled)) {
            Write-Verbose 'Task sequence is disabled. Skipping...'
            continue TaskSequence
        }

        if ($TaskSequence.Name -in $TaskSequenceSkipList) {
            Write-Host 'Task sequence is in the opt out list. Skipping...'
            continue TaskSequence
        }

        $InstallApplicationSteps = Get-CMTSStepInstallApplication -InputObject $TaskSequence

        foreach ($TaskSequenceStep in $InstallApplicationSteps) {
            $ApplicationList = $TaskSequenceStep.ApplicationName.Split(",")

            if ($OldCMApplication.ModelName -in $ApplicationList) {
                Write-Host '[TaskSequence] Old application is in this task sequence'
                $ModelNames = $ApplicationList.Replace($OldCMApplication.ModelName, $NewCMApplication.ModelName)

                if (-not (Get-CMObjectLockDetails -InputObject $TaskSequence).LockState) {
                    Set-CMTSStepInstallApplication -InputObject $TaskSequence -StepName $TaskSequenceStep.Name -Application ($ModelNames | ForEach-Object { Get-CMApplication -ModelName $_ })
                } else {
                    Write-Host '[TaskSequence] CMObject locked. Skipping.'
                    continue TaskSequence
                }

                if (Get-Variable -Name 'ModifiedTaskSequences' -Scope 'Global' -ErrorAction SilentlyContinue) {
                    Write-Host '[TaskSequence] ModifiedTaskSequences variable exists. Adding some info...'

                    $ModifiedTaskSequences.Add([PSCustomObject] @{
                        TSName = $TaskSequence.Name
                        TSStep = $TaskSequenceStep.Name
                    }) | Out-Null
                }
            }
        }
    }
}
#EndRegion '.\private\TaskSequence.ps1' 58
#Region '.\private\Wim.ps1' 0
function Wim {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [IO.DirectoryInfo]
        $Directory
    )

    Write-Host '[Wim] Creating a new source directory'

    <#
        New Source Directory will be used to house the Invoke-WimAppDeploy.ps1 and the Application.wim files.
        $Directory and all of it's contents, which may include template directory contents, will be deleted at the completion of this function to clean up space on servers
    #>


    [IO.DirectoryInfo] $NewSourceDirectory = Join-Path $env:Temp (New-Guid).Guid

    if (-not ($NewSourceDirectory.Exists)) {
        Write-Host '[Wim] New source directory directory does not exist. Creating...'
        $NewSourceDirectory = New-Item $NewSourceDirectory.FullName -ItemType Directory -Force
    }

    [IO.DirectoryInfo] $ApplicationFiles = Join-Path $Directory.FullName 'Application Files'

    if (-not ($ApplicationFiles.Exists)) {
        Write-Host '[Wim] New Application Files directory directory does not exist. Creating...'
        $ApplicationFiles = New-Item $ApplicationFiles.FullName -ItemType Directory -Force
    }

    if (($Configuration.TemplateDirectory) -and (Test-Path $Configuration.TemplateDirectory)) {
        Copy-Item -Path "$($Configuration.TemplateDirectory)\*" -Destination $ApplicationFiles.FullName -Recurse -Force | Out-Null
    }

    Write-Host '[Wim] Moving files from Source Directory to the Application Files directory...'
    foreach ($File in (Get-ChildItem $Directory.FullName -Exclude 'Invoke-WimAppDeploy.ps1', 'Settings.json', 'WimAppDeploy', 'Source Files', 'Application Files')) {
        Write-Host ('[Wim] Moving item: {0}' -f $File.Name)
        Move-Item -Path $File.FullName -Destination $ApplicationFiles.FullName -Force -ErrorAction Stop
    }

    Write-Host '[Wim] Setting up all of the WimAppDeploy files'

    $GLWimAppDeploy = Join-Path $Directory.FullName 'Invoke-WimAppDeploy.ps1'
    Write-Host ('[Wim] GLWimAppDeploy potential path: {0}' -f $GLWimAppDeploy)
    if (Test-Path $GLWimAppDeploy) {
        Copy-Item $GLWimAppDeploy -Destination $NewSourceDirectory.FullName -Force
    }

    $GLSettingsFile = Join-Path $Directory.FullName 'Settings.json'
    Write-Host ('[Wim] GLSettingsFile potential path: {0}' -f $GLSettingsFile)
    if (Test-Path $GLSettingsFile) {
        Copy-Item $GLSettingsFile -Destination $NewSourceDirectory.FullName -Force
    }

    $GLWimAppDeployDir = Join-Path $Directory.FullName 'WimAppDeploy'
    Write-Host ('[Wim] GLWimAppDeployDir potential path: {0}' -f $GLWimAppDeployDir)
    if (Test-Path $GLWimAppDeployDir) {
        Copy-Item $GLWimAppDeployDir -Destination $NewSourceDirectory.FullName -Force -Recurse
    }

    $GLSourceFilesDirectory = Join-Path $Directory.FullName 'Source Files'
    Write-Host ('[Wim] GLSourceFilesDirectory potential path: {0}' -f $GLSourceFilesDirectory)
    if (Test-Path $GLSourceFilesDirectory) {
        Copy-Item $GLSourceFilesDirectory -Destination $NewSourceDirectory.FullName -Force -Recurse
    }



    Write-Host '[Wim] Generating the WIM file'

    [IO.FileInfo] $ImageFile = Join-Path $NewSourceDirectory.FullName 'Application.wim'
    Start-Process 'dism.exe' -ArgumentList "/Capture-Image /ImageFile:""$($ImageFile.FullName)"" /CaptureDir:""$($ApplicationFiles.FullName)"" /Name:""$($Configuration.Name)"" /Description:""$($Configuration.Name)"" /Compress:fast" -Wait -NoNewWindow -ErrorAction 'Stop'

    Write-Host ('[Wim] WIM file filepath: {0}' -f ($ImageFile.FullName))
    Write-Host ('[Wim] WIM file created: {0}' -f ($ImageFile.Exists))

    if (-not ($ImageFile.Exists)) {
        Throw '[Wim] Failed to locate the Application.wim file'
    }

    return $NewSourceDirectory
}
#EndRegion '.\private\Wim.ps1' 82
#Region '.\public\Start-Builder.ps1' 0
function Start-Builder {
    [CmdletBinding()]
    param (
        # Valid path to the configuration file. Currently supported xml and yaml
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateScript({ $_.Exists })]
        [IO.FileInfo]
        $ConfigFile,

        # Directory containing files needed for the Application to install.
        [Parameter(Mandatory = $true)]
        [IO.DirectoryInfo]
        $SourceDirectory,

        # Configuration Manager FQDN ex: configmgr.contoso.com
        [Parameter(Mandatory = $true)]
        [string]
        $SiteServerFQDN,

        # Configuration manager site code ex: CON
        [Parameter(Mandatory)]
        [string]
        $SiteCode,

        # Network share accessable by configmgr to store content.
        [Parameter(Mandatory)]
        [IO.DirectoryInfo]
        $ContentShare,

        # Optional array of distribution points to distribute content too.
        [Parameter()]
        [string[]]
        $DistributionPoints,

        # Optional array of distribution point groups to distribute too.
        [Parameter()]
        [string[]]
        $DistributionPointGroups,

        # Specify a child directory to store applications in.
        [Parameter()]
        [string]
        $ApplicationFolder,

        # Specify a child directory to create collections under.
        [Parameter()]
        [string]
        $CollectionFolder,
        
        # Update old application referenced in a task sequence
        [Parameter()]
        [switch]
        $UpdateTaskSequences,

        # List of Task sequence that will be excluded from having the old application updated.
        [Parameter()]
        [string[]]
        $TaskSequenceSkipList,

        # Copy the deployment from the old application to the new application.
        [Parameter()]
        [switch]
        $CopyDeployment
    )

    Begin {
        $ErrorActionPreference = 'Stop'

        try {
            Import-Module (Join-Path (Split-Path $env:SMS_ADMIN_UI_PATH -Parent) 'ConfigurationManager.psd1')
        } catch {
            throw 'The specified module ''ConfigurationManager'' was not loaded because no valid module file was found. Is the admin console installed?'
        }

        # Read ConfigFile
        switch ($ConfigFile.Extension) {
            '.xml' {
                $XML = New-Object xml
                $XML.PreserveWhitespace = $true
                $XML.Load($ConfigFile.FullName)
        
                $Script:Configuration = $XML.package
            }
            '.yaml' {
                $Script:Configuration = Get-Content $ConfigFile.FullName | ConvertFrom-Yaml
            }
            Default { throw 'Invalid configuration file. '}
        }

        $Script:Version = $Configuration.SoftwareVersion

        Write-Verbose ('[CMBuilder] Configuration: {0}' -f ($Configuration | Out-String))

        Write-Host '[CMBuilder] Importing Configuration Manager PSModule'

        Write-Host '[CMBuilder] Ensuring Configuration Manager Drive is mapped'

        if (-not (Get-PSDrive -Name $SiteCode -ErrorAction SilentlyContinue)) {
            New-PSDrive -Name $SiteCode -PSProvider "CMSite" -Root $SiteServerFQDN -Description "SCCM Site" | Out-Null
        }
    }

    Process {
        Write-Host '[CMBuilder] Moving '
        Write-Host ('[CMBuilder] Source Directory: {0}' -f $SourceDirectory)

        PkgSrc -SourceDirectory $SourceDirectory -ContentRoot $ContentShare

        Push-Location ('{0}:' -f $SiteCode)


        Write-Host '[CMBuilder] Creating/Detecting application folder'

        if ($ApplicationFolder) {
            $Folder = Folder -FolderType 'Application' -RootOverride $ApplicationFolder
        } else {
            $Folder = Folder -FolderType 'Application'
        }


        $ContainerNodeID = (Get-Item $Folder).ContainerNodeID

        Write-Host ('[CMBuilder] Folder: {0}' -f $Folder)
        Write-Host ('[CMBuilder] Folder ContainerNodeId: {0}' -f $ContainerNodeID)


        Write-Host '[CMBuilder] Creating application'

        $CMApplication  = Application -Folder $Folder

        $DeploymentType = DeploymentType -CMApplication $CMApplication

        Write-Host '[CMBuilder] Distributing Application to DPs'
        
        Distribute -CMApplication $CMApplication

        Write-Host '[CMBuilder] Detecting older versions'

        $instanceKeys = (Invoke-CMWmiQuery -Query "select InstanceKey from SMS_ObjectContainerItem where ObjectType='6000' and ContainerNodeID='$ContainerNodeID'").instanceKey
        $ExistingApplications = foreach ($Key in $instanceKeys) {
            Get-CMApplication -ModelName $Key
        }

        Write-Host ('[CMBuilder] Detected {0} applications' -f $ExistingApplications.Count)

        if ($ExistingApplications) {
            $Applications = $ExistingApplications | Where-Object { ($_.SoftwareVersion -ne $Script:Version) -and ($_.SoftwareVersion -notmatch '[a-z]') }

            foreach ($Application in $Applications) {
                if (-not ($Application.SoftwareVersion)) {
                    continue
                }

                $VersionInfo = [Version]$Application.SoftwareVersion

                if (($Null -eq $LatestApplication) -or ($VersionInfo -ge [Version]$LatestApplication.SoftwareVersion)) {
                    $LatestApplication = $Application
                }
            }

            if ($LatestApplication) {
                Write-Host '[CMBuilder] Found a previous version. Superseding it!'
                Write-Host ('[CMBuilder] Old version: {0}; New Version: {1}' -f $LatestApplication.SoftwareVersion, $Script:Version)
                Supersede -NewCMApplication $CMApplication -OldCMApplication $LatestApplication

                if ((-not $Configuration.NoDeploy) -and ($UpdateTaskSequences)) {
                    Write-Host '[CMBuilder] Updating Task Sequences'
                    TaskSequence -NewCMApplication $CMApplication -OldCMApplication $LatestApplication

                    ApplicationGroup -NewCMApplication $CMApplication -OldCMApplication $LatestApplication
                }
            }
        }

        if (-not ($Configuration.NoDeploy)) {
            Write-Host '[CMBuilder] Deploying Application'
            Deploy -NewCMApplication $CMApplication -OldCMApplication $LatestApplication
        } else {
            Write-Warning '[CMBuilder] Configuration has NoDeploy key set. This application will not be automatically deployed.'
        }

        if ($Configuration.SoftwareMetering) {
            $MeteringFolder = Folder -FolderType 'SoftwareMetering'
            Metering -Folder $MeteringFolder
        }
    }

    End {
        #We leave after as to not cause any trouble, perhaps unnecessary
        Pop-Location
    }
}
#EndRegion '.\public\Start-Builder.ps1' 193