UGDSB.ApplicationFactory.psm1

#Region '.\Private\Add-AppFactoryAppEXE.ps1' 0
function Add-AppFactoryAppEXE{
  [cmdletbinding()]
  [OutputType([System.Collections.Generic.List[String[]]])]
  param(
    [Parameter()][String]$directory,    
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]$AppSetupFileName,
    [Parameter()][string]$argumentList,
    [Parameter()][bool]$secureArgumentList,
    [Parameter()][int[]]$SuccessExitCodes,
    [Parameter()][int[]]$rebootExitCodes,
    [Parameter()][int[]]$ignoreExitCodes,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  $ApplicationScriptLines = [System.Collections.Generic.List[String[]]]@()
  # Set the Path that we should be working with
  if($directory -ne ""){
    $executePath = "`"`$($($directory))\`$(`$AppSetupFileName)`""
  }
  else{
    $executePath = "`"`$(`$AppSetupFileName)`""
  }
  $execute = "Start-ADTProcess -FilePath $($executePath) -ArgumentList `"`$(`$argumentList)`""
  if ($PSBoundParameters.ContainsKey("secureArgumentList") -and $secureArgumentList) {$execute = "$($execute) -secureArgumentList"}  
  if ($PSBoundParameters.ContainsKey("SuccessExitCodes") -and $SuccessExitCodes) {$execute = "$($execute) -SuccessExitCodes $($SuccessExitCodes -join ",")"}  
  if ($PSBoundParameters.ContainsKey("rebootExitCodes") -and $rebootExitCodes) {$execute = "$($execute) -rebootExitCodes $($rebootExitCodes -join ",")"}  
  if ($PSBoundParameters.ContainsKey("ignoreExitCodes") -and $ignoreExitCodes) {$execute = "$($execute) -ignoreExitCodes $($ignoreExitCodes -join ",")"}  
  $ApplicationScriptLines.Add("`t`$AppSetupFileName = `"$($AppSetupFileName)`"") | Out-Null
  $ApplicationScriptLines.Add("`t`$argumentList = `"$($argumentList)`"") | Out-Null
  $ApplicationScriptLines.Add("`t$($execute)") | Out-Null
  return @(,$ApplicationScriptLines)            
}
#EndRegion '.\Private\Add-AppFactoryAppEXE.ps1' 32
#Region '.\Private\Add-AppFactoryApplicationBlockingProcess.ps1' 0
function Add-AppFactoryApplicationBlockingProcess{
  [cmdletbinding()]
  [OutputType([System.Collections.Generic.List[String[]]])]
  param(
    [Parameter()][switch]$interactive,   
    [Parameter()][ValidateNotNullOrEmpty()][string[]]$blockingProcess,
    [Parameter()][ValidateNotNullOrEmpty()][int]$deferCount = 0,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  $ApplicationScriptLines = [System.Collections.Generic.List[String[]]]@()
  if($interactive.IsPresent){
    $ApplicationScriptLines.Add("`t`$processExist = `$false") | Out-Null
    foreach ($item in $blockingProcess) {
      $ApplicationScriptLines.Add("`tif(Get-Process -Name `"$($item)`" -ErrorAction SilentlyContinue){`$processExist = `$true}") | Out-Null
    }
    $ApplicationScriptLines.Add("`tif (`$adtSession.IsProcessUserInteractive -and `$processExist) {") | Out-Null
    $ApplicationScriptLines.Add("`t`t`$params = @{") | Out-Null
    $ApplicationScriptLines.Add("`t`t`t`"CloseProcesses`" = '$($blockingProcess -join "','")'") | Out-Null
    $ApplicationScriptLines.Add("`t`t`t`"PersistPrompt`" = `$true") | Out-Null
    if ($deferCount -gt 0) {
      $ApplicationScriptLines.Add("`t`t`t`"AllowDefer`" = `$true") | Out-Null
      $ApplicationScriptLines.Add("`t`t`t`"DeferTimes`" = $($deferCount)") | Out-Null
    }
    $ApplicationScriptLines.Add("`t`t}") | Out-Null
    $ApplicationScriptLines.Add("`t`tShow-ADTInstallationWelcome @params") | Out-Null
    $ApplicationScriptLines.Add("`t}") | Out-Null    
    $ApplicationScriptLines.Add("`telse {") | Out-Null
    foreach ($item in $blockingProcess) {
      $ApplicationScriptLines.Add("`t`t`Get-Process -Name `"$($item)`" -ErrorAction SilentlyContinue | Stop-Process -Force") | Out-Null
    }
    $ApplicationScriptLines.Add("`t}") | Out-Null      
  }
  else{
    foreach($item in $blockingProcess){
      $ApplicationScriptLines.Add("`tGet-Process -Name `"$($item)`" -ErrorAction SilentlyContinue | Stop-Process -Force") | Out-Null
    }
  }
  return @(,$ApplicationScriptLines)
}
#EndRegion '.\Private\Add-AppFactoryApplicationBlockingProcess.ps1' 40
#Region '.\Private\Add-AppFactoryAppMSI.ps1' 0
function Add-AppFactoryAppMSI{
  [cmdletbinding()]
  [OutputType([System.Collections.Generic.List[String[]]])]
  param(
    [Parameter()][String]$directory,    
    [Parameter(Mandatory = $true, ParameterSetName = "AppSetupFileName")][ValidateNotNullOrEmpty()][String]$AppSetupFileName,
    [Parameter(Mandatory = $true, ParameterSetName = "productcode")][ValidateNotNullOrEmpty()][String]$productcode,
    [Parameter()][ValidateSet("Install", "Uninstall")][string]$Action = "Install",
    [Parameter()][string]$argumentList,
    [Parameter()][string]$additionalArgumentList,
    [Parameter()][bool]$secureArgumentList,
    [Parameter()][bool]$SkipMSIAlreadyInstalledCheck,
    [Parameter()][string]$Transforms,
    [Parameter()][int[]]$SuccessExitCodes,
    [Parameter()][int[]]$rebootExitCodes,
    [Parameter()][int[]]$ignoreExitCodes,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  $ApplicationScriptLines = [System.Collections.Generic.List[String[]]]@()
  if($PSBoundParameters.ContainsKey("AppSetupFileName") -and $AppSetupFileName){
    if($directory -ne ""){
      $executePath = "`"`$($($directory))\`$(`$AppSetupFileName)`""
    }
    else{
      $executePath = "`"`$(`$AppSetupFileName)`""
    }
    $execute = "Start-ADTMsiProcess -Action `"$($Action)`" -FilePath $($executePath)"
    $ApplicationScriptLines.Add("`t`$AppSetupFileName = `"$($AppSetupFileName)`"") | Out-Null
  }
  else{
    $execute = "Start-ADTMsiProcess -Action `"$($Action)`" -productcode `"$($productcode)`""
  }
  if ($PSBoundParameters.ContainsKey("Transforms")  -and $Transforms) {
    $ApplicationScriptLines.Add("`t`$Transforms = `"$($Transforms)`"") | Out-Null
    $execute = "$($execute) -Transforms `"`$($($directory))\`$(`$Transforms)`""
  }
  if ($PSBoundParameters.ContainsKey("additionalArgumentList") -and $additionalArgumentList) {
    $ApplicationScriptLines.Add("`t`$additionalArgumentList = `"$($additionalArgumentList)`"") | Out-Null
    $execute = "$($execute) -AdditionalArgumentList `$additionalArgumentList"
  }   
  if ($PSBoundParameters.ContainsKey("argumentList") -and $argumentList) {
    $ApplicationScriptLines.Add("`t`$argumentList = `"$($argumentList)`"") | Out-Null
    $execute = "$($execute) -argumentList `$argumentList"
  }  
  if ($PSBoundParameters.ContainsKey("secureArgumentList") -and $secureArgumentList) {$execute = "$($execute) -secureArgumentList"}  
  if ($PSBoundParameters.ContainsKey("SkipMSIAlreadyInstalledCheck") -and $SkipMSIAlreadyInstalledCheck) {$execute = "$($execute) -SkipMSIAlreadyInstalledCheck"}  
  if ($PSBoundParameters.ContainsKey("SuccessExitCodes") -and $SuccessExitCodes) {$execute = "$($execute) -SuccessExitCodes $($SuccessExitCodes -join ",")"}  
  if ($PSBoundParameters.ContainsKey("rebootExitCodes") -and $rebootExitCodes) {$execute = "$($execute) -rebootExitCodes $($rebootExitCodes -join ",")"}  
  if ($PSBoundParameters.ContainsKey("ignoreExitCodes") -and $ignoreExitCodes) {$execute = "$($execute) -ignoreExitCodes $($ignoreExitCodes -join ",")"}  
  $ApplicationScriptLines.Add("`t$($execute)") | Out-Null
  return @(,$ApplicationScriptLines)              
}
#EndRegion '.\Private\Add-AppFactoryAppMSI.ps1' 53
#Region '.\Private\Add-AppFactoryAppWIM.ps1' 0
function Add-AppFactoryAppWIM{
  [cmdletbinding()]
  [OutputType([System.Collections.Generic.List[String[]]])]
  param(
    [Parameter(Mandatory = $true)][ValidateSet("start","end")][string]$section,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$MountPath,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  ) 
  $ApplicationScriptLines = [System.Collections.Generic.List[String[]]]@() 
  # If this to mount the WIM add the following lines
  if($section -eq "start"){
    $ApplicationScriptLines.Add("`t`$mountPath = `"$($MountPath)`"")  | Out-Null
    $ApplicationScriptLines.Add("`t`$wimFile = Get-Childitem -Path `"`$(`$adtSession.dirFiles)`" -Filter `"*.wim`"")  | Out-Null
    $ApplicationScriptLines.Add("`t`$wimPath = Join-Path `$adtSession.dirFiles -ChildPath `$wimFile")  | Out-Null
    $ApplicationScriptLines.Add("`tMount-ADTWimFile -ImagePath `$wimPath -Path `$mountPath -Index 1")  | Out-Null 
  }
  else{
    $ApplicationScriptLines.Add("`tDismount-ADTWimFile -Path `"$($MountPath)`"")  | Out-Null 
  }
  return @(,$ApplicationScriptLines)
}
#EndRegion '.\Private\Add-AppFactoryAppWIM.ps1' 22
#Region '.\Private\Connect-AppFactoryAzureStorage.ps1' 0

function Connect-AppFactoryAzureStorage {
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$storageContainer,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][securestring]$storageSecret,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose" 
  )
  begin {
    # Generate variables to connect to azure storage
    $storageVars = @{
      "StorageAccountName" = $storageContainer
      "StorageAccountKey"  = ConvertFrom-SecureString $storageSecret -AsPlainText
    }    
  }
  process {
    try {
      if ($script:AppFactoryLogging) {
        Write-PSFMessage -Message "Creating Azure Storage Context for <c='green'>$($storageContainer)</c>" -Level $LogLevel -Tag "Storage", "$($storageContainer)" -Target "Application Factory Service"
      }
      $storageAccountContext = New-AzStorageContext @storageVars
    }
    catch {
      Write-PSFMessage -Message "Error Encountered: $($_)" -Level "Error" -Tag "Storage" -Target "Application Factory Service"
      throw $_
    }
  }
  end {
    return $storageAccountContext
  }
}
#EndRegion '.\Private\Connect-AppFactoryAzureStorage.ps1' 33
#Region '.\Private\Get-AppFactoryAzureStorageAppItem.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to download contacts from Azure Storage Account
  .PARAMETER application
  The application object that we are working for so that we can ensure that get the correct and current data
  .PARAMETER LogLevel
  If logging is enabled, what level of logging do we want, default is verbose.
#>

function Get-AppFactoryAzureStorageAppItem {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose" 
  )
  try {
    if ($script:AppFactoryLogging) {
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Getting Azure Storage based application"  -Level $LogLevel -Tag "Application", "$($application.Information.DisplayName)", "AzureStorage" -Target "Application Factory Service"
    }
    $StorageBlobContents = Get-AzStorageBlob -Container $application.SourceFiles.StorageAccountContainerName -Context $script:appStorageContext -ErrorAction Stop | Where-Object {$_.Name -ne "latest.json"} | Sort-Object LastModified -Descending
    $fileInfo = $StorageBlobContents[0].Name -split "/"
    # Only return the top item
    $PSObject = [PSCustomObject]@{
      "Version" = $fileInfo[0]
      "URI" = $fileInfo[0]
    }
    return $PSObject    
  }
  catch {
    Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Error Occured: $($_)" -Level "Error" -Tag "Application", "$($application.Information.DisplayName)" -Target "Application Factory Service"
    throw $_
  }  
}
#EndRegion '.\Private\Get-AppFactoryAzureStorageAppItem.ps1' 33
#Region '.\Private\Get-AppFactoryAzureStorageFile.ps1' 0
function Get-AppFactoryAzureStorageFile{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$destination,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose" 
  )
  try{
    $path = "$($application.SourceFiles.PackageSource)"
    $StorageBlobContents = Get-AzStorageBlob -Container $application.SourceFiles.StorageAccountContainerName -Context $script:appStorageContext -ErrorAction Stop | Where-Object {$_.Name -like "$($path)*"}
    foreach($blob in $StorageBlobContents){
      $file = $blob.Name -split "/"
      if($file.length -gt 2){
        $directoryPath = Join-path -Path $destination -ChildPath (($file[1..($file.length -2)]) -join "/")
        New-Item -Path $directoryPath -ItemType Directory -Force | Out-Null
      }
      else{
        $directoryPath = $destination
      }
      $params = @{
        Context = $script:appStorageContext
        Container = $application.SourceFiles.StorageAccountContainerName
        Blob = $blob.Name
        Destination = (Join-Path -Path $directoryPath -ChildPath $file[-1])
        Force = $true
      }
      Get-AzStorageBlobContent @params | Out-Null  
    }
  }
  catch {
    Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Error Occured: $($_)" -Level "Error" -Tag "Application", "$($application.Information.DisplayName)" -Target "Application Factory Service"
    throw $_
  }  
}
#EndRegion '.\Private\Get-AppFactoryAzureStorageFile.ps1' 35
#Region '.\Private\Get-AppFactoryECNOAppItem.ps1' 0
function Get-AppFactoryECNOAppItem{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose" 
  )
  if($script:AppFactoryLogging){
    Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Looking for Sharepoint application."  -Level $LogLevel -Tag "Application","$($application.Information.DisplayName)","PSADT" -Target "Application Factory Service" 
  }
  # The PFX used to connect to the Sharepoint Configured
  $pfxPath = Join-Path -Path $script:AppFactoryLocalSupportFiles -ChildPath $script:AppFactorySharepointCertificate
  # Configuration for the PnP PowerShell Module
  $sharepointConfig = @{
    "url"                 = "$($script:AppFactorysharepointurl)/sites/$($script:AppFactorysharepointsite)"
    "CertificatePath"     = $pfxPath
    "CertificatePassword" = $script:AppFactorySharepointCertificateSecret.Password
    "ClientId"            = $script:AppFactorySharepointClientID
    "Tenant"              = $script:AppFactorySharepointTenant
  }
  try { Connect-PnPOnline @sharepointConfig }
  catch {
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "Failed to connect to Sharepoint. Error: $($_)" -Level "Error" -Tag "Process","Sharepoint" -Target "Application Factory Service"
    }
    throw $_
  }
  $listItems = Get-PnPListItem -List $script:AppFactorySharepointDocumentLibrary -PageSize 1000 | Where-Object {$_["FileDirRef"] -like "*$($application.SourceFiles.StorageAccountContainerName)"}
  $listItems = $listItems | Select-Object -Property @(@{name="FileLeafRef"; expr={$_["FileLeafRef"]}},@{name="FileRef"; expr={$_["FileRef"]}},@{name="FileDirRef"; expr={$_["FileDirRef"]}},@{name="Modified"; expr={$_["Modified"]}}) | Sort-Object -Property Modified -Descending
  $PSObject = [PSCustomObject]@{
    "Version" = ($listItems[0].FileLeafRef -replace ".7z","").Trim()
    "URI" = $listItems[0].FileRef
  }
  return $PSObject    
}
#EndRegion '.\Private\Get-AppFactoryECNOAppItem.ps1' 35
#Region '.\Private\Get-AppFactoryECNOFile.ps1' 0
function Get-AppFactoryECNOFile{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$destination,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose" 
  )
  # The PFX used to connect to the Sharepoint Configured
  $pfxPath = Join-Path -Path $script:AppFactoryLocalSupportFiles -ChildPath $script:AppFactorySharepointCertificate
  # Configuration for the PnP PowerShell Module
  $sharepointConfig = @{
    "url"                 = "$($script:AppFactorysharepointurl)/sites/$($script:AppFactorysharepointsite)"
    "CertificatePath"     = $pfxPath
    "CertificatePassword" = $script:AppFactorySharepointCertificateSecret.Password
    "ClientId"            = $script:AppFactorySharepointClientID
    "Tenant"              = $script:AppFactorySharepointTenant
  }
  try { Connect-PnPOnline @sharepointConfig }
  catch {
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "Failed to connect to Sharepoint. Error: $($_)" -Level "Error" -Tag "Process","Sharepoint" -Target "Application Factory Service"
    }
    throw $_
  }
  $fullpath = "$($script:AppFactorysharepointurl)$($application.SourceFiles.PackageSource)"
  $filename = "$($application.SourceFiles.PackageVersion).7z"
  Get-PnPFile -Url $fullpath -AsFile -Path $destination -Filename $filename -Force
  $7ZipPath = Join-Path -Path $script:AppFactorySupportFiles -ChildPath "7zr.exe"
  $7ZipFile = Join-Path -Path $destination -ChildPath $filename
  $vars = @{
    "FilePath" = $7zipPath
    "ArgumentList" = "x `"$($7ZipFile)`" -aoa -o`"$($destination)`""
    "Wait" = $true
  }
  Start-Process @vars
  Remove-Item -Path $7ZipFile -Force
}
#EndRegion '.\Private\Get-AppFactoryECNOFile.ps1' 38
#Region '.\Private\Get-AppFactoryEvergreenAppItem.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to interact with the evergreen powershell module to find the installers for evergreen based installers
  .PARAMETER application
  The application object that we are working for so that we can ensure that get the correct and current data
  .PARAMETER LogLevel
  If logging is enabled, what level of logging do we want, default is verbose.
#>

function Get-AppFactoryEvergreenAppItem{
  [CmdletBinding()]
  [OutputType([PSCustomObject])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter()][ValidateSet("Output","Verbose")][string]$LogLevel = "Verbose"    
  )
  if($script:AppFactoryLogging){
    Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Looking for Evergreen Application with AppID: <c='green'>$($application.SourceFiles.AppID)</c>"  -Level $LogLevel -Tag "Application","$($application.Information.DisplayName)","Evergreen" -Target "Application Factory Service" 
  }

  # Construct array list to build the dynamic filter list
  $FilterList = [System.Collections.Generic.List[PSCustomObject]]@()
  # Process known filter properties and add them to array list if present on current object
  if ($Application.SourceFiles.FilterOptions.Architecture) {
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Architecture Filter: <c='green'>$($Application.SourceFiles.FilterOptions.Architecture)</c>" -Level $LogLevel -Tag "Application","$($application.Information.DisplayName)","Evergreen" -Target "Application Factory Service"
    }
    $FilterList.Add("`$PSItem.Architecture -eq ""$($Application.SourceFiles.FilterOptions.Architecture)""") | Out-Null
  }
  if ($Application.SourceFiles.FilterOptions.Platform) {
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Platform Filter: <c='green'>$($Application.SourceFiles.FilterOptions.Platform)</c>" -Level $LogLevel -Tag "Application","$($application.Information.DisplayName)","Evergreen" -Target "Application Factory Service"
    }
    $FilterList.Add("`$PSItem.Platform -eq ""$($Application.SourceFiles.FilterOptions.Platform)""") | Out-Null
  }
  if ($Application.SourceFiles.FilterOptions.Channel) {
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Channel Filter: <c='green'>$($Application.SourceFiles.FilterOptions.Channel)</c>" -Level $LogLevel -Tag "Application","$($application.Information.DisplayName)","Evergreen" -Target "Application Factory Service"
    }
    $FilterList.Add("`$PSItem.Channel -eq ""$($Application.SourceFiles.FilterOptions.Channel)""") | Out-Null
  }
  if ($Application.SourceFiles.FilterOptions.Type) {
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Type Filter: <c='green'>$($Application.SourceFiles.FilterOptions.Type)</c>" -Level $LogLevel -Tag "Application","$($application.Information.DisplayName)","Evergreen" -Target "Application Factory Service"
    }
    $FilterList.Add("`$PSItem.Type -eq ""$($Application.SourceFiles.FilterOptions.Type)""") | Out-Null
  }
  if ($Application.SourceFiles.FilterOptions.InstallerType) {
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] InstallerType Filter: <c='green'>$($Application.SourceFiles.FilterOptions.InstallerType)</c>" -Level $LogLevel -Tag "Application","$($application.Information.DisplayName)","Evergreen" -Target "Application Factory Service"
    }
    $FilterList.Add("`$PSItem.InstallerType -eq ""$($Application.SourceFiles.FilterOptions.InstallerType)""") | Out-Null
  }
  if ($Application.SourceFiles.FilterOptions.Release) {
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Release Filter: <c='green'>$($Application.SourceFiles.FilterOptions.Release)</c>" -Level $LogLevel -Tag "Application","$($application.Information.DisplayName)","Evergreen" -Target "Application Factory Service"
    }
    $FilterList.Add("`$PSItem.Release -eq ""$($Application.SourceFiles.FilterOptions.Release)""") | Out-Null
  }  
  if ($Application.SourceFiles.FilterOptions.Language) {
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Language Filter: <c='green'>$($Application.SourceFiles.FilterOptions.Language)</c>" -Level $LogLevel -Tag "Application","$($application.Information.DisplayName)","Evergreen" -Target "Application Factory Service"
    }
    $FilterList.Add("`$PSItem.Language -eq ""$($Application.SourceFiles.FilterOptions.Language)""") | Out-Null
  }    
  if ($Application.SourceFiles.FilterOptions.ImageType) {
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] ImageType Filter: <c='green'>$($Application.SourceFiles.FilterOptions.ImageType)</c>" -Level $LogLevel -Tag "Application","$($application.Information.DisplayName)","Evergreen" -Target "Application Factory Service"
    }
    $FilterList.Add("`$PSItem.ImageType -eq ""$($Application.SourceFiles.FilterOptions.ImageType)""") | Out-Null
  } 
  # Construct script block from filter list array
  $FilterExpression = [scriptblock]::Create(($FilterList -join " -and ")) 
  # Get the evergreen app based on dynamic filter list
  if($FilterList.Count -gt 0){
    $EvergreenApp = Get-EvergreenApp -Name $application.SourceFiles.AppID | Where-Object -FilterScript $FilterExpression | Sort-Object Version -Descending | Select-Object -first 1
  }
  else{
    $EvergreenApp = Get-EvergreenApp -Name $application.SourceFiles.AppID | Sort-Object Version -Descending | Select-Object -first 1
  }
  # Only return the top item
  $PSObject = [PSCustomObject]@{
    "Version" = $EvergreenApp.version
    "URI" = $EvergreenApp.URI
  }
  return $PSObject
}
#EndRegion '.\Private\Get-AppFactoryEvergreenAppItem.ps1' 87
#Region '.\Private\Get-AppFactoryExtraFiles.ps1' 0
function Get-AppFactoryExtraFiles{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$destination,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose" 
  )
  try{
    foreach($container in $Application.SourceFiles.ExtraFiles){
      #$path = "$($application.SourceFiles.PackageSource)"
      $StorageBlobContents = Get-AzStorageBlob -Container $container -Context $script:appStorageContext -ErrorAction Stop
      foreach($blob in $StorageBlobContents){
        $file = $blob.Name -split "/"
        if($file.length -gt 2){
          $directoryPath = Join-path -Path $destination -ChildPath (($file[1..($file.length -2)]) -join "/")
          New-Item -Path $directoryPath -ItemType Directory -Force | Out-Null
        }
        else{
          $directoryPath = $destination
        }
        $params = @{
          Context = $script:appStorageContext
          Container = $container
          Blob = $blob.Name
          Destination = (Join-Path -Path $directoryPath -ChildPath $file[-1])
          Force = $true
        }
        Get-AzStorageBlobContent @params | Out-Null  
      }      
    }
  }
  catch {
    Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Error Occured: $($_)" -Level "Error" -Tag "Application", "$($application.Information.DisplayName)" -Target "Application Factory Service"
    throw $_
  }   
}
#EndRegion '.\Private\Get-AppFactoryExtraFiles.ps1' 37
#Region '.\Private\Get-AppFactoryLocalStorageAppItem.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to download contacts from Azure Storage Account
  .PARAMETER application
  The application object that we are working for so that we can ensure that get the correct and current data
  .PARAMETER LogLevel
  If logging is enabled, what level of logging do we want, default is verbose.
#>

function Get-AppFactoryLocalStorageAppItem {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose" 
  )
  try {
    if ($script:AppFactoryLogging) {
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Getting Local Storage based application"  -Level $LogLevel -Tag "Application", "$($application.Information.DisplayName)", "AzureStorage" -Target "Application Factory Service"
    }
    $localAppPath = Join-Path -Path $script:AppFactorySourceDir -ChildPath "LocalInstallers" -AdditionalChildPath $application.SourceFiles.StorageAccountContainerName
    $localAppList = Get-ChildItem -Path $localAppPath | Sort-Object LastWriteTime -Descending
    # Only return the top item
    $PSObject = [PSCustomObject]@{
      "Version" = $localAppList[0].Name
      "URI" = $localAppList[0].FullName
    }
    return $PSObject        
  }
  catch {
    Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Error Occured: $($_)" -Level "Error" -Tag "Application", "$($application.Information.DisplayName)" -Target "Application Factory Service"
    throw $_
  }         
}
#EndRegion '.\Private\Get-AppFactoryLocalStorageAppItem.ps1' 33
#Region '.\Private\Get-AppFactoryPSADTAppItem.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to download contacts from Azure Storage Account
  .PARAMETER application
  The application object that we are working for so that we can ensure that get the correct and current data
  .PARAMETER storageContext
  The azure storage context that has permissions to upload to the Azure Storage Account
  .PARAMETER workingFolder
  Path to the working folder for the process
  .PARAMETER LogLevel
  If logging is enabled, what level of logging do we want, default is verbose.
#>

function Get-AppFactoryPSADTAppItem {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose" 
  )
  if($script:AppFactoryLogging){
    Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Looking for PSADT application."  -Level $LogLevel -Tag "Application","$($application.Information.DisplayName)","PSADT" -Target "Application Factory Service" 
  }  
  if($null -eq $application.Information.AppVersion -or $application.Information.AppVersion -eq "<replaced_by_build>"){
    $version = "1.0"
  }
  else{
    $version = $application.Information.AppVersion
  }
  $AppItem = [PSCustomObject]@{
    "Version" = $version
    "URI" = $null
  }
  return $AppItem
}
#EndRegion '.\Private\Get-AppFactoryPSADTAppItem.ps1' 34
#Region '.\Private\Get-AppFactorySharepointAppItem.ps1' 0
function Get-AppFactorySharepointAppItem{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose" 
  )
  if($script:AppFactoryLogging){
    Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Looking for Sharepoint application."  -Level $LogLevel -Tag "Application","$($application.Information.DisplayName)","PSADT" -Target "Application Factory Service" 
  }
  # The PFX used to connect to the Sharepoint Configured
  $pfxPath = Join-Path -Path $script:AppFactoryLocalSupportFiles -ChildPath $script:AppFactorySharepointCertificate
  # Configuration for the PnP PowerShell Module
  $sharepointConfig = @{
    "url"                 = "$($script:AppFactorysharepointurl)/sites/$($script:AppFactorysharepointsite)"
    "CertificatePath"     = $pfxPath
    "CertificatePassword" = $script:AppFactorySharepointCertificateSecret.Password
    "ClientId"            = $script:AppFactorySharepointClientID
    "Tenant"              = $script:AppFactorySharepointTenant
  }
  try { Connect-PnPOnline @sharepointConfig }
  catch {
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "Failed to connect to Sharepoint. Error: $($_)" -Level "Error" -Tag "Process","Sharepoint" -Target "Application Factory Service"
    }
    throw $_
  }
  $listItems = Get-PnPListItem -List $script:AppFactorySharepointDocumentLibrary -PageSize 1000 | Where-Object {$_["FileDirRef"] -like "*$($application.SourceFiles.StorageAccountContainerName)"}
  $listItems = $listItems | Select-Object -Property @(@{name="FileLeafRef"; expr={$_["FileLeafRef"]}},@{name="FileRef"; expr={$_["FileRef"]}},@{name="FileDirRef"; expr={$_["FileDirRef"]}},@{name="Modified"; expr={$_["Modified"]}}) | Sort-Object -Property Modified -Descending
  $PSObject = [PSCustomObject]@{
    "Version" = $listItems[0].FileLeafRef
    "URI" = $listItems[0].FileRef
  }
  return $PSObject    
}
#EndRegion '.\Private\Get-AppFactorySharepointAppItem.ps1' 35
#Region '.\Private\Get-AppFactorySharepointFile.ps1' 0
function Get-AppFactorySharepointFile{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$destination,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose" 
  )
  # The PFX used to connect to the Sharepoint Configured
  $pfxPath = Join-Path -Path $script:AppFactoryLocalSupportFiles -ChildPath $script:AppFactorySharepointCertificate
  # Configuration for the PnP PowerShell Module
  $sharepointConfig = @{
    "url"                 = "$($script:AppFactorysharepointurl)/sites/$($script:AppFactorysharepointsite)"
    "CertificatePath"     = $pfxPath
    "CertificatePassword" = $script:AppFactorySharepointCertificateSecret.Password
    "ClientId"            = $script:AppFactorySharepointClientID
    "Tenant"              = $script:AppFactorySharepointTenant
  }
  try { Connect-PnPOnline @sharepointConfig }
  catch {
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "Failed to connect to Sharepoint. Error: $($_)" -Level "Error" -Tag "Process","Sharepoint" -Target "Application Factory Service"
    }
    throw $_
  }
  $listItems = Get-PnPListItem -List $script:AppFactorySharepointDocumentLibrary -PageSize 1000 | Where-Object {$_["FileDirRef"] -eq "$($application.SourceFiles.PackageSource)"}
  $listItems = $listItems | Select-Object -Property @(@{name="FileLeafRef"; expr={$_["FileLeafRef"]}},@{name="FileRef"; expr={$_["FileRef"]}})
  foreach($item in $listItems){
    $fullpath = "$($script:AppFactorysharepointurl)$($application.SourceFiles.PackageSource)/$($item.FileLeafRef)"
    Get-PnPFile -Url $fullpath -AsFile -Path $destination -Filename $item.FileLeafRef -Force
  }
}
#EndRegion '.\Private\Get-AppFactorySharepointFile.ps1' 32
#Region '.\Private\Get-AppFactoryWinGetAppItem.ps1' 0
function Get-AppFactoryWinGetAppItem{
  [CmdletBinding()]
  [OutputType([PSCustomObject])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter()][ValidateSet("Output","Verbose")][string]$LogLevel = "Verbose"    
  )
  # Create the argument and then run the winget
  $arguments = @("search", "$($application.SourceFiles.AppID)")
  $winget = & "winget" $arguments | Out-String -Stream
  # Confirm that a package was found
  foreach ($RowItem in $winget) {
    if ($RowItem -eq "No package found matching input criteria.") {
      if($script:AppFactoryLogging){
        Write-PSFMessage -Message "[$($application.Information.DisplayName)] No package found matching specified id: <c='green'>$($application.SourceFiles.AppId)</c>"  -Level "Error" -Tag "Process","Winget" -Target "Application Factory Service"
      }
      return $null
    }
  }
  # Now look for the specific item and get the details and parse them out
  $arguments = @("show", "$($application.SourceFiles.AppID)")
  $winget = & "winget" $arguments | Out-String -Stream
  $PSObject = [PSCustomObject]@{
    "Version" = ($winget | Where-Object { $PSItem -match "^Version\:.*(?<AppVersion>(\d+(\.\d+){0,3}))$" }).Replace("Version:", "").Trim()
    "URI" = (($winget | Where-Object { $PSItem -match "^.*(Download|Installer) Url\:.*$" }) -replace "(Download|Installer) Url:", "").Trim()
  }
  return $PSObject    
}
#EndRegion '.\Private\Get-AppFactoryWinGetAppItem.ps1' 29
#Region '.\Private\Get-ClientAppConfig.ps1' 0
function Get-ClientAppConfig{
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Parameter(Mandatory = $true)][PSCustomObject]$application,
    [Parameter()][PSCustomObject]$customConfig,
    [Parameter()][string]$audience = "Public",
    [Parameter()][ValidateSet("Output","Verbose")][string]$LogLevel = "Verbose"
  )
  if($audience -eq "Public"){
    $AppAudience = "Public"
  }
  else{
    $AppAudience = "Private"
  }
  # Defaults for each application
  $defaultConfig = [PSCustomObject]@{
    "AddToIntune"             = $false
    "AvailableAssignments"    = @()
    "AvailableExceptions"     = @()
    "RequiredAssignments"     = @()
    "RequiredExceptions"      = @()
    "UninstallAssignments"    = @()
    "UninstallExceptions"     = @()
    "UnassignPrevious"        = $false
    "CopyPrevious"            = $false
    "KeepPrevious"            = 0
    "foreground"              = $false
    "filters"                 = @{}
    "espprofiles"             = @()
    "container"               = $AppAudience
    "GUID"                    = $application.GUID
    "IntuneAppName"           = $application.Information.DisplayName
    "AppVersion"              = $null
    "InteractiveInstall"      = $false
    "InteractiveUninstall"    = $false
  }
  if($null -ne $customConfig){
    $customConfig.PSObject.Properties | Where-Object {$_.Name -ne "Value"} | ForEach-Object {
      $defaultConfig.$($_.Name) = $_.Value
    }
  }
  if($null -eq $defaultConfig.AppVersion){
    $temp = ($script:PublishedAppList.$audience | Where-Object {$_.Name -like "$($application.GUID)/*/App.json"}).Name
    $AppVersions = ([regex]::Matches($temp,"/(.*?)/App.json")).Groups.Value | Where-Object {$_ -notlike "*App.json"}
    $defaultConfig.AppVersion = $AppVersions | Sort-Object -Descending | Select-Object -First 1
  }
  return $defaultConfig
}
#EndRegion '.\Private\Get-ClientAppConfig.ps1' 50
#Region '.\Private\Get-MSIMetaData.ps1' 0
function Get-MSIMetaData {
  <#
  .SYNOPSIS
      Retrieve a specific MSI property value from MSI based installation file.
 
  .DESCRIPTION
      Retrieve a specific MSI property value from MSI based installation file.
 
  .PARAMETER Path
      Specify the full path to a MSI based installation file.
 
  .PARAMETER Property
      Specify the MSI database property to retrieve it's value.
 
  .NOTES
      Author: Nickolaj Andersen
      Contact: @NickolajA
      Created: 2020-01-04
      Updated: 2020-01-04
 
      Version history:
      1.0.0 - (2020-01-27) Function created
  #>
    
  [CmdletBinding(SupportsShouldProcess = $true)]
  param(
      [parameter(Mandatory = $true, HelpMessage = "Specify the full path to a MSI based installation file.")]
      [ValidateNotNullOrEmpty()]
      [System.IO.FileInfo]$Path,

      [parameter(Mandatory = $true, HelpMessage = "Specify the MSI database property to retrieve it's value.")]
      [ValidateNotNullOrEmpty()]
      [ValidateSet("ProductCode", "ProductVersion", "ProductName", "Manufacturer", "ProductLanguage", "FullVersion")]
      [string]$Property
  )
  Process {
      try {
          # Read property from MSI database
          $WindowsInstaller = New-Object -ComObject WindowsInstaller.Installer
          $MSIDatabase = $WindowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $null, $WindowsInstaller, @($Path.FullName, 0))
          $Query = "SELECT Value FROM Property WHERE Property = '$($Property)'"
          $View = $MSIDatabase.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $MSIDatabase, ($Query))
          $View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View, $null)
          $Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $null, $View, $null)
          $Value = $Record.GetType().InvokeMember("StringData", "GetProperty", $null, $Record, 1)

          # Commit database and close view
          $MSIDatabase.GetType().InvokeMember("Commit", "InvokeMethod", $null, $MSIDatabase, $null)
          $View.GetType().InvokeMember("Close", "InvokeMethod", $null, $View, $null)           
          $MSIDatabase = $null
          $View = $null

          # Return the value
          return $Value
      } 
      catch {
          Write-Warning -Message $_.Exception.Message; break
      }
  }
  End {
      # Run garbage collection and release ComObject
      [System.Runtime.Interopservices.Marshal]::ReleaseComObject($WindowsInstaller) | Out-Null
      [System.GC]::Collect()
  }
}
#EndRegion '.\Private\Get-MSIMetaData.ps1' 65
#Region '.\Private\Invoke-Process.ps1' 0
function Invoke-Process{
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]$FileName,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]$Arguments,
    [Parameter()][ValidateNotNullOrEmpty()][bool]$CreateNoWindow = $false,
    [Parameter()][ValidateNotNullOrEmpty()][bool]$UseShellExecute = $true,
    [Parameter()][ValidateNotNullOrEmpty()][bool]$RedirectStandardOutput = $false,
    [Parameter()][ValidateNotNullOrEmpty()][bool]$RedirectStandardError = $false
  )
  try{
    $ProcessInfo = New-object -TypeName "System.Diagnostics.ProcessStartInfo"
    $ProcessInfo.FileName = $FileName
    $ProcessInfo.CreateNoWindow = $CreateNoWindow
    $ProcessInfo.UseShellExecute = $UseShellExecute
    $ProcessInfo.RedirectStandardOutput = $RedirectStandardOutput
    $ProcessInfo.RedirectStandardError = $RedirectStandardError
    $ProcessInfo.Arguments = $Arguments
    $Process = New-Object -TypeName "System.Diagnostics.Process"
    $Process.StartInfo = $ProcessInfo
    [void]$Process.Start()
    $Process.WaitForExit()
    return [PSCustomObject]@{
      ExitCode = $Process.ExitCode
    }    
  }
  catch{
    throw $_
  }
}
#EndRegion '.\Private\Invoke-Process.ps1' 32
#Region '.\Private\New-AppFactoryAppFolder.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to create a new application to be handled by the automated process
  .PARAMETER DisplayName
  The DisplayName of the application that we are creating
  .PARAMETER force
  Continue even if the folder already exists, overwriting the current details
  .PARAMETER LogLevel
  If logging is enabled, what level of logging do we want, default is verbose.
 
  .EXAMPLE
 
  Create a new application folder
  New-AppFactoryAppFolder -DisplayName "### DisplayName ###" -LogLevel "Output"
#>

function New-AppFactoryAppFolder {
  [CmdletBinding()]
  [OutputType([string])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]$DisplayName,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]$folderName,
    [Parameter()][bool]$force,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  # Path to the new app folder
  $appFolderPath = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath $folderName
  if ($script:AppFactoryLogging) {
    Write-PSFMessage -Message "[$($DisplayName)] Template folder used <c='green'>$($script:AppFactorySupportTemplateFolder)</c>" -Level $LogLevel -Tag "Application", "$($DisplayName)" -Target "Application Factory Service"
  }
  # Create New Application Folder
  try {
    if ((Test-Path $appFolderPath) -and -not $force) {
      throw "Folder Already Exists $($script:AppFactorySupportTemplateFolder) and force was not set"
    }
    New-Item -Path $appFolderPath -ItemType Directory -Force | Out-Null
    Write-PSFMessage -Message "[$($DisplayName)] Created application folder <c='green'>$($appFolderPath)</c>" -Level $LogLevel -Tag "Application", "$($DisplayName)" -Target "Application Factory Service"
  }
  catch {
    Write-PSFMessage -Message "Error Encountered: $($_)" -Level "Error" -Tag "Application", "$($DisplayName)" -Target "Application Factory Service"
    throw $_
  }
  try {
    # Placeholder Icon File
    $iconFile = Join-Path -Path $script:AppFactorySupportTemplateFolder -ChildPath "PSADT" -AdditionalChildPath "Assets", "AppIcon.png"
    Copy-Item -Path $iconFile -Destination "$($appFolderPath)\Icon.png" -ErrorAction Stop -Force
    Write-PSFMessage -Message "[$($DisplayName)] Copying Placeholder Icon file to <c='green'>$($appFolderPath)</c>" -Level $LogLevel -Tag "Application", "$($DisplayName)" -Target "Application Factory Service"
  }
  catch {
    Remove-Item -Path $appFolderPath -Force
    Write-PSFMessage -Message "Error Encountered: $($_)" -Level "Error" -Tag "Application", "$($DisplayName)" -Target "Application Factory Service"
    throw $_    
  }
  return $appFolderPath
}
#EndRegion '.\Private\New-AppFactoryAppFolder.ps1' 55
#Region '.\Private\New-AppFactoryClientDetectionRule.ps1' 0
function New-AppFactoryClientDetectionRule {
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(  
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$ApplicationFolder,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )  
  $rules = [System.Collections.Generic.List[PSCustomObject]]@() 
  $DetectionRules = $application.DetectionRule
  foreach ($DetectionRuleItem in $DetectionRules) {
    switch ($DetectionRuleItem.Type) {
      "MSI" {
        if ($script:AppFactoryClientLogging) {
          Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Adding MSI Detection Rule" -Level $LogLevel -Tag "Applications", "Intune", "$($application.Information.DisplayName)" -Target "Application Factory Client"
        }
        $DetectionRule = [ordered]@{
          "@odata.type"            = "#microsoft.graph.win32LobAppProductCodeDetection"
          "productCode"            = $DetectionRuleItem.ProductCode
          "productVersionOperator" = $DetectionRuleItem.ProductVersionOperator
          "productVersion"         = $DetectionRuleItem.ProductVersion
        }        
      } 
      "Script" {
        if ($script:AppFactoryClientLogging) {
          Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Adding Script Detection Rule" -Level $LogLevel -Tag "Applications", "Intune", "$($application.Information.DisplayName)" -Target "Application Factory Client"
        }
        # Create a PowerShell script based detection rule
        $ScriptContent = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((Get-Content -Path (Join-Path -Path $ApplicationFolder -ChildPath $DetectionRuleItem.ScriptFile) -Raw -Encoding UTF8)))
        $DetectionRule = [ordered]@{
          "@odata.type"           = "#microsoft.graph.win32LobAppPowerShellScriptDetection"
          "enforceSignatureCheck" = [System.Convert]::ToBoolean($DetectionRuleItem.EnforceSignatureCheck)
          "runAs32Bit"            = [System.Convert]::ToBoolean($DetectionRuleItem.RunAs32Bit)
          "scriptContent"         = $ScriptContent
        }
      } 
      "Registry" {
        if ($script:AppFactoryClientLogging) {
          Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Adding Registry Detection Rule" -Level $LogLevel -Tag "Applications", "Intune", "$($application.Information.DisplayName)" -Target "Application Factory Client"
        }
        switch ($DetectionRuleItem.DetectionMethod) {
          "Existence" {
            $DetectionRule = [ordered]@{
              "@odata.type"          = "#microsoft.graph.win32LobAppRegistryDetection"
              "operator"             = "notConfigured"
              "detectionValue"       = $null
              "check32BitOn64System" = [System.Convert]::ToBoolean($DetectionRuleItem.Check32BitOn64System)
              "keyPath"              = $DetectionRuleItem.KeyPath
              "valueName"            = $DetectionRuleItem.ValueName
              "detectionType"        = $DetectionRuleItem.DetectionType
            }
          }
          "VersionComparison" {
            $DetectionRule = [ordered]@{
              "@odata.type"          = "#microsoft.graph.win32LobAppRegistryDetection"
              "operator"             = $DetectionRuleItem.Operator
              "detectionValue"       = $DetectionRuleItem.Value
              "check32BitOn64System" = [System.Convert]::ToBoolean($DetectionRuleItem.Check32BitOn64System)
              "keyPath"              = $DetectionRuleItem.KeyPath
              "valueName"            = $DetectionRuleItem.ValueName
              "detectionType"        = "version"
            }
          }
          "StringComparison" {
            $DetectionRule = [ordered]@{
              "@odata.type"          = "#microsoft.graph.win32LobAppRegistryDetection"
              "operator"             = $DetectionRuleItem.Operator
              "detectionValue"       = $DetectionRuleItem.Value
              "check32BitOn64System" = [System.Convert]::ToBoolean($DetectionRuleItem.Check32BitOn64System)
              "keyPath"              = $DetectionRuleItem.KeyPath
              "valueName"            = $DetectionRuleItem.ValueName
              "detectionType"        = "string"
            }
          }
          "IntegerComparison" {
            $DetectionRule = [ordered]@{
              "@odata.type"          = "#microsoft.graph.win32LobAppRegistryDetection"
              "operator"             = $DetectionRuleItem.Operator
              "detectionValue"       = $DetectionRuleItem.Value
              "check32BitOn64System" = [System.Convert]::ToBoolean($DetectionRuleItem.Check32BitOn64System)
              "keyPath"              = $DetectionRuleItem.KeyPath
              "valueName"            = $DetectionRuleItem.ValueName
              "detectionType"        = "integer"
            }
          }
          
        }
      }
      "File" {
        if ($script:AppFactoryClientLogging) {
          Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Adding File Detection Rule" -Level $LogLevel -Tag "Applications", "Intune", "$($application.Information.DisplayName)" -Target "Application Factory Client"
        }
        switch ($DetectionRuleItem.DetectionMethod) {
          "Existence" {
            $DetectionRule = [ordered]@{
              "@odata.type" = "#microsoft.graph.win32LobAppFileSystemDetection"
              "operator" = "notConfigured"
              "detectionValue" = $null
              "path" = $DetectionRuleItem.Path
              "fileOrFolderName" = $DetectionRuleItem.FileOrFolder
              "check32BitOn64System" = [System.Convert]::ToBoolean($DetectionRuleItem.Check32BitOn64System)
              "detectionType" = $DetectionRuleItem.DetectionType
          }            
          }
          "DateModified" {
            [datatime]$InputObject = [datatime]$DetectionRuleItem.DateTimeValue
            $DateValueString = Get-Date -Year $InputObject.Year -Month $InputObject.Month -Day $InputObject.Day -Hour $InputObject.Hour -Minute $InputObject.Minute -Second $InputObject.Second -UFormat '+%Y-%m-%dT%H:%M:%S.000Z'
            $DetectionRule = [ordered]@{
              "@odata.type" = "#microsoft.graph.win32LobAppFileSystemDetection"
              "operator" = $DetectionRuleItem.Operator
              "detectionValue" = $DateValueString
              "path" = $DetectionRuleItem.Path
              "fileOrFolderName" = $DetectionRuleItem.FileOrFolder
              "check32BitOn64System" = [System.Convert]::ToBoolean($DetectionRuleItem.Check32BitOn64System)
              "detectionType" = "modifiedDate"
          }            
          }
          "DateCreated" {
            [datatime]$InputObject = [datatime]$DetectionRuleItem.DateTimeValue
            $DateValueString = Get-Date -Year $InputObject.Year -Month $InputObject.Month -Day $InputObject.Day -Hour $InputObject.Hour -Minute $InputObject.Minute -Second $InputObject.Second -UFormat '+%Y-%m-%dT%H:%M:%S.000Z'   
            $DetectionRule = [ordered]@{
              "@odata.type" = "#microsoft.graph.win32LobAppFileSystemDetection"
              "operator" = $DetectionRuleItem.Operator
              "detectionValue" = $DateValueString
              "path" = $DetectionRuleItem.Path
              "fileOrFolderName" = $DetectionRuleItem.FileOrFolder
              "check32BitOn64System" = [System.Convert]::ToBoolean($DetectionRuleItem.Check32BitOn64System)
              "detectionType" = "createdDate"
          }                     
          }
          "Version" {
            $DetectionRule = [ordered]@{
              "@odata.type" = "#microsoft.graph.win32LobAppFileSystemDetection"
              "operator" = $DetectionRuleItem.Operator
              "detectionValue" = $DetectionRuleItem.VersionValue
              "path" = $DetectionRuleItem.Path
              "fileOrFolderName" = $DetectionRuleItem.FileOrFolder
              "check32BitOn64System" = [System.Convert]::ToBoolean($DetectionRuleItem.Check32BitOn64System)
              "detectionType" = "version"
          }            
          }
          "Size" {
            $DetectionRule = [ordered]@{
              "@odata.type" = "#microsoft.graph.win32LobAppFileSystemDetection"
              "operator" = $DetectionRuleItem.Operator
              "detectionValue" = $DetectionRuleItem.SizeInMBValue
              "path" = $DetectionRuleItem.Path
              "fileOrFolderName" = $DetectionRuleItem.FileOrFolder
              "check32BitOn64System" = [System.Convert]::ToBoolean($DetectionRuleItem.Check32BitOn64System)
              "detectionType" = "sizeInMB"
          }            
          }
        }
      }      
    }
    # Add detection rule to list
    $rules.Add($DetectionRule) | Out-Null    
  }  
  return $rules  
}
#EndRegion '.\Private\New-AppFactoryClientDetectionRule.ps1' 161
#Region '.\Private\New-AppFactoryClientRequirementRule.ps1' 0
function New-AppFactoryClientRequirementRule {
  [CmdletBinding()]
  [OutputType([ordered])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  # Construct table for supported architectures
  $ArchitectureTable = @{
    "x64" = "x64"
    "x86" = "x86"
    "All" = "x64,x86"
  }
  # Construct table for supported operating systems
  $OperatingSystemTable = @{
    "W10_1607" = "1607"
    "W10_1703" = "1703"
    "W10_1709" = "1709"
    "W10_1803" = "1803"
    "W10_1809" = "1809"
    "W10_1903" = "1903"
    "W10_1909" = "1909"
    "W10_2004" = "2004"
    "W10_20H2" = "2H20"
    "W10_21H1" = "21H1"
    "W10_21H2" = "Windows10_21H2"
    "W10_22H2" = "Windows10_22H2"
    "W11_21H2" = "Windows11_21H2"
    "W11_22H2" = "Windows11_22H2"
    "W11_23H2" = "Windows11_23H2"
  }  
  $RequirementRule = [ordered]@{
    "applicableArchitectures"        = $ArchitectureTable[$application.RequirementRule.Architecture]
    "minimumSupportedWindowsRelease" = $OperatingSystemTable[$application.RequirementRule.MinimumSupportedWindowsRelease]
  }
  return $RequirementRule
}
#EndRegion '.\Private\New-AppFactoryClientRequirementRule.ps1' 38
#Region '.\Private\New-PSUGUIInputCode.ps1' 0
function New-PSUGUIInputCode{
  [cmdletbinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$id,
    [Parameter()][Object[]]$Code,
    [Parameter()][int]$item_height = 400,
    [Parameter()][int]$item_colspan = 1,
    [Parameter()][string]$language = "powershell"
  )
  New-UDElement -Tag "td" -Attributes @{
    Style = @{
      "vertical-align" = "bottom";
    }
    Colspan = $item_colspan
  } -Content {
    New-UDCodeEditor -Height $item_height -Language $language -id $id -Code ($Code -Join "`n")
  }  
}
#EndRegion '.\Private\New-PSUGUIInputCode.ps1' 19
#Region '.\Private\New-PSUGUIInputSelectGroup.ps1' 0
function New-PSUGUIInputSelectGroup {
  [cmdletbinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$Label,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$id,
    [Parameter()][string[]]$options,
    [Parameter()][string[]]$defaultValue = @(),
    [Parameter()][string]$placeholder = "",
    [Parameter()][string]$onchangeAction = "",
    [Parameter()][int]$label_width = 20,
    [Parameter()][int]$item_width = 30,
    [Parameter()][int]$label_colspan = 1,
    [Parameter()][int]$item_colspan = 1,    
    [Parameter()][switch]$multiselect   
  )
  New-UDElement -Tag "td" -Content {
    New-UDElement -Tag "span" -ClassName "appfactory-label" -Content { New-UDTypography -Text $Label }
  } -Attributes @{"style" = @{"width" = "$($label_width)%"; "vertical-align" = "bottom"; }; Colspan = $label_colspan }
  New-UDElement -Tag "td" -Content {
    New-UDSelect -Multiple:$($multiselect.isPresent) -ClassName "appfactory-select" -DefaultValue $defaultValue -id $id -FullWidth -Option {
      foreach ($option in $options) {
        New-UDSelectOption -Name $option -Value $option
      }
    } -OnChange {
      switch ($onchangeAction) {
        "SourceSelection" {
          Set-UDElement -Id "SourceSelectionFields" -Content {}
          Set-UDElement -Id "SourceSelectionFields" -Content {
            switch ((Get-UDElement $id).value) {
              "StorageAccount" {
                New-PSUGUISourceStorageAccount
              }
              "Sharepoint" {
                New-PSUGUISourceSharepoint
              }
              "Winget" {
                New-PSUGUISourceWinget
              }
              "Evergreen" {
                New-PSUGUISourceEvergreen
              }
              "ECNO" {
                New-PSUGUISourceECNO
              }
              "LocalStorage" {
                New-PSUGUISourceStorageAccount
              }
              "PSADT" {
                New-PSUGUISourcePSADT
              }       
            }
          }
        }
        "InstallSelection" {
          Set-UDElement -Id "InstallSelectionFields" -Content {}
          Set-UDElement -Id "InstallSelectionFields" -Content {
            switch ((Get-UDElement $id).value) {
              "Script" {
                New-PSUGUIInstallScript
              }
              "EXE" {
                New-PSUGUIInstallEXE
              }
              "MSI" {
                New-PSUGUIInstallMSI
              }
            }
          }          
        }
        "UninstallSelection" {
          Set-UDElement -Id "UninstallSelectionFields" -Content {}
          Set-UDElement -Id "UninstallSelectionFields" -Content {
            switch ((Get-UDElement $id).value) {
              "Script" {
                New-PSUGUIUnInstallScript
              }
              "EXE" {
                New-PSUGUIUnInstallEXE
              }
              "MSI" {
                New-PSUGUIUnInstallMSI
              }
              "Name" {
                New-PSUGUIUninstallName
              }
              "GUID" {
                New-PSUGUIUninstallGUID
              }  
            }
          }          
        }        
        "DetectionSelection" {
          Set-UDElement -Id "DetectionSelectionFields" -Content {}
          Set-UDElement -Id "DetectionSelectionFields" -Content {
            switch ((Get-UDElement $id).value) {
              "MSI" {
                New-PSUGUIDetectionMSI
              }
              "Registry Version" {
                New-PSUGUIDetectionRegistryVersion
              }
              "Registry Existance" {
                New-PSUGUIDetectionRegistryExistance
              }              
              "Script" {
                New-PSUGUIDetectionScript
              }
            }
          }
        }
        "AvailableVersionsSelection" {
          Set-UDElement -id "DeleteVersion" -Properties @{
            Disabled = $false
          }
        }
        default {}
      }
    }
  } -Attributes @{"style" = @{"width" = "$($item_width)%"; "vertical-align" = "bottom" }; Colspan = $item_colspan }  
}  
#EndRegion '.\Private\New-PSUGUIInputSelectGroup.ps1' 121
#Region '.\Private\New-PSUGUIInputTextboxGroup.ps1' 0
function New-PSUGUIInputTextboxGroup {
  [cmdletbinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$Label,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$id,
    [Parameter()][switch]$disabled,
    [Parameter()][string]$placeholder = "",
    [Parameter()][int]$label_width = 20,
    [Parameter()][int]$item_width = 30,
    [Parameter()][int]$label_colspan = 1,
    [Parameter()][int]$item_colspan = 1,
    [Parameter()][int]$rows = 5
  )
  New-UDElement -Tag "td" -Attributes @{
    Style = @{
      width = $label_width
      "vertical-align" = "top";
    }
    Colspan = $label_colspan
  } -Content {
    New-UDElement -Tag "span" -ClassName "appfactory-label" -Content { New-UDTypography -Text $Label }
  }  
  New-UDElement -Tag "td" -Attributes @{
    Style = @{
      width = $item_width
      "vertical-align" = "bottom";
    }
    Colspan = $item_colspan
  } -Content {
    New-UDTextbox -Multiline -Rows $rows -FullWidth -id $id -ClassName "appfactory-textbox" -Disabled:$disabled.IsPresent
  }
}
#EndRegion '.\Private\New-PSUGUIInputTextboxGroup.ps1' 33
#Region '.\Private\New-PSUGUIInputTextGroup.ps1' 0
function New-PSUGUIInputTextGroup {
  [cmdletbinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$Label,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$id,
    [Parameter()][switch]$disabled,
    [Parameter()][string]$value = "",
    [Parameter()][string]$placeholder = "",
    [Parameter()][int]$label_width = 20,
    [Parameter()][int]$item_width = 30,
    [Parameter()][int]$label_colspan = 1,
    [Parameter()][int]$item_colspan = 1
  )
  New-UDElement -Tag "td" -Attributes @{
    Style   = @{
      width            = $label_width
      "vertical-align" = "bottom";
    }
    Colspan = $label_colspan
  } -Content {
    New-UDElement -Tag "span" -ClassName "appfactory-label" -Content { New-UDTypography -Text $Label }
  }  
  New-UDElement -Tag "td" -Attributes @{
    Style   = @{
      width            = $item_width
      "vertical-align" = "bottom";
    }
    Colspan = $item_colspan
  } -Content {
    #New-UDElement -Tag "input" -ClassName "appfactory-input" -id $id -Attributes @{
    # value = $value
    # placeholder = $placeholder
    # "aria-label" = $placeholder
    # type = "input"
    #}
    New-UDTextBox -FullWidth -Placeholder $placeholder -id $id -Value $value -ClassName "appfactory-input" -Disabled:$disabled.IsPresent
  }
}
#EndRegion '.\Private\New-PSUGUIInputTextGroup.ps1' 39
#Region '.\Private\New-PSUGUISwitch.ps1' 0
function New-PSUGUISwitch{
  [cmdletbinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$Label,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$id,
    [Parameter()][switch]$checked,
    [Parameter()][int]$label_width = 20,
    [Parameter()][int]$label_colspan = 1,
    [Parameter()][int]$item_colspan = 1
  )  
  New-UDElement -Tag "td" -Attributes @{
    Style = @{
      width = $label_width
      "vertical-align" = "bottom";
    }
    Colspan = $label_colspan
  } -Content {
    New-UDElement -Tag "span" -ClassName "appfactory-label" -Content { New-UDTypography -Text $Label }
  }  
  New-UDElement -Tag "td" -Attributes @{
    Style = @{
      width = $item_width
      "vertical-align" = "bottom";
    }
    Colspan = $item_colspan
  } -Content {
    New-UDSwitch -id $id -Checked $checked.IsPresent
  }  
}
#EndRegion '.\Private\New-PSUGUISwitch.ps1' 30
#Region '.\Private\New-PSUGUUploadIcon.ps1' 0
function New-PSUGUUploadIcon {
  [cmdletbinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$Label,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$id,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$output,
    [Parameter()][string]$default_icon,
    [Parameter()][string]$placeholder = "",
    [Parameter()][int]$label_width = 20,
    [Parameter()][int]$item_width = 30,
    [Parameter()][int]$label_colspan = 1,
    [Parameter()][int]$item_colspan = 1
  )
  New-UDElement -Tag "td" -Attributes @{
    Style   = @{
      width            = $label_width
      "vertical-align" = "top";
    }
    Colspan = $label_colspan
  } -Content {
    New-UDElement -Tag "span" -ClassName "appfactory-label" -Content { New-UDTypography -Text $Label }
  }  
  New-UDElement -Tag "td" -Attributes @{
    Style = @{
      width = $item_width
      "vertical-align" = "bottom";
    }
    Colspan = $item_colspan
  } -Content {
    if([String]::IsNullOrWhiteSpace($default_icon)){
      $default_icon = Join-Path -Path $script:AppFactorySupportTemplateFolder -ChildPath "PSADT" -AdditionalChildPath "Assets","AppIcon.png"
    }
    New-UDElement -Tag "table" -Content {
      New-UDElement -Tag "tr" -Content {
        New-UDElement -Tag "td" -Content {
          New-UDUpload -Id $id -Text $placeholder -OnUpload {
            $Data = $Body | ConvertFrom-Json
            $bytes = [System.Convert]::FromBase64String($Data.Data)
            [System.IO.File]::WriteAllBytes("$($env:TEMP)\PSUApp_Icon.png", $bytes)    
            Set-UDElement -Id "imgdiv" -Content {}
            Set-UDElement -Id "imgdiv" -Content {
              New-UDImage -Id "appimage" -Path "$($env:TEMP)\PSUApp_Icon.png" -Width 50 -Height 50
            }   
          }
        }
        New-UDElement -Tag "td" -Content {
          New-UDElement -Tag "div" -Id "imgdiv" -Content {
            New-UDImage -Id "appimage" -Path $default_icon -Width 50 -Height 50
          }          
        }
      }
    }
    #New-UDTextbox -Multiline -Rows 6 -FullWidth -id $id -ClassName "appfactory-textbox"
  }
}
#EndRegion '.\Private\New-PSUGUUploadIcon.ps1' 56
#Region '.\Private\Write-AppConfiguration.ps1' 0
function Write-AppConfiguration{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$configfile,
    [Parameter()][ValidateSet("Output","Verbose")][string]$LogLevel = "Verbose"
  )  
  try{
    $outputPath = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath $configfile.Information.AppFolderName, "ApplicationConfig.json"
    $configfile | ConvertTo-Json -Depth 10 | Out-File -FilePath $outputPath
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "[$($configfile.Information.DisplayName)] Wrote default app configuration" -Level $LogLevel -Tag "Application", "$($configfile.Information.DisplayName)", "$($configFIle.GUID)" -Target "AppFactory" 
    }    
  }
  catch{
    throw $_ 
  }    
}
#EndRegion '.\Private\Write-AppConfiguration.ps1' 18
#Region '.\Public\Add-AppFactoryClientAppAssignments.ps1' 0
function Add-AppFactoryClientAppAssignments {
  [CmdletBinding()]
  param(  
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$intuneid,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  )  
  if ($script:AppFactoryClientLogging) {
    Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Adding Application Assignments" -Level $LogLevel -Tag "Applications", "Intune", "$($application.IntuneAppName)" -Target "Application Factory Client"
  }
  $commonVariables = @{
    "applicationid" = $intuneid
    "filters"       = $application.filters
  }
  try {
    if ($application.AvailableAssignments -ne "") { Add-GraphIntuneAppAssignment @commonVariables -intent available -groups $application.AvailableAssignments -foreground $application.foreground }
    if ($application.AvailableExceptions -ne "") { Add-GraphIntuneAppAssignment @commonVariables -intent available -groups $application.AvailableExceptions -exclude }
    if ($application.RequiredAssignments -ne "") { Add-GraphIntuneAppAssignment @commonVariables -intent required -groups $application.RequiredAssignments -foreground $application.foreground }
    if ($application.RequiredExceptions -ne "") { Add-GraphIntuneAppAssignment @commonVariables -intent required -groups $application.RequiredExceptions -exclude }
    if ($application.UninstallAssignments -ne "") { Add-GraphIntuneAppAssignment @commonVariables -intent uninstall -groups $application.UninstallAssignments -foreground $application.foreground }
    if ($application.UninstallExceptions -ne "") { Add-GraphIntuneAppAssignment @commonVariables -intent uninstall -groups $application.UninstallExceptions -exclude }
  }
  catch {
    $_
  }
}
#EndRegion '.\Public\Add-AppFactoryClientAppAssignments.ps1' 27
#Region '.\Public\Add-AppFactoryClientESPAssignment.ps1' 0
function Add-AppFactoryClientESPAssignment {
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(  
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$intuneid,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  )
  # Loop through applications
  if ($application.espprofiles) {
    foreach ($esp in $application.espprofiles) {
      if ($script:AppFactoryClientLogging) {
        Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Assigning application to ESP Porfile $($esp)" -Level $LogLevel -Tag "Applications", "Intune", "$($application.IntuneAppName)" -Target "Application Factory Client"
      }
      Add-GraphIntuneAppAddToESP -displayName $esp -applicationid $intuneid
    }
  }
}
#EndRegion '.\Public\Add-AppFactoryClientESPAssignment.ps1' 19
#Region '.\Public\Compare-AppFactoryClientAppVersions.ps1' 0
function Compare-AppFactoryClientAppVersions {
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(  
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][System.Collections.Generic.List[PSCustomObject]]$applicationList,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][System.Collections.Generic.List[PSCustomObject]]$intuneApplications,
    [Parameter()][ValidateNotNullOrEmpty()][switch]$force,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  )
  # Create blank list to store the applications that we will be moving forward with.
  $applications = [System.Collections.Generic.List[PSCustomObject]]@()
  # Loop through the applications
  foreach ($application in $applicationList) {
    if ($script:AppFactoryClientLogging) {
      Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Comparing Version (<c='green'>$($application.AppVersion)</c>) between expected and published" -Level $LogLevel -Tag "Applications", "$($application.IntuneAppName)" -Target "Application Factory Client"
    }
    $intuneApplication = $intuneApplications | Where-Object { $_.Notes -match "STSID:$($application.GUID)" }
    if($null -eq $intuneApplication.displayVersion -or $application.AppVersion -notin $intuneApplication.displayVersion){
      if ($script:AppFactoryClientLogging) {
        Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Current Published Versions: <c='green'>$($intuneApplication.displayVersion -join ",")</c>" -Level $LogLevel -Tag "Applications", "$($application.IntuneAppName)" -Target "Application Factory Client"
        Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Current Expected Version: <c='green'>$($application.AppVersion)</c>" -Level $LogLevel -Tag "Applications", "$($application.IntuneAppName)" -Target "Application Factory Client"
      }
      $applications.Add($application) | Out-Null
    }
    else{
      Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Application version <c='green'>$($intuneApplication.displayVersion -join ",")</c> already published" -Level $LogLevel -Tag "Applications", "$($application.IntuneAppName)" -Target "Application Factory Client"
    }
  }
  return $applications 
}
#EndRegion '.\Public\Compare-AppFactoryClientAppVersions.ps1' 31
#Region '.\Public\Copy-AppFactoryClientAppAssignments.ps1' 0
function Copy-AppFactoryClientAppAssignments {
  [CmdletBinding()]
  param(  
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$intuneid,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][System.Collections.Generic.List[PSCustomObject]]$intuneApplications,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  )  
  if ($application.CopyPrevious) {
    if ($script:AppFactoryClientLogging) {
      Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Copying Application Assignments" -Level $LogLevel -Tag "Applications", "Intune", "$($application.IntuneAppName)" -Target "Application Factory Client"
    }
    $applications = $intuneApplications | Where-Object { $_.Notes -match "STSID:$($application.GUID)" }  | Select-Object id, displayname, @{Label = "Version"; expression = { [version]$_.displayversion } } |  Sort-Object -Property "Version" -Descending
    if ($applications.count -gt 0) {
      Copy-GraphIntuneAppAssignments -applicationid $intuneid -copyapplicationid $applications[0].id
    }
  }    
}
#EndRegion '.\Public\Copy-AppFactoryClientAppAssignments.ps1' 19
#Region '.\Public\Get-AppFactoryApp.ps1' 0
<#
  .DESCRIPTION
  This cmdlet returns a list of applications that are part of the packaging process
  .PARAMETER appSource
  This is a filtering parameter for specific appsources
  .PARAMETER GUID
  The unique identifier for the application that we want to work with
  .PARAMETER displayName
  The unique name of the application that we want to work with
  .PARAMETER AppID
  This is a filtering parameter for specific AppID
  .PARAMETER AppPublisher
  This is a filtering parameter for specific application publisher
  .PARAMETER StorageAccountContainerName
  This is a filtering parameter for specific application azure storage container
  .PARAMETER publishTo
  This is a filtering parameter for specific organization
  .PARAMETER public
  This is a filtering parameter for all public applications
  .PARAMETER LogLevel
  If logging is enabled, what level of logging do we want, default is verbose.
 
  .EXAMPLE
 
  Get a list of all applications
  Get-AppFactoryApp
 
  Get a list of specific application sources
  Get-AppFactoryApp -appSource "### Application Source ###"
 
  Get a list of applications based on GUID
  Get-AppFactoryApp -GUID "### GUID ###"
 
  Get a list of applications based on AppID
  Get-AppFactoryApp -AppID "### AppID ###"
 
  Get a list of applications based on Application Publisher
  Get-AppFactoryApp -AppPublisher "### Publisher ###"
 
  Get a list of applications based on Publish To a client
  Get-AppFactoryApp -publishTo "### Client ###"
 
  Get a list of all public applications
  Get-AppFactoryApp -public
 
  Get a list of all application with logging enabled
  Get-AppFactoryApp -LogLevel "Output"
#>

function Get-AppFactoryApp{
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Parameter()][ValidateSet("StorageAccount", "Sharepoint", "Winget", "Evergreen", "PSADT", "ECNO", "LocalStorage")][string]$appSource,
    [Parameter()][ValidateNotNullOrEmpty()][string]$appGUID,
    [Parameter()][ValidateNotNullOrEmpty()][string]$displayName,
    [Parameter()][ValidateNotNullOrEmpty()][string]$AppID,
    [Parameter()][ValidateNotNullOrEmpty()][string]$Publisher,
    [Parameter()][ValidateNotNullOrEmpty()][string]$StorageAccountContainerName,
    [Parameter()][ValidateNotNullOrEmpty()][string]$publishTo,
    [Parameter()][ValidateNotNullOrEmpty()][string]$dependsOn,
    [Parameter()][switch]$public,
    [Parameter()][switch]$active,
    [Parameter()][ValidateSet("Output","Verbose")][string]$LogLevel = "Verbose"
  )  
  # Get the path to where the application configs are stored
  $applicationFolders = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps"
  # Get a list of all the configuration files for the applications
  $ApplicationConfigFiles = Get-Childitem -Path $applicationFolders -Recurse -Filter "ApplicationConfig.json"  
  if($script:AppFactoryLogging){
    Write-PSFMessage -Message "Retrieved Application Configurations at path <c='green'>$($applicationFolders)</c>" -Level $LogLevel -Tag "Application" -Target "Application Factory Service"
  }  
  # Create an empty array to store the application objects
  $applicaitonList =  [System.Collections.Generic.List[PSCustomObject]]@()
  # Perform any needed filtering based on passed variables
  foreach($file in $ApplicationConfigFiles){
    $json = Get-Content $file.FullName | ConvertFrom-Json
    # If specific parameters are passed, filter the list of applications based on those parameters
    if($PSBoundParameters.ContainsKey("appGUID") -and $json.GUID -ne $appGUID){continue}
    if($PSBoundParameters.ContainsKey("appSource") -and $json.SourceFiles.appSource -ne $appSource){continue}
    if($PSBoundParameters.ContainsKey("displayName") -and $json.Information.displayName -notlike "$($displayName)"){continue}
    if($PSBoundParameters.ContainsKey("AppID") -and $json.SourceFiles.AppID -ne $AppID){continue}
    if($PSBoundParameters.ContainsKey("Publisher") -and $json.Information.Publisher -ne $AppPublisher){continue}
    if($PSBoundParameters.ContainsKey("StorageAccountContainerName") -and $json.SourceFiles.StorageAccountContainerName -ne $StorageAccountContainerName){continue}
    if($PSBoundParameters.ContainsKey("publishTo") -and $publishTo -notin $json.SourceFiles.publishTo){continue}
    if($PSBoundParameters.ContainsKey("dependsOn") -and $dependsOn -notin $json.SourceFiles.dependsOn){continue}
    if($PSBoundParameters.ContainsKey("dependsOn") -and $dependsOn -notin $json.SourceFiles.dependsOn){continue}
    if($active.IsPresent -and (-not $json.SourceFiles.Active)){continue}
    if($public.IsPresent -and $json.SourceFiles.publishTo.count -ne 0){continue}
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "Reading Application: <c='green'>$($json.Information.displayName) ($($json.GUID))</c>"-Level $LogLevel -Tag "Application","$($json.displayName)","$($json.GUID)" -Target "Application Factory Service"
    }
    $applicaitonList.Add($json) | Out-Null
  }
  # Return the application list
  return $applicaitonList  
}
#EndRegion '.\Public\Get-AppFactoryApp.ps1' 97
#Region '.\Public\Get-AppFactoryAppInstall.ps1' 0
function Get-AppFactoryAppInstall{
  [CmdletBinding()]
  [OutputType([Hashtable])]
  param(  
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$appGUID,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  )
  # Get the application Config
  $configfile = Get-AppFactoryApp -appGUID $appGUID -LogLevel $LogLevel
  if (-not ($configfile)) {
    throw "Application with GUID $($GUID) does not exist."
  } 
  return $configfile.install  
}
#EndRegion '.\Public\Get-AppFactoryAppInstall.ps1' 15
#Region '.\Public\Get-AppFactoryAppUninstall.ps1' 0
function Get-AppFactoryAppUninstall{
  [CmdletBinding()]
  [OutputType([Hashtable])]
  param(  
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$appGUID,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  )
  # Get the application Config
  $configfile = Get-AppFactoryApp -appGUID $appGUID -LogLevel $LogLevel
  if (-not ($configfile)) {
    throw "Application with GUID $($GUID) does not exist."
  } 
  return $configfile.uninstall    
}
#EndRegion '.\Public\Get-AppFactoryAppUninstall.ps1' 15
#Region '.\Public\Get-AppFactoryClientApp.ps1' 0
function Get-AppFactoryClientApp{
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Parameter()][string]$appGUID,
    [Parameter()][string]$AppName,
    [Parameter()][bool]$AddToIntune,
    [Parameter()][string]$AvailableAssignments,
    [Parameter()][string]$AvailableExceptions,
    [Parameter()][string]$RequiredAssignments,
    [Parameter()][string]$RequiredExceptions,
    [Parameter()][string]$UninstallAssignments,
    [Parameter()][string]$UninstallExceptions,
    [Parameter()][ValidateSet("Output","Verbose")][string]$LogLevel = "Verbose"
  )
  # App Folders
  $applicationFolderPath = Join-Path -Path $script:AppFactoryClientSourceDir -ChildPath "Apps"
  # Get All Applications
  $applicationConfigFiles = Get-Childitem -Path $applicationFolderPath
  # Blank List for the Apps
  $applicationList =  [System.Collections.Generic.List[PSCustomObject]]@()
  # Loop through the configuration and add to the list.
  foreach($file in $applicationConfigFiles){
    $json = Get-Content $file.FullName | ConvertFrom-Json
    $skip = $false
    foreach($param in ($PSBoundParameters.GetEnumerator() | Where-Object {$_.Key -ne "LogLevel"})){
      if($json.$($param.Key).getType().BaseType.Name -eq "Array"){
        if($param.Value -notin $json.$($param.Key) ){$skip = $true; break}
      }
      else{
        if($param.Value -ne $json.$($param.Key)){$skip = $true; break}
      }
    }
    if(-not $skip){
      $applicationList.Add($json) | Out-Null
    }
  } 
  return $applicationList   
}
#EndRegion '.\Public\Get-AppFactoryClientApp.ps1' 40
#Region '.\Public\Get-AppFactoryClientAppFiles.ps1' 0
function Get-AppFactoryClientAppFiles {
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(  
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$applications,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  )
  $downloadFolder = Join-Path -Path $script:AppFactoryClientWorkspace -ChildPath "Downloads" -AdditionalChildPath $application.IntuneAppName
  if(Test-Path $downloadFolder){
    Remove-Item -Path $downloadFolder -Force -Recurse -ErrorAction SilentlyContinue
  }
  New-Item -Path $downloadFolder -ItemType Directory | Out-Null
  $container = $script:AppFactoryClientSASPublicContainerName
  $sas = $script:AppFactoryClientSASpublic
  if($application.container -ne $script:AppFactoryClientSASPublicContainerName){
    $container = $script:AppFactoryClientSASOrganizationContainerName
    $sas = $script:AppFactoryClientSASorganization
  }
  $endpoint = "$($script:AppFactoryClientSASStoragePath)/$($container)/$($application.GUID)/$($application.AppVersion)"
  if($script:AppFactoryClientLogging){
    Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Downloading files from $($endpoint)" -Level $LogLevel -Tag "Applications","$($application.IntuneAppName)" -Target "Application Factory Client"
  }
  $filelist = @("$($application.IntuneAppName).intunewin","App.json")
  foreach($file in $filelist){
    try{
      Invoke-WebRequest -Uri "$($endpoint)/$($file)?$($sas)" -OutFile "$($downloadFolder)\$($file)" | Out-Null
    }
    catch{
      throw "[<c='green'>$($application.IntuneAppName)</c>] Failed to download $($file) from $($baseURI). $($_.Exception.Message)"
    }    
  }
  $AppData = Get-Content "$($downloadFolder)\App.json" | ConvertFrom-JSON
  $downloadFiles = [System.Collections.Generic.List[String]]@()
  $downloadFiles.Add($AppData.PackageInformation.IconFile) | Out-Null
  if($AppData.DetectionRule.Type -eq "Script"){
    $downloadFiles.Add($AppData.DetectionRule.ScriptFile) | Out-Null
  }
  foreach($file in $downloadFiles){
    try{
      Invoke-WebRequest -Uri "$($endpoint)/$($file)?$($sas)" -OutFile "$($downloadFolder)\$($file)" | Out-Null
    }
    catch{
      throw "[<c='green'>$($application.IntuneAppName)</c>] Failed to download $($file) from $($baseURI). $($_.Exception.Message)"
    }
  }  
}
#EndRegion '.\Public\Get-AppFactoryClientAppFiles.ps1' 47
#Region '.\Public\Get-AppFactoryClientAppList.ps1' 0
function Get-AppFactoryClientAppList {
  [CmdletBinding()]
  param(
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"    
  )
  # Where to save file
  $ApplicationFolder = Join-Path -Path $script:AppFactoryClientSourceDir -ChildPath "Apps" 
  # Create the folder if it doesn't exist
  Remove-Item -Path $ApplicationFolder -Force -Recurse
  if (-not (Test-Path -Path $ApplicationFolder)) {
    New-Item -Path $ApplicationFolder -ItemType Directory | Out-Null
  }
  # What is the PSU Endpoint we should be using
  $PSUEndpoint = "$($script:AppFactoryClientApiEndpoint)/AppList"   
  # Retrieve Data
  $headers = @{
    "content-type"  = "application/json"
    "authorization" = "bearer $(ConvertFrom-SecureString $script:AppFactoryClientAPISecret -AsPlainText)"
  }
  $applicationList = Invoke-RestMethod -Method "GET" -Uri $PSUEndpoint -Headers $headers -StatusCodeVariable "statusCode"
  foreach($application in $applicationList){
    $applicationPath = Join-Path -Path $ApplicationFolder -ChildPath "$($application.IntuneAppName).json"
    $application | ConvertTo-Json -Depth 10 | Out-File -FilePath $applicationPath -Force
  }
  
}
#EndRegion '.\Public\Get-AppFactoryClientAppList.ps1' 27
#Region '.\Public\Get-AppFactoryClientGraphApp.ps1' 0
function Get-AppFactoryClientGraphApp{
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Parameter()][ValidateSet("Output","Verbose")][string]$LogLevel = "Verbose"
  )
  Get-GraphAccessToken -clientID $script:AppFactoryClientClientID -tenantID $script:AppFactoryClientTenantID  -clientSecret $script:AppFactoryClientAppRegSecret | Out-Null
  $intuneApplications = Get-GraphIntuneApp -type "microsoft.graph.win32LobApp"
  return $intuneApplications
}
#EndRegion '.\Public\Get-AppFactoryClientGraphApp.ps1' 11
#Region '.\Public\Get-AppFactoryClientSAS.ps1' 0
function Get-AppFactoryClientSAS{
  [CmdletBinding()]
  param(
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"    
  )
  # What is the PSU Endpoint we should be using
  $PSUEndpoint = "$($script:AppFactoryClientApiEndpoint)/SAS"
  # Retrieve Data
  $headers = @{
    "content-type"  = "application/json"
    "authorization" = "bearer $(ConvertFrom-SecureString $script:AppFactoryClientAPISecret -AsPlainText)"
  }
  $sas = Invoke-RestMethod -Method "GET" -Uri $PSUEndpoint -Headers $headers -StatusCodeVariable "statusCode" 
  $script:AppFactoryClientSASorganization = $sas.organization
  $script:AppFactoryClientSASPublicContainerName = $sas.PublicContainerName
  $script:AppFactoryClientSASStoragePath = $sas.StoragePath
  $script:AppFactoryClientSASOrganizationContainerName = $sas.OrganizationContainerName
  $script:AppFactoryClientSASpublic = $sas.public
}
#EndRegion '.\Public\Get-AppFactoryClientSAS.ps1' 20
#Region '.\Public\Get-AppFactoryInstaller.ps1' 0
function Get-AppFactoryInstaller {
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][System.Collections.Generic.List[PSCustomObject]]$applicationList,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"    
  )
  # Create list to store results of the process
  $applications = [System.Collections.Generic.List[PSCustomObject]]@()
  foreach ($application in $applicationList) {
    try {
      $AppSetupFolderPath = Join-Path -Path $script:AppFactoryWorkspace -ChildPath "Installers" -AdditionalChildPath $application.Information.AppFolderName
      # Create path if it doesn't exist
      if (-not(Test-Path -Path $AppSetupFolderPath -PathType "Container")) {
        try {
          New-Item -Path $AppSetupFolderPath -ItemType "Container" -ErrorAction "Stop" | Out-Null
        }
        catch [System.Exception] {
          throw "[$($application.Information.DisplayName)] Failed to create '$($Path)' with error message: $($_.Exception.Message)"
        }
      }
      # Download installer file
      try {
        $OutFilePath = Join-Path -Path $AppSetupFolderPath -ChildPath $application.SourceFiles.AppSetupFileName
        if ($script:AppFactoryLogging) {
          Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Downloading setupfile <c='green'>$($application.SourceFiles.PackageSource)</c>" -Level $LogLevel -Tag "Application", "$($application.Information.DisplayName)", "Download" -Target "Application Factory Service"
        }
        switch ($Application.SourceFiles.AppSource) {
          "ECNO" {
            Get-AppFactoryECNOFile -application $application -Destination $AppSetupFolderPath -LogLevel $LogLevel
          }
          "Sharepoint" {
            Get-AppFactorySharepointFile -application $application -Destination $AppSetupFolderPath -LogLevel $LogLevel
          }
          "LocalStorage" {
            Get-ChildItem -Path $application.SourceFiles.PackageSource | foreach-object {Copy-Item $_.FullName -Destination $AppSetupFolderPath -Force -ErrorAction "Stop" -Recurse} | Out-Null
          }
          "StorageAccount" {
            Get-AppFactoryAzureStorageFile -application $application -Destination $AppSetupFolderPath -LogLevel $LogLevel 
          }
          "PSADT" {}
          default {
            Invoke-WebRequest -Uri $application.SourceFiles.PackageSource -OutFile $OutFilePath -UseBasicParsing -ErrorAction "Stop" 
          }
        }
      }
      catch [System.Exception] {
        throw "[$($application.Information.DisplayName)] Failed to download file from '$($application.SourceFiles.PackageSource)' with error message: $($_.Exception.Message)"
      }
    }
    catch {
      if ($script:AppFactoryLogging) {
        Write-PSFMessage -Message $_ -Level "Error" -Tag "Application", "$($application.Information.DisplayName)", "Error" -Target "Application Factory Service"
      }
      continue
    }
    # Download Extra Files
    try{
      if($Application.SourceFiles.ExtraFiles){
        $OutFilePath = Join-Path -Path $AppSetupFolderPath -ChildPath $application.SourceFiles.AppSetupFileName
        Get-AppFactoryExtraFiles -application $application -Destination $AppSetupFolderPath -LogLevel $LogLevel 
      }
    }
    catch {
      if ($script:AppFactoryLogging) {
        Write-PSFMessage -Message $_ -Level "Error" -Tag "Application", "$($application.Information.DisplayName)", "Error" -Target "Application Factory Service"
      }
      continue
    }

    if ($script:AppFactoryLogging) {
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Successfully downloaded files." -Level $LogLevel -Tag "Application", "$($application.Information.DisplayName)", "WinGet" -Target "AppFactory"
    }    
    $applications.Add($application)
  }
  return $applications
}
#EndRegion '.\Public\Get-AppFactoryInstaller.ps1' 78
#Region '.\Public\Get-AppFactoryPSUApplications.ps1' 0
function Get-AppFactoryPSUApplications {
  [cmdletbinding()]
  param()
  New-UDElement -Tag "figure" -ClassName "text-center" -Content {
    New-UDElement -Tag "h4" -ClassName "display-4" -Content { "Application Factory Applications" }
  }  
  New-UDElement -Tag "div" -ClassName "container-fluid" -Content {
    New-UDElement -Tag "div" -ClassName "row justify-content-start" -Content {
      New-UDElement -Tag "div" -ClassName "col-5" -Content {
        New-UDElement -Tag "div" -ClassName "card-body rounded" -Content {
          New-UDTypography -Text "Application List" -Variant "h5" -ClassName "card-title rounded x-card-title"
          New-UDElement -Tag "div" -id "ApplicationList" -Content {
            Initialize-AppFactoryProcess -ApplicationServicePath $AppFactory_ApplicationPath
            New-UDDataGrid -id "ApplicationListTableData" -LoadRows {
              Initialize-AppFactoryProcess -ApplicationServicePath $AppFactory_ApplicationPath
              $ClientList = Get-AppFactoryServiceClient
              $TableData = Get-AppFactoryApp | Select-Object -Property @{Label = "ID"; expression = { $_.GUID } }, @{Label = "Name"; expression = { $_.Information.DisplayName } }, @{Label = "Availability"; expression = { 
                  if ($_.SourceFiles.publishTo.count -eq 0) {
                    "All"
                  }
                  else {
                    $orgList = [System.Collections.Generic.List[String]]::new()
                    foreach ($obj in $_.SourceFiles.publishTo) {
                      $orgList.Add(($ClientList | Where-Object { $_.GUID -eq $obj }).Name) | Out-Null
                    }
                    $orgList -join ", "
                  }
                }
              }, @{Label = "Active"; expression = { $_.SourceFiles.Active } }, @{Label = "Source"; expression = { $_.SourceFiles.AppSource } }, @{Label = "Updated"; expression = { (Get-Date -Date $_.SourceFiles.LastUpdate).ToString("yyyy/MM/dd HH:mm:ss") } }, Information, SourceFiles, Install, Uninstall, RequirementRule, Program, DetectionRule
              $TableData | Out-UDDataGridData -Context $EventData -TotalRows $Rows.Length
            } -Columns @(
              New-UDDataGridColumn -Field Name -Flex 1.5 -Render {
                $EventData.Name
                New-UDElement -tag "div" -content {} -Attributes @{style = @{width = "10px"}}
                if($null -ne $EventData.Information.InformationURL -and $EventData.Information.InformationURL -ne "") {
                  New-UDLink -Url $EventData.Information.InformationURL -OpenInNewWindow -Content {
                    New-UDImage -URL "/assets/images/information.png" -Attributes @{
                      alt = "$($EventData.Information.InformationURL)"
                      style = @{
                        width = "20px"
                        height = "20px"
                      }
                    }
                  }
                }
                New-UDElement -tag "div" -content {} -Attributes @{style = @{width = "10px"}}
                if($null -ne $EventData.Information.PrivacyURL -and $EventData.Information.PrivacyURL -ne "") {
                  New-UDLink -Url $EventData.Information.PrivacyURL -OpenInNewWindow -Content {
                    New-UDImage -URL "/assets/images/privacy.png" -Attributes @{
                      alt = "$($EventData.Information.PrivacyURL)"
                      style = @{
                        width = "20px"
                        height = "20px"
                      }
                    }
                  }
                }
              }
              New-UDDataGridColumn -Field Availability -Flex 2.0
              New-UDDataGridColumn -Field Active -Flex 1.0
              New-UDDataGridColumn -Field Updated -Flex 1.0
              New-UDDataGridColumn -Field Source -Flex 0 -DisableColumnMenu -Render {} -DisableExport -DisableReorder -Hide
              New-UDDataGridColumn -Field Information -Flex 0 -DisableColumnMenu -Render {} -DisableExport -DisableReorder -Hide
              New-UDDataGridColumn -Field SourceFiles -Flex 0 -DisableColumnMenu -Render {} -DisableExport -DisableReorder -Hide
              New-UDDataGridColumn -Field Install -Flex 0 -DisableColumnMenu -Render {} -DisableExport -DisableReorder -Hide
              New-UDDataGridColumn -Field Uninstall -Flex 0 -DisableColumnMenu -Render {} -DisableExport -DisableReorder -Hide
              New-UDDataGridColumn -Field RequirementRule -Flex 0 -DisableColumnMenu -Render {} -DisableExport -DisableReorder -Hide
              New-UDDataGridColumn -Field DetectionRule -Flex 0 -DisableColumnMenu -Render {} -DisableExport -DisableReorder -Hide
              New-UDDataGridColumn -Field Program -Flex 0 -DisableColumnMenu -Render {} -DisableExport -DisableReorder -Hide
            ) -StripedRows -AutoHeight $true -PageSize 10 -RowsPerPageOptions @(10,25,50,100,1000) -ShowPagination -DefaultSortColumn Name -OnSelectionChange {
              Import-Module -Name $AppFactory_Module -Force
              $TableData = Get-UDElement -Id "ApplicationListTableData"
              $selectedRow = ((Get-UDElement -Id "ApplicationListTableData").selection)[0]
              $selectedRowData = $TableData.Data.Rows | Where-Object { $_.ID -eq $selectedRow } 
              $AppVersions = Get-AppFactoryServiceAppVersions -appGUID $selectedRow -AllAppList $script:PublishedAppList 
              # Standard Text Based Elements to Set
              $setTextElements = @(
                @{
                  id          = "ApplicationGUID"
                  value       = $selectedRow
                  type        = "text"
                  placeholder = ""
                },
                @{
                  id          = "ApplicationName"
                  value       = $selectedRowData.Information.DisplayName
                  type        = "text"
                  placeholder = "Application Name"
                },
                @{
                  id          = "Publisher"
                  value       = $selectedRowData.Information.Publisher
                  type        = "text"
                  placeholder = "Publisher Name"                  
                },
                @{
                  id          = "PrivacyURL"
                  value       = $selectedRowData.Information.PrivacyURL
                  type        = "text"
                  placeholder = "Information URL"                  
                },
                @{
                  id          = "InformationURL"
                  value       = $selectedRowData.Information.InformationURL
                  type        = "text"
                  placeholder = "Privacy URL"                  
                },
                @{
                  id          = "Owner"
                  value       = $selectedRowData.Information.Owner
                  type        = "text"
                  placeholder = "Owner Name"                  
                }
              )
              foreach ($item in $setTextElements) {
                Set-UDElement -id $item.id -Properties @{
                  value = $item.value
                }                 
              }
              if ($selectedRowData.Availability -ne "All") {
                $ClientListValue = $selectedRowData.Availability -split ", "
              }
              else {
                $ClientListValue = ""
              }
              # Standard Select Based Elements to Set
              $setSelectElements = @(
                @{
                  id    = "Client"
                  value = $ClientListValue
                },
                @{
                  id    = "Architecture"
                  Value = $selectedRowData.RequirementRule.Architecture
                },
                @{
                  id    = "MinimumSupportedWindowsRelease"
                  Value = $selectedRowData.RequirementRule.MinimumSupportedWindowsRelease
                },
                @{
                  id    = "Source"
                  Value = $selectedRowData.SourceFiles.AppSource
                },
                @{
                  id    = "Install"
                  Value = $selectedRowData.Install.type
                },
                @{
                  id    = "Uninstall"
                  Value = $selectedRowData.Uninstall.type
                },
                @{
                  id    = "InstallExperience"
                  Value = $selectedRowData.Program.InstallExperience.tolower()
                }
              )
              foreach ($item in $setSelectElements) {
                Set-UDElement -id $item.id -Properties @{
                  Value = $item.Value
                }
              }
              # Start Text Box Based Elements to Set
              $setTextboxElements = @(
                @{
                  id    = "Description"
                  value = $selectedRowData.Information.Description
                },
                @{
                  id    = "Notes"
                  value = $selectedRowData.Information.Notes
                }                
              )
              foreach ($item in $setTextboxElements) {
                Set-UDElement -id $item.id -Properties @{
                  Value = $item.Value
                }
              }  
              # Start Switch Based Elements to Set
              $setSwitchElements = @(
                @{
                  id      = "Active"
                  Checked = $selectedRowData.SourceFiles.Active
                },
                @{
                  id      = "pauseUpdate"
                  Checked = $selectedRowData.SourceFiles.pauseUpdate
                }
              )
              foreach ($item in $setSwitchElements) {
                Set-UDElement -id $item.id -Properties @{
                  Checked = $item.Checked
                }
              }
              # Available versions based on application
              Set-UDElement -id "AvailableVersions" -Properties @{
                Options = @(
                  foreach ($version in ($AppVersions | Sort-Object -Descending)) {
                    New-UDSelectOption -Name $version -Value $version 
                  }
                )
              }
              # Detection drop down
              Set-UDElement -Id "DetectionSelectionFields" -Content {}
              Set-UDElement -Id "DetectionSelectionFields" -Content {              
                switch ($selectedRowData.DetectionRule.Type) {
                  "MSI" {
                    Set-UDElement -id "Detection" -Properties @{
                      Value = "MSI"
                    }  
                    New-PSUGUIDetectionMSI -SourceData $selectedRowData.DetectionRule                
                  }
                  "Registry" {
                    if ($selectedRowData.DetectionRule.DetectionMethod -eq "VersionComparison") {
                      Set-UDElement -id "Detection" -Properties @{
                        Value = "Registry Version"
                      }              
                      New-PSUGUIDetectionRegistryVersion -SourceData $selectedRowData.DetectionRule                      
                    }
                    else {
                      Set-UDElement -id "Detection" -Properties @{
                        Value = "Registry Existance"
                      }       
                      New-PSUGUIDetectionRegistryExistance -SourceData $selectedRowData.DetectionRule                             
                    }
                  } 
                  "Script" {
                    Set-UDElement -id "Detection" -Properties @{
                      Value = "Script"
                    }  
                    $scriptPath = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath $selectedRowData.information.appFolderName, "Detection.ps1" -ErrorAction SilentlyContinue
                    New-PSUGUIDetectionScript -SourceData $selectedRowData.DetectionRule -scriptPath $scriptPath                               
                  }
                }
              }
              # Set Icon FIle
              $ApplicationPath = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath $selectedRowData.Information.AppFolderName
              $AppIconFile = Join-Path -Path $ApplicationPath -ChildPath "Icon.png"
              Set-UDElement -Id "imgdiv" -Content {}
              Set-UDElement -Id "imgdiv" -Content {
                New-UDImage -Id "appimage" -Path "$($AppIconFile)" -Width 50 -Height 50
              }
              # Set Source Files
              Set-UDElement -Id "SourceSelectionFields" -Content {}
              Set-UDElement -Id "SourceSelectionFields" -Content {
                switch ($selectedRowData.sourceFiles.appSource) {
                  "StorageAccount" {
                    New-PSUGUISourceStorageAccount -SourceData $selectedRowData.sourcefiles
                  }
                  "Sharepoint" {
                    New-PSUGUISourceSharepoint -SourceData $selectedRowData.sourcefiles
                  }
                  "Winget" {
                    New-PSUGUISourceWinget -SourceData $selectedRowData.sourcefiles
                  }
                  "LocalStorage" {
                    New-PSUGUISourceStorageAccount -SourceData $selectedRowData.sourcefiles
                  }
                  "ECNO" {
                    New-PSUGUISourceECNO -SourceData  $selectedRowData.sourcefiles
                  }
                  "Evergreen" {
                    New-PSUGUISourceEvergreen -SourceData  $selectedRowData.sourcefiles
                  }
                  "PSADT" {
                    New-PSUGUISourcePSADT -SourceData  $selectedRowData.Information
                  }
                  "StorageAccount - PowerShell Script" {
                    New-PSUGUISourceStorageAccountScript -SourceData  $selectedRowData.Information
                  }                  
                }
              }
              # Set Install Data
              Set-UDElement -Id "InstallSelectionFields" -Content {}
              Set-UDElement -Id "InstallSelectionFields" -Content {
                switch ($selectedRowData.Install.type) {
                  "Script" {
                    New-PSUGUIInstallScript -SourceData $selectedRowData.install
                  }
                  "EXE" {
                    New-PSUGUIInstallEXE -SourceData $selectedRowData.install
                  }
                  "MSI" {
                    New-PSUGUIInstallMSI -SourceData $selectedRowData.install
                  }
                  "Powershell" {
                    New-PSUGUIInstallPowershell -SourceData  $selectedRowData.install -versiondata $selectedRowData.Information
                  }
                }
              }              
              # Set Uninstall Data
              Set-UDElement -Id "UninstallSelectionFields" -Content {}
              Set-UDElement -Id "UninstallSelectionFields" -Content {
                switch ($selectedRowData.Uninstall.type) {
                  "Script" {
                    New-PSUGUIUnInstallScript -SourceData $selectedRowData.Uninstall
                  }
                  "EXE" {
                    New-PSUGUIUnInstallEXE -SourceData $selectedRowData.Uninstall
                  }
                  "MSI" {
                    New-PSUGUIUnInstallMSI -SourceData $selectedRowData.Uninstall
                  }
                  "Name" {
                    New-PSUGUIUninstallName -SourceData $selectedRowData.Uninstall
                  }
                  "GUID" {
                    New-PSUGUIUninstallGUID -SourceData $selectedRowData.Uninstall
                  }  
                  "Powershell" {
                    New-PSUGUIUninstallPowershell -SourceData  $selectedRowData.uninstall
                  }                
                }
              }               
              Set-UDElement -id "UpdateApplication" -Properties @{
                Disabled = $false
              }
              Set-UDElement -id "DeleteApplication" -Properties @{
                Disabled = $false
              }
              Set-UDElement -id "DeleteVersion" -Properties @{
                Disabled = $true
              }
              Set-UDElement -id "AvailableVersions" -Properties @{
                Value = ""
              }
            }
          }
        }
      }
      New-UDElement -Tag "div" -ClassName "col-1" -Content {}
      New-UDElement -Tag "div" -ClassName "col-6" -Content {
        New-UDElement -Tag "div" -ClassName "card-body rounded" -Content {
          New-UDTypography -Text "Application Details" -Variant "h5" -ClassName "card-title rounded x-card-title"
          New-UDButton -Id "NewApplication" -Text "New Application" -ClassName "btn btn-primary" -OnClick {
            $global:submitApp = $true
            $AppInformation = Get-PSUGUIAppInfo
            $AppInstall = Get-PSUGUIAppInstallInfo
            $AppUninstall = Get-PSUGUIAppUninstallInfo
            $AppDetection = Get-PSUGuiAppDetectionInfo
            if ($global:submitApp) {
              $AppInformation.Add("AppFolderName", $AppInformation.displayName) | Out-Null
              $AppFolder = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath $AppInformation.AppFolderName
              $IconFileData = Get-UDElement -Id "IconFile"
              $IconDesination = Join-Path -Path $AppFolder -ChildPath "Icon.png"
              if ([String]::IsNullOrWhiteSpace($IconFileData.value)) {
                $IconPath = Join-Path -Path $script:AppFactorySupportTemplateFolder -ChildPath "PSADT" -AdditionalChildPath "Assets", "AppIcon.png"
              }
              else {
                $IconPath = "$($env:TEMP)\PSUApp_Icon.png"
              }
              $application = New-AppFactoryApp @AppInformation
              Set-AppFactoryAppInstall -appGUID $application.GUID @AppInstall
              Set-AppFactoryAppUninstall -appGUID $application.GUID @AppUninstall
              Set-AppFactoryAppDetectionRule -appGUID $application.GUID @AppDetection
              if($AppDetection.Type -eq "Script"){
                $ScriptData = (Get-UDElement -Id "Detection_Script").code
                $DetectionPS = Join-Path -Path $AppFolder -ChildPath "Detection.ps1"
                $ScriptData | Out-File -FilePath $DetectionPS -Force
              }
              Copy-Item -Path $IconPath -Destination $IconDesination -Force
              Set-UDElement -id "AppTabs" -Content {New-PSUGUIAppTabs}
              Sync-UDElement -Id 'ApplicationListTableData'              
            }
          }
          New-UDButton -Id "UpdateApplication" -Text "Update Application" -Disabled -ClassName "btn btn-primary" -OnClick {
            Import-Module -Name $AppFactory_Module -Force
            Initialize-AppFactoryProcess -ApplicationServicePath $AppFactory_ApplicationPath            
            $global:submitApp = $true            
            $AppInformation = Get-PSUGUIAppInfo
            $AppInstall = Get-PSUGUIAppInstallInfo
            $AppUninstall = Get-PSUGUIAppUninstallInfo
            $AppDetection = Get-PSUGuiAppDetectionInfo
            if ($global:submitApp) {
              $selectedRow = ((Get-UDElement -Id "ApplicationListTableData").selection)[0]
              $application = Set-AppFactoryApp -appGUID $selectedRow @AppInformation                 
              $AppFolder = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath $application.Information.AppFolderName
              $IconFileData = Get-UDElement -Id "IconFile"
              $IconDesination = Join-Path -Path $AppFolder -ChildPath "Icon.png"
              if (-not [String]::IsNullOrWhiteSpace($IconFileData.value)) {
                $IconPath = "$($env:TEMP)\PSUApp_Icon.png"
                Copy-Item -Path $IconPath -Destination $IconDesination -Force
              }
              Set-AppFactoryAppInstall -appGUID $application.GUID @AppInstall
              Set-AppFactoryAppUninstall -appGUID $application.GUID @AppUninstall
              Set-AppFactoryAppDetectionRule -appGUID $application.GUID @AppDetection
              if($AppDetection.Type -eq "Script"){
                $ScriptData = (Get-UDElement -Id "Detection_Script").code
                $DetectionPS = Join-Path -Path $AppFolder -ChildPath "Detection.ps1"
                $ScriptData | Out-File -FilePath $DetectionPS -Force
              }
              Sync-UDElement -Id 'ApplicationListTableData'              
            }            
          }
          New-UDButton -Id "DeleteApplication" -Text "Delete Application" -Disabled -ClassName "btn btn-primary" -OnClick {
            Show-UDModal -MaxWidth lg -Content {
              Import-Module -Name $AppFactory_Module -Force
              Initialize-AppFactoryProcess -ApplicationServicePath $AppFactory_ApplicationPath
              $TableData = Get-UDElement -Id "ApplicationListTableData"
              $selectedRow = ((Get-UDElement -Id "ApplicationListTableData").selection)[0]
              $selectedRowData = $TableData.Data.Rows | Where-Object { $_.ID -eq $selectedRow }               
              $displayName = $selectedRowData.Information.DisplayName
              New-UDTypography -Text "Are you sure you want to delete this application? $($displayName)" -Variant "h5"
              New-UDButton -Text "Yes"  -ClassName "btn btn-primary" -OnClick {
                Remove-AppFactoryApp -appGUID $selectedRow
                Sync-UDElement -Id 'ApplicationListTableData' 
                Set-UDElement -id "AppTabs" -Content {New-PSUGUIAppTabs}
                Hide-UDModal   
              }
              New-UDButton -Text "No"  -ClassName "btn btn-primary" -OnClick {
                Hide-UDModal
              }
            }
          }
          New-UDButton -Id "DeleteVersion" -Text "Delete Version" -Disabled -ClassName "btn btn-primary" -OnClick {
            Show-UDModal -MaxWidth lg -Content {
              Import-Module -Name $AppFactory_Module -Force
              Initialize-AppFactoryProcess -ApplicationServicePath $AppFactory_ApplicationPath
              $TableData = Get-UDElement -Id "ApplicationListTableData"
              $selectedRow = ((Get-UDElement -Id "ApplicationListTableData").selection)[0]
              $selectedRowData = $TableData.Data.Rows | Where-Object { $_.ID -eq $selectedRow }               
              $displayName = $selectedRowData.Information.DisplayName
              $versionToDelete = (Get-UDElement -id "AvailableVersions").Value
              New-UDTypography -Text "Are you sure you want to delete this version $($versionToDelete) from $($displayName)?" -Variant "h5"
              New-UDButton -Text "Yes"  -ClassName "btn btn-primary" -OnClick {
                Remove-AppFactoryServiceAppVersions -appGUID $selectedRow -version $versionToDelete -AllAppList $script:PublishedAppList
                $AppVersions = Get-AppFactoryServiceAppVersions -appGUID $selectedRow -AllAppList $script:PublishedAppList 
                Set-UDElement -id "AvailableVersions" -Properties @{
                  Options = @(
                    foreach ($version in ($AppVersions | Sort-Object -Descending)) {
                      New-UDSelectOption -Name $version -Value $version 
                    }
                  )
                }                
                Hide-UDModal   
              }
              New-UDButton -Text "No"  -ClassName "btn btn-primary" -OnClick {
                Hide-UDModal
              }
            }            
          }      
          New-UDElement -id "AppTabs" -Content {
            New-PSUGUIAppTabs
          }    
        }    
        New-UDElement -Tag "pre" -id "datahelper" -Content {}    
      }
    }
  }
}
#EndRegion '.\Public\Get-AppFactoryPSUApplications.ps1' 450
#Region '.\Public\Get-AppfactoryPSUClientApplications.ps1' 0
function Get-AppfactoryPSUClientApplications {
  [cmdletbinding()]
  param()
  Initialize-AppFactoryProcess -ApplicationServicePath $AppFactory_ApplicationPath
  $ids = ($Roles -match "(^([0-9A-Fa-f]{8}[-]?[0-9A-Fa-f]{4}[-]?[0-9A-Fa-f]{4}[-]?[0-9A-Fa-f]{4}[-]?[0-9A-Fa-f]{12})$)")
  $clients = Get-AppFactoryServiceClient
  if ($roles.Contains($PSU_GUI_AdminRole)) {
    $defaultValue = $clients[0].GUID
  }
  else {
    $defaultValue = $ids[0]
  }
  New-UDSelect -Id "ClientIDs" -ClassName "inputrequired"  -DefaultValue $defaultValue -FullWidth -Option {
    foreach ($client in $clients) {
      if ($ids.Contains($client.GUID) -or $roles.Contains($PSU_GUI_AdminRole)) {
        New-UDSelectOption -Name $client.Name -Value $client.GUID
      }
    }
  } -OnChange {
    Sync-UDElement -Id 'ApplicationListTableData'
  }
  New-UDElement -tag "br"
  New-UDElement -tag "br"
  New-UDElement -Tag "div" -ClassName "container-fluid" -Content {
    New-UDElement -Tag "div" -ClassName "row justify-content-start" -Content {
      New-UDElement -Tag "div" -ClassName "col-5" -Content {
        New-UDElement -Tag "div" -ClassName "card-body rounded" -Content {
          New-UDTypography -Text "Application List" -Variant "h5" -ClassName "card-title rounded x-card-title"
          New-UDElement -Tag "div" -id "ApplicationList" -Content {
            New-UDDataGrid -id "ApplicationListTableData" -LoadRows {
              Initialize-AppFactoryProcess -ApplicationServicePath $AppFactory_ApplicationPath
              $tableData = [System.Collections.Generic.List[PSCustomObject]]@()
              $clientGUID = (Get-UDElement -id "ClientIDs").value
              $rawData = Get-AppFactoryApp -Active | Where-Object { [String]::IsNullOrWhiteSpace($_.SourceFiles.publishTo) -or (-not [String]::IsNullOrWhiteSpace($_.SourceFiles.publishTo) -and $_.SourceFiles.publishTo -contains $clientGUID) }
              foreach ($item in $rawData) {
                $AppDetails = Get-AppFactoryServiceClientAppConfig -orgGUID $clientGUID -appGUID $item.GUID
                if ([String]::IsNullOrWhiteSpace($AppDetails.AddToIntune)) { $AddToIntune = "False" }
                else { $AddToIntune = "True" }
                $obj = [PSCustomObject]@{
                  id             = $item.GUID
                  Name           = $item.Information.DisplayName
                  Enabled        = $AddToIntune
                  Updated        = $item.SourceFiles.LastUpdate
                  ClientDetails  = ($AppDetails | ConvertTo-Json -Depth 5)
                  InformationURL = $item.information.InformationURL
                  PrivacyURL     = $item.Information.PrivacyURL
                }
                $tableData.Add($obj) | Out-Null
              }              
              $TableData | Out-UDDataGridData -Context $EventData -TotalRows $Rows.Length
            } -Columns @(
              New-UDDataGridColumn -Field ID -Flex 0 -DisableColumnMenu -DisableReorder -Hide
              New-UDDataGridColumn -Field Name -Flex 1.5 -Render {
                $EventData.Name
                New-UDElement -tag "div" -content {} -Attributes @{style = @{width = "10px" } }
                if ($null -ne $EventData.InformationURL -and $EventData.InformationURL -ne "") {
                  New-UDLink -Url $EventData.InformationURL -OpenInNewWindow -Content {
                    New-UDImage -URL "/assets/images/information.png" -Attributes @{
                      alt   = "$($EventData.InformationURL)"
                      style = @{
                        width  = "20px"
                        height = "20px"
                      }
                    }
                  }
                }
                New-UDElement -tag "div" -content {} -Attributes @{style = @{width = "10px" } }
                if ($null -ne $EventData.PrivacyURL -and $EventData.PrivacyURL -ne "") {
                  New-UDLink -Url $EventData.PrivacyURL -OpenInNewWindow -Content {
                    New-UDImage -URL "/assets/images/privacy.png" -Attributes @{
                      alt   = "$($EventData.PrivacyURL)"
                      style = @{
                        width  = "20px"
                        height = "20px"
                      }
                    }
                  }
                }
              }
              New-UDDataGridColumn -Field Enabled -Flex 1.5
              New-UDDataGridColumn -Field Updated -Flex 1.5
              New-UDDataGridColumn -Field ClientDetails -Flex 0 -DisableColumnMenu -DisableExport -DisableReorder -Hide -Render {}
            ) -StripedRows -AutoHeight $true -PageSize 10 -RowsPerPageOptions @(10, 25, 50, 100, 1000) -ShowPagination -DefaultSortColumn Name -OnSelectionChange {
              Import-Module -Name $AppFactory_Module -Force
              $TableData = Get-UDElement -Id "ApplicationListTableData"
              $selectedRow = ((Get-UDElement -Id "ApplicationListTableData").selection)[0]
              $selectedRowData = $TableData.Data.Rows | Where-Object { $_.ID -eq $selectedRow } 
              $AppVersions = Get-AppFactoryServiceAppVersions -appGUID $selectedRow -AllAppList $script:PublishedAppList 
              $AppDetails = $selectedRowData.ClientDetails | ConvertFrom-Json
              $PreviousVersionNumber = 0
              if (-not [String]::IsNullOrWhiteSpace($AppDetails.KeepPrevious)) {
                $PreviousVersionNumber = $AppDetails.KeepPrevious
              }
              Set-UDElement -id "ApplicationGUID" -Attributes @{ "value" = $selectedRow }
              Set-UDElement -id "ApplicationName" -Attributes @{ "value" = $selectedRowData.Name }
              Set-UDElement -id "Enabled" -Attributes @{ "checked" = [System.Convert]::ToBoolean($selectedRowData.Enabled) }
              Set-UDElement -id "DownloadForground" -Attributes @{ "checked" = [System.Convert]::ToBoolean($AppDetails.foreground) }
              Set-UDElement -id "PreviousVersions" -Attributes @{ "value" = $PreviousVersionNumber }
              Set-UDElement -id "ESP" -Attributes @{ "value" = $AppDetails.espprofiles -join ", " }
              Set-UDElement -id "CopyPrevious" -Attributes @{ "checked" = [System.Convert]::ToBoolean($AppDetails.CopyPrevious) }
              Set-UDElement -id "RemovePrevious" -Attributes @{ "checked" = [System.Convert]::ToBoolean($AppDetails.UnassignPrevious) }
              Set-UDElement -id "InteractiveInstall" -Attributes @{ "checked" = [System.Convert]::ToBoolean($AppDetails.InteractiveInstall) }
              Set-UDElement -id "InteractiveUninstall" -Attributes @{ "checked" = [System.Convert]::ToBoolean($AppDetails.InteractiveUninstall) }
              Set-UDElement -id "Available_Install" -Attributes @{ "value" = $AppDetails.AvailableAssignments -join ", " }
              Set-UDElement -id "Available_Exceptions" -Attributes @{ "value" = $AppDetails.AvailableExceptions -join ", " }
              Set-UDElement -id "Required_Install" -Attributes @{ "value" = $AppDetails.RequiredAssignments -join ", " }
              Set-UDElement -id "Required_Exceptions" -Attributes @{ "value" = $AppDetails.RequiredExceptions -join ", " }
              Set-UDElement -id "Required_Uninstall" -Attributes @{ "value" = $AppDetails.UninstallAssignments -join ", " }
              Set-UDElement -id "Uninstall_Exceptions" -Attributes @{ "value" = $AppDetails.UninstallExceptions -join ", " }
              $FilterDetails = [System.Collections.Generic.List[String]]@()
              if ($appdetails.filters) {
                $FilterNames = ($appdetails.filters | get-member | where-Object { $_.MemberType -eq "NoteProperty" } | select-object Name).Name
                foreach ($filter in $FilterNames) {
                  $FilterDetails.Add("$($filter);$($appdetails.filters.$filter.filterName);$($appdetails.filters.$filter.filterType)")
                }  
              }
              Set-UDElement -id "FilterList" -Attributes @{ "value" = $FilterDetails -join ", " }
              if ([String]::IsNullOrWhiteSpace($AppDetails.AppVersion)) {
                $VersionValue = "0.0"
              }
              else {
                $VersionValue = $AppDetails.AppVersion
              }              
              Set-UDElement -id "SelectedVersion" -Properties @{
                Options      = @(
                  New-UDSelectOption -Name "Latest" -Value "0.0"
                  foreach ($version in ($AppVersions | Sort-Object -Descending)) {
                    New-UDSelectOption -Name $version -Value $version 
                  }
                )
                DefaultValue = $VersionValue
              }
              Set-UDElement -id "UpdateApplication" -Properties @{
                Disabled = $false
              }
            }
          }
        }
      }
      New-UDElement -Tag "div" -ClassName "col-1" -Content {}
      New-UDElement -Tag "div" -ClassName "col-6" -Content {
        New-UDElement -Tag "div" -ClassName "card-body rounded" -Content {
          New-UDTypography -Text "Application Details" -Variant "h5" -ClassName "card-title rounded x-card-title"
          New-UDButton -Id "UpdateApplication" -Text "Update Application" -ClassName "btn btn-primary" -Disabled -OnClick {
            Import-Module -Name $AppFactory_Module -Force
            Initialize-AppFactoryProcess -ApplicationServicePath $AppFactory_ApplicationPath        
            $selectedRow = ((Get-UDElement -Id "ApplicationListTableData").selection)[0]
            $orgID = (Get-UDElement -id "ClientIDs").value
            $AppVersionSelected = (Get-UDElement -id "SelectedVersion").Value
            $AppConfig = @{
              orgGUID              = $orgID
              appGUID              = $selectedRow
              AddToIntune          = (Get-UDElement -id "Enabled").checked
              AvailableAssignments = ((Get-UDelement -id "Available_Install").Value -split ",")
              AvailableExceptions  = ((Get-UDelement -id "Available_Exceptions").Value -split ",")
              RequiredAssignments  = ((Get-UDelement -id "Required_Install").Value -split ",")
              RequiredExceptions   = ((Get-UDelement -id "Required_Exceptions").Value -split ",")
              UninstallAssignments = ((Get-UDelement -id "Required_Uninstall").Value -split ",")
              UninstallExceptions  = ((Get-UDelement -id "Uninstall_Exceptions").Value -split ",")
              UnassignPrevious     = (Get-UDElement -id "RemovePrevious").checked
              CopyPrevious         = (Get-UDElement -id "CopyPrevious").checked
              KeepPrevious         = (Get-UDElement -id "PreviousVersions").Value
              foreground           = (Get-UDElement -id "DownloadForground").checked
              espprofiles          = ((Get-UDelement -id "ESP").Value -split ",")
              InteractiveInstall   = (Get-UDElement -id "InteractiveInstall").checked
              InteractiveUninstall = (Get-UDElement -id "InteractiveUninstall").checked
              AppVersion           = $AppVersionSelected
            }    
            $AllFilters = (Get-UDElement -id "FilterList").Value -split ","
            $filters = @{}
            foreach ($filter in $AllFilters) {
              $filterDetails = $filter -split ";"
              if (-not [String]::IsNullOrWhiteSpace($filterDetails[0])) {
                $filters.Add($filterDetails[0], @{filterName = $filterDetails[1]; filterType = $filterDetails[2] })
              }
            }
            
            $AppConfig.Add("filters", $filters)            
            Set-AppFactoryServiceClientAppConfig @AppConfig
            Sync-UDElement -Id 'ApplicationListTableData'  
          }
          New-UDElement -Tag "table"  -Attributes @{
            "style"       = @{
              "width" = "100%";
  
            }
            "cellpadding" = "1"
          } -Content {          
            New-UDElement -Tag "tr" -Content {
              New-PSUGUIInputTextGroup -Label "GUID:" -id "ApplicationGUID" -placeholder "" -disabled -item_colspan 3 -item_width 80
            }
            New-UDElement -Tag "tr" -Content {
              New-PSUGUIInputTextGroup -Label "Display Name:" -id "ApplicationName" -placeholder "" -disabled -item_colspan 3 -item_width 80
            }          
            New-UDElement -Tag "tr" -Content {
              New-PSUGUISwitch -Label "Enabled:" -id "Enabled"
              New-PSUGUISwitch -Label "Download Foreground:" -id "DownloadForground"
            }     
            New-UDElement -Tag "tr" -Content {
              New-PSUGUIInputTextGroup -Label "Keep Previous Versions:" -id "PreviousVersions" -placeholder ""
              New-PSUGUIInputSelectGroup -Label "Version:" -id "SelectedVersion" -placeholder "" -DefaultValue ""
              
            }                                     
            New-UDElement -Tag "tr" -Content {
              New-PSUGUISwitch -Label "Copy Previous Assignments:" -id "CopyPrevious"
              New-PSUGUISwitch -Label "Unassign Previous Assignments:" -id "RemovePrevious"
            }      
            New-UDElement -Tag "tr" -Content {
              New-PSUGUISwitch -Label "Interactive Install:" -id "InteractiveInstall"
              New-PSUGUISwitch -Label "Interactive Uninstall:" -id "InteractiveUninstall"
            }
            New-UDElement -Tag "tr" -Content {
              New-PSUGUIInputTextGroup -Label "ESP Assignments:" -id "ESP" -placeholder "Names of entra groups comma seperated" -item_colspan 3 -item_width 80
            }
            New-UDElement -Tag "tr" -Content {
              New-PSUGUIInputTextGroup -Label "Filters:" -id "FilterList" -placeholder "See docuemntation for proper format" -item_colspan 3 -item_width 80
            }                         
            New-UDElement -Tag "tr" -content { New-UDElement -Tag 'td' -content { New-UDElement -Tag "br" -content {} } }
            New-UDElement -Tag "tr" -Content {
              New-UDElement -Tag "td" -Content {
                New-UDTypography -Text "Application Install Assignments" -Variant "h6" -ClassName "card-title rounded x-card-title"
              } -Attributes @{Colspan = 4 }
            } 
            New-UDElement -Tag "tr" -Content {
              New-PSUGUIInputTextGroup -Label "Availabe:" -id "Available_Install" -placeholder "Names of entra groups comma seperated" -item_colspan 3 -item_width 80
            }
            New-UDElement -Tag "tr" -Content {
              New-PSUGUIInputTextGroup -Label "Availabe Exceptions:" -id "Available_Exceptions" -placeholder "Names of entra groups comma seperated" -item_colspan 3 -item_width 80
            }            
            New-UDElement -Tag "tr" -Content {
              New-PSUGUIInputTextGroup -Label "Required:" -id "Required_Install" -placeholder "Names of entra groups comma seperated" -item_colspan 3 -item_width 80
            }
            New-UDElement -Tag "tr" -Content {
              New-PSUGUIInputTextGroup -Label "Required Exceptions:" -id "Required_Exceptions" -placeholder "Names of entra groups comma seperated" -item_colspan 3 -item_width 80
            }                        
            New-UDElement -Tag "tr" -content { New-UDElement -Tag 'td' -content { New-UDElement -Tag "br" -content {} } }
            New-UDElement -Tag "tr" -Content {
              New-UDElement -Tag "td" -Content {
                New-UDTypography -Text "Application Uninstall Assignments" -Variant "h6" -ClassName "card-title rounded x-card-title"
              } -Attributes @{Colspan = 4 }
            }             
            New-UDElement -Tag "tr" -Content {
              New-PSUGUIInputTextGroup -Label "Required:" -id "Required_Uninstall" -placeholder "Names of entra groups comma seperated" -item_colspan 3 -item_width 80
            }
            New-UDElement -Tag "tr" -Content {
              New-PSUGUIInputTextGroup -Label "Exceptions:" -id "Uninstall_Exceptions" -placeholder "Names of entra groups comma seperated" -item_colspan 3 -item_width 80
            }              
          }
        }        
        New-UDElement -Tag "div" -id "ts_step" -content {}
      }      
    }
  }
}
#EndRegion '.\Public\Get-AppfactoryPSUClientApplications.ps1' 255
#Region '.\Public\Get-AppfactoryPSUClients.ps1' 0
function Get-AppfactoryPSUClients {
  [cmdletbinding()]
  param()
  New-UDElement -Tag "figure" -ClassName "text-center" -Content {
    New-UDElement -Tag "h4" -ClassName "display-4" -Content { "Application Factory Clients" }
  }
  New-UDElement -Tag "div" -ClassName "container-fluid" -Content {
    New-UDElement -Tag "div" -ClassName "row justify-content-start" -Content {
      New-UDElement -Tag "div" -ClassName "col-7" -Content {
        New-UDElement -Tag "div" -ClassName "card-body rounded" -Content {
          New-UDTypography -Text "Client Lists" -Variant "h5" -ClassName "card-title rounded x-card-title"
          New-UDElement -Tag "div" -id "ClientList" -Content {
            New-UDDataGrid -id "ClientTableData" -LoadRows {
              Initialize-AppFactoryProcess -ApplicationServicePath $AppFactory_ApplicationPath
              Get-AppFactoryServiceClient | Select-Object -Property @{Label = "ID"; expression = { $_.GUID } }, Name, Contacts | Out-UDDataGridData -Context $EventData -TotalRows $Rows.Length
            } -Columns @(
              New-UDDataGridColumn -Field Name -Flex 1.5
              New-UDDataGridColumn -Field Contacts -Flex 1.5 -Render { $EventData.Contacts -join ", " }
            ) -StripedRows -AutoHeight $true -PageSize 1000 -DefaultSortColumn Name -OnSelectionChange {
              $TableData = Get-UDElement -Id "ClientTableData"
              $selectedRow = ((Get-UDElement -Id "ClientTableData").selection)[0]
              $selectedRowData = $TableData.Data.Rows | Where-Object {$_.ID -eq $selectedRow}
              Set-UDElement -id "clientNameText" -Properties @{
                Attributes = @{
                  value = $selectedRowData.Name
                  type  = "text"
                  class = "form-control"
                  classname = $null
                  placeholder = "Client Name"
                }
              }
              Set-UDElement -id "contactListTextArea" -Properties @{
                Tag = "textarea"
                Attributes = @{
                  value = ($selectedRowData.Contacts -join "`n")
                  type  = "text"
                  class = "form-control"
                  classname = $null
                  rows = 4
                  placeholder = "Contact List. One per line."
                }
              }  
              Set-UDElement -id "UpdateClient" -Properties @{
                Disabled = $false
              }
              Set-UDElement -id "deleteClient" -Properties @{
                Disabled = $false
              }    
            }  
          }
        }
      }
      New-UDElement -Tag "div" -ClassName "col-1" -Content {}
      New-UDElement -Tag "div" -ClassName "col-4" -Content {
        New-UDElement -Tag "div" -ClassName "card-body rounded" -Content {
          New-UDTypography -Text "Client Details" -Variant "h5" -ClassName "card-title rounded x-card-title"
          New-UDButton -Id "NewClient" -Text "New Client" -ClassName "btn btn-primary" -OnClick {
            $clientName = (Get-UDElement -id "clientNameText").Attributes.Value
            $contactList = (Get-UDElement -id "contactListTextArea").Attributes.Value -split "\n"
            if ([string]::IsNullOrEmpty($clientName)){
              Show-UDToast -Message "Client Name is Required" -Duration 5000 -Position "topCenter" -BackgroundColor "#FF0000"
              return
            }
            New-AppFactoryServiceClient -clientName $clientName -clientContacts $contactList
            Sync-UDElement -Id 'ClientTableData'
            Set-UDElement -id "clientNameText" -Properties @{
              Attributes = @{
                value = ""
                type  = "text"
                class = "form-control"
                classname = $null
                placeholder = "Client Name"
              }
            }
            Set-UDElement -id "contactListTextArea" -Properties @{
              Attributes = @{
                value = ""
                type  = "text"
                class = "form-control"
                classname = $null
                rows = 4
                placeholder = "Contact List. One per line."
              }
            }            
          }
          New-UDButton -Id "UpdateClient" -Text "Update Client" -ClassName "btn btn-primary" -Disabled -OnClick {
            Show-UDModal -MaxWidth lg -Content {
              $clientName = (Get-UDElement -id "clientNameText").Attributes.Value
              New-UDTypography -Text "Are you sure you want to update this client? $($clientName)" -Variant "h5"
              New-UDButton -Text "Yes"  -ClassName "btn btn-primary" -OnClick {
                $TableData = Get-UDElement -Id "ClientTableData"
                $selectedRow = ((Get-UDElement -Id "ClientTableData").selection)[0]
                $selectedRowData = $TableData.Data.Rows | Where-Object {$_.ID -eq $selectedRow}                
                $clientName = (Get-UDElement -id "clientNameText").Attributes.Value
                $contactList = (Get-UDElement -id "contactListTextArea").Attributes.Value -split "\n"
                $params = @{
                  clientGUID = $selectedRow
                }
                $updated = $false
                if(($contactList -join ", ") -ne $selectedRowData.renderedcontacts){
                  $updated = $true
                  $params.Add("clientContact",$contactList)
                }
                if($clientName -ne $selectedRowData.Name){
                  $updated = $true
                  $params.Add("clientName",$clientName)
                }
                if($updated){
                  Set-AppFactoryServiceClient @params
                  Sync-UDElement -Id 'ClientTableData'
                  Hide-UDModal
                }
              }
              New-UDButton -Text "No"  -ClassName "btn btn-primary" -OnClick {
                Hide-UDModal
              }              
            }
          }
          New-UDButton -Id "deleteClient" -Text "Delete Client" -ClassName "btn btn-primary" -Disabled -OnClick {
            Show-UDModal -MaxWidth lg -Content {
              $clientName = (Get-UDElement -id "clientNameText").Attributes.Value
              New-UDTypography -Text "Are you sure you want to delete this client? $($clientName)" -Variant "h5"
              New-UDButton -Text "Yes"  -ClassName "btn btn-primary" -OnClick {
                $clientGUID = ((Get-UDElement -Id "ClientTableData").selection)[0]
                Remove-AppFactoryServiceClient -clientGUID $clientGUID
                Sync-UDElement -Id 'ClientTableData'
                Set-UDElement -id "clientNameText" -Properties @{
                  Attributes = @{
                    value = ""
                    type  = "text"
                    class = "form-control"
                    classname = $null
                    placeholder = "Client Name"
                  }
                }
                Set-UDElement -id "contactListTextArea" -Properties @{
                  Attributes = @{
                    value = ""
                    type  = "text"
                    class = "form-control"
                    classname = $null
                    rows = 4
                    placeholder = "Contact List. One per line."
                  }
                }                             
                Hide-UDModal
              }
              New-UDButton -Text "No"  -ClassName "btn btn-primary" -OnClick {
                Hide-UDModal
              }
            }
          }
          New-UDElement -Tag "div" -ClassName "mb-3" -Content {
            New-UDElement -Tag "label" -Attributes @{
              for   = "Client Name"
              class = "form-label"
            } -Content {
              "Client Name"
            }
            New-UDElement -Tag "input" -id "clientNameText" -Attributes @{
              type        = "text"
              class       = "form-control"
              placeholder = "Client Name"
            }
          }
          New-UDElement -Tag "div" -ClassName "mb-3" -Content {
            New-UDElement -Tag "label" -Attributes @{
              for   = "Contact List"
              class = "form-label"
            } -Content {
              "Client List"
            }
            New-UDElement -Tag "textarea" -id "contactListTextArea" -Attributes @{
              rows        = "4"
              class       = "form-control"
              placeholder = "Contact List. One per line."
            }
          }          
        }
      }
    }
  }
}
<#
New-UDApp -Content {
    $Rows = 1..100 | % {
        @{ Id = $_; Name = 'Adam'; Number = Get-Random}
    }
    New-UDDataGrid -id DataGrid -LoadRows {
    $Rows| Out-UDDataGridData -Context $EventData -TotalRows $Rows.Length
} -Columns @(
    New-UDDataGridColumn -Field name
    New-UDDataGridColumn -Field number
) -AutoHeight $true -Pagination -CheckboxSelection -CheckboxSelectionVisibleOnly -DisableRowSelectionOnClick
#>

#EndRegion '.\Public\Get-AppfactoryPSUClients.ps1' 196
#Region '.\Public\Get-AppFactoryServiceAppVersions.ps1' 0
function Get-AppFactoryServiceAppVersions{
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[Version]])]
  param(  
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$appGUID,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject[]]$AllAppList,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  )  
  $AppList = (($AllAppList | Select-Object -Property Values).Values).Name | Sort-Object -Unique | Where-Object { $_ -like "$($appGUID)/*/App.json" } 
  if (-not [String]::IsNullOrWhiteSpace($AppList)) {
    [System.Collections.Generic.List[Version]]$AppVersions = ([regex]::Matches($AppList , "/(.*?)/App.json")).Groups.Value | Where-Object { $_ -notlike "*App.json" }
  }
  return $AppVersions 
}
#EndRegion '.\Public\Get-AppFactoryServiceAppVersions.ps1' 15
#Region '.\Public\Get-AppFactoryServiceClient.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to list out the clients that are configured
  .PARAMETER GUID
  The unique identifer for the specific client
  .PARAMETER Name
  Get an client based on the name
  .PARAMETER Contact
  Get clients that have a specific contact
  .PARAMETER LogLevel
  If logging is enabled, what level of logging do we want, default is verbose.
 
  .EXAMPLE
  Get all client
    Get-AppFactoryClient
 
  Get specific client by clientGUID
    Get-AppFactoryClient -clientGUID "### GUID ###"
 
  Get specific client by clientName
    Get-AppFactoryClient -clientName "### client Name ###"
 
  Get specific client by Contact
    Get-AppFactoryClient -clientContact "### Contact Name ###"
#>

function Get-AppFactoryServiceClient {
  [CmdletBinding()]
  [Alias("Get-AppFactoryOrganization")]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Alias("GUID")][Parameter()][ValidateNotNullOrEmpty()][string]$clientGUID,
    [Alias("Name")][Parameter()][ValidateNotNullOrEmpty()][string]$clientName,
    [Alias("Contact")][Parameter()][ValidateNotNullOrEmpty()][string]$clientContact,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  # Client Configuration Folder
  $clientsFolder = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Clients"
  if ($script:AppFactoryLogging) {
    Write-PSFMessage -Message "Reading client files in path <c='green'>$($clientsFolder)</c>" -Level $LogLevel -Tag "Clients" -Target "Application Factory Service"
  }
  # Get list of client configuration files
  $clientConfigs = Get-Childitem -Path $clientsFolder  
  # Create list that we will return with the clients configurations
  $clients = [System.Collections.Generic.List[PSCustomObject]]@()
  foreach ($file in $clientConfigs) {
    # Read the clients file
    $json = Get-Content $file.FullName | ConvertFrom-Json
    if ($PSBoundParameters.ContainsKey("clientGUID") -and $json.GUID -ne $clientGUID) { continue }
    # If Name is set, match the name variable.
    if ($PSBoundParameters.ContainsKey("clientName") -and $json.Name -notlike "$($clientName)") { continue }
    # If Contact is set, match entries that have that contact
    if ($PSBoundParameters.ContainsKey("clientContact") -and $Json.Contacts.indexOf($clientContact) -eq -1) { continue }
    if ($script:AppFactoryLogging) {
      Write-PSFMessage -Message "Reading client file <c='green'>$($file)</c>" -Level $LogLevel -Tag "Clients", "$($json.Name)" -Target "Application Factory Service"
    }
    # Add the path to the file for specific client
    $json | Add-Member -MemberType "NoteProperty" -Name "FileName" -Value $file.Name
    $clients.Add($json) | Out-Null    
  }
  # Return the clients list
  return $clients  
}
#EndRegion '.\Public\Get-AppFactoryServiceClient.ps1' 63
#Region '.\Public\Get-AppFactoryServiceClientApp.ps1' 0
function Get-AppFactoryServiceClientApp{
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$orgGUID,
    [Parameter()][ValidateNotNullOrEmpty()][string]$appGUID,
    [Parameter()][switch]$AddToIntune,
    [Parameter()][ValidateSet("Output","Verbose")][string]$LogLevel = "Verbose"
  )
  # Blank List for the Apps
  $applicaitonList =  [System.Collections.Generic.List[PSCustomObject]]@()
  # Where are custom configs stored
  $ClientConfigFolderPath = Join-Path -Path $script:AppFactoryClientConfigDir -ChildPath "$($orgGUID)"
  # Public application list
  $publicApps = Get-AppFactoryApp -active -public -LogLevel $LogLevel
  # Client specific application list
  $clientApps = Get-AppFactoryApp -active -publishTo $orgGUID -LogLevel $LogLevel
  # Loop through each public app
  foreach($app in $publicApps){
    $customConfigPath = Join-Path -Path $ClientConfigFolderPath -ChildPath "$($app.GUID).json"
    $customConfig = $null
    if(Test-Path -Path $customConfigPath){
      $customConfig = Get-Content -Path $customConfigPath | ConvertFrom-Json -Depth 5
    }
    $AppConfig = Get-ClientAppConfig -application $app -customConfig $customConfig -audience "Public" -LogLevel $LogLevel
    if($AddToIntune.IsPresent -and -not $AppConfig.AddToIntune){
      continue      
    }  
    $applicaitonList.Add($AppConfig)
  }
  foreach($app in $clientApps){
    $customConfigPath = Join-Path -Path $ClientConfigFolderPath -ChildPath "$($app.GUID).json"
    $customConfig = $null
    if(Test-Path -Path $customConfigPath){
      $customConfig = Get-Content -Path $customConfigPath | ConvertFrom-Json -Depth 5
    }
    $AppConfig = Get-ClientAppConfig -application $app -customConfig $customConfig -audience $orgGUID -LogLevel $LogLevel
    if($AddToIntune.IsPresent -and -not $AppConfig.AddToIntune){
      continue      
    }
    $applicaitonList.Add($AppConfig)
  }  
  return $applicaitonList 
}
#EndRegion '.\Public\Get-AppFactoryServiceClientApp.ps1' 45
#Region '.\Public\Get-AppFactoryServiceClientAppConfig.ps1' 0
function Get-AppFactoryServiceClientAppConfig{
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$orgGUID,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$appGUID,
    [Parameter()][ValidateSet("Output","Verbose")][string]$LogLevel = "Verbose"
  )
  $appConfigPath = Join-Path -Path $script:AppFactoryClientConfigDir -ChildPath "$($orgGUID)\$($appGUID).json"  
  if(-not (Test-Path $appConfigPath)){
    return $false
  }
  return (Get-Content -Path $appConfigPath | ConvertFrom-JSON)
}
#EndRegion '.\Public\Get-AppFactoryServiceClientAppConfig.ps1' 15
#Region '.\Public\Get-AppFactoryStorageSAS.ps1' 0
function Get-AppFactoryStorageSAS{
  [CmdletBinding()]
  [OutputType([Hashtable])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$GUID,
    [Parameter()][ValidateNotNullOrEmpty()][int]$hours = 2,
    [Parameter()][ValidateSet("Output","Verbose")][string]$LogLevel = "Verbose"
  )
  # Create expiry time
  $SASExpiry = (Get-Date).ToUniversalTime().AddHours($hours) 
  # Azure Storage Contexts.
  $script:psadtStorageContext = Connect-AppFactoryAzureStorage -storageContainer $script:AppFactoryDeploymentsContainer -storageSecret $script:AppFactoryDeploymentsSecret -LogLevel $LogLevel   
  # Store the details for the connectivity and return it
  try{
    $SASTokens = @{
      "StoragePath" = "https://$($script:AppFactoryDeploymentsContainer).blob.core.windows.net"
      "PublicContainerName" = $script:AppFactoryPublicFolder
      "OrganizationContainerName" = $GUID
      "public" = New-AzStorageContainerSASToken -Context $script:psadtStorageContext -Name $script:AppFactoryPublicFolder -Permission r  -ExpiryTime $SASExpiry
      "organization" = New-AzStorageContainerSASToken -Context $script:psadtStorageContext -Name $GUID -Permission r -ExpiryTime $SASExpiry    
    }
    if ($script:AppFactoryLogging) {
      Write-PSFMessage -Message "Created SAS Tokens for client with GUID <c='green'>$($GUID)</c>" -Level $LogLevel -Tag "Azure","Storage","SAS" -Target "Application Factory Service"
    }
    return $SASTokens  
  }
  catch{
    if ($script:AppFactoryLogging) {
      Write-PSFMessage -Message "Unable to creat SAS Tokens for client with GUID <c='green'>$($GUID)</c>" -Level "Error" -Tag "Azure","Storage","SAS" -Target "Application Factory Service"
    }
    throw $_
  }
}
#EndRegion '.\Public\Get-AppFactoryStorageSAS.ps1' 34
#Region '.\Public\Get-NewlyPublishedApplications.ps1' 0
function Get-NewlyPublishedApplications {
  [CmdletBinding()]
  param(
    [Parameter()][datetime]$startDate = ((Get-Date).AddDays(-1)),
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  # Connect to get graph token
  Get-GraphAccessToken -clientID $script:AppFactoryServiceClientID -tenantID $script:AppFactoryServiceTenantID -clientSecret (ConvertFrom-SecureString -SecureString $script:AppFactoryServiceSecret -AsPlainText) | Out-Null
  # Get the list of applications
  $UpdatedApplications = Get-AppFactoryApp -LogLevel $LogLevel | Where-Object { $null -ne $_.SourceFiles.LastUpdate -and $_.SourceFiles.LastUpdate -ne "" -and (Get-Date -Date $_.SourceFiles.LastUpdate) -ge $startDate }
  $Clients = Get-AppFactoryServiceClient -LogLevel $LogLevel
  foreach($client in $clients){
    $emailbody = [System.Collections.Generic.List[String]]::new()
    $emailBody.add("<html>")
    $emailBody.add(" <body>")
    $emailBody.add(" <h1>Newly Published Applications - Public</h1>")
    $emailBody.add(" <table style='width: 100%;'>")
    $emailBody.add(" <tr>")
    $emailbody.Add(" <th style='width: 33%;background-color:#c0c0c0;border:1px solid black;'>Application</th>")
    $emailbody.Add(" <th style='width: 33%;background-color:#c0c0c0;border:1px solid black;'>Version</th>")
    $emailbody.Add(" <th style='width: 33%;background-color:#c0c0c0;border:1px solid black;'>Updated</th>")
    $emailbody.Add(" </tr>")
    foreach($app in ($UpdatedApplications | Where-Object {-not $_.SourceFiles.publishTo})){
      $emailBody.add(" <tr>")
      $emailBody.Add(" <td>$($app.Information.DisplayName)</td>")
      $emailBody.Add(" <td>$($app.Information.AppVersion)</td>")
      $emailBody.Add(" <td>$($app.SourceFiles.LastUpdate)</td>")
      $emailBody.add(" </tr>")
    }
    $emailBody.add(" </table>")
    $emailBody.add(" <h1>Newly Published Applications - $($client.Name)</h1>")
    $emailBody.add(" <table style='width: 100%;'>")
    $emailBody.add(" <tr>")
    $emailbody.Add(" <th style='width: 33%;background-color:#c0c0c0;border:1px solid black;'>Application</th>")
    $emailbody.Add(" <th style='width: 33%;background-color:#c0c0c0;border:1px solid black;'>Version</th>")
    $emailbody.Add(" <th style='width: 33%;background-color:#c0c0c0;border:1px solid black;'>Updated</th>")
    $emailbody.Add(" </tr>")
    foreach($app in ($UpdatedApplications | Where-Object {$_.SourceFiles.publishTo})){
      if($client.guid -in $app.SourceFiles.publishTo){
        $emailBody.add(" <tr>")
        $emailBody.Add(" <td>$($app.Information.DisplayName)</td>")
        $emailBody.Add(" <td>$($app.Information.AppVersion)</td>")
        $emailBody.Add(" <td>$($app.SourceFiles.LastUpdate)</td>")
        $emailBody.add(" </tr>")  
      }
    }
    $emailBody.add(" </table>")    
    
    
    $emailBody.add(" </body>")
    $emailBody.add("</html>")
    if($client.Contacts){
      $params = @{
        from = $script:AppFactoryServiceSendMailAs
        subject = "Newly Published Applications"
        to = $clients.Contacts
        message = $emailbody
      }
      Send-GraphMailMessage @params
    }
  }


  
  #$UpdatedApplications.Information.DisplayName
}
#EndRegion '.\Public\Get-NewlyPublishedApplications.ps1' 67
#Region '.\Public\Get-PSUGuiAppDetectionInfo.ps1' 0
function Get-PSUGuiAppDetectionInfo{
  [cmdletbinding()]
  [OutputType([System.Collections.Generic.List[Hashtable]])]
  param(
  ) 
  $AppDetection = @{}
  $UninstallInformationMap = @(
    @{
      id    = "Detection_VersionCompare"
      param = "ProductVersionOperator"
      type  = "text"
    },
    @{
      id    = "Detection_Script_32bit"
      param = "RunAs32Bit"
      type  = "switch"
    },
    @{
      id    = "Detection_Keypath"
      param = "KeyPath"
      type  = "text"
    },
    @{
      id    = "Detection_Item"
      param = "ValueName"
      type  = "text"
    },
    @{
      id    = "Detection_Operator"
      param = "Operator"
      type  = "text"
    },
    @{
      id    = "Detection_32bit"
      param = "Check32BitOn64System"
      type  = "switch"
    }
  )  
  $AppDetectionName = (Get-UDElement -id "Detection").Value
  if ([String]::IsNullOrWhiteSpace($AppDetectionName)) {
    Show-UDToast -Message "Application Detection is required on the detection tab." -Duration 3000 -Position "topCenter" -BackgroundColor "#FF0000"
    $global:submitApp = $false
  }
  else{
    if($AppDetectionName -eq "Registry Version"){
      $AppDetection.Add("Type", "Registry") | Out-Null
      $AppDetection.Add("DetectionMethod", "VersionComparison") | Out-Null
    } 
    elseif($AppDetectionName -eq "Registry Existance"){
      $AppDetection.Add("Type", "Registry") | Out-Null
      $AppDetection.Add("DetectionMethod", "Existence") | Out-Null
    }
    else{
      $AppDetection.Add("Type", $AppDetectionName) | Out-Null
    }
    foreach ($item in $UninstallInformationMap) {
      $data = Get-UDElement -id $item.ID
      if ($item.type -eq "switch" -and $null -ne $data.checked) {
        $AppDetection.Add($item.param, $data.checked) | Out-Null
      }
      else{
        if (-not [String]::IsNullOrWhiteSpace($data.value)) {
          $AppDetection.Add($item.param, $data.value) | Out-Null
        }
      }
    }    
  }
  return $AppDetection  
}
#EndRegion '.\Public\Get-PSUGuiAppDetectionInfo.ps1' 70
#Region '.\Public\Get-PSUGUIAppInfo.ps1' 0
function Get-PSUGUIAppInfo {
  [cmdletbinding()]
  [OutputType([System.Collections.Generic.List[Hashtable]])]
  param(
  )
  $AppInformation = @{}
  $FilterInformation = @{}
  $InformationFieldMap = @(
    @{
      id       = "ApplicationName"
      param    = "displayName"
      required = $true
      type     = "text"
    },
    @{
      id       = "Publisher"
      param    = "publisher"
      required = $true
      type     = "text"
    },
    @{
      id       = "Owner"
      param    = "owner"
      required = $false
      type     = "text"
    },
    @{
      id       = "Active"
      param    = "Active"
      required = $false
      type     = "switch"
    },
    @{
      id       = "pauseUpdate"
      param    = "pauseUpdate"
      required = $false
      type     = "switch"
    },
    @{
      id       = "InstallExperience"
      param    = "InstallExperience"
      required = $false
      type     = "select"
    },
    @{
      id       = "Client"
      param    = "publishTo"
      required = $false
      type     = "select_client"
    },
    @{
      id       = "informationURL"
      param    = "informationURL"
      required = $false
      type     = "text"
    },
    @{
      id       = "PrivacyURL"
      param    = "privacyURL"
      required = $false
      type     = "text"
    },
    @{
      id       = "Architecture"
      param    = "Architecture"
      required = $false
      type     = "select"
    },
    @{
      id       = "MinimumSupportedWindowsRelease"
      param    = "MinimumSupportedWindowsRelease"
      required = $false
      type     = "select"
    },
    @{
      id       = "Description"
      param    = "Description"
      required = $true
      type     = "text"
    },
    @{
      id       = "Notes"
      param    = "Notes"
      required = $false
      type     = "text"
    }
  )
  $SourceInformatinMap = @(
    @{
      id           = "StorageContainer"
      param        = "StorageAccountContainerName"
      required     = $true
      required_for = @("storageAccount", "Sharepoint", "LocalStorage")
      type         = "text"
    },
    @{
      id           = "Installer"
      param        = "AppSetupFileName"
      required     = $true
      required_for = @("storageAccount", "Sharepoint", "Winget", "Evergreen", "LocalStorage")
      type         = "text"
    },
    @{
      id           = "AppID"
      param        = "AppID"
      required     = $true
      required_for = @("Winget", "Evergreen")
      type         = "text"
    },
    @{
      id           = "AppVersion"
      param        = "AppVersion"
      required     = $true
      required_for = @("PSADT","PowerShell")
      type         = "text"
    } 
  )
  $FilterInformationMap = @(
    @{
      id    = "Filter_Architecture"
      param = "architecture"
    },
    @{
      id    = "Filter_Platform"
      param = "Platform"
    },
    @{
      id    = "Filter_Channel"
      param = "Channel"
    },
    @{
      id    = "Filter_Type"
      param = "Type"
    },
    @{
      id    = "Filter_Release"
      param = "Release"
    },
    @{
      id    = "Filter_Language"
      param = "Language"
    },
    @{
      id    = "Filter_ImageType"
      param = "ImageType"
    }
  )
  foreach ($item in $InformationFieldMap) {
    $data = Get-UDElement -id $item.id
    if ([String]::IsNullOrWhiteSpace($data.value) -and $item.required) {
      Show-UDToast -Message "$($item.id) is required on the Information tab." -Duration 3000 -Position "topCenter" -BackgroundColor "#FF0000"
      $global:submitApp = $false
    }
    else {
      if ($item.type -eq "switch" -and $null -ne $data.checked) {
        $AppInformation.Add($item.param, $data.checked) | Out-Null
      }
      elseif($item.type -eq "select_client"){
        $Clients = [System.Collections.Generic.List[String]]::new()
        if($data.value){
          $clientList = Get-AppFactoryServiceClient
          foreach($obj in $data.value){
            $client = $clientList | Where-Object {$_.Name -eq $obj}
            $Clients.Add($client.GUID)
          }  
        }
        $AppInformation.Add($item.param, $Clients) | Out-Null
      }
      else {
        if (-not [String]::IsNullOrWhiteSpace($data.value)) {
          $AppInformation.Add($item.param, $data.value) | Out-Null
        }
      }
    }
  }
  $AppSourceName = (Get-UDElement -id "Source").Value
  if ([String]::IsNullOrWhiteSpace($AppSourceName)) {
    Show-UDToast -Message "Application Source is required on the source tab." -Duration 3000 -Position "topCenter" -BackgroundColor "#FF0000"
    $global:submitApp = $false
  }
  else {
    $AppInformation.Add("AppSource", $AppSourceName) | Out-Null
    foreach ($item in $SourceInformatinMap) {
      $data = Get-UDElement -id $item.ID
      if ([String]::IsNullOrWhiteSpace($data.value) -and $item.required -and ($AppInformation.AppSource -in $item.required_for)) {
        Show-UDToast -Message "$($item.id) is required on the source tab." -Duration 3000 -Position "topCenter" -BackgroundColor "#FF0000"
        $global:submitApp = $false
      }
      else {
        if ($item.type -eq "switch" -and $null -ne $data.checked) {
          $AppInformation.Add($item.param, $data.checked) | Out-Null
        }
        else {
          if (-not [String]::IsNullOrWhiteSpace($data.value)) {
            $AppInformation.Add($item.param, $data.value) | Out-Null
          }
        }
      }                
    }
    if ($AppSourceName -eq "Evergreen") {
      foreach ($item in $FilterInformationMap) {
        $data = Get-UDElement -id $item.id
        if (-not [String]::IsNullOrWhiteSpace($data.value)) {
          $FilterInformation.Add($item.param, $data.value) | Out-Null
        }
      }
      $AppInformation.Add("filterOptions", $FilterInformation) | Out-Null
    }    
  }  
  return $AppInformation
}
#EndRegion '.\Public\Get-PSUGUIAppInfo.ps1' 212
#Region '.\Public\Get-PSUGUIAppInstallInfo.ps1' 0
function Get-PSUGUIAppInstallInfo{
  [cmdletbinding()]
  [OutputType([System.Collections.Generic.List[Hashtable]])]
  param(
  )  
  $AppInstall = @{}
  $InstallInformationMap = @(
    @{
      id    = "Install_installer"
      param = "installer"
      type  = "text"
    },
    @{
      id    = "Install_argumentList"
      param = "argumentList"
      type  = "text"
    },
    @{
      id    = "Install_conflictingProcessStart"
      param = "conflictingProcessStart"
      type  = "array"
    },
    @{
      id    = "Install_conflictingProcessEnd"
      param = "conflictingProcessEnd"
      type  = "array"
    },
    @{
      id    = "Install_ignoreExitCodes"
      param = "ignoreExitCodes"
      type  = "array"
    },
    @{
      id    = "Install_WIM"
      param = "wim"
      type  = "switch"
    },
    @{
      id    = "Install_secureArgumentList"
      param = "secureArgumentList"
      type  = "switch"
    },
    @{
      id    = "Install_transforms"
      param = "transforms"
      type  = "text"
    },
    @{
      id    = "Install_additionalArgumentList"
      param = "additionalArgumentList"
      type  = "text"
    },
    @{
      id    = "Install_SkipMSIAlreadyInstalledCheck"
      param = "SkipMSIAlreadyInstalledCheck"
      type  = "switch"
    },
    @{
      id    = "Install_Script"
      param = "script"
      type  = "code"
    }
  )  
  $AppInstallName = (Get-UDElement -id "Install").Value
  if ([String]::IsNullOrWhiteSpace($AppInstallName)) {
    Show-UDToast -Message "Application Install is required on the install tab." -Duration 3000 -Position "topCenter" -BackgroundColor "#FF0000"
    $global:submitApp = $false
  }
  else{
    $AppInstall.Add("Type", $AppInstallName) | Out-Null
    foreach ($item in $InstallInformationMap) {
      $data = Get-UDElement -id $item.ID
      if ($item.type -eq "switch" -and $null -ne $data.checked) {
        $AppInstall.Add($item.param, $data.checked) | Out-Null
      }
      elseif($item.type -eq "array" -and (-not [String]::IsNullOrWhiteSpace($data.value))){
        $AppInstall.Add($item.param, ($data.value -split ",")) | Out-Null
      }
      elseif($item.type -eq "code" -and (-not [String]::IsNullOrWhiteSpace($data.code))){
        $AppInstall.Add($item.param, ($data.code -split "`r`n")) | Out-Null
      }
      else{
        if (-not [String]::IsNullOrWhiteSpace($data.value)) {
          $AppInstall.Add($item.param, $data.value) | Out-Null
        }
      }
    }
  }
  return $AppInstall  
}
#EndRegion '.\Public\Get-PSUGUIAppInstallInfo.ps1' 91
#Region '.\Public\Get-PSUGUIAppUninstallInfo.ps1' 0
function Get-PSUGUIAppUninstallInfo {
  [cmdletbinding()]
  [OutputType([System.Collections.Generic.List[Hashtable]])]
  param(
  ) 
  $AppUninstall = @{}  
  $UninstallInformationMap = @(
    @{
      id    = "Uninstall_installer"
      param = "installer"
      type  = "text"
    },
    @{
      id    = "Uninstall_argumentList"
      param = "argumentList"
      type  = "text"
    },
    @{
      id    = "Uninstall_conflictingProcessStart"
      param = "conflictingProcessStart"
      type  = "array"
    },
    @{
      id    = "Uninstall_conflictingProcessEnd"
      param = "conflictingProcessEnd"
      type  = "array"
    },
    @{
      id    = "Uninstall_ignoreExitCodes"
      param = "ignoreExitCodes"
      type  = "array"
    },
    @{
      id    = "Uninstall_wim"
      param = "wim"
      type  = "switch"
    },
    @{
      id    = "Uninstall_dirFiles"
      param = "dirFiles"
      type  = "switch"
    },
    @{
      id    = "Uninstall_secureArgumentList"
      param = "secureArgumentList"
      type  = "switch"
    },
    @{
      id    = "Uninstall_productCode"
      param = "productCode"
      type  = "text"
    },
    @{
      id    = "Uninstall_additionalArgumentList"
      param = "additionalArgumentList"
      type  = "text"
    },
    @{
      id    = "Uninstall_name"
      param = "name"
      type  = "text"
    },
    @{
      id    = "Uninstall_Script"
      param = "script"
      type  = "code"
    }
  )
  $AppUninstallName = (Get-UDElement -id "Uninstall").Value
  if ([String]::IsNullOrWhiteSpace($AppUninstallName)) {
    Show-UDToast -Message "Application Uninstall is required on the uninstall tab." -Duration 3000 -Position "topCenter" -BackgroundColor "#FF0000"
    $global:submitApp = $false
  }
  else{
    $AppUninstall.Add("Type", $AppUninstallName) | Out-Null
    foreach ($item in $UninstallInformationMap) {
      $data = Get-UDElement -id $item.ID
      if ($item.type -eq "switch" -and $null -ne $data.checked) {
        $AppUninstall.Add($item.param, $data.checked) | Out-Null
      }
      elseif($item.type -eq "array" -and (-not [String]::IsNullOrWhiteSpace($data.value))){
        $AppUninstall.Add($item.param, ($data.value -split ",")) | Out-Null
      }
      elseif($item.type -eq "code" -and (-not [String]::IsNullOrWhiteSpace($data.code))){
        $AppUninstall.Add($item.param, ($data.code -split "`r`n")) | Out-Null
      }
      else{
        if (-not [String]::IsNullOrWhiteSpace($data.value)) {
          $AppUninstall.Add($item.param, $data.value) | Out-Null
        }
      }
    }
  }  
  return $AppUninstall
}
#EndRegion '.\Public\Get-PSUGUIAppUninstallInfo.ps1' 96
#Region '.\Public\Import-AppFactoryECNOApp.ps1' 0
function Import-AppFactoryECNOApp {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$applicationName,
    [Parameter()][String[]]$publishTo = @(),
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  # First download the application from sharepoint so that we can inspect it
  $application = [PSCustomObject]@{
    "Information" = [PSCustomObject]@{
      "DisplayName" = $applicationName
    }
    "SourceFiles" = [PSCustomObject]@{
      "StorageAccountContainerName" = $applicationName
    }
  }
  $details = Get-AppFactoryECNOAppItem -application $application -LogLevel $LogLevel
  $AppSetupFolderPath = Join-Path -Path $script:AppFactoryWorkspace -ChildPath "Installers" -AdditionalChildPath $applicationName
  # Create path if it doesn't exist
  if (-not(Test-Path -Path $AppSetupFolderPath -PathType "Container")) {
    try {
      New-Item -Path $AppSetupFolderPath -ItemType "Container" -ErrorAction "Stop" | Out-Null
    }
    catch [System.Exception] {
      throw "[$($application.Information.DisplayName)] Failed to create '$($Path)' with error message: $($_.Exception.Message)"
    }
  }  
  $Application.SourceFiles | Add-Member -MemberType NoteProperty -Name "PackageVersion" -Value $details.Version -Force
  $Application.SourceFiles | Add-Member -MemberType NoteProperty -Name "PackageSource" -Value $details.URI -Force  
  Get-AppFactoryECNOFile -application $application -Destination $AppSetupFolderPath -LogLevel $LogLevel
  # Read the config file to get details
  $configFile = Join-Path -Path $AppSetupFolderPath -ChildPath "_win32app.txt"
  $file = Get-Content -Path $configFile
  $publisherName = $file[45].trim()
  $informationURL = $file[53].trim()
  $privacyURL = $file[55].trim()
  $Description = $file[43].trim()
  $Notes = $file[61].trim()
  # Create application cinfig
  $NewApplication = @{
    displayName                    = $applicationName
    publisher                      = $publisherName
    description                    = $Description
    notes                          = $Notes
    owner                          = "ECNO"
    AppSource                      = "ECNO"
    StorageAccountContainerName    = $applicationName
    informationURL                 = $informationURL
    PrivacyURL                     = $privacyURL
    publishTo                      = $publishTo
  }
  $NewApplication = New-AppFactoryApp @NewApplication
  $Detection = @{
    appGUID                        = $NewApplication.GUID
    Type                           = "Script"
  }
  Set-AppFactoryAppDetectionRule @Detection
  $Install = @{
    appGUID                        = $NewApplication.GUID
    Type                           = "ECNO"
  }
  Set-AppFactoryAppInstall @Install
  Set-AppFactoryAppUninstall @Install  
  $iconFolder = Join-Path -Path $AppSetupFolderPath -ChildPath "osi"
  $icon = Get-ChildItem -Path $iconFolder -Filter "*.png" -Recurse
  $scriptFile = Join-Path -Path $AppSetupFolderPath -ChildPath "_detect.ps1"
  $appDestination = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath $applicationName
  Remove-Item -Path "$($appDestination)\detection.ps1" -Force
  Remove-Item -Path "$($appDestination)\Icon.png" -Force
  Copy-Item -Path $icon.FullName -Destination $appDestination -Force
  Copy-Item -Path $scriptFile -Destination $appDestination -Force  
  Rename-Item -Path "$($appDestination)\$($icon.Name)" -NewName "Icon.png" -Force
  Rename-Item -Path "$($appDestination)\_detect.ps1" -NewName "detection.ps1" -Force  
  Remove-Item -Path $AppSetupFolderPath -Recurse -Force -Confirm:$false
}
#EndRegion '.\Public\Import-AppFactoryECNOApp.ps1' 76
#Region '.\Public\Import-PreviousVersion.ps1' 0
function Import-PreviousVersion {
  [cmdletbinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$AppDetailsPath
  )
  $test = $false
  $ApplicationData = Get-Content -Path $AppDetailsPath | ConvertFrom-JSON -Depth 10
  #region Application Data
  $App = @{
    displayName    = ($ApplicationData.AppData.IntuneAppName -replace "STS-")
    publisher      = $ApplicationData.AppData.AppPublisher
    description    = $ApplicationData.AppConfig.Information.description
    owner          = "ECNO"
    AppSource      = $ApplicationData.AppData.AppSource
    informationURL = $ApplicationData.AppData.informationURL
    PrivacyURL     = $ApplicationData.AppData.PrivacyURL
    publishTo      = $ApplicationData.AppData.publishTo    
  }
  switch ($ApplicationData.AppData.AppSource) {
    "Evergreen" {
      $App.Add("appID", $ApplicationData.AppData.appID)
      $App.Add("appSetupName", $ApplicationData.AppData.AppSetupFileName)
      $App.Add("filterOptions", $ApplicationData.AppData.filterOptions)
    }    
    "StorageAccount" {
      $App.Add("StorageAccountContainerName", $ApplicationData.AppData.StorageAccountContainerName)
      $App.Add("appSetupName", $ApplicationData.AppData.AppSetupFileName)
    }    
    "ECNO" {
      $App.Add("StorageAccountContainerName", $ApplicationData.AppData.StorageAccountContainerName)
    }
    "Winget" {
      $App.Add("appID", $ApplicationData.AppData.appID)
      $App.Add("appSetupName", $ApplicationData.AppData.AppSetupFileName)        
    }
  }
if(-not $test){
  $application = New-AppFactoryApp @App
}
else{
  "#################### APP ########################"
  $App
}  
  #endregion
  #region Install Data
  $install = @{
    type    = $ApplicationData.Install.type
    appGUID = $application.GUID
  }
  foreach ($member in ($ApplicationData.Install  | get-member | where-object { $_.MemberType -eq "NoteProperty" })) {
    switch ($member.Name) {
      "afterstopProcess" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.Install.afterstopProcess)){
          $install.Add("conflictingProcessEnd", $ApplicationData.Install.afterstopProcess)
        }
      }
      "beginstopProcess" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.Install.beginstopProcess)){
          $install.Add("conflictingProcessStart", $ApplicationData.Install.beginstopProcess)
        }
      }
      "ignoreExit" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.Install.ignoreExit)){
          $install.Add("ignoreExitCodes", $ApplicationData.Install.ignoreExit)
        }
      }
      "installer" {
        if((-not ([String]::IsNullOrWhiteSpace($ApplicationData.Install.installer)) -and $ApplicationData.Install.installer -ne "###SETUPFILENAME###")){
          $install.Add("installer", $ApplicationData.Install.installer)
        }
      }
      "mst" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.Install.mst)){
          $install.Add("transforms", $ApplicationData.Install.mst)
        }
      }
      "Parameters" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.Install.Parameters)){
          if ($ApplicationData.Install.type -eq "MSI") {
            $install.Add("additionalArgumentList", $ApplicationData.Install.Parameters)
          }
          else{
            $install.Add("argumentList", $ApplicationData.Install.Parameters)
          }
        }
      }
      "script" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.Install.script)){
          $install.type = "Script"
          $install.Add("script", $ApplicationData.Install.script)
        }
      }
      "WIM" {
        if([bool]$ApplicationData.Install.WIM){
          $Install.Add("WIM", $true)
        }
      }                 
    }
  }
if(-not $test){    
  Set-AppFactoryAppInstall @Install
}
else{
  "#################### INSTALL ########################"
  $ApplicationData.Install  | get-member | where-object { $_.MemberType -eq "NoteProperty" } | format-table
  $Install
}
  #endregion
  #region Uninstall Data
  $uninstall = @{
    type    = $ApplicationData.uninstall.type
    appGUID = $application.GUID
  }
  foreach ($member in ($ApplicationData.unInstall  | get-member | where-object { $_.MemberType -eq "NoteProperty" })) {
    switch ($member.Name) {
      "afterstopProcess" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.unInstall.afterstopProcess)){
          $unInstall.Add("conflictingProcessEnd", $ApplicationData.unInstall.afterstopProcess)
        }
      }
      "beginstopProcess" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.unInstall.beginstopProcess)){
          $unInstall.Add("conflictingProcessStart", $ApplicationData.unInstall.beginstopProcess)
        }
      }
      "DirFiles" {
        if([bool]$ApplicationData.unInstall.DirFiles){
          $unInstall.Add("DirFiles", $true)
        }
      }
      "ignoreExit" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.unInstall.ignoreExit)){
          $unInstall.Add("ignoreExitCodes", $ApplicationData.unInstall.ignoreExit)
        }
      }
      "installer" {
        if(-not ([String]::IsNullOrWhiteSpace($ApplicationData.unInstall.installer) -and $ApplicationData.unInstall.installer -ne "###SETUPFILENAME###")){
          $unInstall.Add("installer", $ApplicationData.unInstall.installer)
        }
      }
      "MSIGUID" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.unInstall.MSIGUID)){
          $unInstall.Add("productCode", $ApplicationData.unInstall.MSIGUID)
        }
      }
      "Name" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.unInstall.Name)){
          $unInstall.Add("Name", $ApplicationData.unInstall.Name)
        }
      }
      "Parameters" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.unInstall.Parameters)){
          if ($ApplicationData.unInstall.type -eq "MSI") {
            $unInstall.Add("additionalArgumentList", $ApplicationData.unInstall.Parameters)
          }
          else{
            $unInstall.Add("argumentList", $ApplicationData.unInstall.Parameters)
          }
        }
      } 
      "script" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.unInstall.script)){
          $unInstall.type = "Script"
          $unInstall.Add("script", $ApplicationData.unInstall.script)
        }
      }
      "WIM" {
        if([bool]$ApplicationData.unInstall.WIM){
          $unInstall.Add("WIM", $true)
        }
      }                      
    }
  }
if(-not $test){  
  Set-AppFactoryAppUninstall @Uninstall
}
else{
  "#################### UNINSTALL ########################"
  $ApplicationData.unInstall  | get-member | where-object { $_.MemberType -eq "NoteProperty" } | format-table
  $Uninstall
}
  #endregion
  #region Detection Data
  $detection = @{
    type    = $ApplicationData.AppConfig.DetectionRule.type
    appGUID = $application.GUID
  } 
  foreach ($member in ($ApplicationData.AppConfig.DetectionRule  | get-member | where-object { $_.MemberType -eq "NoteProperty" })) {
    switch ($member.Name) {
      "Name" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.AppConfig.DetectionRule.Check32BitOn64System)){
          $detection.Add("Check32BitOn64System", $ApplicationData.AppConfig.DetectionRule.Check32BitOn64System)
        }
      }
      "DetectionMethod" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.AppConfig.DetectionRule.DetectionMethod)){
          $detection.Add("DetectionMethod", $ApplicationData.AppConfig.DetectionRule.DetectionMethod)
        }
      }
      "KeyPath" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.AppConfig.DetectionRule.KeyPath)){
          $detection.Add("KeyPath", $ApplicationData.AppConfig.DetectionRule.KeyPath)
        }
      }
      "Operator" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.AppConfig.DetectionRule.Operator)){
          $detection.Add("Operator", $ApplicationData.AppConfig.DetectionRule.Operator)
        }
      }
      "ValueName" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.AppConfig.DetectionRule.ValueName)){
          $detection.Add("ValueName", $ApplicationData.AppConfig.DetectionRule.ValueName)
        }
      }
      "ProductVersionOperator" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.AppConfig.DetectionRule.ProductVersionOperator)){
          $detection.Add("ProductVersionOperator", $ApplicationData.AppConfig.DetectionRule.ProductVersionOperator)
        }
      }
      "DetectionType" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.AppConfig.DetectionRule.DetectionType)){
          $detection.Add("DetectionType", $ApplicationData.AppConfig.DetectionRule.DetectionType)
        }
      }
      "RunAs32Bit" {
        if([System.Convert]::ToBoolean($ApplicationData.AppConfig.DetectionRule.RunAs32Bit)){
          $detection.Add("RunAs32Bit", $true)
        }
      }
      "ScriptFile" {
        if(-not [String]::IsNullOrWhiteSpace($ApplicationData.AppConfig.DetectionRule.ScriptFile)){
          $detection.Add("ScriptFile", $ApplicationData.AppConfig.DetectionRule.ScriptFile)
        }
      }
    }
  }
if(-not $test){  
  Set-AppFactoryAppDetectionRule @detection
}
else{
  "##################### DETECTION ######################"
  $ApplicationData.AppConfig.DetectionRule  | get-member | where-object { $_.MemberType -eq "NoteProperty" } | format-table
  $detection  
}
  #endregion
  #region FInal Steps to Import file

if(-not $test){
  $appFolderPath = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath $App.displayName
  $IconPath = Join-Path -Path $appFolderPath -ChildPath "Icon.png"
  Remove-Item -path $IconPath -Force
  $ImportFrom = ((Get-Item $AppDetailsPath).Directory).FullName
  Copy-Item -Path "$($ImportFrom)\Icon.png" -Destination $appFolderPath -Force -ErrorAction SilentlyContinue
  Copy-Item -Path "$($ImportFrom)\Detection.ps1" -Destination $appFolderPath -Force -ErrorAction SilentlyContinue
  $temp = Get-Content -Path "$($appFolderPath)\ApplicationConfig.json" | ConvertFrom-Json -Depth 10
  $temp.GUID = $ApplicationData.AppData.GUID
  $temp | ConvertTo-Json -Depth 10 | Set-Content -Path "$($appFolderPath)\ApplicationConfig.json" -Force
}

  #endregion
}
# [String]::IsNullOrWhiteSpace
<#
#>


<#
 
#>

#EndRegion '.\Public\Import-PreviousVersion.ps1' 269
#Region '.\Public\Initialize-AppFactoryClientProcess.ps1' 0
function Initialize-AppFactoryClientProcess{
  [CmdletBinding()]
  param(
    [Alias("Path")][Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$ClientServicePath,
    [Parameter()][ValidateNotNullOrEmpty()][string]$configuration = "Configuration.json",
    [Parameter()][string]$LocalModule = $null,
    [Parameter()][switch]$EnableLogging,
    [Parameter()][int]$retries = 5,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  # Set if we should be logging with the PSFramework Module
  $script:AppFactoryClientLogging = $EnableLogging.IsPresent 
  # Where is the configuration file stored
  $script:AppFactoryClientSourceDir = $ClientServicePath
  $script:AppFactoryClientConfigDir = Join-Path -Path $script:AppFactoryClientSourceDir -ChildPath "Configurations"    
  # Workspace
  $script:AppFactoryClientWorkspace = Join-Path -Path $script:AppFactoryClientSourceDir -ChildPath "Workspace"
  if ($script:AppFactoryClientLogging) {
    $AppFactoryClientLogDir = Join-Path -Path $script:AppFactoryClientSourceDir -ChildPath "Logs"  
    # Name for the log file to be used
    $logFile = "$($AppFactoryClientLogDir)\AppFactoryClient-%Date%.csv"  
    $paramSetPSFLoggingProvider = @{
      Name         = "logfile"
      InstanceName = "AppFactoryClient"
      FilePath     = $logFile
      Enabled      = $true
      Wait         = $true
    }
    Set-PSFLoggingProvider @paramSetPSFLoggingProvider 
    Write-PSFMessage -Message "Logging Configured" -Level $LogLevel -Tag "Setup" -Target "Application Factory Client"
    Write-PSFMessage -Message "Log File: <c='green'>$($logFile)</c>" -Level $LogLevel -Tag "Setup" -Target "Application Factory Client"
    Write-PSFMessage -Message "Reading Configuration File" -Level $LogLevel -Tag "Setup" -Target "Application Factory Client"
  }  
  try {
    $configurationPath = Join-Path $script:AppFactoryClientConfigDir -ChildPath $configuration
    $configurationDetails = Get-Content -Path $configurationPath -ErrorAction Stop | ConvertFrom-JSON
    $script:AppFactoryClientClientRetries = $configurationDetails.retries
    $script:AppFactoryClientClientID = $configurationDetails.clientID
    $script:AppFactoryClientTenantID = $configurationDetails.tenantID
    $script:AppFactoryClientApiEndpoint = $configurationDetails.apiendpoint
    $script:AppFactoryClientAppRegSecret = Get-Secret -Vault $configurationDetails.keyVault -Name $configurationDetails.appregistrationSecret -AsPlainText
    $script:AppFactoryClientAPISecret = Get-Secret -Vault $configurationDetails.keyVault -Name $configurationDetails.apisecret
    $script:AppFactoryClientPrefix = $configurationDetails.prefix
  }
  catch {
    throw $_
  }   
}
#EndRegion '.\Public\Initialize-AppFactoryClientProcess.ps1' 49
#Region '.\Public\Initialize-AppFactoryProcess.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to set all the required variables and settings for the process
  .PARAMETER Path
  Where the application configuration files are stored
  .PARAMETER configuration
  The name of the configuration file if not the default
  .PARAMETER LocalModule
  If we should load the module from a local source vs installed module
  .PARAMETER EnableLogging
  If logging should be enabled with PSFramework
  .PARAMETER LogLevel
  If logging is enabled, what level of logging do we want, default is verbose.
  .EXAMPLE
 
  Start Application Factory with the default configuration file and no logging
    Initialize-AppFactoryProcess -Path "### PATH TO PROCESS FILES ###"
 
  Start Application Factory with a different configuration file and logging enabled
    Initialize-AppFactoryProcess -Path "### PATH TO PROCESS FILES ###" -configuration "### CONFIGURATION JSON FILE NAME ###" -EnableLogging
 
  Start Application Factory with a local module loaded for testing
    Initialize-AppFactoryProcess -Path "### PATH TO PROCESS FILES ###" -LocalModule "### PATH TO LOCAL MODULE ###"
#>

function Initialize-AppFactoryProcess{
  [CmdletBinding()]
  [Alias("Start-AppFactory")]
  param(
    [Alias("Path")][Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$ApplicationServicePath,
    [Parameter()][ValidateNotNullOrEmpty()][string]$configuration = "Configuration.json",
    [Parameter()][string]$LocalModule = $null,
    [Parameter()][switch]$EnableLogging,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  # Set if we should be logging with the PSFramework Module
  $script:AppFactoryLogging = $EnableLogging.IsPresent
  if($null -ne $LocalModule){
    $script:LocalModulePath = $LocalModule
  }
  # Determine what the Source Directory we aer using is
  $script:AppFactorySourceDir = $ApplicationServicePath  
  # Where are the supporting files stored
  $script:AppFactorySupportFiles = Join-Path -Path $PSScriptRoot -ChildPath "SupportFiles"
  $script:AppFactorySupportTemplateFolder = Join-Path -Path $PSScriptRoot -ChildPath "Templates"
  $script:AppFactoryLocalSupportFiles = Join-Path -Path $script:AppFactorySourceDir -ChildPath "SupportFiles"
  $script:AppFactoryWorkspace = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Workspace"
  # Setup Logging Configuration
  if ($script:AppFactoryLogging) {
    $AppFactoryLogDir = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Logs"
    # Name for the log file to be used
    $logFile = "$($AppFactoryLogDir)\AppFactoryService-%Date%.csv"   
    $paramSetPSFLoggingProvider = @{
      Name         = "logfile"
      InstanceName = "AppFactoryService"
      FilePath     = $logFile
      Enabled      = $true
      Wait         = $true
    }
    Set-PSFLoggingProvider @paramSetPSFLoggingProvider 
    Write-PSFMessage -Message "Logging Configured" -Level $LogLevel -Tag "Setup" -Target "Application Factory Service"
    Write-PSFMessage -Message "Log File: <c='green'>$($logFile)</c>" -Level $LogLevel -Tag "Setup" -Target "Application Factory Service"
    Write-PSFMessage -Message "Reading Configuration File" -Level $LogLevel -Tag "Setup" -Target "Application Factory Service"     
  }  
  # Where is the configuration folder
  $script:AppFactoryConfigDir = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Configurations"
  $script:AppFactoryClientConfigDir = Join-Path -Path $script:AppFactorySourceDir -ChildPath "ClientConfigurations"
  # What is the path to the configuration file
  $AFConfigFile = Join-Path $script:AppFactoryconfigDir -ChildPath $configuration
  # Read the configuration file into an object
  $configDetails = Get-Content -Path $AFConfigFile -ErrorAction Stop | ConvertFrom-JSON
  $script:AppFactoryInstallersContainer = $configDetails.storage.installers.name
  $script:AppFactoryInstallersSecret = Get-Secret -Vault $configDetails.keyVault -Name $configDetails.storage.installers.secret
  $script:AppFactoryDeploymentsContainer = $configDetails.storage.deployments.name
  $script:AppFactoryDeploymentsSecret = Get-Secret -Vault $configDetails.keyVault -Name $configDetails.storage.deployments.secret
  $script:AppFactoryPublicFolder = $configDetails.publicContainer
  $script:AppFactorySharepointTenant = $configDetails.sharepoint.tenant
  $script:AppFactorySharepointClientID = $configDetails.sharepoint.clientId
  $script:AppFactorysharepointsite = $configDetails.sharepoint.sharepointsite
  $script:AppFactorysharepointurl = $configDetails.sharepoint.sharepointurl
  $script:AppFactorySharepointCertificate = $configDetails.sharepoint.certificateFile
  $script:AppFactorySharepointCertificateSecret = Get-Secret -Vault $configDetails.keyVault -Name $configDetails.sharepoint.certificateSecret
  $script:AppFactorySharepointVersionField = $configDetails.sharepoint.versionField
  $script:AppFactorySharepointDocumentLibrary = $configDetails.sharepoint.documentLibrary  
  $script:AppFactoryServiceSendMailAs = $configDetails.ApplicationSendMailAs
  $script:AppFactoryServiceClientID = $configDetails.ApplicationClientID
  $script:AppFactoryServiceTenantID = $configDetails.ApplicationTenantID
  $script:AppFactoryServiceSecret = Get-Secret -vault $configDetails.keyVault -Name $configDetails.ApplicationSecret
  if ($script:AppFactoryLogging) {
    Write-PSFMessage -Message "Loaded variables for the Application Factory Service" -Level $LogLevel -Tag "Setup" -Target "Application Factory Service"
  }    
  # Initialize the Azure Storage and Available apps
  # Azure Storage Contexts. The first is for exe installers, the 2nd is for payload free installers
  $script:appStorageContext = Connect-AppFactoryAzureStorage -storageContainer $script:AppFactoryInstallersContainer -storageSecret $script:AppFactoryInstallersSecret -LogLevel $LogLevel
  $script:psadtStorageContext = Connect-AppFactoryAzureStorage -storageContainer $script:AppFactoryDeploymentsContainer -storageSecret $script:AppFactoryDeploymentsSecret -LogLevel $LogLevel
  # Get a Hashtable of All the Current Blobs that are present so we can check to see if the version is not packaged in at least one of the containers
  $script:PublishedAppList = @{
    "public" = Get-AzStorageBlob -Container public -Context $psadtStorageContext
  }
  $Clients = Get-AppFactoryServiceClient -LogLevel $LogLevel
  foreach($Client in $Clients){
    $script:PublishedAppList.Add($Client.GUID, (Get-AzStorageBlob -Container $Client.GUID -Context $psadtStorageContext))
  }  
}
#EndRegion '.\Public\Initialize-AppFactoryProcess.ps1' 104
#Region '.\Public\New-AppFactoryApp.ps1' 0

function New-AppFactoryApp {
  [CmdletBinding(DefaultParameterSetName = 'PSADTECNO')]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]$displayName,
    [Parameter()][ValidateNotNullOrEmpty()][String]$AppFolderName,
    [Parameter()][String]$description,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]$publisher,
    [Parameter()][String]$notes = "",
    [Parameter()][String]$owner = "",
    [Parameter()][String]$informationURL = "",
    [Parameter()][String]$privacyURL = "",
    [Parameter(Mandatory = $true)][ValidateSet("StorageAccount", "Sharepoint", "Winget", "Evergreen", "PSADT", "ECNO", "LocalStorage")][String]$AppSource,
    [Parameter(Mandatory = $true, ParameterSetName = 'WingetEvergreen')]
    [ValidateNotNullOrEmpty()][string]$appID,
    [Parameter(Mandatory = $true, ParameterSetName = 'AzureLocalStorage')]
    [Parameter(Mandatory = $true, ParameterSetName = 'Sharepoint')]
    [Parameter(Mandatory = $true, ParameterSetName = 'WingetEvergreen')]
    [Alias("appSetupName")][ValidateNotNullOrEmpty()][string]$AppSetupFileName = "",
    [Parameter(Mandatory = $true, ParameterSetName = 'ECNO')]
    [Parameter(Mandatory = $true, ParameterSetName = 'AzureLocalStorage')]
    [Alias("storageContainerName")][ValidateNotNullOrEmpty()][string]$StorageAccountContainerName = "",
    [Parameter()][String[]]$ExtraFiles = @(),
    [Parameter()][PSCustomObject]$filterOptions = @{},
    [Parameter()][String[]]$publishTo = @(),
    [Parameter()][String]$AppVersion = "<replaced_by_build>",
    [Parameter()][String[]]$AvailableVersions = @(),
    [Parameter()][ValidateSet("system", "user")][string]$InstallExperience = "system",
    [Parameter()][ValidateSet("suppress", "force", "basedOnReturnCode", "allow")][string]$DeviceRestartBehavior = "suppress",
    [Parameter()][ValidateSet("true", "false")][string]$AllowAvailableUninstall = $true,
    [Parameter()][ValidateSet("W10_1607", "W10_1703", "W10_1709", "W10_1809", "W10_1909", "W10_2004", "W10_20H2", "W10_21H1", "W10_21H2", "W10_22H2", "W11_21H2", "W11_22H2")][string]$MinimumSupportedWindowsRelease = "W10_1607",
    [Parameter()][ValidateSet("All", "x64", "x86")][string]$Architecture = "All",
    [Parameter()][String[]]$DependsOn = @(),    
    [Parameter()][bool]$active = $true,
    [Parameter()][bool]$pauseUpdate = $false,
    [Parameter()][switch]$force,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  if (-not $PSBoundParameters.ContainsKey("AppFolderName")) {
    $AppFolderName = $displayName
  }  
  try {
    # Create the package folders for the application we are creating
    New-AppFactoryAppFolder -displayName $displayName -folderName $AppFolderName -LogLevel $LogLevel -Force $force | Out-Null
  }
  catch {
    throw $_
  }
  # Read Template Config File
  $configFilePath = Join-Path -Path $script:AppFactorySupportTemplateFolder -ChildPath "Application" -AdditionalChildPath "ApplicationConfig.json"
  $configFIle = Get-Content -Path $configFilePath | ConvertFrom-Json
  # Get a new GUID for the application
  $appGUID = (New-GUID).Guid
  # Set Configuration File values
  $configFIle.GUID = $appGUID
  $configfile.Information.DisplayName = $displayName
  $configfile.Information.AppFolderName = $AppFolderName
  $configfile.Information.AppVersion = $AppVersion
  $configfile.Information.Description = $Description
  $configfile.Information.Publisher = $Publisher
  $configfile.Information.Notes = $Notes
  $configfile.Information.owner = $owner
  $configfile.Information.informationURL = $informationURL
  $configfile.Information.PrivacyURL = $PrivacyURL
  $configfile.SourceFiles.AppSource = $AppSource
  $configfile.SourceFiles.AppID = $AppID
  $configfile.SourceFiles.AppSetupFileName = $AppSetupFileName
  $configfile.SourceFiles.StorageAccountContainerName = $StorageAccountContainerName
  $configfile.SourceFiles.ExtraFiles = $ExtraFiles
  $configfile.SourceFiles.FilterOptions = $FilterOptions
  $configfile.SourceFiles.publishTo = $publishTo
  $configfile.SourceFiles.active = $active
  $configfile.SourceFiles.pauseUpdate = $pauseUpdate
  $configfile.Program.InstallExperience = $InstallExperience
  $configfile.Program.DeviceRestartBehavior = $DeviceRestartBehavior
  $configfile.Program.AllowAvailableUninstall = $AllowAvailableUninstall
  $configfile.RequirementRule.MinimumSupportedWindowsRelease = $MinimumSupportedWindowsRelease
  $configfile.RequirementRule.Architecture = $Architecture

  # If depends on was passed, check to make sure that the application exists
  if ($PSBoundParameters.ContainsKey("DependsOn")) {
    foreach ($app in $DependsOn) {
      $exists = Get-AppFactoryApp -appGUID $app
      if ($null -eq $exists) {
        Write-PSFMessage -Message "Error Encountered: Dependent Application with GUID $($app) does not exist" -Level "Error" -Tag "Application", "$($displayName)", "$($appGUID)" -Target "Application Factory Service"
        throw "Dependent Application with GUID $($app) does not exist"
      }
    }
    $configfile.SourceFiles.DependsOn = $DependsOn
  }
  # Create the configuration for the application
  try {
    Write-AppConfiguration -configfile $configfile -LogLevel $LogLevel
  }
  catch {
    Write-PSFMessage -Message "Error Encountered: $($_)" -Level "Error" -Tag "Application", "$($displayName)", "$($configFIle.GUID)" -Target "Application Factory Service"
    throw $_     
  }
  return $configfile
}

#EndRegion '.\Public\New-AppFactoryApp.ps1' 102
#Region '.\Public\New-AppFactoryClientSetup.ps1' 0
function New-AppFactoryClientSetup {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$path,
    [Parameter()][switch]$createSecretVault,
    [Parameter()][string]$vaultName = "ApplicationFactory",
    [Parameter(Mandatory = $true)][string]$AppRegistrationClientID,
    [Parameter(Mandatory = $true)][string]$AppRegistrationTenantID,
    [Parameter(Mandatory = $true)][string]$AppRegSecretName,
    [Parameter()][string]$AppRegSecretValue,
    [Parameter(Mandatory = $true)][string]$APIEndpoint,
    [Parameter(Mandatory = $true)][string]$APISecretName,
    [Parameter()][string]$prefix,
    [Parameter()][string]$APISecretValue
  )
  If (-Not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { throw "You need to run this function as administrator." } 
  # List of Required Moodules
  $modules = @("Microsoft.PowerShell.SecretManagement", "SecretManagement.JustinGrote.CredMan", "PSFramework", "UGDSB.PS.Graph","IntuneWin32App")
  # Install Required Modules
  foreach ($module in $modules) {
    Install-Module -Name $module -Scope AllUsers
  }
  # Create Secret Vault if configured to do so
  if ($PSBoundParameters.ContainsKey("createSecretVault")) {
    Register-SecretVault -Name $vaultName -ModuleName "SecretManagement.JustinGrote.CredMan"
  }
  # Create the secrets if values where passed
  if ($PSBoundParameters.ContainsKey("AppRegSecretValue")) {
    Set-Secret -Name $AppRegSecretName -Secret $AppRegSecretValue -Vault $vaultName
  }
  if ($PSBoundParameters.ContainsKey("APISecretValue")) {
    Set-Secret -Name $APISecretName -Secret $APISecretValue -Vault $vaultName
  }  
  # Create the base folder
  if (-not (Test-Path -path $path)) {
    New-Item -Path $path -ItemType Directory -Force
  }
  # Create the Client Folders
  $folders = @("Apps", "Configurations", "Logs", "Workspace")
  foreach ($folder in $folders) {
    $folderPath = Join-Path -Path $path -ChildPath $folder
    if (-not (Test-Path -path $folderPath)) {
      New-Item -Path $folderPath -ItemType Directory -Force
    }
  }
  # Create the Client Configurations
  $config = @{
    keyVault              = $vaultName
    clientID              = $AppRegistrationClientID
    tenantID              = $AppRegistrationTenantID
    appregistrationSecret = $AppRegSecretName
    apiendpoint           = $APIEndpoint
    apisecret             = $APISecretName
    prefix                = $prefix
  }
  $configPath = Join-Path -Path $path -ChildPath "Configurations" -AdditionalChildPath "Configuration.json"
  $config | ConvertTo-Json | Set-Content -Path $configPath
}
#EndRegion '.\Public\New-AppFactoryClientSetup.ps1' 59
#Region '.\Public\New-AppFactoryIntuneFile.ps1' 0
function New-AppFactoryIntuneFile {
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$applicationList,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  # Create blank list to store the applications that we will be moving forward with.
  $applications = [System.Collections.Generic.List[PSCustomObject]]@()
  # Loop through each of the applications
  foreach ($application in $applicationList) {
    try{
      $SourceFolder = Join-Path -Path $script:AppFactoryWorkspace -ChildPath "Publish" -AdditionalChildPath $application.Information.AppFolderName
      $IntuneWinAppUtilPath = Join-Path -Path $script:AppFactorySupportFiles -ChildPath "IntuneWinAppUtil.exe"
      $OutputPackage = Join-Path -Path $SourceFolder -ChildPath "$([System.IO.Path]::GetFileNameWithoutExtension($application.PackageInformation.SetupFile)).intunewin"
      $param = @{
        FilePath = $IntuneWinAppUtilPath
        ArgumentList = "-c ""$($SourceFolder)"" -s ""$($application.PackageInformation.SetupFile)"" -o ""$($SourceFolder)"" -q"
        LoadUserProfile = $false
        Passthru = $true
        UseNewEnvironment = $true
        Wait = $true
      }
      $process = Start-Process @param
      if($process.ExitCode -eq 0){
        Rename-Item -path $OutputPackage -NewName "$($application.Information.DisplayName).intunewin" -Force
      }
      else{
        throw "Process failed with exit code $($process.ExitCode)"
      }
      $application.SourceFiles | Add-Member -MemberType NoteProperty -Name IntunePackage -Value "$($application.Information.DisplayName).intunewin" -Force
      $applications.Add($application)
    }
    catch{
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Unable to create intune win files. Error: $($_)" -Level  "Warning" -Tag "Process",$application.Information.DisplayName -Target "Application Factory Service"
      continue          
    }
  }   
  return $applications 
}
#EndRegion '.\Public\New-AppFactoryIntuneFile.ps1' 41
#Region '.\Public\New-AppFactoryPackage.ps1' 0
function New-AppFactoryPackage{
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][System.Collections.Generic.List[PSCustomObject]]$applicationList,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  )
  # Create a list to store the results of the process
  $applications = [System.Collections.Generic.List[PSCustomObject]]@()
  foreach ($application in $applicationList) {
    try{
      $AppPublishFolderPath = Join-Path -Path $script:AppFactoryWorkspace -ChildPath "Publish" -AdditionalChildPath $application.Information.AppFolderName
      $AppSetupFolderPath = Join-Path -Path $script:AppFactoryWorkspace -ChildPath "Installers" -AdditionalChildPath $application.Information.AppFolderName
      $ApplicationDirectory = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath $application.Information.AppFolderName
      $ToolkitFile = Join-Path -Path $script:AppFactoryWorkspace -ChildPath "Publish" -AdditionalChildPath $application.Information.AppFolderName,"Invoke-AppDeployToolkit.ps1"
      Remove-Item -Path $AppPublishFolderPath -Force -Recurse -ErrorAction SilentlyContinue
      #region Create Publish Folder
      try {
        New-Item -Path $AppPublishFolderPath -ItemType Directory -ErrorAction "Stop" | Out-Null
      }
      catch {
        throw "[$($application.Information.DisplayName)] Failed to create '$($AppPublishFolderPath)' with error message: $($_.Exception.Message)"
      }
      #endregion
      #region Copy PSADT template files
      try{
        Copy-Item -Path "$($script:AppFactorySupportTemplateFolder)\PSADT\*" -Destination $AppPublishFolderPath -Recurse -Force -Confirm:$false
      }
      catch {
        throw "[$($application.Information.DisplayName)] Unable to copy template files: $($_.Exception.Message)"
      }
      #endregion
      #region Copy Application Files
      if($application.SourceFiles.AppSource -ne "PSADT"){
        try{
          New-Item -Path "$($AppPublishFolderPath)\Files" -ItemType Directory -Force | Out-Null
          Copy-Item -Path "$($AppSetupFolderPath)\*" -Destination "$($AppPublishFolderPath)\Files" -Recurse -Force -Confirm:$false
        }
        catch {
          throw "[$($application.Information.DisplayName)] Unable to copy template files: $($_.Exception.Message)"
        }
      }
      #endregion
      #region Inject Install/Uninstall in Deployment Script
      $ToolkitContent = Get-Content -Path $ToolkitFile -Raw
      $ToolkitContent = $ToolkitContent -replace "###INTUNEAPPNAME###", $application.Information.DisplayName
      $ToolkitContent = $ToolkitContent -replace "###APPPUBLISHER###", $application.Information.Publisher
      $ToolkitContent = $ToolkitContent -replace "###VERSION###", $application.SourceFiles.PackageVersion
      $ToolkitContent = $ToolkitContent -replace "###APPARCH###", $application.RequirementRule.Architecture
      $ToolkitContent = $ToolkitContent -replace "###AppLang###", "English"
      $ToolkitContent = $ToolkitContent -replace "###APPDATE###", (Get-Date).ToShortDateString()
      $InstallerContent = Get-Content -Path "$($ApplicationDirectory)\install.ps1" -ErrorAction SilentlyContinue
      $UninstallerContent = Get-Content -Path "$($ApplicationDirectory)\uninstall.ps1" -ErrorAction SilentlyContinue
      $ToolKitInstallStart = $ToolkitContent -split " ## <Perform Installation tasks here>"
      $ToolkitUninstallStart = $ToolKitInstallStart[1] -split " ## <Perform Uninstallation tasks here>"
      $NewContent = [System.Collections.Generic.List[string]]::new()
      foreach($line in $ToolKitInstallStart[0]){
        $NewContent.Add($line)
      }
      $NewContent.Add(" ## <Perform Installation tasks here>")
      foreach($line in $InstallerContent){
        $NewContent.Add(($line -replace "###SETUPFILENAME###", $($application.SourceFiles.AppSetupFileName)))
      }
      foreach($line in $ToolkitUninstallStart[0]){
        $NewContent.Add($line)
      }
      $NewContent.Add(" ## <Perform Uninstallation tasks here>")
      foreach($line in $UninstallerContent){
        $NewContent.Add(($line -replace "###SETUPFILENAME###", $($application.SourceFiles.AppSetupFileName)))
      }      
      foreach($line in $ToolkitUninstallStart[1]){
        $NewContent.Add($line)
      }
      Out-File -InputObject $NewContent -FilePath $ToolkitFile -Encoding "utf8" -Force -Confirm:$false  
      #endregion
      #region Update Install Experience if set to user
      if($application.Program.InstallExperience -eq "User"){
        $configPSPath = Join-Path -Path $AppPublishFolderPath -ChildPath "Config" -AdditionalChildPath "config.psd1"
        $configPSContent = Get-Content -Path $configPSPath
        $configPSContent = $configPSContent.replace('RequireAdmin = $true','RequireAdmin = $false')
        $configPSContent | Out-File $configPSPath -Force
      }

      #endregion
      $iconPath = Join-Path -Path $AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath  $application.Information.AppFolderName,"Icon.png"
      $iconDestination = Join-Path -Path $AppPublishFolderPath -ChildPath "Icon.png"
      Copy-Item -Path $iconPath -Destination $iconDestination -Force -Confirm:$false  
      if($application.DetectionRule.Type -eq "Script"){
        $detectionPath = Join-Path -Path $AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath  $application.Information.AppFolderName,$application.DetectionRule.ScriptFile
        $detectionDestination = Join-Path -Path $AppPublishFolderPath -ChildPath $application.DetectionRule.ScriptFile
        $detectionContent = Get-Content -Path $detectionPath -Raw
        $detectionContent = $detectionContent -replace "###VERSION###", $application.SourceFiles.PackageVersion
        Out-File -InputObject $detectionContent -FilePath $detectionDestination -Encoding "utf8" -Force -Confirm:$false  
      }
    }
    catch{
      if ($script:AppFactoryLogging) {
        Write-PSFMessage -Message $_ -Level "Error" -Tag "Application", "$($application.Information.DisplayName)", "Error" -Target "Application Factory Service"
      }
      continue
    }
    $applications.add($application)
  }
  return $applications
}
#EndRegion '.\Public\New-AppFactoryPackage.ps1' 106
#Region '.\Public\New-AppFactoryServiceClient.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to create a new organization
  .PARAMETER clientName
  The name of the organization
  .PARAMETER clientContacts
  The list of contacts for the organization
  .PARAMETER force
  Overwrite the current file with a new configuration.
  .PARAMETER LogLevel
  If logging is enabled, what level of logging do we want, default is verbose.
 
  .EXAMPLE
 
  Create a new organization
    New-AppFactoryClient -clientName "### Organization Name ###"
 
  Create a new organization with contacts
    New-AppFactoryClient -clientName "### Organization Name ###" -clientContacts "### Contact 1 ###", "### Contact 2 ###"
#>

function New-AppFactoryServiceClient {
  [CmdletBinding()]
  [Alias("New-AppFactoryOrganization")]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Alias('Name')][Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$clientName,
    [Alias('contactList')][Parameter()][string[]]$clientContacts = @(),
    [Parameter()][switch]$force,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"      
  )
  # Generate a unique GUID for client
  $GUID = (New-GUID).Guid
  # Create the client file to write out
  $obj = [PSCustomObject]@{
    GUID     = $GUID
    Name     = $clientName
    Contacts = $clientContacts
  }  
  # Path for the new client file
  $clientFile = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Clients" -AdditionalChildPath "$($clientName).json"
  # Client File Path
  if ($script:AppFactoryLogging) {
    Write-PSFMessage -Message "Creating client with GUID: <c='green'>$($GUID)</c>" -Level $LogLevel -Tag "Clients", "$($clientName)" -Target "Application Factory Service"
    Write-PSFMessage -Message "Client file path: <c='green'>$($clientFile)</c>" -Level $LogLevel -Tag "Clients", "$($clientName)" -Target "Application Factory Service"
  }
  # First write out the client json
  try {
    if ((Test-Path -Path $clientFile) -and -not $force.IsPresent) {
      throw "Client already exists with this name."
    }
    $obj | ConvertTo-Json | Out-File -FilePath $clientFile -Force
    Write-PSFMessage -Message "Created client configuration file." -Level $LogLevel -Tag "Clients", "$($clientName)" -Target "Application Factory Service"
  }
  catch {
    Write-PSFMessage -Message "Error Encountered: $($_)" -Level "Error" -Tag "Clients", "$($clientName)" -Target "Application Factory Service"
    throw $_
  }
  # Create Azure Storage Container to store client specific applications
  try {
    $clientStorageContainerContext = Connect-AppFactoryAzureStorage -storageContainer $script:AppFactoryDeploymentsContainer -storageSecret $script:AppFactoryDeploymentsSecret
    New-AzStorageContainer -Name $GUID -Permission Off -Context $clientStorageContainerContext | Out-Null
    if ($script:AppFactoryLogging) {
      Write-PSFMessage -Message "Created clients storage container <c='green'>$($GUID)</c>" -Level $LogLevel -Tag "Clients", "$($clientName)" -Target "AppFactory"
    }
  }
  catch {
    Remove-Item -Path $clientFile -Force
    Write-PSFMessage -Message "Error Encountered: $($_)" -Level "Error" -Tag "Clients", "$($clientName)" -Target "Application Factory Service"
    throw $_
  }
  return $obj      
}
#EndRegion '.\Public\New-AppFactoryServiceClient.ps1' 73
#Region '.\Public\New-PSUGUIAppTabs.ps1' 0
function New-PSUGUIAppTabs{
  [cmdletbinding()]
  param()
  New-UDTabs -Tabs {
    New-UDTab -Text 'Information' -Content {
      New-UDElement -Tag "div" -ClassName "card-body rounded" -Content {
        New-UDElement -Tag "table"  -Attributes @{
          "style"       = @{
            "width" = "100%";

          }
          "cellpadding" = "1"
        } -Content {
          New-UDElement -Tag "tr" -Content {
            New-PSUGUIInputTextGroup -Label "GUID:" -id "ApplicationGUID" -placeholder "" -disabled -item_colspan 3 -item_width 80
          }
          New-UDElement -Tag "tr" -Content {
            New-PSUGUIInputTextGroup -Label "Name:" -id "ApplicationName" -placeholder "Application Name"
            New-PSUGUIInputTextGroup -Label "Publisher:" -id "Publisher" -placeholder "Publisher Name"
          }
          New-UDElement -Tag "tr" -Content {
            New-PSUGUIInputSelectGroup -Label "Versions:" -id "AvailableVersions" -placeholder "Available Versions" -onchangeAction "AvailableVersionsSelection"
            New-PSUGUIInputTextGroup -Label "Owner:" -id "Owner" -placeholder "Owner Name"
          }
          New-UDElement -Tag "tr" -Content {
            New-PSUGUISwitch -Label "Active:" -id "Active" -Checked
            New-PSUGUISwitch -Label "Paused:" -id "pauseUpdate"
          }
          New-UDElement -Tag "tr" -Content {
            New-PSUGUIInputSelectGroup -Label "Install As:" -id "InstallExperience" -options @("system", "user") -DefaultValue "System"
            New-PSUGUUploadIcon -Label "Icon:" -id "IconFile" -placeholder "Upload Icon" -Output "$($env:TEMP)\PSUApp_Icon.png"
          }
          New-UDElement -Tag "tr" -Content {
            New-PSUGUIInputSelectGroup -Label "Clients:" -id "Client" -placeholder "Client Specific" -multiselect -options ((Get-AppFactoryServiceClient | Select-Object Name).Name | Sort-Object) -item_colspan 3 -item_width 80
          }
          New-UDElement -Tag "tr" -Content {
            New-PSUGUIInputTextGroup -Label "Information URL:" -id "InformationURL" -placeholder "Information URL" -item_colspan 3 -item_width 80
          }
          New-UDElement -Tag "tr" -Content {
            New-PSUGUIInputTextGroup -Label "Privacy URL:" -id "PrivacyURL" -placeholder "Privacy URL" -item_colspan 3 -item_width 80
          }  
          New-UDElement -Tag "tr" -Content {
            New-PSUGUIInputSelectGroup -Label "Architecture:" -id "Architecture" -placeholder "Available Architecture" -options @("All", "x86", "x64") -DefaultValue "All"
            New-PSUGUIInputSelectGroup -Label "Min Win OS:" -id "MinimumSupportedWindowsRelease" -placeholder "Minimum Windows OS Version" -options @("W10_1607", "W10_1703", "W10_1709", "W10_1809", "W10_1909", "W10_2004", "W10_20H2", "W10_21H1", "W10_21H2", "W10_22H2", "W11_21H2", "W11_22H2") -DefaultValue "W10_1607"
          } 
          New-UDElement -Tag "tr" -Content {
            New-PSUGUIInputTextboxGroup -Label "Description:" -id "Description" -placeholder "Description" -item_colspan 3 -item_width 80
          }
          New-UDElement -Tag "tr" -Content {
            New-PSUGUIInputTextboxGroup -Label "Notes:" -id "Notes" -placeholder "Notes" -item_colspan 3 -item_width 80
          }
        }
      }
    }
    New-UDTab -Text 'Source' -Content {
      New-UDElement -Tag "div" -ClassName "card-body rounded" -Content {
        New-UDElement -Tag "table"  -Attributes @{
          "style"       = @{
            "width" = "100%";

          }
          "cellpadding" = "1"
        } -Content {
          New-UDElement -Tag "tr" -Content {
            New-PSUGUIInputSelectGroup -Label "Source:" -id "Source" -placeholder "Select Source" -options @("StorageAccount", "Sharepoint", "Winget", "Evergreen", "PSADT", "ECNO", "LocalStorage") -item_colspan 3 -item_width 80 -onchangeAction "SourceSelection"
          }
        }                
      }
      New-UDelement -Tag "div" -ClassName "card-body rounded" -id "SourceSelectionFields" -Content {}
    }
    New-UDTab -Text 'Install' -Content {
      New-UDElement -Tag "div" -ClassName "card-body rounded" -Content {
        New-UDElement -Tag "table"  -Attributes @{
          "style"       = @{
            "width" = "100%";

          }
          "cellpadding" = "1"
        } -Content {
          New-UDElement -Tag "tr" -Content {
            New-PSUGUIInputSelectGroup -Label "Install:" -id "Install" -placeholder "Select Install Type" -options @("None", "EXE", "MSI", "ECNO", "Script") -item_colspan 3 -item_width 80 -onchangeAction "InstallSelection"
          }
        }                
      }
      New-UDelement -Tag "div" -ClassName "card-body rounded" -id "InstallSelectionFields" -Content {}             
    }
    New-UDTab -Text 'Uninstall' -Content {
      New-UDElement -Tag "div" -ClassName "card-body rounded" -Content {
        New-UDElement -Tag "table"  -Attributes @{
          "style"       = @{
            "width" = "100%";

          }
          "cellpadding" = "1"
        } -Content {
          New-UDElement -Tag "tr" -Content {
            New-PSUGUIInputSelectGroup -Label "Uninstall:" -id "Uninstall" -placeholder "Select Uninstall Type" -options @("NONE", "MSI", "EXE", "NAME", "GUID", "ECNO", "Script") -item_colspan 3 -item_width 80 -onchangeAction "UninstallSelection"
          }
        }                
      }
      New-UDelement -Tag "div" -ClassName "card-body rounded" -id "UninstallSelectionFields" -Content {}               
    }
    New-UDTab -Text 'Detection' -Content {
      New-UDElement -Tag "div" -ClassName "card-body rounded" -Content {
        New-UDElement -Tag "table"  -Attributes @{
          "style"       = @{
            "width" = "100%";

          }
          "cellpadding" = "1"
        } -Content {
          New-UDElement -Tag "tr" -Content {
            New-PSUGUIInputSelectGroup -Label "Detection:" -id "Detection" -placeholder "Select Detection Type" -options @("MSI", "Registry Version", "Registry Existance", "Script") -item_colspan 3 -item_width 80 -onchangeAction "DetectionSelection"
          }
        }                
      }
      New-UDelement -Tag "div" -ClassName "card-body rounded" -id "DetectionSelectionFields" -Content {}                  
    }
  }  
}
#EndRegion '.\Public\New-PSUGUIAppTabs.ps1' 121
#Region '.\Public\New-PSUGUIDetectionMSI.ps1' 0
function New-PSUGUIDetectionMSI{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputSelectGroup -Label "Version Comparison:" -id "Detection_VersionCompare" -placeholder "" -options @("notConfigured","equal","notEqual","greaterThanOrEqual","greaterThan","lessThanOrEqual","lessThan") -item_colspan 3 -item_width 80 -defaultValue $SourceData.ProductVersionOperator
    }              
  }    
}
#EndRegion '.\Public\New-PSUGUIDetectionMSI.ps1' 18
#Region '.\Public\New-PSUGUIDetectionRegistryExistance.ps1' 0
function New-PSUGUIDetectionRegistryExistance{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    $registryKey = $SourceData.keyPath -split "\\"
    if($registryKey[-1] -ne "###PRODUCTCODE###"){$value = $registryKey[-1]}
    else{$value = ""}    
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Registry Key:" -id "Detection_Keypath" -placeholder "### Only set if do not want to detect automatically from MSI ###"  -item_width 80 -item_colspan 3 -Value $value
    }
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Registry Item:" -id "Detection_Item" -placeholder "DisplayVersion"  -item_width 80 -item_colspan 3 -Value $SourceData.valueName
    }  
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputSelectGroup -Label "Detection Type:" -id "Detection_Operator" -placeholder "" -options @("notConfigured","equal","notEqual","greaterThanOrEqual","greaterThan","lessThanOrEqual","lessThan") -item_colspan 3 -item_width 80 -DefaultValue $SourceData.operator
    }  
    New-UDElement -Tag "tr" -Content {
      New-PSUGUISwitch -Label "32-Bit:" -id "Detection_32bit" -Checked:$([System.Convert]::ToBoolean($SourceData.Check32BitOn64System))
    }                       
  }   
}
#EndRegion '.\Public\New-PSUGUIDetectionRegistryExistance.ps1' 30
#Region '.\Public\New-PSUGUIDetectionRegistryVersion.ps1' 0
function New-PSUGUIDetectionRegistryVersion{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    $registryKey = $SourceData.keyPath -split "\\"
    if($registryKey[-1] -ne "###PRODUCTCODE###"){$value = $registryKey[-1]}
    else{$value = ""}
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Registry Key:" -id "Detection_Keypath" -placeholder "### Only set if do not want to detect automatically from MSI ###"  -item_width 80 -item_colspan 3 -Value $value
    }
    New-UDElement -Tag "tr" -Content {
      New-PSUGUISwitch -Label "32-Bit:" -id "Detection_32bit" -Checked:$([System.Convert]::ToBoolean($SourceData.Check32BitOn64System))
    }                   
  }    
}
#EndRegion '.\Public\New-PSUGUIDetectionRegistryVersion.ps1' 24
#Region '.\Public\New-PSUGUIDetectionScript.ps1' 0
function New-PSUGUIDetectionScript{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData,
    [Parameter()][string]$scriptPath
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    New-UDElement -Tag "tr" -Content {
      if(Test-Path $scriptPath){
        $code = Get-Content -Path $scriptPath -ErrorAction SilentlyContinue
      }
      New-PSUGUIInputCode -id "Detection_Script" -item_colspan 4 -Code ($code -join "`n")
    }   
    New-UDElement -Tag "tr" -Content {
      New-PSUGUISwitch -Label "32-Bit:" -id "Detection_Script_32bit" -Checked:$([System.Convert]::ToBoolean($SourceData.RunAs32Bit))
    } 
  }    
}
#EndRegion '.\Public\New-PSUGUIDetectionScript.ps1' 25
#Region '.\Public\New-PSUGUIInstallEXE.ps1' 0
function New-PSUGUIInstallEXE{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Setup Name:" -id "Install_installer" -placeholder "### Only set if not the same download file ###"  -item_width 80 -item_colspan 3 -Value $SourceData.installer
    }
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Parameters:" -id "Install_argumentList" -placeholder "Parameters" -item_width 80 -item_colspan 3 -Value $SourceData.argumentList
    } 
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Conflicting Processes (Start):" -id "Install_conflictingProcessStart" -placeholder "Comma seperated list of conflicting processes" -item_width 80 -item_colspan 3 -Value ($SourceData.conflictingProcessStart -Join ", ")
    }
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Conflicting Processes (End):" -id "Install_conflictingProcessEnd" -placeholder "Comma seperated list of conflicting processes" -item_width 80 -item_colspan 3 -Value ($SourceData.conflictingProcessEnd -Join ", ")
    } 
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Ignore Exit Codes:" -id "Install_ignoreExitCodes" -placeholder "Comma seperated list of exit codes to ignore" -item_width 80 -item_colspan 3 -Value ($SourceData.ignoreExitCodes -Join ", ")
      
    } 
    New-UDElement -Tag "tr" -Content {
      New-PSUGUISwitch -Label "WIM:" -id "Install_WIM" -Checked:$SourceData.wim
      New-PSUGUISwitch -Label "Secure Argument List:" -id "Install_secureArgumentList" -Checked:$SourceData.secureArgumentList
    }               
  }   
}
#EndRegion '.\Public\New-PSUGUIInstallEXE.ps1' 35
#Region '.\Public\New-PSUGUIInstallMSI.ps1' 0
function New-PSUGUIInstallMSI{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Setup Name:" -id "Install_installer" -placeholder "### Only set if not the same download file ###"  -item_width 80 -item_colspan 3 -Value $SourceData.installer
    }
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Transform (MST):" -id "Install_transforms" -placeholder "MST File"  -item_width 80 -item_colspan 3 -Value $SourceData.transforms
    }    
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Parameters:" -id "Install_additionalArgumentList" -placeholder "Parameters" -item_width 80 -item_colspan 3 -Value $SourceData.additionalArgumentList
    } 
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Conflicting Processes (Start):" -id "Install_conflictingProcessStart" -placeholder "Comma seperated list of conflicting processes" -item_width 80 -item_colspan 3 -Value ($SourceData.conflictingProcessStart -Join ", ")
    }
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Conflicting Processes (End):" -id "Install_conflictingProcessEnd" -placeholder "Comma seperated list of conflicting processes" -item_width 80 -item_colspan 3 -Value ($SourceData.conflictingProcessEnd -Join ", ")
    } 
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Ignore Exit Codes:" -id "Install_ignoreExitCodes" -placeholder "Comma seperated list of exit codes to ignore" -item_width 80 -item_colspan 3 -Value ($SourceData.ignoreExitCodes -Join ", ")
    } 
    New-UDElement -Tag "tr" -Content {
      New-PSUGUISwitch -Label "WIM:" -id "Install_WIM" -Checked:$SourceData.wim
      New-PSUGUISwitch -Label "Secure Argument List:" -id "Install_secureArgumentList" -Checked:$SourceData.secureArgumentList
    }               
    New-UDElement -Tag "tr" -Content {
      New-PSUGUISwitch -Label "Skip Already Installed Check:" -id "Install_SkipMSIAlreadyInstalledCheck" -Checked:$SourceData.SkipMSIAlreadyInstalledCheck
    }
  }   
}
#EndRegion '.\Public\New-PSUGUIInstallMSI.ps1' 40
#Region '.\Public\New-PSUGUIInstallScript.ps1' 0
function New-PSUGUIInstallScript{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputCode -id "Install_Script" -item_colspan 4 -Code $SourceData.Script
    }    
  }   
}
#EndRegion '.\Public\New-PSUGUIInstallScript.ps1' 18
#Region '.\Public\New-PSUGUISourceECNO.ps1' 0
function New-PSUGUISourceECNO{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Folder/Container:" -id "StorageContainer" -placeholder "Folder/Container Name" -Value $SourceData.storageAccountContainerName
    }    
  }   
}
#EndRegion '.\Public\New-PSUGUISourceECNO.ps1' 18
#Region '.\Public\New-PSUGUISourceEvergreen.ps1' 0
function New-PSUGUISourceEvergreen{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Installer:" -id "Installer" -placeholder "Installer File Name" -Value $SourceData.appSetupFileName
      New-PSUGUIInputTextGroup -Label "Evergreen ID:" -id "AppID" -placeholder "Evergreen ID" -Value $SourceData.appID
    } 
    New-UDElement -Tag "tr" -content {New-UDElement -Tag 'td' -content {New-UDElement -Tag "br" -content {}}}
    New-UDElement -Tag "tr" -Content {
      New-UDElement -Tag "td" -Content {
        New-UDTypography -Text "Filter Options" -Variant "h6" -ClassName "card-title rounded x-card-title"
      } -Attributes @{Colspan = 4 }
    }
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Architecture:" -id "Filter_Architecture" -placeholder "" -Value $SourceData.filterOptions.architecture
      New-PSUGUIInputTextGroup -Label "Platform:" -id "Filter_Platform" -placeholder "" -Value $SourceData.filterOptions.Platform
    } 
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Channel:" -id "Filter_Channel" -placeholder "" -Value $SourceData.filterOptions.Channel
      New-PSUGUIInputTextGroup -Label "Type:" -id "Filter_Type" -placeholder "" -Value $SourceData.filterOptions.Type
    } 
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Installer Type:" -id "Filter_InstallerType" -placeholder "" -Value $SourceData.filterOptions.InstallerType
      New-PSUGUIInputTextGroup -Label "Release:" -id "Filter_Release" -placeholder "" -Value $SourceData.filterOptions.Release
    } 
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Language:" -id "Filter_Language" -placeholder "" -Value $SourceData.filterOptions.Language
      New-PSUGUIInputTextGroup -Label "Image Type:" -id "Filter_ImageType" -placeholder "" -Value $SourceData.filterOptions.ImageType
    }                 
  }     
}
#EndRegion '.\Public\New-PSUGUISourceEvergreen.ps1' 41
#Region '.\Public\New-PSUGUISourcePSADT.ps1' 0
function New-PSUGUISourcePSADT{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    New-UDElement -Tag "tr" -Content {
      if(-not $SourceData -or $SourceData.AppVersion -eq "<replaced_by_build>"){
        [version]$VersionData = [Version]"1.0.0"
      }
      else{
        [version]$VersionData = [Version]$SourceData.AppVersion
      }
      New-PSUGUIInputTextGroup -Label "Script Version:" -id "AppVersion" -placeholder "Script Version i.e. 1.0.0" -Value $VersionData
    }    
  }   
}
#EndRegion '.\Public\New-PSUGUISourcePSADT.ps1' 24
#Region '.\Public\New-PSUGUISourceStorageAccount.ps1' 0
function New-PSUGUISourceStorageAccount {
  [cmdletbinding()]
  [Alias("New-PSUGUISourceSharepoint")]
  param(
    [Parameter()][PSCustomObject]$SourceData
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Installer:" -id "Installer" -placeholder "Installer File Name" -Value $SourceData.appSetupFileName
      New-PSUGUIInputTextGroup -Label "Folder/Container:" -id "StorageContainer" -placeholder "Folder/Container Name" -Value $SourceData.storageAccountContainerName
    }    
  }  
}
#EndRegion '.\Public\New-PSUGUISourceStorageAccount.ps1' 20
#Region '.\Public\New-PSUGUISourceWinget.ps1' 0
function New-PSUGUISourceWinget{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData  
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Installer:" -id "Installer" -placeholder "Installer File Name" -Value $SourceData.appSetupFileName
      New-PSUGUIInputTextGroup -Label "Winget ID:" -id "AppID" -placeholder "Winget ID" -Value $SourceData.appID
    }    
  }   
}
#EndRegion '.\Public\New-PSUGUISourceWinget.ps1' 19
#Region '.\Public\New-PSUGUIUninstallEXE.ps1' 0
function New-PSUGUIUninstallEXE{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Setup Name:" -id "Uninstall_installer" -placeholder "### Only set if not the same download file ###"  -item_width 80 -item_colspan 3 -Value $SourceData.installer
    }
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Parameters:" -id "Uninstall_argumentList" -placeholder "Parameters" -item_width 80 -item_colspan 3 -Value $SourceData.argumentList
    } 
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Conflicting Processes (Start):" -id "Uninstall_conflictingProcessStart" -placeholder "Comma seperated list of conflicting processes" -item_width 80 -item_colspan 3 -Value ($SourceData.conflictingProcessStart -Join ", ")
    }
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Conflicting Processes (End):" -id "Uninstall_conflictingProcessEnd" -placeholder "Comma seperated list of conflicting processes" -item_width 80 -item_colspan 3 -Value ($SourceData.conflictingProcessEnd -Join ", ")
    } 
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Ignore Exit Codes:" -id "Uninstall_ignoreExitCodes" -placeholder "Comma seperated list of exit codes to ignore" -item_width 80 -item_colspan 3 -Value ($SourceData.ignoreExitCodes -Join ", ")
    } 
    New-UDElement -Tag "tr" -Content {
      New-PSUGUISwitch -Label "WIM:" -id "Uninstall_wim" -Checked:$SourceData.wim
      New-PSUGUISwitch -Label "Packaged File (Dirfiles):" -id "Uninstall_dirFiles" -Checked:$SourceData.dirFiles
    }   
    New-UDElement -Tag "tr" -Content {
      New-PSUGUISwitch -Label "Secure Argument List:" -id "Uninstall_secureArgumentList" -Checked:$SourceData.secureArgumentList
    }            
  }   
}
#EndRegion '.\Public\New-PSUGUIUninstallEXE.ps1' 37
#Region '.\Public\New-PSUGUIUninstallGUID.ps1' 0
function New-PSUGUIUninstallGUID{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Product Code:" -id "Uninstall_productCode" -placeholder "MSI Product Code"  -item_width 80 -item_colspan 3 -Value $SourceData.productCode
    }          
  }    
}
#EndRegion '.\Public\New-PSUGUIUninstallGUID.ps1' 18
#Region '.\Public\New-PSUGUIUninstallMSI.ps1' 0
function New-PSUGUIUninstallMSI{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Setup Name:" -id "Uninstall_installer" -placeholder "### Only set if not the same download file ###"  -item_width 80 -item_colspan 3 -Value $SourceData.installer
    }
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Parameters:" -id "Uninstall_additionalArgumentList" -placeholder "Parameters" -item_width 80 -item_colspan 3 -Value $SourceData.additionalArgumentList
    } 
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Conflicting Processes (Start):" -id "Uninstall_conflictingProcessStart" -placeholder "Comma seperated list of conflicting processes" -item_width 80 -item_colspan 3 -Value ($SourceData.conflictingProcessStart -Join ", ")
    }
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Conflicting Processes (End):" -id "Uninstall_conflictingProcessEnd" -placeholder "Comma seperated list of conflicting processes" -item_width 80 -item_colspan 3 -Value ($SourceData.conflictingProcessEnd -Join ", ")
    } 
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Ignore Exit Codes:" -id "Uninstall_ignoreExitCodes" -placeholder "Comma seperated list of exit codes to ignore" -item_width 80 -item_colspan 3 -Value ($SourceData.ignoreExitCodes -Join ", ")
    } 
    New-UDElement -Tag "tr" -Content {
      New-PSUGUISwitch -Label "WIM:" -id "Uninstall_wim" -Checked:$SourceData.wim
      New-PSUGUISwitch -Label "Packaged File (Dirfiles):" -id "Uninstall_dirFiles" -Checked:$SourceData.dirFiles
    }   
    New-UDElement -Tag "tr" -Content {
      New-PSUGUISwitch -Label "Secure Argument List:" -id "Uninstall_secureArgumentList" -Checked:$SourceData.secureArgumentList
    }            
  } 
}
#EndRegion '.\Public\New-PSUGUIUninstallMSI.ps1' 37
#Region '.\Public\New-PSUGUIUninstallName.ps1' 0
function New-PSUGUIUninstallName{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputTextGroup -Label "Name:" -id "Uninstall_name" -placeholder "Name of Application"  -item_width 80 -item_colspan 3 -Value $SourceData.name
    }          
  }    
}
#EndRegion '.\Public\New-PSUGUIUninstallName.ps1' 18
#Region '.\Public\New-PSUGUIUninstallScript.ps1' 0
function New-PSUGUIUninstallScript{
  [cmdletbinding()]
  param(
    [Parameter()][PSCustomObject]$SourceData
  )
  New-UDElement -Tag "table"  -Attributes @{
    "style"       = @{
      "width" = "100%";

    }
    "cellpadding" = "1"
  } -Content {
    New-UDElement -Tag "tr" -Content {
      New-PSUGUIInputCode -id "Uninstall_Script" -item_colspan 4 -Code $SourceData.Script
    }    
  }  
}
#EndRegion '.\Public\New-PSUGUIUninstallScript.ps1' 18
#Region '.\Public\Publish-AppFactoryAppInstall.ps1' 0
function Publish-AppFactoryAppInstall {
  [CmdletBinding()]
  param(  
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  )
  $installScript = [System.Collections.Generic.List[String]]@()
  $installScript.Add(" ## <Perform Installation tasks here>") | Out-Null
  if ($application.install.Type -eq "None") {
    return
  }
  $script:installerPath = "`$adtSession.dirFiles"
  #region Preinstall
  if ($application.install.conflictingProcessStart) {
    $params = @{
      interactive     = $true
      blockingProcess = $application.install.conflictingProcessStart
      deferCount      = 3
      LogLevel        = $LogLevel
    }
    foreach ($line in $((Add-AppFactoryApplicationBlockingProcess @params -LogLevel $LogLevel).SyncRoot)) {
      $installScript.Add($line)
    }    
  }
  #endregion
  #region WimMount
  if ($application.install.wim) {
    $mountPath = Join-Path -Path "$($ENV:ALLUSERSPROFILE)" -ChildPath "AFS" -AdditionalChildPath $application.Information.AppFolderName
    $script:installerPath = "`$mountPath"
    foreach ($line in $((Add-AppFactoryAppWIM -section "start" -MountPath $mountPath -LogLevel $LogLevel).SyncRoot)) {
      $installScript.Add($line)
    }     
  }
  #endregion
  #region Install Script
  if($application.install.installer -ne "===SETUPFILENAME==="){
    $setup_install = $application.install.installer
  }
  else{
    $setup_install = $application.SourceFiles.AppSetupFileName
  }
  $params = @{
    directory          = $script:installerPath
    AppSetupFileName   = $setup_install
    argumentList       = $application.install.argumentList
    secureArgumentList = $application.install.secureArgumentList
    SuccessExitCodes   = $application.install.SuccessExitCodes
    rebootExitCodes    = $application.install.rebootExitCodes
    ignoreExitCodes    = $application.install.ignoreExitCodes  
    LogLevel           = $LogLevel  
  }
  switch ($application.install.type) {
    "EXE" {
      foreach ($line in $((Add-AppFactoryAppEXE @params).SyncRoot)) {
        $installScript.Add($line)
      }      
    }
    "MSI" {
      $params.add("Transforms", $application.install.Transforms)
      $params.add("Action", "Install")
      $params.add("additionalArgumentList", $application.install.additionalArgumentList)
      $params.add("SkipMSIAlreadyInstalledCheck", $application.Install.SkipMSIAlreadyInstalledCheck)
      foreach ($line in $((Add-AppFactoryAppMSI @params).SyncRoot)) {
        $installScript.Add($line)
      }              
    }
    "Script" {
      foreach($line in $application.Install.script){
        $installScript.Add("`t$($line)") | Out-Null
      }
    }
    "PowerShell" {
      $installScript.Add("`tExecute-Process -Path `"powershell.exe`" -Parameters `"-ExecutionPolicy Bypass -File `"`"`$($($script:installerPath))\$($application.Install.script)`"`"`"")  | Out-Null 
    }
    "ECNO" {
      $installScript.Add("`tPush-Location $($script:installerPath)")  | Out-Null 
      $installScript.Add("`tStart-Process -FilePath powershell.exe -ArgumentList `"-ExecutionPolicy Bypass -File _action.ps1 install`" -NoNewWindow -Wait")  | Out-Null 
      $installScript.Add("`tPop-Location")  | Out-Null 
    }
  }
  #endregion
  #region WIM Dismount
  if ($application.install.wim) {
    foreach ($line in $(Add-AppFactoryAppWIM -section "end" -MountPath $mountPath -LogLevel $LogLevel).SyncRoot) {
      $installScript.Add($line)
    }    
  }
  #endregion
  #region Postinstall
  if ($application.install.conflictingProcessEnd) {
    $params = @{
      interactive     = $false
      blockingProcess = $application.install.conflictingProcessEnd
      LogLevel        = $LogLevel
    }
    foreach ($line in $((Add-AppFactoryApplicationBlockingProcess @params -LogLevel $LogLevel).SyncRoot)) {
      $installScript.Add($line)
    }
  }
  #endregion


  #C:\DevOps\ApplicationFactoryApplications\Workspace\Publish\7-Zip\Invoke-AppDeployToolkit.ps1
  $publishPath = Join-Path -Path $script:AppFactoryWorkspace -ChildPath "Publish" -AdditionalChildPath $application.Information.AppFolderName
  $publishFile = Join-Path -Path $publishPath -ChildPath "Invoke-AppDeployToolkit.ps1"
  $outputFile = Get-Content -Path $publishFile
  $outputFile -replace " ## <Perform Installation tasks here>",$($installScript -join "`r`n") | Set-Content -Path $publishFile
  return
}
#EndRegion '.\Public\Publish-AppFactoryAppInstall.ps1' 110
#Region '.\Public\Publish-AppFactoryAppUninstall.ps1' 0
function Publish-AppFactoryAppUninstall{
  [CmdletBinding()]
  param(  
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  )
  $uninstallScript = [System.Collections.Generic.List[String]]@()
  $uninstallScript.Add(" ## <Perform Uninstallation tasks here>") | Out-Null
  if ($application.Uninstall.Type -eq "None") {
    return
  }  
  if($application.Uninstall.dirFiles -or $application.Uninstall.type -eq "ECNO"){
    $script:uninstallerPath = "`$adtSession.dirFiles"
  }
  else{
    $script:uninstallerPath = ""
  }
  #region Preinstall
  if ($application.Uninstall.conflictingProcessStart) {
    $params = @{
      interactive     = $true
      blockingProcess = $application.Uninstall.conflictingProcessStart
      deferCount      = 3
      LogLevel        = $LogLevel
    }
    foreach ($line in $((Add-AppFactoryApplicationBlockingProcess @params -LogLevel $LogLevel).SyncRoot)) {
      $uninstallScript.Add($line)
    }    
  }
  #endregion
  #region WimMount
  if ($application.Uninstall.wim) {
    $mountPath = Join-Path -Path "$($ENV:ALLUSERSPROFILE)" -ChildPath "AFS" -AdditionalChildPath $application.Information.AppFolderName
    $script:uninstallerPath = "`$mountPath"
    foreach ($line in $((Add-AppFactoryAppWIM -section "start" -MountPath $mountPath -LogLevel $LogLevel).SyncRoot)) {
      $uninstallScript.Add($line)
    }     
  }
  #endregion
  #region Uninstall Script
  if($application.Uninstall.installer -ne "===SETUPFILENAME==="){
    $setup_uninstall = $application.Uninstall.installer
  }
  else{
    $setup_uninstall = $application.SourceFiles.AppSetupFileName
  }
  $params = @{
    directory          = $script:uninstallerPath
    AppSetupFileName   = $setup_uninstall
    argumentList       = $application.Uninstall.argumentList
    secureArgumentList = $application.Uninstall.secureArgumentList
    SuccessExitCodes   = $application.Uninstall.SuccessExitCodes
    rebootExitCodes    = $application.Uninstall.rebootExitCodes
    ignoreExitCodes    = $application.Uninstall.ignoreExitCodes  
    LogLevel           = $LogLevel  
  }  
  switch ($application.Uninstall.type) {
    "MSI" {
      $params.add("Transforms", $application.Uninstall.Transforms)
      $params.add("Action", "Uninstall")
      $params.add("additionalArgumentList", $application.Uninstall.additionalArgumentList)
      foreach ($line in $((Add-AppFactoryAppMSI @params).SyncRoot)) {
        $uninstallScript.Add($line)
      }              
    }
    "EXE" {
      foreach ($line in $((Add-AppFactoryAppEXE @params).SyncRoot)) {
        $uninstallScript.Add($line)
      }      
    }
    "Name" {
      $execute = "Uninstall-ADTApplication -Name `"$($application.Uninstall.name)`""
      if($application.Uninstall.filterScript){$execute = "$($execute) -filterScript `{$($application.Uninstall.filterScript)`}"}
      $uninstallScript.Add("`t$($execute)")  | Out-Null
    }
    "GUID" {
      $uninstallScript.Add("`tUninstall-ADTApplication -ProductCode '$($application.Uninstall.productCode)'")  | Out-Null
    }
    "ECNO" {
      $uninstallScript.Add("`tPush-Location $($script:uninstallerPath)")  | Out-Null 
      $uninstallScript.Add("`tStart-Process -FilePath powershell.exe -ArgumentList `"-ExecutionPolicy Bypass -File _action.ps1 remove`" -NoNewWindow -Wait")  | Out-Null 
      $uninstallScript.Add("`tPop-Location")  | Out-Null 
    }
    "Script" {
      foreach($line in $application.Uninstall.script){
        $uninstallScript.Add("`t$($line)") | Out-Null
      }
    }
  }
  #endregion
  #region WIM Dismount
  if ($application.Uninstall.wim) {
    foreach ($line in $(Add-AppFactoryAppWIM -section "end" -MountPath $mountPath -LogLevel $LogLevel).SyncRoot) {
      $uninstallScript.Add($line)
    }    
  }
  #endregion
  #region Postinstall
  if ($application.Uninstall.conflictingProcessEnd) {
    $params = @{
      interactive     = $false
      blockingProcess = $application.Uninstall.conflictingProcessEnd
      LogLevel        = $LogLevel
    }
    foreach ($line in $((Add-AppFactoryApplicationBlockingProcess @params -LogLevel $LogLevel).SyncRoot)) {
      $uninstallScript.Add($line)
    }
  }
  #endregion
  $publishPath = Join-Path -Path $script:AppFactoryWorkspace -ChildPath "Publish" -AdditionalChildPath $application.Information.AppFolderName
  $publishFile = Join-Path -Path $publishPath -ChildPath "Invoke-AppDeployToolkit.ps1"
  $outputFile = Get-Content -Path $publishFile
  $outputFile -replace " ## <Perform Uninstallation tasks here>",$($uninstallScript -join "`r`n") | Set-Content -Path $publishFile
  return
}
#EndRegion '.\Public\Publish-AppFactoryAppUninstall.ps1' 116
#Region '.\Public\Publish-AppFactoryClientApp.ps1' 0
function Publish-AppFactoryClientApp {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  if ($script:AppFactoryClientLogging) {
    Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Publishing Application" -Level $LogLevel -Tag "Applications", "Intune", "$($application.IntuneAppName)" -Target "Application Factory Client"
  }
  # Get configuration details for application
  $ApplicationFolder = Join-Path -Path $script:AppFactoryClientWorkspace -ChildPath "Downloads" -AdditionalChildPath $application.IntuneAppName
  $ApplicationConfig = Get-Content -Path (Join-Path -Path $ApplicationFolder -ChildPath "App.json") | ConvertFrom-JSON -Depth 5
  # Configure Requirement Rule
  $RequirementRule = New-AppFactoryClientRequirementRule -application $ApplicationConfig -LogLevel $LogLevel
  # Configure Detection Rule
  $DetectionRules = New-AppFactoryClientDetectionRule -application $ApplicationConfig -applicationFolder $ApplicationFolder  -LogLevel $LogLevel
  # Create the base 64 image file
  $Icon = [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes("$(Join-Path -Path $ApplicationFolder -ChildPath $ApplicationConfig.PackageInformation.IconFile)"))
  $IntuneAppPackage = Get-ChildItem "$($ApplicationFolder)\*.intunewin"
  $Win32AppArgs = @{
    "FilePath"          = $IntuneAppPackage.FullName
    "DisplayName"       = "$($script:AppFactoryClientPrefix)$($application.IntuneAppName) $($application.AppVersion)"
    "AppVersion"        = $ApplicationConfig.Information.AppVersion
    "Publisher"         = $ApplicationConfig.Information.Publisher
    "InstallExperience" = $ApplicationConfig.Program.InstallExperience
    "RestartBehavior"   = $ApplicationConfig.Program.DeviceRestartBehavior
    "DetectionRule"     = $DetectionRules
    "RequirementRule"   = $RequirementRule
    "Notes"             = "$($ApplicationConfig.Information.Notes)`n`rSTSID:$($application.GUID)"
  }
  # Dynamically add additional parameters for Win32 app
  if (-not([string]::IsNullOrEmpty($ApplicationConfig.Information.Description))) {
    $Win32AppArgs.Add("Description", $ApplicationConfig.Information.Description)
  }    
  else{
    $Win32AppArgs.Add("Description", $Win32AppArgs.DisplayName)
  }
  if ($null -ne $RequirementRules) {
    $Win32AppArgs.Add("AdditionalRequirementRule", $RequirementRules)
  }
  if (Test-Path -Path (Join-Path -Path $ApplicationFolder -ChildPath $ApplicationConfig.PackageInformation.IconFile)) {
    $Win32AppArgs.Add("Icon", $Icon)
  }
  if (-not([string]::IsNullOrEmpty($ApplicationConfig.Information.InformationURL))) {
    $Win32AppArgs.Add("InformationURL", $ApplicationConfig.Information.InformationURL)
  }  
  if (-not([string]::IsNullOrEmpty($ApplicationConfig.Information.PrivacyURL))) {
    $Win32AppArgs.Add("PrivacyURL", $ApplicationConfig.Information.PrivacyURL)
  }   
  if (-not([string]::IsNullOrEmpty($ApplicationConfig.Information.Owner))) {
    $Win32AppArgs.Add("Owner", $ApplicationConfig.Information.Owner)
  }
  if (-not([string]::IsNullOrEmpty($ApplicationConfig.Program.InstallCommand))) {
    if($application.InteractiveInstall){
      $Win32AppArgs.Add("InstallCommandLine", $ApplicationConfig.Program.InstallCommandInteractive)
    }
    else{
      $Win32AppArgs.Add("InstallCommandLine", $ApplicationConfig.Program.InstallCommand)
    }
  }
  if (-not([string]::IsNullOrEmpty($ApplicationConfig.Program.UninstallCommand))) {
    if($application.InteractiveUninstall){
      $Win32AppArgs.Add("UninstallCommandLine", $ApplicationConfig.Program.UninstallCommandInteractive)
    }
    else{
      $Win32AppArgs.Add("UninstallCommandLine", $ApplicationConfig.Program.UninstallCommand)
    }
  }
  if (-not([string]::IsNullOrEmpty($ApplicationConfig.Program.AllowAvailableUninstall))) {
    if ($ApplicationConfig.Program.AllowAvailableUninstall -eq $true) {
      $Win32AppArgs.Add("AllowAvailableUninstall", $true)
    }
  }  
  try{
    Remove-Variable -Scope Global -Name AccessToken -ErrorAction SilentlyContinue -Force
    Connect-MSIntuneGraph -TenantID $script:AppFactoryClientTenantID -ClientID $script:AppFactoryClientClientID -ClientSecret $script:AppFactoryClientAppRegSecret | Out-Null
    $Application = Add-IntuneWin32App @Win32AppArgs -UseAzCopy -ErrorAction Stop -WarningAction Stop
    return $Application
  }
  catch{
    $intuneApp = Get-GraphIntuneApp -displayName $application.IntuneAppName
    if ($intuneApp.count -gt 1) {
      $intuneApp = $intuneApp | Sort-Object -Property createdDateTime -Descending |  Select-Object -First 1
    }
    Remove-GraphIntuneApp -applicationid $intuneApp.id
    throw $_    
  }
}
#EndRegion '.\Public\Publish-AppFactoryClientApp.ps1' 89
#Region '.\Public\Publish-AppFactoryIntunePackage.ps1' 0
function Publish-AppFactoryIntunePackage {
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][System.Collections.Generic.List[PSCustomObject]]$applicationList,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  )
  # Create a list to store the results of the process
  $applications = [System.Collections.Generic.List[PSCustomObject]]@()
  foreach ($application in $applicationList) {
    try {
      # Check what Azure Storage Containers we should be uploading the files to
      $containerUploads = [System.Collections.Generic.List[PSCustomObject]]@()
      if ((($application.SourceFiles.PublishTo.getType()).BaseType.Name -eq "Array" -and $application.SourceFiles.publishTo.Count -gt 0) -or (($application.SourceFiles.PublishTo.getType()).BaseType.Name -eq "Object" -and $application.SourceFiles.publishTo.length -gt 1)) {
        foreach ($org in $application.SourceFiles.PublishTo) {
          $containerUploads.Add($org) | Out-Null
        }    
      }
      else {
        $containerUploads.Add($script:AppFactoryPublicFolder) | Out-Null
      } 
      # File Upload Details
      $SourceFolder = Join-Path -Path $script:AppFactoryWorkspace -ChildPath "Publish" -AdditionalChildPath $application.Information.AppFolderName
      $applicationJSON = $application.PSObject.Copy()
      $applicationJSON.PSObject.Properties.Remove("GUID")
      $applicationJSON.PSObject.Properties.Remove("SourceFiles")
      $applicationJSON.PSObject.Properties.Remove("Install")
      $applicationJSON.PSObject.Properties.Remove("Uninstall")
      $applicationJSONPath = Join-Path -Path $SourceFolder -ChildPath "App.json"
      if($applicationJSON.DetectionRule.KeyPath -match "###PRODUCTCODE###"){
        $msi = Join-Path -Path $SourceFolder -ChildPath "files" -AdditionalChildPath $application.SourceFiles.AppSetupFileName
        $productCode = get-msiMetaData -path $msi -Property ProductCode
        $applicationJSON.DetectionRule[0].KeyPath = $applicationJSON.DetectionRule.KeyPath -replace "###PRODUCTCODE###",[regex]::Match($productCode,"{.*}").value
      }      
      $applicationJSON | ConvertTo-JSON -Depth 10 | Out-File -Path $applicationJSONPath -Force
      # Application Files to Upload
      $ApplicationPath = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath $application.Information.AppFolderName
      $ScriptDataFile = Join-Path -Path $SourceFolder -ChildPath $application.DetectionRule.ScriptFile
      $AppIconFile = Join-Path -Path $ApplicationPath -ChildPath $application.PackageInformation.IconFile  
      $IntunePackage = Join-Path -Path $SourceFolder -ChildPath $application.SourceFiles.IntunePackage  
      $appUploads = [PSCustomObject]@(
        @{
          "File" = $applicationJSONPath
          "Blob" = "$($application.GUID)/$($application.Information.AppVersion)/App.json"
        },
        @{
          "File" = $IntunePackage
          "Blob" = "$($application.GUID)/$($application.Information.AppVersion)/$($application.SourceFiles.IntunePackage)"
        },
        @{
          "File" = $AppIconFile
          "Blob" = "$($application.GUID)/$($application.Information.AppVersion)/$($application.PackageInformation.IconFile)"
        },
        @{
          "File" = $ScriptDataFile
          "Blob" = "$($application.GUID)/$($application.Information.AppVersion)/detection.ps1"
        }       
      )
      # Process the uploads for each of the files as required to each container that is required
      foreach ($container in $containerUploads) {
        if ($script:AppFactoryLogging) {
          Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Uploading files to storage" -Level $LogLevel -Tag "Application", "$($application.Information.DisplayName)" -Target "Application Factory Service"
        }
        foreach ($upload in $appUploads) {
          if ((Test-Path $upload.File) -and (Get-Item $upload.File).Attributes[0] -ne "Directory") {
            try {
              if ($script:AppFactoryLogging) {
                Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>)] Uploading <c='green'>$($upload.File)</c> to <c='green'>$($container)</c> container" -Level $LogLevel -Tag "Application", "$($application.Information.DisplayName)" -Target "Application Factory Service"
              }
              Set-AzStorageBlobContent @upload -Container $container -Context $script:psadtStorageContext -Force -ErrorAction Stop | Out-Null
            }
            catch {
              throw "[$($application.IntuneAppName)] Unable able to upload file: $($_.Exception.Message)"
            }        
          }
        }             
      }
      $application.SourceFiles.LastUpdate = (Get-Date).ToString("yyyy/MM/dd HH:mm:ss")
      $originalAppInfo = Get-Content -Path "$($ApplicationPath)\ApplicationConfig.json" | ConvertFrom-JSON
      $application.DetectionRule = $originalAppInfo.DetectionRule
      $application | ConvertTo-JSON -Depth 10 | Out-File -FilePath "$($ApplicationPath)\ApplicationConfig.json" -Force
      $applications.Add($application)
    } 
    catch {
      if ($script:AppFactoryLogging) {
        Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Failed to upload intune file" -Level "Error" -Tag "Application", "$($application.Information.DisplayName)" -Target "Application Factory Service"
      }        
      throw $_
    }
  } 
  
  return $applications 
}
#EndRegion '.\Public\Publish-AppFactoryIntunePackage.ps1' 94
#Region '.\Public\Remove-AppFactoryApp.ps1' 0
function Remove-AppFactoryApp{
  [CmdletBinding()]
  param(  
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$appGUID,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  )
  # Get the application Config
  $configfile = Get-AppFactoryApp -appGUID $appGUID -LogLevel $LogLevel
  if (-not ($configfile)) {
    throw "Application with GUID $($appGUID) does not exist."
  }
  # Get the application folder
  $ApplicationFolder = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath $configfile.Information.AppFolderName
  # Remove the folder
  try{
    Remove-Item -Path $ApplicationFolder -Recurse -Force | Out-Null
    Write-PSFMessage -Message "[$($configfile.Information.DisplayName)] Removed Configuration Files at path $($ApplicationFolder)" -Level $LogLevel -Tag "Application","$($appGUID)","$($configfile.Information.DisplayName)"  -Target "Application Factory Service"
  }
  catch{
    Write-PSFMessage -Message "Error Encountered: $($_)" -Level "Error" -Tag "Application","$($appGUID)","$($configfile.Information.DisplayName)" -Target "Application Factory Service"
    throw $_  
  }
}  
#EndRegion '.\Public\Remove-AppFactoryApp.ps1' 24
#Region '.\Public\Remove-AppFactoryClientApp.ps1' 0
function Remove-AppFactoryClientApp {
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(  
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$application,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][System.Collections.Generic.List[PSCustomObject]]$intuneApplications,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  )
  # Loop through the applications and remove ones that are no longer required
  if ($script:AppFactoryClientLogging) {
    Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Removing Previous Applications" -Level $LogLevel -Tag "Applications", "Intune", "$($application.IntuneAppName)" -Target "Application Factory Client"
  }
  $applications = $intuneApplications | Where-Object { $_.Notes -match "STSID:$($application.GUID)" }  | Select-Object id, displayname, @{Label = "Version"; expression = { [version]$_.displayversion } } |  Sort-Object -Property "Version"
  $keepApplications = 0
  if($application.KeepPrevious){
    $keepApplications = $application.KeepPrevious
  }
  if ($applications.count -gt $($keepApplications)) {
    $removalCount = $applications.count - $keepApplications
    if ($script:AppFactoryClientLogging) {
      Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Based on keeping $($keepApplications) previous versions there are $($removalCount) applications to remove." -Level $LogLevel -Tag "Applications", "Intune", "$($application.IntuneAppName)" -Target "Application Factory Client"
    }
    for ($i = 0; $i -lt $removalCount; $i++) {
      Remove-GraphIntuneApp -applicationid $applications[$i].id
    }
  }
  else {
    if ($script:AppFactoryClientLogging) {
      Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Based on keeping $($keepApplications) previous versions there are no applications to remove." -Level $LogLevel -Tag "Applications", "Intune", "$($application.IntuneAppName)" -Target "Application Factory Client"
    }
  }
}
#EndRegion '.\Public\Remove-AppFactoryClientApp.ps1' 33
#Region '.\Public\Remove-AppFactoryClientAppFiles.ps1' 0
function Remove-AppFactoryClientAppFiles{
  [CmdletBinding()]
  param(  
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$applications,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  ) 
  $downloadFolder = Join-Path -Path $script:AppFactoryClientWorkspace -ChildPath "Downloads" -AdditionalChildPath $application.IntuneAppName
  try{
    Remove-Item -Path $downloadFolder -Force -Recurse -Confirm:$false
    if($script:AppFactoryClientLogging){
      Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Removed files." -Level $LogLevel -Tag "Applications","$($application.IntuneAppName)" -Target "Application Factory Client"
    }
  } 
  catch{
    if($script:AppFactoryClientLogging){
      Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Unable to delete files. Error: $($_)" -Level Error -Tag "Applications","$($application.IntuneAppName)" -Target "Application Factory Client"
    }
    throw $_
  }
}
#EndRegion '.\Public\Remove-AppFactoryClientAppFiles.ps1' 21
#Region '.\Public\Remove-AppFactoryProcessFiles.ps1' 0
function Remove-AppFactoryProcessFiles{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$applicationList,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  foreach($application in $applicationList){
    $installersPath = Join-Path -Path $script:AppFactoryWorkspace -ChildPath "Installers" -AdditionalChildPath $application.Information.AppFolderName
    $publishPath = Join-Path -Path $script:AppFactoryWorkspace -ChildPath "Publish" -AdditionalChildPath $application.Information.AppFolderName
    Remove-Item -path $installersPath -Recurse -Force -Confirm:$false
    if ($script:AppFactoryLogging) {
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Application Installer Folder Removed [<c='green'>$($installersPath)</c>]." -Level  "Output" -Tag "Process",$application.Information.DisplayName -Target "Application Factory Service"
    }
    Remove-Item -path $publishPath -Recurse -Force -Confirm:$false
    if ($script:AppFactoryLogging) {
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Application Publish Folder Removed [<c='green'>$($publishPath )</c>]." -Level  "Output" -Tag "Process",$application.Information.DisplayName -Target "Application Factory Service"
    }    
  }
}
#EndRegion '.\Public\Remove-AppFactoryProcessFiles.ps1' 20
#Region '.\Public\Remove-AppFactoryServiceAppVersions.ps1' 0
function Remove-AppFactoryServiceAppVersions{
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[Version]])]
  param(  
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$appGUID,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$version,
    [Parameter()][ValidateNotNullOrEmpty()][PSCustomObject[]]$AllAppList,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  ) 
  if (-not $PSBoundParameters.ContainsKey("AllAppList")){
    $AllAppList = $script:PublishedAppList
  }
  foreach($key in $AllAppList.GetEnumerator().keys){
    $container = $AllAppList.$($key)
    $blobs = $container | Where-Object { $_.Name -like "$($appGUID)/$($version)/*" }
    foreach($blob in $blobs){
      Remove-AzStorageBlob -Blob $blob.name -Container $key -Context $script:psadtStorageContext -Force
    }
  }
}
#EndRegion '.\Public\Remove-AppFactoryServiceAppVersions.ps1' 21
#Region '.\Public\Remove-AppFactoryServiceClient.ps1' 0
function Remove-AppFactoryServiceClient{
  [CmdletBinding()]
  param(  
    [Alias("GUID")][Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$clientGUID,
    [Parameter()][ValidateSet("Output","Verbose")][string]$LogLevel = "Verbose"  
  )
  # Get Client Configuration Files
  $client = Get-AppFactoryServiceClient -GUID $clientGUID -LogLevel $LogLevel
  if ($null -eq $client) {
    Write-PSFMessage -Message "Error Encountered: Client not found." -Level "Error" -Tag "Client","$($clientGUID)" -Target "Application Factory Service"
    throw "CLient not found." 
  }
  try{
    $clientStorageContainerContext = Connect-AppFactoryAzureStorage -storageContainer $script:AppFactoryDeploymentsContainer -storageSecret $script:AppFactoryDeploymentsSecret
    # Remove Azure Storage Container
    Remove-AzStorageContainer -Name $clientGUID -Context $clientStorageContainerContext -Force -ErrorAction Stop 
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "[$($client.Name)] Removed Azure Container" -Level $LogLevel -Tag "Clients","ContactList","$($client.Name)","$($clientGUID)" -Target "Application Factory Service"
    }    
    # Remove Configuration File.
    $fileName = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Clients" -AdditionalChildPath $client.fileName
    Remove-Item -Path $fileName -Force
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "[$($client.Name)] Removed configuration file" -Level $LogLevel -Tag "Clients","ContactList","$($client.Name)","$($clientGUID)" -Target "Application Factory Service"
    }    
  }
  catch{
    Write-PSFMessage -Message "Error Encountered: $($_)" -Level "Error" -Tag "Clients","ContactList","$($client.Name)","$($clientGUID)" -Target "Application Factory Service"
    throw $_
  }  
}
#EndRegion '.\Public\Remove-AppFactoryServiceClient.ps1' 32
#Region '.\Public\Set-AppFactoryApp.ps1' 0
function Set-AppFactoryApp {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]$appGUID,
    [Parameter()][ValidateNotNullOrEmpty()][String]$displayName,
    [Parameter()][string]$AppVersion,
    [Parameter()][ValidateNotNullOrEmpty()][String]$AppFolderName,
    [Parameter()][ValidateNotNullOrEmpty()][String]$description,
    [Parameter()][ValidateNotNullOrEmpty()][String]$publisher,
    [Parameter()][String]$notes = "",
    [Parameter()][String]$owner = "",
    [Parameter()][String]$informationURL = "",
    [Parameter()][String]$privacyURL = "",
    [Parameter()][ValidateSet("StorageAccount", "Sharepoint", "Winget", "Evergreen", "PSADT", "ECNO", "LocalStorage")][String]$AppSource,
    [Parameter()][ValidateNotNullOrEmpty()][string]$appID,
    [Alias("appSetupName")][Parameter()][ValidateNotNullOrEmpty()][string]$AppSetupFileName = "",
    [Alias("storageContainerName")][Parameter()][ValidateNotNullOrEmpty()][string]$StorageAccountContainerName = "",
    [Parameter()][String[]]$ExtraFiles = @(),
    [Parameter()][PSCustomObject]$filterOptions = @{},
    [Parameter()][String[]]$publishTo = @(),
    [Parameter()][String[]]$AvailableVersions = @(),
    [Parameter()][ValidateSet("system", "user")][string]$InstallExperience = "system",
    [Parameter()][ValidateSet("suppress", "force", "basedOnReturnCode", "allow")][string]$DeviceRestartBehavior = "suppress",
    [Parameter()][ValidateSet("true", "false")][string]$AllowAvailableUninstall = $true,
    [Parameter()][ValidateSet("W10_1607", "W10_1703", "W10_1709", "W10_1809", "W10_1909", "W10_2004", "W10_20H2", "W10_21H1", "W10_21H2", "W10_22H2", "W11_21H2", "W11_22H2")][string]$MinimumSupportedWindowsRelease = "W10_1607",
    [Parameter()][ValidateSet("All", "x64", "x86")][string]$Architecture = "All",
    [Parameter()][String[]]$DependsOn = @(),  
    [Parameter()][bool]$active,
    [Parameter()][bool]$pauseUpdate,
    [Parameter()][switch]$force,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  # Get the application Config
  $configfile = Get-AppFactoryApp -appGUID $appGUID -LogLevel $LogLevel
  if (-not ($configfile)) {
    throw "Application with GUID $($GUID) does not exist."
  }  
  # Take note of the original name in case we are changing it
  $originalFolderName = $configfile.Information.AppFolderName
  # Update the config file with the new values
  
  if ($PSBoundParameters.ContainsKey("AppFolderName")) {
    $configfile.Information.AppFolderName = $AppFolderName
    $originalPath = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath $originalFolderName
    Rename-Item -Path $originalPath -NewName $AppFolderName
  }
  if ($PSBoundParameters.ContainsKey("displayName")) { $configfile.Information.DisplayName = $displayName }
  if ($PSBoundParameters.ContainsKey("AppVersion")) { $configfile.Information.AppVersion = $AppVersion }
  if ($PSBoundParameters.ContainsKey("description")) { $configfile.Information.Description = $Description }  
  if ($PSBoundParameters.ContainsKey("publisher")) { $configfile.Information.Publisher = $Publisher }  
  if ($PSBoundParameters.ContainsKey("notes")) { $configfile.Information.Notes = $Notes }  
  if ($PSBoundParameters.ContainsKey("owner")) { $configfile.Information.owner = $owner } 
  if ($PSBoundParameters.ContainsKey("informationURL")) { $configfile.Information.informationURL = $informationURL }  
  if ($PSBoundParameters.ContainsKey("privacyURL")) { $configfile.Information.PrivacyURL = $PrivacyURL } 
  if ($PSBoundParameters.ContainsKey("AppSource")) { $configfile.SourceFiles.AppSource = $AppSource }  
  if ($PSBoundParameters.ContainsKey("appID")) { $configfile.SourceFiles.AppID = $AppID }
  if ($PSBoundParameters.ContainsKey("AppSetupFileName")) { $configfile.SourceFiles.AppSetupFileName = $AppSetupFileName }    
  if ($PSBoundParameters.ContainsKey("StorageAccountContainerName")) { $configfile.SourceFiles.StorageAccountContainerName = $StorageAccountContainerName }
  if ($PSBoundParameters.ContainsKey("ExtraFiles")) { $configfile.SourceFiles.ExtraFiles = $ExtraFiles }
  if ($PSBoundParameters.ContainsKey("filterOptions")) { $configfile.SourceFiles.FilterOptions = $FilterOptions }  
  if ($PSBoundParameters.ContainsKey("publishTo")) { $configfile.SourceFiles.publishTo = $publishTo }  
  if ($PSBoundParameters.ContainsKey("AvailableVersions")) {$configfile.SourceFiles.AvailableVersions = $AvailableVersions }
  if ($PSBoundParameters.ContainsKey("InstallExperience")) { $configfile.Program.InstallExperience = $InstallExperience }   
  if ($PSBoundParameters.ContainsKey("DeviceRestartBehavior")) { $configfile.Program.DeviceRestartBehavior = $DeviceRestartBehavior }
  if ($PSBoundParameters.ContainsKey("AllowAvailableUninstall")) { $configfile.Program.AllowAvailableUninstall = $AllowAvailableUninstall } 
  if ($PSBoundParameters.ContainsKey("MinimumSupportedWindowsRelease")) { $configfile.RequirementRule.MinimumSupportedWindowsRelease = $MinimumSupportedWindowsRelease }    
  if ($PSBoundParameters.ContainsKey("Architecture")) { $configfile.RequirementRule.Architecture = $Architecture } 
  if ($PSBoundParameters.ContainsKey("active")) { $configfile.SourceFiles.Active = $Active } 
  if ($PSBoundParameters.ContainsKey("pauseUpdate")) { $configfile.SourceFiles.pauseUpdate = $pauseUpdate } 
  if ($PSBoundParameters.ContainsKey("DependsOn")) {
    if ($null -ne $DepandsOn -and $DepandsOn -ne "") {
      foreach ($app in $DependsOn) {
        $exists = Get-AppFactoryApp -appGUID $app
        if ($null -eq $exists) {
          Write-PSFMessage -Message "Error Encountered: Dependent Application with GUID $($app) does not exist" -Level "Error" -Tag "Application", "$($originalDisplayName)", "$($appGUID)" -Target "Application Factory Service"
          throw "Dependent Application with GUID $($app) does not exist"
        }
      }
    }
    else {
      $configfile.SourceFiles.DependsOn = @()
    }
  }
  # Create the configuration for the application
  try{
    Write-AppConfiguration -configfile $configfile -LogLevel $LogLevel
  }
  catch{
    Write-PSFMessage -Message "Error Encountered: $($_)" -Level "Error" -Tag "Application", "$($displayName)", "$($configFIle.GUID)" -Target "Application Factory Service"
    throw $_     
  }  
  return $configfile       
}
#EndRegion '.\Public\Set-AppFactoryApp.ps1' 94
#Region '.\Public\Set-AppFactoryAppDetectionRule.ps1' 0
function Set-AppFactoryAppDetectionRule{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$appGUID,
    [Parameter(Mandatory = $true)][ValidateSet("MSI","Registry","Script")][string]$Type,
    [Parameter()][ValidateSet("notConfigured","equal","notEqual","greaterThanOrEqual","greaterThan","lessThanOrEqual","lessThan")][string]$ProductVersionOperator = "notConfigured",
    [Parameter()][ValidateSet("Existence","VersionComparison")][string]$DetectionMethod,
    [Parameter()][ValidateNotNullOrEmpty()][string]$KeyPath = "###PRODUCTCODE###",
    [Parameter()][ValidateNotNullOrEmpty()][string]$ValueName = "DisplayVersion",
    [Parameter()][ValidateSet("exists","notExists")][string]$DetectionType,
    [Parameter()][switch]$Check32BitOn64System = $false,
    [Parameter()][Switch]$EnforceSignatureCheck = $false,
    [Parameter()][Switch]$RunAs32Bit = $false,
    [Parameter()][ValidateSet("notConfigured","equal","notEqual","greaterThanOrEqual","greaterThan","lessThanOrEqual","lessThan")][string]$Operator = "greaterThanOrEqual",
    [Parameter()][ValidateNotNullOrEmpty()][string]$ScriptFile = "detection.ps1",
    [Parameter()][ValidateSet("Output","Verbose")][string]$LogLevel = "Verbose"
  )
  # Get the application Config
  $configfile = Get-AppFactoryApp -appGUID $appGUID -LogLevel $LogLevel
  if (-not ($configfile)) {
    throw "Application with GUID $($GUID) does not exist."
  }  
  # Create a object with the details for the detection type
  $detectionRule = [PSCustomObject]@{
    "Type"                    = $Type
  }
  # Depending on the type, set the appropriate details
  switch($Type){
    "MSI"{
      $detectionRule | Add-Member -MemberType "NoteProperty" -Name "ProductCode" -Value "<replaced_by_pipeline>"
      $detectionRule | Add-Member -MemberType "NoteProperty" -Name "ProductVersionOperator" -Value $ProductVersionOperator
      if($ProductVersionOperator -ne "notConfigured"){
        $detectionRule | Add-Member -MemberType "NoteProperty" -Name "ProductVersion" -Value "<replaced_by_pipeline>"
      }
    }
    "Registry"{
      $detectionRule | Add-Member -MemberType "NoteProperty" -Name "DetectionMethod" -Value $DetectionMethod
      $detectionRule | Add-Member -MemberType "NoteProperty" -Name "KeyPath" -Value "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$($KeyPath)"
      $detectionRule | Add-Member -MemberType "NoteProperty" -Name "ValueName" -Value $ValueName
      if($DetectionType){
        $detectionRule | Add-Member -MemberType "NoteProperty" -Name "DetectionType" -Value $DetectionType
      }
      if($Operator){
        $detectionRule | Add-Member -MemberType "NoteProperty" -Name "Operator" -Value $Operator
        $detectionRule | Add-Member -MemberType "NoteProperty" -Name "Value" -Value "<replaced_by_pipeline>"
      }
      $detectionRule | Add-Member -MemberType "NoteProperty" -Name "Check32BitOn64System" -Value "$($Check32BitOn64System.IsPresent)"
    }
    "Script"{
      $detectionRule | Add-Member -MemberType "NoteProperty" -Name "ScriptFile" -Value $ScriptFile
      $detectionRule | Add-Member -MemberType "NoteProperty" -Name "EnforceSignatureCheck" -Value "$($EnforceSignatureCheck.IsPresent)"
      $detectionRule | Add-Member -MemberType "NoteProperty" -Name "RunAs32Bit" -Value "$($RunAs32Bit.IsPresent)"
      $detectionScriptPath = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath $configfile.Information.AppFolderName,"detection.ps1"
      if(-not (Test-Path $detectionScriptPath)){
        $detectiontemplate = Join-Path -Path $script:AppFactorySupportTemplateFolder -ChildPath "Application" -AdditionalChildPath "detection.ps1"
        Copy-Item -Path $detectiontemplate -Destination $detectionScriptPath
      }
    }
  }  
  $configfile.DetectionRule = ,$detectionRule
  try{
    Write-AppConfiguration -configfile $configfile -LogLevel $LogLevel
  }
  catch{
    Write-PSFMessage -Message "Error Encountered: $($_)" -Level "Error" -Tag "Application", "$($configfile.Information.displayName)", "$($configFIle.GUID)" -Target "Application Factory Service"
    throw $_
  }
}
#EndRegion '.\Public\Set-AppFactoryAppDetectionRule.ps1' 69
#Region '.\Public\Set-AppFactoryAppInstall.ps1' 0
function Set-AppFactoryAppInstall {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$appGUID,
    [Parameter(Mandatory = $true)][ValidateSet("None","Script", "PowerShell", "ECNO", "EXE", "MSI")][string]$Type,
    [Parameter()][string]$argumentList, 
    [Parameter()][string]$additionalArgumentList, 
    [Parameter()][bool]$secureArgumentList, 
    [Parameter()][int[]]$successExitCodes,
    [Parameter()][int[]]$rebootExitCodes,
    [Parameter()][int[]]$ignoreExitCodes,
    [Parameter()][String[]]$conflictingProcessStart,
    [Parameter()][String[]]$conflictingProcessEnd,
    [Parameter()][ValidateNotNullOrEmpty()][string]$installer,
    [Parameter()][ValidateNotNullOrEmpty()][string]$transforms,  
    [Parameter()][bool]$SkipMSIAlreadyInstalledCheck,
    [Parameter()][string[]]$script,
    [Parameter()][bool]$wim,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )  
  # Get the application Config
  $configfile = Get-AppFactoryApp -appGUID $appGUID -LogLevel $LogLevel
  if (-not ($configfile)) {
    throw "Application with GUID $($GUID) does not exist."
  } 
  if ($PSBoundParameters.ContainsKey("type")) { $configfile.install.type = $type } 
  if ($PSBoundParameters.ContainsKey("argumentList")) { $configfile.install.argumentList = $argumentList } 
  if ($PSBoundParameters.ContainsKey("additionalArgumentList")) { $configfile.install.additionalArgumentList = $additionalArgumentList } 
  if ($PSBoundParameters.ContainsKey("secureArgumentList")) { $configfile.install.secureArgumentList = $secureArgumentList } 
  if ($PSBoundParameters.ContainsKey("successExitCodes")) { $configfile.install.successExitCodes = $successExitCodes } 
  if ($PSBoundParameters.ContainsKey("rebootExitCodes")) { $configfile.install.rebootExitCodes = $rebootExitCodes } 
  if ($PSBoundParameters.ContainsKey("ignoreExitCodes")) { $configfile.install.ignoreExitCodes = $ignoreExitCodes } 
  if ($PSBoundParameters.ContainsKey("conflictingProcessStart")) { $configfile.install.conflictingProcessStart = $conflictingProcessStart } 
  if ($PSBoundParameters.ContainsKey("conflictingProcessEnd")) { $configfile.install.conflictingProcessEnd = $conflictingProcessEnd } 
  if ($PSBoundParameters.ContainsKey("installer")) { $configfile.install.installer = $installer } 
  if ($PSBoundParameters.ContainsKey("transforms")) { $configfile.install.transforms = $transforms } 
  if ($PSBoundParameters.ContainsKey("SkipMSIAlreadyInstalledCheck")) { $configfile.install.SkipMSIAlreadyInstalledCheck = $SkipMSIAlreadyInstalledCheck } 
  if ($PSBoundParameters.ContainsKey("script")) { $configfile.install.script = $script -split "`n" } 
  if ($PSBoundParameters.ContainsKey("wim")) { $configfile.install.wim = $wim } 
  # Create the configuration for the application
  try{
    Write-AppConfiguration -configfile $configfile -LogLevel $LogLevel
  }
  catch{
    Write-PSFMessage -Message "Error Encountered: $($_)" -Level "Error" -Tag "Application", "$($configfile.Information.displayName)", "$($configFIle.GUID)" -Target "Application Factory Service"
    throw $_     
  }    
}
#EndRegion '.\Public\Set-AppFactoryAppInstall.ps1' 49
#Region '.\Public\Set-AppFactoryAppUninstall.ps1' 0
function Set-AppFactoryAppUninstall {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$appGUID,
    [Parameter(Mandatory = $true)][ValidateSet("None", "MSI", "EXE", "Name", "GUID", "ECNO", "Script", "Powershell")][string]$type,
    [Parameter()][string]$name,
    [Parameter()][ValidateSet('Contains','Exact','Wildcard','Regex')][string]$nameMatch,
    [Parameter()][string]$productCode,
    [Parameter()][string]$filterScript,
    [Parameter()][string]$argumentList, 
    [Parameter()][string]$additionalArgumentList, 
    [Parameter()][bool]$secureArgumentList, 
    [Parameter()][string[]]$script,
    [Parameter()][string]$installer,
    [Parameter()][bool]$wim,
    [Parameter()][bool]$dirFiles,
    [Parameter()][int[]]$ignoreExitCodes,
    [Parameter()][String[]]$conflictingProcessStart,
    [Parameter()][String[]]$conflictingProcessEnd,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose" 
  )
  # Get the application Config
  $configfile = Get-AppFactoryApp -appGUID $appGUID -LogLevel $LogLevel
  if (-not ($configfile)) {
    throw "Application with GUID $($GUID) does not exist."
  } 
  if ($PSBoundParameters.ContainsKey("type")) { $configfile.Uninstall.type = $type } 
  if ($PSBoundParameters.ContainsKey("name")) { $configfile.Uninstall.name = $name } 
  if ($PSBoundParameters.ContainsKey("nameMatch")) { $configfile.Uninstall.nameMatch = $nameMatch } 
  if ($PSBoundParameters.ContainsKey("productCode")) { $configfile.Uninstall.productCode = $productCode } 
  if ($PSBoundParameters.ContainsKey("filterScript")) { $configfile.Uninstall.filterScript = $filterScript } 
  if ($PSBoundParameters.ContainsKey("argumentList")) { $configfile.Uninstall.argumentList = $argumentList } 
  if ($PSBoundParameters.ContainsKey("additionalArgumentList")) { $configfile.Uninstall.additionalArgumentList = $additionalArgumentList } 
  if ($PSBoundParameters.ContainsKey("secureArgumentList")) { $configfile.Uninstall.secureArgumentList = $secureArgumentList } 
  if ($PSBoundParameters.ContainsKey("script")) { $configfile.Uninstall.script = $script -split "`n" } 
  if ($PSBoundParameters.ContainsKey("installer")) { $configfile.Uninstall.installer = $installer } 
  if ($PSBoundParameters.ContainsKey("wim")) { $configfile.Uninstall.wim = $wim } 
  if ($PSBoundParameters.ContainsKey("dirFiles")) { $configfile.Uninstall.dirFiles = $dirFiles } 
  if ($PSBoundParameters.ContainsKey("ignoreExitCodes")) { $configfile.Uninstall.ignoreExitCodes = $ignoreExitCodes } 
  if ($PSBoundParameters.ContainsKey("conflictingProcessStart")) { $configfile.Uninstall.conflictingProcessStart = $conflictingProcessStart } 
  if ($PSBoundParameters.ContainsKey("conflictingProcessEnd")) { $configfile.Uninstall.conflictingProcessEnd = $conflictingProcessEnd } 
  # Create the configuration for the application
  try{
    Write-AppConfiguration -configfile $configfile -LogLevel $LogLevel
  }
  catch{
    Write-PSFMessage -Message "Error Encountered: $($_)" -Level "Error" -Tag "Application", "$($configfile.Information.displayName)", "$($configFIle.GUID)" -Target "Application Factory Service"
    throw $_     
  }  
}
#EndRegion '.\Public\Set-AppFactoryAppUninstall.ps1' 51
#Region '.\Public\Set-AppFactoryServiceClient.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to update the details of an client
  .PARAMETER GUID
  The unique identifier for the client that we want to work with
    .PARAMETER name
  The name of the client
  .PARAMETER contactList
  The list of contacts for the client
  .PARAMETER LogLevel
  If logging is enabled, what level of logging do we want, default is verbose.
 
  .EXAMPLE
 
  Update an client name
    Set-AppFactoryClient -clientGUID "### GUID ###" -clientName "### Client Name ###"
 
  Update an client cotacts
    Set-AppFactoryClient -clientGUID "### GUID ###" -clientContact "### Contact 1 ###","### Contact 2 ###"
#>

function Set-AppFactoryServiceClient{
  [CmdletBinding()]
  [Alias("Set-AppFactoryOrganization")]
  param(  
    [Alias("GUID")][Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$clientGUID,
    [Alias("Name")][Parameter()][ValidateNotNullOrEmpty()][string]$clientName,
    [Alias("contactList")][Parameter()][string[]]$clientContact,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"  
  )  
  # Get Client Configuration Files
  $client = Get-AppFactoryServiceClient -GUID $clientGUID -LogLevel $LogLevel
  if ($null -eq $client) {
    Write-PSFMessage -Message "Error Encountered: Client not found." -Level "Error" -Tag "Client","$($clientGUID)" -Target "Application Factory Service"
    throw "CLient not found." 
  }
  # Flag to know if we actually updated anything
  $changed = $false
  # If Contact List is Set to Be Updated
  if($null-ne $clientContact -and $clientContact -ne "" -and $PSBoundParameters.ContainsKey("clientContact")){
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "[$($client.Name)] Updating Contact List - <c='green'>$($clientContact -join ",")</c>" -Level $LogLevel -Tag "Clients","ContactList","$($client.Name)","$($clientGUID)" -Target "Application Factory Service"
    }
    $client.Contacts = $clientContact
    $changed = $true
  }
  # If the contact list is meant to be cleaned
  elseif($PSBoundParameters.ContainsKey("clientContact")){
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "[$($client.Name)] Clearning contact list." -Level $LogLevel -Tag "Clients","ContactList","$($client.Name)","$($clientGUID)" -Target "Application Factory Service"
    }
    $client.Contacts = @()
    $changed = $true
  }
  if($PSBoundParameters.ContainsKey("clientName")){
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "[$($client.Name)] Updating Name - <c='green'>$($clientName)</c>" -Level $LogLevel -Tag "Clients","clientName","$($client.Name)","$($clientGUID)" -Target "Application Factory Service"
    }
    $client.Name = $clientName
    $changed = $true    
  }
  # If something has been changed, lets do some updates
  if($changed){
    # Get Current Path of client File
    $fileName = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Clients" -AdditionalChildPath $client.FileName
    # Remove the filename property since we will be writing out the configuration
    $client.PsObject.properties.Remove("fileName")
    try{
      $client | ConvertTo-Json -Depth 3 | Out-File $fileName
      if($script:AppFactoryLogging){
        Write-PSFMessage -Message "[$($client.Name)] Saved configuration file" -Level $LogLevel -Tag "Clients","$($client.Name)","$($clientGUID)" -Target "Application Factory Service"
      }      
      # If the client name is to be changed. update the filename to match.
      if($PSBoundParameters.ContainsKey("clientName")){
        Rename-Item -Path $fileName -NewName "$($clientName).json" -Force
        if($script:AppFactoryLogging){
          Write-PSFMessage -Message "[$($client.Name)] Renamed configuration file" -Level $LogLevel -Tag "Clients","$($client.Name)","$($clientGUID)" -Target "Application Factory Service"
        }        
      }
    }
    catch{
      Write-PSFMessage -Message "Error Encountered: $($_)" -Level "Error" -Tag "Clients","$($client.Name)","$($clientGUID)" -Target "Application Factory Service"
      throw $_   
    }    
  }  
  return $client
}
#EndRegion '.\Public\Set-AppFactoryServiceClient.ps1' 87
#Region '.\Public\Set-AppFactoryServiceClientAppConfig.ps1' 0
function Set-AppFactoryServiceClientAppConfig{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$orgGUID,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$appGUID,
    [Parameter()][bool]$AddToIntune,
    [Parameter()][string[]]$AvailableAssignments,
    [Parameter()][string[]]$AvailableExceptions,
    [Parameter()][string[]]$RequiredAssignments,
    [Parameter()][string[]]$RequiredExceptions,
    [Parameter()][string[]]$UninstallAssignments,
    [Parameter()][string[]]$UninstallExceptions,
    [Parameter()][bool]$UnassignPrevious,
    [Parameter()][bool]$CopyPrevious,
    [Parameter()][int]$KeepPrevious,
    [Parameter()][bool]$foreground,
    [Parameter()][PSCustomObject]$filters,
    [Parameter()][string[]]$espprofiles,
    [Parameter()][bool]$InteractiveInstall,
    [Parameter()][bool]$InteractiveUninstall,
    [Parameter()][String]$AppVersion,
    [Parameter()][ValidateSet("Output","Verbose")][string]$LogLevel = "Verbose"
  )
  $currentConfig = Get-AppFactoryServiceClientAppConfig -orgGUID $orgGUID -appGUID $appGUID -LogLevel $LogLevel
  if(-not $currentConfig){$currentConfig = [PSCustomObject]@{}}
  foreach($item in $PSBoundParameters.GetEnumerator()){
    if($item.key -in ("orgGUID","appGUID","LogLevel")){continue}
    #$itemType = $item.Value.GetType().Name
    $action = $null
    if($item.key -in $currentConfig.psobject.properties.Name){
      $Action = "Update"
      switch -regex ($item.Value.GetType().Name){
        "Boolean" {
          if(-not  [System.Convert]::ToBoolean($item.Value)){
            $action = "Remove"
          }
        }
        "String|String\[\]|PSCustomObject" {
          if([String]::IsNullOrWhiteSpace($item.value) -or $item.value -eq "0.0"){
            $Action = "Remove"
          }
        }
        "Int32" {
          if($item.value -eq 0){
            $Action = "Remove"
          }
        }
        default {
          $Action = "Unknown"
        }
      }
    }
    else{
      switch -regex ($item.Value.GetType().Name){
        "Boolean" {
          if([System.Convert]::ToBoolean($item.Value)){
            $action = "Add"
          }
        }
        "String|String\[\]|PSCustomObject" {
          if(-not ([String]::IsNullOrWhiteSpace($item.value)) -and $item.value -ne "0.0"){
            $Action = "Add"
          }
        }
        "Int32" {
          if($item.value -ne 0){
            $Action = "Add"
          }
        }
      }
    }
    switch($Action){
      "Add"{ 
        $currentConfig | Add-Member -MemberType NoteProperty -Name $item.Key -Value $item.Value -Force
      }
      "Remove"{ 
        $currentConfig.PSObject.Properties.Remove($item.Key)
      }
      "Update"{ 
        $currentConfig.$($item.Key) = $item.Value
      }

    }
  }
  $ClientConfigFolderPath = Join-Path -Path $script:AppFactoryClientConfigDir -ChildPath "$($orgGUID)"
  $ClientAppConfig = Join-Path -Path $ClientConfigFolderPath -ChildPath "$($appGUID).json"
  if(-not (Test-Path $ClientConfigFolderPath)){
    New-Item -Path $ClientConfigFolderPath -ItemType Directory -Force | Out-Null
  }
  $currentConfig | ConvertTo-JSON | Out-File -Path $ClientAppConfig -Force  
}
#EndRegion '.\Public\Set-AppFactoryServiceClientAppConfig.ps1' 92
#Region '.\Public\Start-AppFactoryClient.ps1' 0
function Start-AppFactoryClient {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$ClientServicePath,
    [Parameter()][ValidateNotNullOrEmpty()][string]$configuration = "Configuration.json",
    [Parameter()][string]$LocalModule = $null,
    [Parameter()][switch]$EnableLogging,
    [Parameter()][int]$retries = 5,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  # Start the Application Factory Process
  $init = @{
    ClientServicePath = $ClientServicePath
    EnableLogging = $EnableLogging.IsPresent
    configuration = $configuration
    retries = $retries
    LocalModule = $LocalModule
  }
  Initialize-AppFactoryClientProcess @init
  Write-PSFMessage -Message "Getting list of applications configurations from service." -Level  $LogLevel -Tag "Process" -Target "Application Factory Client"
  # Clean Up Old Configuration Files that may remain
  Get-ChildItem -Path (Join-Path -Path $script:AppFactoryClientSourceDir -ChildPath "Apps") -filter "*.json" | Remove-Item -Force
  Get-AppFactoryClientAppList -LogLevel $LogLevel
  # Clean old credential that is set to global by third party module
  $PublishedApplications = [System.Collections.Generic.List[PSCustomObject]]@()
  for($x = 0; $x -lt $retries; $x++){
    try{
      # Keep track of packaged applications
      $applicationList = Get-AppFactoryClientApp -AddToIntune $true -LogLevel $LogLevel
      Write-PSFMessage -Message "There are <c='green'>$($applicationList.count)</c> application set to be configured in intune" -Level  $LogLevel -Tag "Process" -Target "Application Factory Client"
      if($applicationList.count -eq 0){
        Write-PSFMessage -Message "No applications found to process" -Level  $LogLevel -Tag "Process" -Target "Application Factory Client"
        return $PublishedApplications
      }
      $intuneApplications = Get-AppFactoryClientGraphApp -LogLevel $LogLevel
      $publish = Compare-AppFactoryClientAppVersions -applicationList $applicationList -intuneapplications $intuneApplications -LogLevel $LogLevel
      Write-PSFMessage -Message "There are <c='green'>$($publish.count)</c> application set to be configured in intune" -Level  $LogLevel -Tag "Process" -Target "Application Factory Client" 
      if($publish.count -eq 0){
        return $PublishedApplications
      }
      # Make a note of how many applications we are expecting to update
      $originalCount = $publish.count
      # Get application SAS tokens
      Get-AppFactoryClientSAS  
      foreach($application in $publish){
        try{
          Get-AppFactoryClientAppFiles -applications $application -LogLevel $LogLevel
          Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Downloaded files." -Level $LogLevel -Tag "Applications","$($application.IntuneAppName)" -Target "Application Factory Client"
          $PublishedApp = Publish-AppFactoryClientApp -application $application -LogLevel $LogLevel
          Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Pausing to ensure replication." -Level $LogLevel -Tag "Applications","$($application.IntuneAppName)" -Target "Application Factory Client"
          Start-Sleep -Seconds 30
          if($PublishedApp.UploadState -eq 0){
            throw "Failed to upload files."
          }
          Add-AppFactoryClientAppAssignments -application $application -intuneid $PublishedApp.id -LogLevel $LogLevel
          Copy-AppFactoryClientAppAssignments -application $application -intuneid $PublishedApp.id -intuneApplications $intuneApplications -LogLevel $LogLevel
          Remove-AppFactoryClientApp -application $application -intuneApplications $intuneApplications -LogLevel $LogLevel
          Add-AppFactoryClientESPAssignment -application $application -intuneid $PublishedApp.id -LogLevel $LogLevel
          Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Completed." -Level $LogLevel -Tag "Applications","$($application.IntuneAppName)" -Target "Application Factory Client"
          $PublishedApplications.Add($application) | Out-Null          
        }
        catch{
          if($PublishedApp){
            Remove-GraphIntuneApp -applicationid $PublishedApp.id
          }
          Remove-AppFactoryClientAppFiles -applications $application -LogLevel $LogLevel
          Write-PSFMessage -Message "[<c='green'>$($application.IntuneAppName)</c>] Unable to complete process for application, please review the logs to what has failed. Error: $($_)" -Level Error -Tag "Applications","$($application.IntuneAppName)" -Target "Application Factory Client"
          continue
        }
      } 
      if($originalCount -ne $PublishedApplications.Count){
        throw
      }
      return $PublishedApplications
    }
    catch{
      Write-PSFMessage -Message "==========================================================================================" -Level  "Error" -Tag "Process","IntuneError" -Target "Application Factory Client"
      Write-PSFMessage -Message "An Error Occured and not all applications where published. We will try again in 5." -Level  "Error" -Tag "Process","IntuneError" -Target "Application Factory Client"
      Write-PSFMessage -Message "==========================================================================================" -Level  "Error" -Tag "Process","IntuneError" -Target "Application Factory Client"
      Start-Sleep -Seconds 300
    }
  }
}
#EndRegion '.\Public\Start-AppFactoryClient.ps1' 84
#Region '.\Public\Start-AppFactoryProcess.ps1' 0
function Start-AppFactoryProcess{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$ApplicationServicePath,
    [Parameter()][switch]$EnableLogging,
    [Parameter()][string[]]$AppList,
    [Parameter()][switch]$force,
    [Parameter()][switch]$testmode,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  # Start the Application Factory Process
  Initialize-AppFactoryProcess -ApplicationServicePath $ApplicationServicePath -EnableLogging:$EnableLogging.IsPresent -LogLevel $LogLevel 
  # Get All applications in the process
  $AllApplications = Get-AppFactoryApp -active
  # If set to filter to specific application file
  if ($PSBoundParameters.ContainsKey("AppList")){
    $AllApplications = $AllApplications | Where-Object { $_.GUID -in $AppList }
  }
  if($EnableLogging.IsPresent){
    Write-PSFMessage -Message "There are <c='green'>$($AllApplications.count)</c> applications configured in AppFactory"  -Level  "Output" -Tag "Process" -Target "Application Factory Service"
    Write-PSFMessage -Message "Getting the latest version number for each application in the process" -Level  "Output" -Tag "Process" -Target "Application Factory Service"
  }
  if($AllApplications.Count -eq 0){
    Write-PSFMessage -Message "No applications set to active in AppFactory" -Level  "Output" -Tag "Process" -Target "Application Factory Service"
    return
  }
  $ApplicationList = Test-AppFactoryAppVersion -applicationList $AllApplications -LogLevel $LogLevel -force:$force.IsPresent
  if($ApplicationList.Count -eq 0){
    Write-PSFMessage -Message "No new versions of applications found" -Level  "Output" -Tag "Process" -Target "Application Factory Service"
    return
  }
  if($EnableLogging.IsPresent){
    Write-PSFMessage -Message "There are <c='green'>$($applicationList.count)</c> apps that require an update." -Level  "Output" -Tag "Process" -Target "Application Factory Service"
  }

  $PublishedApplications = [System.Collections.Generic.List[PSCustomObject]]@()
  foreach($Application in $ApplicationList){
    try{
      $publish = Test-AppFactoryFiles -applicationList $Application -LogLevel $LogLevel
      if($publish.Count -eq 0){throw "No applications to process."}
      $publish = Get-AppFactoryInstaller -applicationList $publish -LogLevel $LogLevel
      if($publish.Count -eq 0){throw "No applications to process."}
      $publish = New-AppFactoryPackage -applicationList $publish -LogLevel $LogLevel
      if($publish.Count -eq 0){throw "No applications to process."}
      try{
        Publish-AppFactoryAppInstall -application $publish -LogLevel $LogLevel
      }
      catch{
        throw "Updated install lines for application."
      }
      try{
        Publish-AppFactoryAppUninstall -application $publish -LogLevel $LogLevel  
      }
      catch{
        throw "Updated uninstall lines for application."
      }
      $publish = New-AppFactoryIntuneFile -applicationList $publish -LogLevel $LogLevel
      if($publish.Count -eq 0){throw "No applications to process."}
      if(-not $testmode.IsPresent){
        $publish = Publish-AppFactoryIntunePackage -applicationList $publish -LogLevel $LogLevel
        if($publish.Count -gt 0){
          foreach($app in $publish){
            Write-PSFMessage -Message "[<c='green'>$($app.Information.DisplayName)</c>] Application published." -Level  "Output" -Tag "Process",$app.Information.DisplayName -Target "Application Factory Service"
          }
        }
        else{throw "No applications to process."}    
        Remove-AppFactoryProcessFiles -applicationList $publish -LogLevel $LogLevel
          
      }
      else{
        if($EnableLogging.IsPresent){
          Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] <c='yellow'>running in test mode so did not publish or remove files.</c>" -Level  "Output" -Tag "Process",$application.Information.DisplayName -Target "Application Factory Service"
        }        
      }
      $PublishedApplications.Add($Application)
    }
    catch{
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Application failed to upload with error: $($_)." -Level  "Error" -Tag "Process",$application.Information.DisplayName,"IntuneError" -Target "Application Factory Service"
    }
  }
}
#EndRegion '.\Public\Start-AppFactoryProcess.ps1' 82
#Region '.\Public\Test-AppFactoryAppVersion.ps1' 0
function Test-AppFactoryAppVersion{
  [CmdletBinding()]
  [OutputType([System.Collections.Hashtable])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][System.Collections.Generic.List[PSCustomObject]]$applicationList,
    [Parameter()][ValidateNotNullOrEmpty()][switch]$force,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )  
  $FilteredList = [System.Collections.Generic.List[PSCustomObject]]::new()
  foreach($Application in $ApplicationList){
    if($application.SourceFiles.PauseUpdate){
      if ($script:AppFactoryLogging) {
        Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] <c='yellow'>Updates are paused for this application.</c>" -Level $LogLevel -Tag "Application", "$($application.Information.DisplayName)" -Target "AppFactory"
      }
      continue
    }
    switch ($Application.SourceFiles.AppSource) {
      "ECNO" {$AppItem = Get-AppFactoryECNOAppItem -application $Application -LogLevel $LogLevel}
      "Sharepoint" {$AppItem = Get-AppFactorySharepointAppItem -application $Application -LogLevel $LogLevel}
      "LocalStorage" {$AppItem = Get-AppFactoryLocalStorageAppItem -application $Application -LogLevel $LogLevel}
      "StorageAccount" {$AppItem = Get-AppFactoryAzureStorageAppItem -application $Application -LogLevel $LogLevel}
      "Winget" { $AppItem = Get-AppFactoryWinGetAppItem -application $Application -LogLevel $LogLevel }
      "Evergreen" {$AppItem = Get-AppFactoryEvergreenAppItem -application $Application -LogLevel $LogLevel}
      "PSADT" { $AppItem = Get-AppFactoryPSADTAppItem -application $application -LogLevel $LogLevel }
    }
    if($null -ne $AppItem){
      $required = $false
      if($application.SourceFiles.publishTo.count -eq 0){
        if($script:PublishedAppList.Public.Name -notcontains "$($application.GUID)/$($AppItem.Version)/App.json"){
          if ($script:AppFactoryLogging) {
            Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Specified version (<c='green'>$($AppItem.Version)</c>) is not already packaged in the public container." -Level $LogLevel -Tag "Application", "$($application.Information.DisplayName)" -Target "AppFactory"
          }
          $required = $true
        }
      }
      else{
        foreach($publishTo in $application.SourceFiles.publishTo){
          if($script:PublishedAppList.$($publishTo).Name -notcontains "$($application.GUID)/$($AppItem.Version)/App.json"){
            Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Specified version (<c='green'>$($AppItem.Version)</c>) is not already packaged in the <c='green'>$($publishTo)</c> container." -Level $LogLevel -Tag "Application", "$($application.Information.DisplayName)" -Target "AppFactory"
            $required = $true
          }
        }
      }
      if($required -or $force.IsPresent){
        if ($script:AppFactoryLogging) {
          if($force.IsPresent){
            Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Currently the specified version (<c='green'>$($AppItem.Version)</c>) of the application is already packaged but force is set." -Level $LogLevel -Tag "Application", "$($application.Information.DisplayName)", "WinGet" -Target "AppFactory"
          }
          else{
            Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Specified version (<c='green'>$($AppItem.Version)</c>) is not already packaged in at least one of the expected clients." -Level $LogLevel -Tag "Application", "$($application.Information.DisplayName)", "WinGet" -Target "AppFactory"
          }
        }
        $Application.Information.AppVersion = $AppItem.Version
        if(($application.DetectionRule | Get-Member -Name Value)){
          $application.DetectionRule[0].Value = $AppItem.Version
        }
        $Application.SourceFiles | Add-Member -MemberType NoteProperty -Name "PackageVersion" -Value $AppItem.Version -Force
        $Application.SourceFiles | Add-Member -MemberType NoteProperty -Name "PackageSource" -Value $AppItem.URI -Force
        $FilteredList.Add($Application)
      }
      else{
        if ($script:AppFactoryLogging) {
          Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] Currently the version specfieid (<c='green'>$($AppItem.Version)</c>) of the application is already packaged and force is not set." -Level $LogLevel -Tag "Application", "$($application.Information.DisplayName)", "WinGet" -Target "AppFactory"
        }
      }
    }
  }
  return $FilteredList
}
#EndRegion '.\Public\Test-AppFactoryAppVersion.ps1' 70
#Region '.\Public\Test-AppFactoryFiles.ps1' 0
function Test-AppFactoryFiles {
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$applicationList,
    [Parameter()][ValidateSet("Output", "Verbose")][string]$LogLevel = "Verbose"
  )
  # Create blank list to store the applications that we will be moving forward with.
  $applications = [System.Collections.Generic.List[PSCustomObject]]@()    
  # What files should exist
  $AppFileNames = @("Install.ps1","unInstall.ps1","detection.ps1", "Icon.png","ApplicationConfig.json")
  # Loop through each of the applications
  foreach ($application in $applicationList) {
    $AppPackageFolderPath = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps" -AdditionalChildPath $application.Information.AppFolderName
    # Check for the required files
    try{
      foreach ($AppFileName in $AppFileNames) {
        $filepath = Join-Path -Path $AppPackageFolderPath -ChildPath $AppFileName
        switch($AppFileName){
          {$_ -eq "icon.png"} {
            if (-not(Test-Path -Path $filepath)) {
              throw "[$($application.Information.DisplayName)] File Not Found $($filepath). Skipping Application."
            }
          }
          "detection.ps1" {
            if (-not(Test-Path -Path $filepath) -and $application.DetectionRule.Type -eq "Script") {
              throw "[$($application.Information.DisplayName)] File Not Found $($filepath). Skipping Application."
            }
          }
          "ApplicationConfig.json" {
            if ($application.DetectionRule.Count -eq 0) {
              throw "[$($application.Information.DisplayName)] Could not find any detection rule defined, ensure ApplicationConfig.json contains atleast one detection rule element. Skipping Application."
            }
            if ($application.DetectionRule.Count -ge 2) {
              if ($application.DetectionRule.Type -like "Script") {
                throw "[$($application.Information.DisplayName)] Multiple detection rule types are defined, where at least one of them are of type 'Script', which is not a supported configuration in Intune. Skipping Application."
              }
            }
            $content = Get-Content -Path $filepath
            if($content -match "^.*`"###.*###`".*$"){
              throw "[$($application.Information.DisplayName)] Not all fields that need to configure inApplicationConfig.json have been updated. Skipping Application."
            }
          }
        }
      }
    }
    catch{
      if ($script:AppFactoryLogging) {
        Write-PSFMessage -Message $_ -Level "Error" -Tag "Application", "$($application.Information.DisplayName)", "Error" -Target "Application Factory Service"
      }
      continue
    }
    $applications.Add($application)
    if($script:AppFactoryLogging){
      Write-PSFMessage -Message "[<c='green'>$($application.Information.DisplayName)</c>] All files and configurations appear to be correct for application."  -Level $LogLevel -Tag "Process","Files" -Target "Application Factory Service"
    }    
  }
  return $applications
}
#EndRegion '.\Public\Test-AppFactoryFiles.ps1' 60
#Region '.\Public\Update-AppFactoryServiceAppConfig.ps1' 0
function Update-AppFactoryServiceAppConfig{
  [cmdletbinding()]
  param()
  $ApplicationsPath = Join-Path -Path $script:AppFactorySourceDir -ChildPath "Apps"
  $TemplatePath = Join-Path -Path $script:AppFactorySupportTemplateFolder -ChildPath "Application" -AdditionalChildPath "ApplicationConfig.json"
  $AllApps = Get-ChildItem -Path $ApplicationsPath -Filter "ApplicationConfig.json" -Recurse
  $TemplateSections = @("Information","SourceFiles","Install","Uninstall")
  $Template = Get-Content -Path $TemplatePath -Raw | ConvertFrom-JSON -Depth 10
  foreach($app in $AllApps){
    $AppDetails = Get-Content -path $app.FullName -Raw | ConvertFrom-JSON -Depth 10
    foreach($obj in $TemplateSections){
      $properties = $Template.$obj | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
      foreach($property in $properties){
        if(-not ($AppDetails.$obj.PSObject.Properties.Name -contains $property)){
          $AppDetails.$obj | Add-Member -MemberType NoteProperty -Name $property -Value $Template.$obj.$property
        }
      }
    }
    $AppDetails | ConvertTo-JSON -depth 10 | Out-File -FilePath $app.FullName -Force
  } 
}
#EndRegion '.\Public\Update-AppFactoryServiceAppConfig.ps1' 22