Key takeaways
- Loose-file applications - most often in-house developed tools - can be packaged in App Readiness in two ways: manual repackaging on a VM, or scripted repackaging that runs unattended.
- The recommended approach is a small, reusable PowerShell script that copies the files and imports the registry entries. Write it once with variables; per application you only change the variable values, never the logic.
- The script also solves the primary installer file requirement: loose-file ZIPs often contain no supported installer format to select, but a .ps1 is a supported format. The script itself is your primary installer.
- The output is a standard MSI, generated by App Readiness from the captured changes, ready to deploy through the tools you already run, such as MECM (SCCM) and Intune.
This is one of the most common questions we get. Not every application comes as an MSI or a setup.exe. Sometimes all you have is a set of loose files (a folder of binaries, config files, maybe a .reg file with some registry settings) and you need to turn that into a proper package you can deploy.
In our experience, when we do see loose-file packages, they tend to be in-house developed applications. These usually come with instructions along the lines of "copy these files to this location and add these registry entries."
Juriba App Readiness offers two solutions to achieve this. Here are the two options, including our recommendation.
Zip up your loose files and upload the ZIP as your source media, then run the manual repackaging process.
App Readiness spins up a VM and makes your uploaded files available on it automatically. From there, you do the work by hand: copy the files to the locations you want, add registry entries, create shortcuts, and make any other configuration changes. When you are done, App Readiness captures everything you changed on the VM and generates an MSI package from it.
This works, and it is a reasonable choice for a genuine one-off. But every package means another VM session and another round of manual work; and manual work is where inconsistencies creep in.
Option 2: Scripted repackaging (recommended)
Instead of doing the work by hand on the VM, you can write a small PowerShell script that does it for you: copy the files, import the registry entries, done. Place the script alongside your loose files in the source media.
Now you can choose unattended repackaging. App Readiness runs the script on the VM in the background, captures the result, and hands you an MSI. No VM session, no clicking around. This fits how App Readiness is meant to be used: automated, end-to-end.
For every media upload, App Readiness asks you to select a primary installer file. The supported formats are:
.exe, .bat, .cmd, .msi, .vbs, .ps1, .msix, .appv, .intunewin, .vhd, .vhdx, .cim, .zip
Here is where loose-file packages get stuck: often none of the files in your ZIP have any of these extensions. Only DLLs, config files, XML, and so on. With nothing to select as the primary installer, you can't complete the upload, and this is the point where many customers don't know what to do next.
The fix depends on which approach you are taking.
If you use the scripted approach, there is nothing extra to do. Your PowerShell script (a .ps1, which is a supported format) is the installer. Select it as the primary installer file and move on. This is one more reason we recommend Option 2.
If you prefer manual repackaging, add a small dummy file to your ZIP and select that as the primary installer. Pick a simple, harmless format like .cmd, .bat, or .ps1. Since you will be doing all the work manually on the VM, this file is never meaningfully executed as an installer, meaning its effect on your package is zero. (In automated scenarios like unattended repackaging or smoke testing, the primary installer does get triggered, which is exactly why in this manual case a do-nothing file is safe.)
Rather than leaving the dummy file completely empty, give it one line that exits cleanly. That way, if anything ever does trigger it, it simply reports success and does nothing. For example, a dummy.cmd:
@echo off
REM Placeholder primary installer - performs no action.
exit /b 0
Or the same as a dummy.ps1:
# Placeholder primary installer - performs no action.
exit 0
No, which is the important part. You write the script once, in a flexible way, using variables for the things that change between applications: the application name, the destination folder, the name of the .reg file. For each new set of loose files, you change a few variable values at the top of the script. The logic never changes.
Packaging a new app then looks like this: drop the loose files and the script into a folder, edit three or four variables, upload, run unattended repackaging. That's it.
At the end of this post, you will find a complete, working script you can use as-is or adapt. It does three things: copies all loose files (and their entire folder structure) from wherever the script sits to a destination folder, creating any missing folders on the way; imports a .reg file if you have one; and returns meaningful exit codes, so if something fails you can see exactly which step broke without guessing.
Everything you would change per application sits in the variables block at the top:
# ============================================================================
# VARIABLES - edit these per application
# ============================================================================
$AppName = 'MyApp' # Application name
$DestinationPath = "$env:ProgramFiles\$AppName" # Where files are copied
$RegFileName = 'settings.reg' # .reg file in source folder; set to '' if none
$ExcludeFromCopy = @('Install.ps1','Uninstall.ps1','*.reg') # Files NOT copied to destination
# ============================================================================
A few notes on how it behaves:
Folder structure is preserved. Whatever structure your loose files have next to the script is mirrored exactly under the destination folder. If your source has a bin\ and a config\templates\ subfolder, so will the destination. And the destination does not have to be Program Files\<AppName> — set $DestinationPath to any path you like.
HKCU registry entries work naturally here. During unattended repackaging, the script runs under an admin account on the repackaging VM — not as SYSTEM. So any HKEY_CURRENT_USER entries in your .reg file import into that account's hive as you would expect, the repackaging capture picks them up, and they end up in the generated MSI as per-user registry entries. The MSI then delivers them to users when it is deployed.
Exit codes tell you what failed. 0 means success. Each failure point has its own code: 69001 (couldn't create destination folder), 69002 (file copy failed), 69003 (.reg file missing), 69004 (registry import failed). Everything is also written to a log file in %windir%\Temp.
To package a new set of loose files in App Readiness:
powershell.exe -ExecutionPolicy Bypass -NoProfile -File .\Install.ps1Write the script once. After that, packaging loose files is a variables edit and an upload.
Here is the complete install script referenced throughout this post. A matching uninstall script (same variables, removes the folder and the registry keys) follows the same pattern, so the generated package cleans up after itself properly too.
<#
.SYNOPSIS
Generic install script: copies loose files from the script's folder
to a destination folder and imports a .reg file.
.NOTES
- Keep this script in the same folder as the loose files and the .reg file.
- Designed for App Readiness unattended repackaging, where it runs under an
admin account on the repackaging VM. HKCU entries in the .reg file import
into that account's hive and are captured into the generated MSI as
per-user registry entries.
- Install command line:
powershell.exe -ExecutionPolicy Bypass -NoProfile -File .\Install.ps1
EXIT CODES
0 Success
69001 Failed to create destination folder
69002 File copy failed
69003 .reg file specified but not found in source folder
69004 Registry import failed
69099 Unexpected error
#>
# ============================================================================
# VARIABLES - edit these per application
# ============================================================================
$AppName = 'MyApp' # Application name
$DestinationPath = "$env:ProgramFiles\$AppName" # Where files are copied
$RegFileName = 'settings.reg' # .reg file in source folder; set to '' if none
$ExcludeFromCopy = @('Install.ps1','Uninstall.ps1','*.reg') # Files NOT copied to destination
# ============================================================================
$SourcePath = $PSScriptRoot
$LogFile = "$env:windir\Temp\$($AppName)_Install.log"
function Write-Log {
param([string]$Message, [string]$Level = 'INFO')
$line = "{0} [{1}] {2}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Level, $Message
Add-Content -Path $LogFile -Value $line -ErrorAction SilentlyContinue
Write-Output $line
}
try {
Write-Log "===== Install of '$AppName' started ====="
Write-Log "Source: $SourcePath"
Write-Log "Destination: $DestinationPath"
# ------------------------------------------------------------------
# 1. Create destination folder (and any missing parent folders)
# ------------------------------------------------------------------
if (-not (Test-Path -Path $DestinationPath)) {
try {
New-Item -Path $DestinationPath -ItemType Directory -Force -ErrorAction Stop | Out-Null
Write-Log "Created destination folder."
} catch {
Write-Log "Failed to create destination folder: $($_.Exception.Message)" 'ERROR'
exit 69001
}
}
# ------------------------------------------------------------------
# 2. Copy loose files and subfolders to the destination
# (Note: $ExcludeFromCopy applies to the top-level source folder only)
# ------------------------------------------------------------------
try {
Get-ChildItem -Path $SourcePath -Exclude $ExcludeFromCopy |
Copy-Item -Destination $DestinationPath -Recurse -Force -ErrorAction Stop
Write-Log "File copy completed."
} catch {
Write-Log "File copy failed: $($_.Exception.Message)" 'ERROR'
exit 69002
}
# ------------------------------------------------------------------
# 3. Import registry entries
# ------------------------------------------------------------------
if (-not [string]::IsNullOrWhiteSpace($RegFileName)) {
$regFilePath = Join-Path -Path $SourcePath -ChildPath $RegFileName
if (-not (Test-Path -Path $regFilePath)) {
Write-Log ".reg file not found: $regFilePath" 'ERROR'
exit 69003
}
reg.exe import "$regFilePath" 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Log "Registry import failed. reg.exe exit code: $LASTEXITCODE" 'ERROR'
exit 69004
}
Write-Log "Registry import successful: $RegFileName"
} else {
Write-Log "No .reg file configured. Skipping registry import."
}
Write-Log "===== Install of '$AppName' completed successfully ====="
exit 0
}
catch {
Write-Log "Unexpected error: $($_.Exception.Message)" 'ERROR'
exit 69099
}
Frequently asked questions
Bundle the loose files into a ZIP and upload it as your source media, then repackage. You can do the installation steps manually on the repackaging VM, or, the recommended way, include a small PowerShell script that copies the files and imports the registry entries, and run unattended repackaging. Either way, App Readiness captures the changes and generates an MSI.
If you are using the scripted approach, select the PowerShell script (.ps1) as a supported primary installer format. If you are repackaging manually, add a small dummy file in a supported format (such as .cmd or .ps1) that simply exits with code 0, and select that. In the manual flow, it is never meaningfully executed, so it has no effect on the package.
No. Write the script once, with variables for the things that change between applications e.g. application name, destination folder, .reg file name. For each new application you edit a few variable values at the top of the script; the logic stays the same.
During unattended repackaging the script runs under an admin account on the repackaging VM, so HKEY_CURRENT_USER entries import into that account's hive normally. The repackaging capture picks them up and builds them into the generated MSI as per-user registry entries, which the MSI delivers to users at deployment time.
A standard Windows Installer (MSI) package, built from everything captured during repackaging such as files, folders, registry entries, and shortcuts. It deploys through your existing tools, such as MECM (SCCM) and Intune, and uninstalls cleanly like any other MSI.