Public/New-Win32App.ps1
<#
.Synopsis Created on: 14/03/2021 Updated on: 12/11/2023 Created by: Ben Whitmore Filename: New-Win32App.ps1 The Win32 App Migration Tool is designed to inventory ConfigMgr Applications and Deployment Types, build .intunewin files and create Win3Apps in The Intune Admin Center. .Description **Version 2.0.12 BETA** .PARAMETER LogId The component (script name) passed as LogID to the 'Write-Log' function. .PARAMETER AppName Pass a string to the toll to search for applications in ConfigMgr .PARAMETER DownloadContent When passed, the content for the deployment type is saved locally to the working folder "Content" .PARAMETER SiteCode Specify the Sitecode you wish to connect to .PARAMETER ProviderMachineName Specify the Site Server to connect to .PARAMETER ExportIcon When passed, the Application icon is decoded from base64 and saved to the Logos folder .PARAMETER WorkingFolder This is the working folder for the Win32AppMigration Tool. Note: Care should be given when specifying the working folder because downloaded content can increase the working folder size considerably .PARAMETER PackageApps Pass this parameter to package selected apps in the .intunewin format .PARAMETER CreateApps Pass this parameter to create the Win32apps in Intune .PARAMETER ResetLog Pass this parameter to reset the log file .PARAMETER ExcludePMPC Pass this parameter to exclude apps created by PMPC from the results. Filter is applied to Application "Comments". string can be modified in Get-AppList Function .PARAMETER ExcludeFilter Pass this parameter to exclude specific apps from the results. string value that accepts wildcards e.g. "Microsoft*" .PARAMETER Win32ContentPrepToolUri URI for Win32 Content Prep Tool .PARAMETER OverrideIntuneWin32FileName Override intunewin filename. Default is the name calcualted from the install command line. You only need to pass the file name, not the extension .EXAMPLE New-Win32App -SiteCode "BB1" -ProviderMachineName "SCCM1.byteben.com" -AppName "Microsoft Edge Chromium *" .EXAMPLE New-Win32App -SiteCode "BB1" -ProviderMachineName "SCCM1.byteben.com" -AppName "Microsoft Edge Chromium *" -DownloadContent .EXAMPLE New-Win32App -SiteCode "BB1" -ProviderMachineName "SCCM1.byteben.com" -AppName "Microsoft Edge Chromium *" -ExportLogo .EXAMPLE New-Win32App -SiteCode "BB1" -ProviderMachineName "SCCM1.byteben.com" -AppName "Microsoft Edge Chromium *" -ExportLogo -PackageApps .EXAMPLE New-Win32App -SiteCode "BB1" -ProviderMachineName "SCCM1.byteben.com" -AppName "Microsoft Edge Chromium *" -ExportLogo -PackageApps -CreateApps .EXAMPLE New-Win32App -SiteCode "BB1" -ProviderMachineName "SCCM1.byteben.com" -AppName "Microsoft Edge Chromium *" -ExportLogo -PackageApps -CreateApps -ResetLog .EXAMPLE New-Win32App -SiteCode "BB1" -ProviderMachineName "SCCM1.byteben.com" -AppName "Microsoft Edge Chromium *" -ExportLogo -PackageApps -CreateApps -ResetLog -ExcludePMPC .EXAMPLE New-Win32App -SiteCode "BB1" -ProviderMachineName "SCCM1.byteben.com" -AppName "Microsoft Edge Chromium *" -ExportLogo -PackageApps -CreateApps -ResetLog -ExcludePMPC -ExcludeFilter "Microsoft*" .EXAMPLE New-Win32App -SiteCode "BB1" -ProviderMachineName "SCCM1.byteben.com" -AppName "Microsoft Edge Chromium *" -ExportLogo -PackageApps -CreateApps -ResetLog -ExcludePMPC -ExcludeFilter "Microsoft*" -OverrideIntuneWin32FileName "application" #> function New-Win32App { [CmdletBinding()] param ( [Parameter(Mandatory = $false, ValuefromPipeline = $false, HelpMessage = "The component (script name) passed as LogID to the 'Write-Log' function")] [string]$LogId = $($MyInvocation.MyCommand).Name, [Parameter(Mandatory = $true, ValueFromPipeline = $false, Position = 0, HelpMessage = 'The Site Code of the ConfigMgr Site')] [ValidatePattern('(?##The Site Code must be only 3 alphanumeric characters##)^[a-zA-Z0-9]{3}$')] [string]$SiteCode, [Parameter(Mandatory = $true, ValueFromPipeline = $false, Position = 1, HelpMessage = 'Server name that has an SMS Provider site system role')] [string]$ProviderMachineName, [Parameter(Mandatory = $true, ValueFromPipeline = $false, Position = 2, HelpMessage = 'The name of the application to search for. Accepts wildcards *')] [string]$AppName, [Parameter(Mandatory = $false, ValueFromPipeline = $false, HelpMessage = 'DownloadContent: When passed, the content for the deployment type is saved locally to the working folder "Content"')] [Switch]$DownloadContent, [Parameter(Mandatory = $false, ValueFromPipeline = $false, HelpMessage = 'ExportLogo: When passed, the Application icon is decoded from base64 and saved to the Logos folder')] [Switch]$ExportIcon, [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 3, HelpMessage = 'The working folder for the Win32AppMigration Tool. Care should be given when specifying the working folder because downloaded content can increase the working folder size considerably')] [string]$workingFolder = "C:\Win32AppMigrationTool", [Parameter(Mandatory = $false, ValueFromPipeline = $false, HelpMessage = 'PackageApps: Pass this parameter to package selected apps in the .intunewin format')] [Switch]$PackageApps, [Parameter(Mandatory = $false, ValueFromPipeline = $false, HelpMessage = 'CreateApps: Pass this parameter to create the Win32apps in Intune')] [Switch]$CreateApps, [Parameter(Mandatory = $false, ValueFromPipeline = $false, HelpMessage = 'ResetLog: Pass this parameter to reset the log file')] [Switch]$ResetLog, [Parameter(Mandatory = $false, ValueFromPipeline = $false, HelpMessage = 'ExcludePMPC: Pass this parameter to exclude apps created by PMPC from the results. Filter is applied to Application "Comments". string can be modified in Get-AppList Function')] [Switch]$ExcludePMPC, [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 4, HelpMessage = 'ExcludeFilter: Pass this parameter to exclude specific apps from the results. string value that accepts wildcards e.g. "Microsoft*"')] [string]$ExcludeFilter, [Parameter(Mandatory = $false, ValueFromPipeline = $false, HelpMessage = 'NoOGV: When passed, the Out-Gridview is suppressed')] [Switch]$NoOgv, [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 5, HelpMessage = 'URI for Win32 Content Prep Tool')] [string]$Win32ContentPrepToolUri = 'https://github.com/microsoft/Microsoft-Win32-Content-Prep-Tool/raw/master/IntuneWinAppUtil.exe', [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 6, HelpMessage = 'Override intunewin filename. Default is the name calcualted from the install command line')] [string]$OverrideIntuneWin32FileName ) # Create global variable(s) $global:workingFolder_Root = $workingFolder #region Prepare_Workspace # Initialize folders to prepare workspace for logging Write-Host "Initializing required folders..." -ForegroundColor Cyan foreach ($folder in $workingFolder_Root, "$workingFolder_Root\Logs") { if (-not (Test-Path -Path $folder)) { Write-Host ("Working folder root does not exist at '{0}'. Creating environemnt..." -f $folder) -ForegroundColor Cyan New-Item -Path $folder -ItemType Directory -Force -ErrorAction Stop | Out-Null } else { Write-Host ("Folder '{0}' already exists. Skipping folder creation" -f $folder) -ForegroundColor Yellow } } # Rest the log file if the -ResetLog parameter is passed if ($ResetLog -and (Test-Path -Path "$workingFolder_Root\Logs") ) { Write-Log -Message $null -ResetLogFile } #endregion # Begin Script New-VerboseRegion -Message 'Start Win32AppMigrationTool' -ForegroundColor 'Gray' $ScriptRoot = $PSScriptRoot Write-Log -Message ("ScriptRoot is '{0}'" -f $ScriptRoot) -LogId $LogId # Connect to Site Server Connect-SiteServer -SiteCode $SiteCode -ProviderMachineName $ProviderMachineName # Check the folder structure for the working directory and create if necessary New-VerboseRegion -Message 'Checking Win32AppMigrationTool folder structure' -ForegroundColor 'Gray' #region Create_Folders Write-Host "Creating additionl folders..." -ForegroundColor Cyan Write-Log -Message ("New-FolderToCreate -Root '{0}' -FolderNames @('Icons', 'Content', 'ContentPrepTool', 'Details', 'Win32Apps')" -f $workingFolder_Root) -LogId $LogId New-FolderToCreate -Root $workingFolder_Root -FolderNames @('Icons', 'Content', 'ContentPrepTool', 'Details', 'Win32Apps') #endRegion #region Get_Content_Tool New-VerboseRegion -Message 'Checking if the Win32contentpreptool is required' -ForegroundColor 'Gray' # Download the Win32 Content Prep Tool if the PackageApps parameter is passed if ($PackageApps) { Write-Host "Downloading the Win32contentpreptool..." -ForegroundColor Cyan if (Test-Path (Join-Path -Path "$workingFolder_Root\ContentPrepTool" -ChildPath "IntuneWinAppUtil.exe")) { Write-Log -Message ("Information: IntuneWinAppUtil.exe already exists at '{0}'. Skipping download" -f "$workingFolder_Root\ContentPrepTool") -LogId $LogId -Severity 2 Write-Host ("Information: IntuneWinAppUtil.exe already exists at '{0}'. Skipping download" -f "$workingFolder_Root\ContentPrepTool") -ForegroundColor Yellow } else { Write-Log -Message ("Get-FileFromInternet -URI '{0} -Destination {1}" -f $Win32ContentPrepToolUri, "$workingFolder_Root\ContentPrepTool") -LogId $LogId Get-FileFromInternet -Uri $Win32ContentPrepToolUri -Destination "$workingFolder_Root\ContentPrepTool" } } else { Write-Log -Message "The 'PackageApps' parameter was not passed. Skipping downloading of the Win32 Content Prep Tool" -LogId $LogId -Severity 2 Write-Host "The 'PackageApps' parameter was not passed. Skipping downloading of the Win32 Content Prep Tool" -ForegroundColor Yellow } #endRegion #region Display_Application_Results New-VerboseRegion -Message 'Filtering application results' -ForegroundColor 'Gray' # Build a hash table of switch parameters to pass to the Get-AppList function $paramsToPassApp = @{} if ($ExcludePMPC) { $paramsToPassApp.Add('ExcludePMPC', $true) Write-Log -Message "The ExcludePMPC parameter was passed. Ignoring all PMPC created applications" -LogId $LogId -Severity 2 Write-Host "The ExcludePMPC parameter was passed. Ignoring all PMPC created applications" -ForegroundColor Cyan } if ($ExcludeFilter) { $paramsToPassApp.Add('ExcludeFilter', $ExcludeFilter) Write-Log -Message ("The 'ExcludeFilter' parameter was passed. Ignoring applications that match '{0}'" -f $ExcludeFilter) -LogId $LogId -Severity 2 Write-Host ("The 'ExcludeFilter' parameter was passed. Ignoring applications that match '{0}'" -f $ExcludeFilter) -ForegroundColor Cyan } if ($NoOGV) { $paramsToPassApp.Add('NoOGV', $true) Write-Log -Message "The 'NoOgv' parameter was passed. Suppressing Out-GridView" -LogId $LogId -Severity 2 Write-Host "The 'NoOgv' parameter was passed. Suppressing Out-GridView" -ForegroundColor Cyan } Write-Log -Message ("Running function 'Get-AppList' -AppName '{0}'" -f $AppName) -LogId $LogId Write-Host ("Running function 'Get-AppList' -AppName '{0}'" -f $AppName) -ForegroundColor Cyan $applicationName = Get-AppList -AppName $AppName @paramsToPassApp # ApplicationName(s) returned from the Get-AppList function if ($applicationName) { Write-Log -Message "The Win32App Migration Tool will process the following applications:" -LogId $LogId Write-Host "The Win32App Migration Tool will process the following applications:" -ForegroundColor Cyan foreach ($application in $ApplicationName) { Write-Log -Message ("Id = '{0}', Name = '{1}'" -f $application.Id, $application.LocalizedDisplayName) -LogId $LogId Write-Host ("Id = '{0}', Name = '{1}'" -f $application.Id, $application.LocalizedDisplayName) -ForegroundColor Green } } else { Write-Log -Message ("There were no applications found that match the crieria '{0}' or the Out-GrideView was closed with no selection made. Cannot continue" -f $AppName) -LogId $LogId -Severity 3 Write-Warning -Message ("There were no applications found that match the crieria '{0}' or the Out-GrideView was closed with no selection made. Cannot continue" -f $AppName) Get-ScriptEnd } #endRegion #region Get_App_Details New-VerboseRegion -Message 'Getting application details' -ForegroundColor 'Gray' # Calling function to grab application details Write-Log -Message "Calling 'Get-AppInfo' function to grab application details" -LogId $LogId Write-Host "Calling 'Get-AppInfo' function to grab application details" -ForegroundColor Cyan $app_Array = Get-AppInfo -ApplicationName $applicationName #endregion #region Get_DeploymentType_Details New-VerboseRegion -Message 'Getting deployment type details' -ForegroundColor 'Gray' # Calling function to grab deployment types details Write-Log -Message "Calling 'Get-DeploymentTypeInfo' function to grab deployment type details" -LogId $LogId Write-Host "Calling 'Get-DeploymentTypeInfo' function to grab deployment type details" -ForegroundColor Cyan $deploymentTypes_Array = foreach ($app in $app_Array) { Get-DeploymentTypeInfo -ApplicationId $app.Id } #endregion #region Get_DeploymentType_Content New-VerboseRegion -Message 'Getting deployment type content information' -ForegroundColor 'Gray' # Calling function to grab deployment type content information Write-Log -Message "Calling 'Get-ContentFiles' function to grab deployment type content" -LogId $LogId Write-Host "Calling 'Get-ContentFiles' function to grab deployment type content" -ForegroundColor Cyan $content_Array = foreach ($deploymentType in $deploymentTypes_Array) { # Build or reset a hash table of switch parameters to pass to the Get-ContentFiles function $paramsToPassContent = @{} if ($deploymentType.InstallContent) { $paramsToPassContent.Add('InstallContent', $deploymentType.InstallContent) } $paramsToPassContent.Add('UninstallSetting', $deploymentType.UninstallSetting) if ($deploymentType.UninstallContent) { $paramsToPassContent.Add('UninstallContent', $deploymentType.UninstallContent) } $paramsToPassContent.Add('ApplicationId', $deploymentType.Application_Id) $paramsToPassContent.Add('ApplicationName', $deploymentType.ApplicationName) $paramsToPassContent.Add('DeploymentTypeLogicalName', $deploymentType.LogicalName) $paramsToPassContent.Add('DeploymentTypeName', $deploymentType.Name) $paramsToPassContent.Add('InstallCommandLine', $deploymentType.InstallCommandLine) # If we have content, call the Get-ContentInfo function if ($deploymentType.InstallContent -or $deploymentType.UninstallContent) { Get-ContentInfo @paramsToPassContent } } # If $DownloadContent was passed, download content to the working folder New-VerboseRegion -Message 'Copying content files' -ForegroundColor 'Gray' if ($DownloadContent) { Write-Log -Message "The 'DownloadContent' parameter passed" -LogId $LogId foreach ($content in $content_Array) { Get-ContentFiles -Source $content.Install_Source -Destination $content.Install_Destination # If the uninstall content is different to the install content, copy that too if ($content.Uninstall_Setting -eq 'Different') { Get-ContentFiles -Source $content.Uninstall_Source -Destination $content.Uninstall_Destination -Flags 'UninstallDifferent' } } } else { Write-Log -Message "The 'DownloadContent' parameter was not passed. Skipping content download" -LogId $LogId -Severity 2 Write-Host "The 'DownloadContent' parameter was not passed. Skipping content download" -ForegroundColor Yellow } #endregion #region Exporting_Csv data # Export $DeploymentTypes to CSV for reference New-VerboseRegion -Message 'Exporting collected data to Csv' -ForegroundColor 'Gray' $detailsFolder = (Join-Path -Path $workingFolder_Root -ChildPath 'Details') Write-Log -Message ("Destination folder will be '{0}\Details" -f $workingFolder_Root) -LogId $LogId -Severity 2 Write-Host ("Destination folder will be '{0}\Details" -f $workingFolder_Root) -ForegroundColor Cyan # Export application information to CSV for reference Export-CsvDetails -Name 'Applications' -Data $app_Array -Path $detailsFolder # Export deployment type information to CSV for reference Export-CsvDetails -Name 'DeploymentTypes' -Data $deploymentTypes_Array -Path $detailsFolder # Export content information to CSV for reference Export-CsvDetails -Name 'Content' -Data $content_Array -Path $detailsFolder #endregion #region Exporting_Logos # Export icon(s) for the applications New-VerboseRegion -Message 'Exporting icon(s)' -ForegroundColor 'Gray' if ($ExportIcon) { Write-Log -Message "The 'ExportIcon' parameter passed" -LogId $LogId foreach ($applicationIcon in $app_Array) { Write-Log -Message ("Exporting icon for '{0}' to '{1}'" -f $applicationIcon.Name, $applicationIcon.IconPath) -Logid $LogId Write-Host ("Exporting icon for '{0}' to '{1}'" -f $applicationIcon.Name, $applicationIcon.IconPath) -ForegroundColor Cyan Export-Icon -AppName $applicationIcon.Name -IconPath $applicationIcon.IconPath -IconData $applicationIcon.IconData } } else { Write-Log -Message "The 'ExportIcon' parameter was not passed. Skipping icon export" -LogId $LogId -Severity 2 Write-Host "The 'ExportIcon' parameter was not passed. Skipping icon export" -ForegroundColor Yellow } #endregion #region Package_Apps if ($PackageApps) { # If the $PackageApps parameter was passed. Use the Win32Content Prep Tool to build Intune.win files Write-Log -Message "The 'PackageApps' Parameter passed" -LogId $LogId New-VerboseRegion -Message 'Creating intunewin file(s)' -ForegroundColor 'Gray' foreach ($content in $content_Array) { Write-Log -Message ("Working on application '{0}'..." -f $content.Application_Name) -LogId $LogId Write-Host ("`nWorking on application '{0}'..." -f $content.Application_Name) -ForegroundColor Cyan # Create the Win32app folder for the .intunewin files New-FolderToCreate -Root "$workingFolder_Root\Win32Apps" -FolderNames $content.Win32app_Destination # Create intunewin files Write-Log -Message ("Creating intunewin file for the deployment type '{0}' for app '{1}'" -f $content.DeploymentType_Name, $content.Application_Name) -LogId $LogId Write-Host ("Creating intunewin file for the deployment type '{0}' for app '{1}'" -f $content.DeploymentType_Name, $content.Application_Name) -ForegroundColor Cyan # Build parameters to splat at the New-IntuneWin function $paramsToPassIntuneWin = @{} $paramsToPassIntuneWin.Add('ContentFolder', $content.Install_Destination) $paramsToPassIntuneWin.Add('OutputFolder', (Join-Path -Path "$workingFolder_Root\Win32Apps" -ChildPath $content.Win32app_Destination)) $paramsToPassIntuneWin.Add('SetupFile', $content.Install_CommandLine) if ($OverrideIntuneWin32FileName) { $paramsToPassIntuneWin.Add('OverrideIntuneWin32FileName', $OverrideIntuneWin32FileName) } # Create the .intunewin file New-IntuneWin @paramsToPassIntuneWin } } else { Write-Log -Message "The 'PackageApps' parameter was not passed. Intunewin files will not be created" -LogId $LogId -Severity 2 Write-Host "The 'PackageApps' parameter was not passed. Intunewin files will not be created" -ForegroundColor Yellow } #endRegion Get-ScriptEnd } |