First of all – sorry for the delay as we promised to post this script much earlier. However as usual there are only 48 hours in a day 
If you attended our session “Sequencing applications with App-V 5.1 – Best Practices Edition”, Roy Essers and me showed you the latest techniques to prepare and analyze to virtualize apps using the latest version of Microsoft App-V. One of those best practices techniques is to prepare your sequencer the automated way :
Another App-V MVP called Dan Gough originally created this script , but it wasn’t really a good solution in our eyes. The reason for that , was that it uses modules from chocolatey and boxstarter directly of the internet and we didn’t trust the big bad cloudy internet. Anyone could change the source code and inject malicious code anytime . Also changes made in revisions would also create an inconsistency in your build platform.
# Disclaimer: This script is provided as-is, without warranty of any kind
#
# App-V sequencer / client generic build script
# Version 1.0 - Dan Gough 04/07/15
# Version 1.1 - Fixed HideSCAHealth registry key
# Version 1.2 - Fixed install on 32-bit Windows
# Version 1.3 - Update for .NET 4.6 and Windows 10, better method of disabling Defender on Win8+
# Version 1.4 - Roy Essers - added native functions from chocolatey and boxstarter (so no need for those modules).
# -sequencer only
# -depends on local sources (dotnet, wmf, etc), if not found will try to download them from the internet
# -added some features, onedrive removal.
# planning to add option to (download and) install vcredists
function Download-File() {
<#
.SYNOPSIS
Downloads a file showing the progress of the download
.DESCRIPTION
This Script will download a file locally while showing the progress of the download
.EXAMPLE
.\Download-File.ps1 'http:\\someurl.com\somefile.zip'
.EXAMPLE
.\Download-File.ps1 'http:\\someurl.com\somefile.zip' 'C:\Temp\somefile.zip'
.PARAMETER url
url to be downloaded
.PARAMETER localFile
the local filename where the download should be placed
.NOTES
FileName : Download-File.ps1
Author : CrazyDave
LastModified : 18 Jan 2011 9:39 AM PST
#Requires -Version 2.0
#>
param(
[Parameter(Mandatory=$true)]
[String] $url,
[Parameter(Mandatory=$false)]
[String] $localFile = (Join-Path $pwd.Path $url.SubString($url.LastIndexOf('/')))
)
begin {
$client = New-Object System.Net.WebClient
$Global:downloadComplete = $false
$eventDataComplete = Register-ObjectEvent $client DownloadFileCompleted `
-SourceIdentifier WebClient.DownloadFileComplete `
-Action {$Global:downloadComplete = $true}
$eventDataProgress = Register-ObjectEvent $client DownloadProgressChanged `
-SourceIdentifier WebClient.DownloadProgressChanged `
-Action { $Global:DPCEventArgs = $EventArgs }
}
process {
Write-Progress -Activity 'Downloading file' -Status $url
$client.DownloadFileAsync($url, $localFile)
while (!($Global:downloadComplete)) {
$pc = $Global:DPCEventArgs.ProgressPercentage
if ($pc -ne $null) {
Write-Progress -Activity 'Downloading file' -Status $url -PercentComplete $pc
}
}
Write-Progress -Activity 'Downloading file' -Status $url -Complete
}
end {
Unregister-Event -SourceIdentifier WebClient.DownloadProgressChanged
Unregister-Event -SourceIdentifier WebClient.DownloadFileComplete
$client.Dispose()
$Global:downloadComplete = $null
$Global:DPCEventArgs = $null
Remove-Variable client
Remove-Variable eventDataComplete
Remove-Variable eventDataProgress
[GC]::Collect()
}
}
function Install-PinnedTaskBarItem {
<#
.SYNOPSIS
Creates an item in the task bar linking to the provided path.
.PARAMETER TargetFilePath
The path to the application that should be launched when clicking on the task bar icon.
.EXAMPLE
Install-PinnedTaskBarItem "${env:ProgramFiles(x86)}\Microsoft Visual Studio 11.0\Common7\IDE\devenv.exe"
This will create a Visual Studio task bar icon.
#>
param(
[string] $targetFilePath
)
Write-Debug "Running 'Install-PinnedTaskBarItem' with targetFilePath:`'$targetFilePath`'";
if (Test-Path($targetFilePath)) {
$verb = "Pin To Taskbar"
$path= split-path $targetFilePath
$shell=new-object -com "Shell.Application"
$folder=$shell.Namespace($path)
$item = $folder.Parsename((split-path $targetFilePath -leaf))
$itemVerb = $item.Verbs() | ? {$_.Name.Replace("&","") -eq $verb}
if($itemVerb -eq $null){
Write-Host "TaskBar verb not found for $item. It may have already been pinned" -ForegroundColor Yellow
} else {
$itemVerb.DoIt()
}
Write-host "`'$((Get-ChildItem $targetFilePath).Name)`' has been pinned to the task bar on your desktop." -ForegroundColor Green
} else {
$errorMessage = "`'$((Get-ChildItem $targetFilePath).Name)`' does not exist, not able to pin to task bar"
}
if($errorMessage){
Write-host $errorMessage -ForegroundColor Red
throw $errorMessage
}
}
function Set-StartScreenOptions {
<#
.SYNOPSIS
Sets options for the Windows Start Screen.
.PARAMETER EnableBootToDesktop
When I sign in or close all apps on a screen, go to the desktop instead of Start
.PARAMETER DisableBootToDesktop
Disables the Boot to Desktop Option, see enableBootToDesktop
.PARAMETER EnableDesktopBackgroundOnStart
Show Desktop background on Start
.PARAMETER DisableDesktopBackgroundOnStart
Do not show Desktop background on Start
.PARAMETER EnableShowStartOnActiveScreen
Show Start on the display I'm using when I press the Windows logo key
.PARAMETER DisableShowStartOnActiveScreen
Disables the displaying of the Start screen on active screen, see enableShowStartOnActiveScreen
.PARAMETER EnableShowAppsViewOnStartScreen
Show the Apps view automatically when I go to Start
PARAMETER DisableShowAppsViewOnStartScreen
Disables the showing of Apps View when Start is activated, see enableShowAppsViewOnStartScreen
.PARAMETER EnableSearchEverywhereInAppsView
Search everywhere instead of just my apps when I search from the Apps View
.PARAMETER DisableSearchEverywhereInAppsView
Disables the searching of everywhere instead of just apps, see enableSearchEverywhereInAppsView
.PARAMETER EnableListDesktopAppsFirst
List desktop apps first in the Apps view when it's sorted by category
.PARAMETER DisableListDesktopAppsFirst
Disables the ability to list desktop apps first when sorted by category, see enableListDesktopAppsFirst
.LINK
http://boxstarter.org
#>
[CmdletBinding()]
param(
[switch]$EnableBootToDesktop,
[switch]$DisableBootToDesktop,
[switch]$EnableDesktopBackgroundOnStart,
[switch]$DisableDesktopBackgroundOnStart,
[switch]$EnableShowStartOnActiveScreen,
[switch]$DisableShowStartOnActiveScreen,
[switch]$EnableShowAppsViewOnStartScreen,
[switch]$DisableShowAppsViewOnStartScreen,
[switch]$EnableSearchEverywhereInAppsView,
[switch]$DisableSearchEverywhereInAppsView,
[switch]$EnableListDesktopAppsFirst,
[switch]$DisableListDesktopAppsFirst
)
$PSBoundParameters.Keys | %{
if($_-like "En*"){ $other="Dis" + $_.Substring(2)}
if($_-like "Dis*"){ $other="En" + $_.Substring(3)}
if($PSBoundParameters[$_] -and $PSBoundParameters[$other]){
throw new-Object -TypeName ArgumentException "You may not set both $_ and $other. You can only set one."
}
}
$key = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer'
$startPageKey = "$key\StartPage"
$accentKey = "$key\Accent"
if(Test-Path -Path $startPageKey) {
if($enableBootToDesktop) { Set-ItemProperty -Path $startPageKey -Name 'OpenAtLogon' -Value 0 }
if($disableBootToDesktop) { Set-ItemProperty -Path $startPageKey -Name 'OpenAtLogon' -Value 1 }
if($enableShowStartOnActiveScreen) { Set-ItemProperty -Path $startPageKey -Name 'MonitorOverride' -Value 1 }
if($disableShowStartOnActiveScreen) { Set-ItemProperty -Path $startPageKey -Name 'MonitorOverride' -Value 0 }
if($enableShowAppsViewOnStartScreen) { Set-ItemProperty -Path $startPageKey -Name 'MakeAllAppsDefault' -Value 1 }
if($disableShowAppsViewOnStartScreen) { Set-ItemProperty -Path $startPageKey -Name 'MakeAllAppsDefault' -Value 0 }
if($enableSearchEverywhereInAppsView) { Set-ItemProperty -Path $startPageKey -Name 'GlobalSearchInApps' -Value 1 }
if($disableSearchEverywhereInAppsView) { Set-ItemProperty -Path $startPageKey -Name 'GlobalSearchInApps' -Value 0 }
if($enableListDesktopAppsFirst) { Set-ItemProperty -Path $startPageKey -Name 'DesktopFirst' -Value 1 }
if($disableListDesktopAppsFirst) { Set-ItemProperty -Path $startPageKey -Name 'DesktopFirst' -Value 0 }
}
if(Test-Path -Path $accentKey) {
if($EnableDesktopBackgroundOnStart) { Set-ItemProperty -Path $accentKey -Name 'MotionAccentId_v1.00' -Value 219 }
if($DisableDesktopBackgroundOnStart) { Set-ItemProperty -Path $accentKey -Name 'MotionAccentId_v1.00' -Value 221 }
}
}
function Get-CurrentUser {
<#
.SYNOPSIS
Returns the domain and username of the currently logged in user.
#>
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$parts = $identity.Name -split "\\"
return @{Domain=$parts[0];Name=$parts[1]}
}
function Restart-Explorer {
try{
Write-Host "Restarting the Windows Explorer process..." -ForegroundColor Cyan
$user = Get-CurrentUser
try { $explorer = Get-Process -Name explorer -ErrorAction stop -IncludeUserName }
catch {$global:error.RemoveAt(0)}
if($explorer -ne $null) {
$explorer | ? { $_.UserName -eq "$($user.Domain)\$($user.Name)"} | Stop-Process -Force -ErrorAction Stop | Out-Null
}
Start-Sleep 1
if(!(Get-Process -Name explorer -ErrorAction SilentlyContinue)) {
$global:error.RemoveAt(0)
start-Process -FilePath explorer
}
} catch {$global:error.RemoveAt(0)}
}
function Set-WindowsExplorerOptions {
<#
.SYNOPSIS
Sets options on the Windows Explorer shell
.PARAMETER EnableShowHiddenFilesFoldersDrives
If this flag is set, hidden files will be shown in Windows Explorer
.PARAMETER DisableShowHiddenFilesFoldersDrives
Disables the showing on hidden files in Windows Explorer, see EnableShowHiddenFilesFoldersDrives
.PARAMETER EnableShowProtectedOSFiles
If this flag is set, hidden Operating System files will be shown in Windows Explorer
.PARAMETER DisableShowProtectedOSFiles
Disables the showing of hidden Operating System Files in Windows Explorer, see EnableShowProtectedOSFiles
.PARAMETER EnableShowFileExtensions
Setting this switch will cause Windows Explorer to include the file extension in file names
.PARAMETER DisableShowFileExtensions
Disables the showing of file extension in file names, see EnableShowFileExtensions
.PARAMETER EnableShowFullPathInTitleBar
Setting this switch will cause Windows Explorer to show the full folder path in the Title Bar
.PARAMETER DisableShowFullPathInTitleBar
Disables the showing of the full path in Windows Explorer Title Bar, see EnableShowFullPathInTitleBar
.LINK
http://boxstarter.org
#>
[CmdletBinding()]
param(
[switch]$EnableShowHiddenFilesFoldersDrives,
[switch]$DisableShowHiddenFilesFoldersDrives,
[switch]$EnableShowProtectedOSFiles,
[switch]$DisableShowProtectedOSFiles,
[switch]$EnableShowFileExtensions,
[switch]$DisableShowFileExtensions,
[switch]$EnableShowFullPathInTitleBar,
[switch]$DisableShowFullPathInTitleBar
)
$PSBoundParameters.Keys | % {
if($_-like "En*"){ $other="Dis" + $_.Substring(2)}
if($_-like "Dis*"){ $other="En" + $_.Substring(3)}
if($PSBoundParameters[$_] -and $PSBoundParameters[$other]) {
throw new-Object -TypeName ArgumentException "You may not set both $_ and $other. You can only set one."
}
}
$key = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer'
$advancedKey = "$key\Advanced"
$cabinetStateKey = "$key\CabinetState"
Write-Host "Setting Windows Explorer options..." -ForegroundColor Cyan
if(Test-Path -Path $advancedKey) {
if($EnableShowHiddenFilesFoldersDrives) {Set-ItemProperty $advancedKey Hidden 1}
if($DisableShowHiddenFilesFoldersDrives) {Set-ItemProperty $advancedKey Hidden 0}
if($EnableShowFileExtensions) {Set-ItemProperty $advancedKey HideFileExt 0}
if($DisableShowFileExtensions) {Set-ItemProperty $advancedKey HideFileExt 1}
if($EnableShowProtectedOSFiles) {Set-ItemProperty $advancedKey ShowSuperHidden 1}
if($DisableShowProtectedOSFiles) {Set-ItemProperty $advancedKey ShowSuperHidden 0}
Restart-Explorer
}
if(Test-Path -Path $cabinetStateKey) {
if($EnableShowFullPathInTitleBar) {Set-ItemProperty $cabinetStateKey FullPath 1}
if($DisableShowFullPathInTitleBar) {Set-ItemProperty $cabinetStateKey FullPath 0}
Restart-Explorer
}
}
function Get-IsMicrosoftUpdateEnabled {
<#
.SYNOPSIS
Returns $True if Microsoft Update is currently enabled
.LINK
http://boxstarter.org
#>
# Default response to false, unless proven otherwise
$installed = $false
$serviceManager = New-Object -ComObject Microsoft.Update.ServiceManager -Strict
$serviceManager.ClientApplicationID = "Sequencer"
foreach ($service in $serviceManager.Services) {
if( $service.Name -eq "Microsoft Update") {
$installed = $true;
break;
}
}
return $installed
}
function Enable-MicrosoftUpdate {
<#
.SYNOPSIS
Turns on Microsoft Update, so additional updates for other Microsoft products, installed on the system, will be included when running Windows Update.
.LINK
http://boxstarter.org
Disable-MicrsoftUpdate
#>
if(!(Get-IsMicrosoftUpdateEnabled)) {
Write-Host "Microsoft Update is currently disabled." -ForegroundColor Yellow
Write-Host "Enabling Microsoft Update..." -ForegroundColor Cyan
$serviceManager = New-Object -ComObject Microsoft.Update.ServiceManager -Strict
$serviceManager.ClientApplicationID = "Sequencer"
$serviceManager.AddService2("7971f918-a847-4430-9279-4a52d1efe18d",7,"")
}
else {
Write-Host "Microsoft Update is already enabled, no action will be taken." -ForegroundColor Green
}
}
function Start-TimedSection {
<#
.SYNOPSIS
Begins a timed section
.DESCRIPTION
A timed section is a portion of script that is timed. Used
with Stop-TimedSection, the beginning and end of the section
are logged to both the console and the log along with the
amount of time elapsed.
The function returns a guid that is used to identify the
section when stopping it.
.PARAMETER SectionName
The Title or Label of the section being timed. This string
is used in the logging to identify the section.
.PARAMETER Verbose
Instructs Start-TimedSection to write to the Verbose stream. Although
this will always log messages to the Boxstarter log, it will only log
to the console if the session's VerbosePreference is set to capture
the Verbose stream or the -Verbose switch was set when calling
Install-BoxstarterPackage.
.EXAMPLE
$session=Start-TimedSection "My First Section"
Stop-TimedSection $session
This creates a block as follows:
+ Boxstarter starting My First Section
Some stuff happens here.
+ Boxstarter finished My First Section 00:00:00.2074282
.EXAMPLE
Timed Sections can be nested or staggered. You can have
multiple sections running at once.
$session=Start-TimedSection "My First Section"
$innerSession=Start-TimedSection "My Inner Section"
Stop-TimedSection $innerSession
Stop-TimedSection $session
This creates a block as follows:
+ Boxstarter starting My First Section
Some stuff happens here.
++ Boxstarter starting My Inner Section
Some inner stuff happens here.
++ Boxstarter finished My Inner Section 00:00:00.1074282
Some more stuff happens here.
+ Boxstarter finished My First Section 00:00:00.2074282
Note that the number of '+' chars indicate nesting level.
.EXAMPLE
$session=Start-TimedSection "My First Section" -Verbose
Stop-TimedSection $session
This will write the start and finish messages to the
Boxstarter log but will not write to the console unless the
user has the the VerbosePreference variable or used the
Verbose switch of Install-BoxstarterPackage.
.NOTES
If the SuppressLogging setting of the $Boxstarter variable is true,
logging messages will be suppressed and not sent to the console or the
log.
.LINK
http://boxstarter.org
Stop-TimedSection
about_boxstarter_logging
#>
param(
[string]$sectionName,
[switch]$Verbose)
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
$guid = [guid]::NewGuid().ToString()
$timerEntry=@{title=$sectionName;stopwatch=$stopwatch;verbose=$Verbose}
if(!$script:boxstarterTimers) {$script:boxstarterTimers=@{}}
$boxstarterTimers.$guid=$timerEntry
$padCars="".PadLeft($boxstarterTimers.Count,"+")
Write-host "$sectionName" -ForegroundColor Cyan
return $guid
}
function Stop-TimedSection {
<#
.SYNOPSIS
Ends a timed section
.DESCRIPTION
A timed section is a portion of script that is timed. Used
with Start-TimedSection, the beginning and end of the section
are logged to both the console and the log along with the
amount of time elapsed.
.PARAMETER SectionId
The guid that was generated by Start-TimedSection and
identifies which section is ending.
.EXAMPLE
$session=Start-TimedSection "My First Section"
Stop-TimedSection $session
This creates a block as follows:
+ Boxstarter starting My First Section
Some stuff happens here.
+ Boxstarter finished My First Section 00:00:00.2074282
.EXAMPLE
Timed Sections can be nested or staggered. You can have
multiple sections running at once.
$session=Start-TimedSection "My First Section"
$innerSession=Start-TimedSection "My Inner Section"
Stop-TimedSection $innerSession
Stop-TimedSection $session
This creates a block as follows:
+ Boxstarter starting My First Section
Some stuff happens here.
++ Boxstarter starting My Inner Section
Some inner stuff happens here.
++ Boxstarter finished My Inner Section 00:00:00.1074282
Some more stuff happens here.
+ Boxstarter finished My First Section 00:00:00.2074282
Note that the number of '+' chars indicate nesting level.
.LINK
http://boxstarter.org
Start-TimedSection
about_boxstarter_logging
#>
param([string]$SectionId)
$timerEntry=$script:boxstarterTimers.$SectionId
if(!$timerEntry){return}
$padCars="".PadLeft($boxstarterTimers.Count,"+")
$script:boxstarterTimers.Remove($SectionId)
$stopwatch = $timerEntry.stopwatch
Write-Host "Finished $($timerEntry.Title) $($stopwatch.Elapsed.ToString())" -ForegroundColor Cyan
$stopwatch.Stop()
}
function Install-WindowsUpdate {
<#
.SYNOPSIS
Downloads and installs updates via Windows Update
.DESCRIPTION
This uses the windows update service to search, download and install updates. By default, only critical updates are included and a reboot will be induced if required.
.PARAMETER GetUpdatesFromMS
If this switch is set, the default windows update server, if any, is bypassed and windows update requests go to the public Microsoft Windows update service.
.PARAMETER AcceptEula
If any update requires a Eula acceptance, setting this switch will accept the Eula and allow the update to be installed.
.PARAMETER SuppressReboots
Setting this switch will suppress a reboot in the event that any update requires one.
.PARAMETER Criteria
The criteria used for searching updates. The default criteria is "IsHidden=0 and IsInstalled=0 and Type='Software'" which is effectively just critical updates.
.LINK
http://boxstarter.org
#>
param(
[switch]$getUpdatesFromMS,
[switch]$acceptEula,
[switch]$SuppressReboots,
[string]$criteria="IsHidden=0 and IsInstalled=0 and Type='Software' and BrowseOnly=0"
)
<#
if(Get-IsRemote){
Invoke-FromTask @"
Import-Module $($boxstarter.BaseDir)\boxstarter.WinConfig\Boxstarter.Winconfig.psd1
Install-WindowsUpdate -GetUpdatesFromMS:`$$GetUpdatesFromMS -AcceptEula:`$$AcceptEula -SuppressReboots -Criteria "$Criteria"
"@ -IdleTimeout 0 -TotalTimeout 0
if(Test-PendingReboot){
Invoke-Reboot
}
return
}
#>
try{
$searchSession=Start-TimedSection "Checking for updates..."
$updateSession =new-object -comobject "Microsoft.Update.Session"
$Downloader =$updateSession.CreateUpdateDownloader()
$Installer =$updateSession.CreateUpdateInstaller()
$Searcher =$updatesession.CreateUpdateSearcher()
if($getUpdatesFromMS) {
$Searcher.ServerSelection = 2 #2 is the Const for the Windows Update server
}
$wus=Get-WmiObject -Class Win32_Service -Filter "Name='wuauserv'"
$origStatus=$wus.State
$origStartupType=$wus.StartMode
Write-warning "Update service is in the $origStatus state and its startup type is $origStartupType." -verbose
if($origStartupType -eq "Auto"){
$origStartupType = "Automatic"
}
if($origStatus -eq "Stopped"){
if($origStartupType -eq "Disabled"){
Set-Service wuauserv -StartupType Automatic
}
Write-Host "Starting windows update service..." -ForegroundColor Cyan
Start-Service -Name wuauserv
}
else {
# Restart in case updates are running in the background
Write-Host "Restarting windows update service..." -ForegroundColor Cyan
Restart-Service -Name wuauserv -Force -WarningAction SilentlyContinue
}
$Result = $Searcher.Search($criteria)
Stop-TimedSection $searchSession
$totalUpdates = $Result.updates.count
If ($totalUpdates -ne 0)
{
Write-Host "$($Result.updates.count) Updates found." -ForegroundColor Magenta
$currentCount = 0
foreach($update in $result.updates) {
++$currentCount
if(!($update.EulaAccepted)){
if($acceptEula) {
$update.AcceptEula()
}
else {
Write-host " * $($update.title) has a user agreement that must be accepted. Call Install-WindowsUpdate with the -AcceptEula parameter to accept all user agreements. This update will be ignored." -ForegroundColor Yellow
continue
}
}
$Result= $null
if ($update.isDownloaded -eq "true" -and ($update.InstallationBehavior.CanRequestUserInput -eq $false )) {
Write-Host " * $($update.title) already downloaded" -ForegroundColor Green
$result = install-Update $update $currentCount $totalUpdates
}
elseif($update.InstallationBehavior.CanRequestUserInput -eq $true) {
Write-Host " * $($update.title) Requires user input and will not be downloaded." -ForegroundColor Yellow
}
else {
Download-Update $update
$result = Install-Update $update $currentCount $totalUpdates
}
}
if($result -ne $null -and $result.rebootRequired) {
if($SuppressReboots) {
Write-Host "A Restart is Required." -ForegroundColor Yellow
} else {
$Rebooting=$true
Write-Host "Restart Required. Restarting now..." -ForegroundColor Yellow
Stop-TimedSection $installSession
if(test-path function:\Invoke-Reboot) {
return Invoke-Reboot
} else {
Restart-Computer -force
}
}
}
}
else{Write-Host "There is no update applicable to this machine." -ForegroundColor Yellow}
}
catch {
Write-Host "There were problems installing updates: $($_.ToString())." -ForegroundColor Red
throw
}
finally {
if($origAUVal){
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate\AU -Name UseWuServer -Value $origAUVal -ErrorAction SilentlyContinue
}
if($origStatus -eq "Stopped")
{
Write-Host "Stopping win update service and setting its startup type to $origStartupType." -ForegroundColor Yellow
Set-Service wuauserv -StartupType $origStartupType
stop-service wuauserv -WarningAction SilentlyContinue
}
}
}
function Download-Update($update) {
$downloadSession=Start-TimedSection "Download of $($update.Title)"
$updates= new-Object -com "Microsoft.Update.UpdateColl"
$updates.Add($update) | out-null
$Downloader.Updates = $updates
$Downloader.Download() | Out-Null
Stop-TimedSection $downloadSession
}
function Install-Update($update, $currentCount, $totalUpdates) {
$installSession=Start-TimedSection "Install $currentCount of $totalUpdates updates: $($update.Title)"
$updates= new-Object -com "Microsoft.Update.UpdateColl"
$updates.Add($update) | out-null
$Installer.updates = $Updates
try { $result = $Installer.Install() } catch {
if(!($SuppressReboots)){
Restart-Computer -force
}
# Check for WU_E_INSTALL_NOT_ALLOWED
if($_.Exception.HResult -eq -2146233087) {
Write-Host "There is either an update in progress or there is a pending reboot blocking the install." -ForegroundColor Yellow
$global:error.RemoveAt(0)
}
else { throw }
}
Stop-TimedSection $installSession
return $result
}
if ([string]::IsNullOrEmpty($PSScriptRoot)) { $PSScriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition }
$Wow6432Node = "" ; if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64") { $Wow6432Node = "Wow6432Node" }
try
{
Clear-Host
Write-Host "Installing App-V pre-reqs..." -ForegroundColor Magenta
if (([environment]::OSVersion.Version.Major -eq 10))
{
if (-not (Test-Path 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5'))
{
Write-Host "Win10 detected, let's only enable NetFx3...." -ForegroundColor Yellow
try
{
DISM /Online /Enable-Feature /FeatureName:NetFx3 /All /LimitAccess /Source:$PSScriptRoot
Write-host ".Net3.5 installed." -ForegroundColor Green
}
catch
{
Write-Warning ".Net3.5 failed to install."
}
} else {write-host "NetFx3 allready installed." -ForegroundColor Green}
}
else
{
if (-not (Test-Path 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5'))
{
try
{
$NetFx3 = (Join-Path $PSScriptRoot "dotnetfx35.exe")
if(-not (Test-Path $NetFx3))
{
Write-Host ".Net3.5 source not found, let's download..." -ForegroundColor Yellow
Download-File -url "http://download.microsoft.com/download/6/0/f/60fc5854-3cb8-4892-b6db-bd4f42510f28/dotnetfx35.exe" -localFile $NetFx3
}
Write-Host "Installing .Net3.5 ..." -ForegroundColor Cyan
Start-Process -FilePath `"$NetFx3`" -ArgumentList "/q /norestart" -Wait | Out-Null
Write-host ".Net3.5 installed." -ForegroundColor Green
}
catch
{
Write-Warning ".Net3.5 failed to install."
}
} else {write-host ".Net3.5 allready installed." -ForegroundColor Green}
if (-not (Test-Path 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\SKUs\.NETFramework,Version=v4.6'))
{
try
{
$NetFx4 = (Join-Path $PSScriptRoot "NDP46-KB3045557-x86-x64-AllOS-ENU.exe")
if(-not (Test-Path $NetFx4))
{
Write-Host ".Net4.6 source not found, let's download..." -ForegroundColor Yellow
Download-File -url "https://download.microsoft.com/download/C/3/A/C3A5200B-D33C-47E9-9D70-2F7C65DAAD94/NDP46-KB3045557-x86-x64-AllOS-ENU.exe" -localFile $NetFx4
}
Write-Host "Installing .Net4.6..." -ForegroundColor Cyan
Start-Process -FilePath `"$NetFx4`" -Wait | Out-Null
Write-host ".Net4.6 installed." -ForegroundColor Green
}
catch
{
Write-Warning ".Net4.6 failed to install."
}
} else {write-host ".Net4.6 allready installed." -ForegroundColor Green}
if ($host.version.Major -le 3)
{
try
{
If ($env:PROCESSOR_ARCHITECTURE -eq "AMD64")
{
$WMF4 = (Join-Path $PSScriptRoot "Windows6.1-KB2819745-x64-MultiPkg.msu")
if(-not (Test-Path $WMF4))
{
Write-Host ".WMF4x64 source not found, let's download..." -ForegroundColor Yellow
Download-File -url "https://download.microsoft.com/download/3/D/6/3D61D262-8549-4769-A660-230B67E15B25/Windows6.1-KB2819745-x64-MultiPkg.msu" -localFile $WMF4
}
}
else
{
$WMF4 = (Join-Path $PSScriptRoot "Windows6.1-KB2819745-x86-MultiPkg.msu")
if(-not (Test-Path $WMF4))
{
Write-Host ".WMF4x86 source not found, let's download..." -ForegroundColor Yellow
Download-File -url "https://download.microsoft.com/download/3/D/6/3D61D262-8549-4769-A660-230B67E15B25/Windows6.1-KB2819745-x86-MultiPkg.msu" -localFile $WMF4
}
}
Write-Host "Installing WMF4 ..." -ForegroundColor Cyan
Start-Process -FilePath wusa.exe -ArgumentList `"$WMF4`", "/quiet", "/norestart" -Wait | Out-Null
Write-host "WMF4 installed." -ForegroundColor Green
}
catch
{
Write-Warning "WMF4 failed to install."
}
} else {write-host "WMF4 or higher allready installed." -ForegroundColor Green}
}
Write-Host "Disabling automatic Windows Updates..." -ForegroundColor Cyan
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" -Name AUOptions -PropertyType DWord -Value 1 -Force | Out-Null
if (([environment]::OSVersion.Version.Major -eq 6 -and [environment]::OSVersion.Version.Minor -gt 1) -or ([environment]::OSVersion.Version.Major -gt 6))
{
Write-Host "Disabling automatic Windows Store Updates..." -ForegroundColor Cyan
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsStore\WindowsUpdate" -Name AutoDownload -PropertyType DWord -Value 2 -Force | Out-Null
}
if (Test-Connection -ComputerName 2001:4860:4860::8888 -Count 3 -ErrorAction SilentlyContinue) # ping google for internet
{
Enable-MicrosoftUpdate
try {Install-WindowsUpdate -Criteria "IsHidden=0 and IsInstalled=0 and Type='Software'" }
catch { Install-WindowsUpdate -Criteria "IsHidden=0 and IsInstalled=0 and Type='Software'" }
}
Set-Service wuauserv -StartupType Disabled
Write-Host "Disabling Security Center..." -ForegroundColor Cyan
Set-Service -Name wscsvc -StartupType Disabled
if ([environment]::OSVersion.Version.Major -lt 10)
{
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer" -Name HideSCAHealth -PropertyType DWord -Value 1 -Force | Out-Null
}
else
{
if (!(Test-Path -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer")) { New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer" | Out-Null }
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer" -Name DisableNotificationCenter -PropertyType DWord -Value 1 -Force | Out-Null
}
Write-Host "Disabling System Restore..." -ForegroundColor Cyan
Disable-ComputerRestore -Drive "C:\"
Start-Process -FilePath vssadmin -ArgumentList "delete shadows /for=c: /all /quiet" -Wait | Out-Null
Write-Host "Peforming SSD optimisations..." -ForegroundColor Cyan
Set-Service -Name SysMain -StartupType Disabled
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\PrefetchParameters" -Name EnableSuperfetch -PropertyType DWord -Value 0 -Force | Out-Null
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\PrefetchParameters" -Name EnablePrefetcher -PropertyType DWord -Value 0 -Force | Out-Null
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\WMI\Autologger\ReadyBoot" -Name Start -PropertyType DWord -Value 0 -Force | Out-Null
Write-Host "Disabling computer password change..." -ForegroundColor Cyan
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters" -Name DisablePasswordChange -PropertyType DWord -Value 1 -Force | Out-Null
Write-Host "Configuring power options..." -ForegroundColor Cyan
powercfg -change -monitor-timeout-ac 0
powercfg -change -standby-timeout-ac 0
powercfg -h off
Write-Host "Enabling Remote Desktop..." -ForegroundColor Cyan
$oTS = Get-WmiObject -Class "Win32_TerminalServiceSetting" -Namespace root\cimv2\terminalservices
if($oTS -eq $null) { Write-host "Unable to locate terminalservices namespace. Remote Desktop is not enabled." -ForegroundColor Red }
try { $oTS.SetAllowTsConnections(1,1) | out-null }
catch { Write-host "There was a problem enabling remote desktop. Make sure your operating system supports remote desktop and there is no group policy preventing you from enabling it." -ForegroundColor Red }
Write-Host "Configuring Windows Explorer options..." -ForegroundColor Cyan
Set-WindowsExplorerOptions -EnableShowHiddenFilesFoldersDrives -EnableShowFileExtensions
If ([environment]::OSVersion.Version.Major -eq 6 -and [environment]::OSVersion.Version.Minor -gt 1) {
Set-StartScreenOptions -EnableBootToDesktop -EnableDesktopBackgroundOnStart -EnableShowStartOnActiveScreen -EnableShowAppsViewOnStartScreen -EnableSearchEverywhereInAppsView -EnableListDesktopAppsFirst
}
Write-Host "Pinning taskbar shortcuts..." -ForegroundColor Cyan
Install-PinnedTaskBarItem "${env:ProgramFiles(x86)}\Internet Explorer\iexplore.exe"
Install-PinnedTaskBarItem "$env:windir\System32\cmd.exe"
Install-PinnedTaskBarItem "$env:windir\System32\WindowsPowerShell\v1.0\powershell.exe"
Install-PinnedTaskBarItem "$env:windir\regedit.exe"
Install-PinnedTaskBarItem "$env:windir\Notepad.exe"
Install-PinnedTaskBarItem "$env:windir\System32\SnippingTool.exe"
Write-Host "Configuring Internet Explorer..." -ForegroundColor Cyan
if(Test-Path "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}")
{
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}" -Name "IsInstalled" -Value 0
}
if(Test-Path "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}")
{
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}" -Name "IsInstalled" -Value 0
}
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Internet Explorer\Main" -Name DisableFirstRunCustomize -PropertyType DWord -Value 1 -Force | Out-Null
New-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Internet Explorer\Main" -Name "Start Page" -PropertyType String -Value "about:blank" -Force | Out-Null
Restart-Explorer
Write-host "IE Enhanced Security Configuration (ESC) has been disabled." -ForegroundColor Green
if (Get-Process "OneDrive" -ErrorAction SilentlyContinue)
{
Write-Host "Removing OneDrive..." -ForegroundColor Cyan
Start-Process -FilePath "taskkill.exe" -ArgumentList "/f /im OneDrive.exe" -Wait | Out-Null
Start-Process -FilePath "C:\Windows\SysWOW64\OneDriveSetup.exe" -ArgumentList "/uninstall" -Wait | Out-Null
Set-ItemProperty -Path "HKCU:\Software\classes\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}" -Name "System.IsPinnedToNameSpaceTree" -Value 0 -Force | Out-Null
}
Write-Host "Setting up App-V Sequencer build..." -ForegroundColor Magenta
Write-Host "Disabling UAC..." -ForegroundColor Cyan
New-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\policies\system" -Name EnableLUA -PropertyType DWord -Value 0 -Force | Out-Null
Write-Host "Disabling Windows Defender..." -ForegroundColor Cyan
if ([environment]::OSVersion.Version.Major -eq 6 -and [environment]::OSVersion.Version.Minor -lt 2)
{
Set-Service -Name WinDefend -StartupType Disabled
}
else
{
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender" -Name "DisableAntiSpyware" -PropertyType dword -Value 1 -Force | Out-Null
}
if (Get-Service CCmExec -ErrorAction SilentlyContinue)
{
Write-Host "Disabling SMS agent host..." -ForegroundColor Cyan
Set-Service -Name WSearch -StartupType Disabled
}
Write-Host "Disabling Windows Search..." -ForegroundColor Cyan
Set-Service -Name WSearch -StartupType Disabled
Write-Host "Disabling FontCache..." -ForegroundColor Cyan
Set-Service -Name FontCache3.0.0.0 -StartupType Disabled
Write-Host "Disabling Sheduled Tasks..." -ForegroundColor Cyan
schtasks /change /tn "\Microsoft\Windows\Application Experience\AitAgent" /disable | Out-Null
schtasks /change /tn "\Microsoft\Windows\Application Experience\ProgramDataUpdater" /disable | Out-Null
schtasks /change /tn "\Microsoft\Windows\AutoChk\Proxy" /disable | Out-Null
schtasks /change /tn "\Microsoft\Windows\Customer Experience Improvement Program\Consolidator" /disable | Out-Null
schtasks /change /tn "\Microsoft\Windows\Customer Experience Improvement Program\KernelCeipTask" /disable | Out-Null
schtasks /change /tn "\Microsoft\Windows\Customer Experience Improvement Program\UsbCeip" /disable | Out-Null
schtasks /change /tn "\Microsoft\Windows\Defrag\ScheduledDefrag" /disable | Out-Null
schtasks /change /tn "\Microsoft\Windows\Diagnosis\Scheduled" /disable | Out-Null
schtasks /change /tn "\Microsoft\Windows\DiskDiagnostic\Microsoft-Windows-DiskDiagnosticDataCollector" /disable | Out-Null
schtasks /change /tn "\Microsoft\Windows\DiskDiagnostic\Microsoft-Windows-DiskDiagnosticResolver" /disable | Out-Null
schtasks /change /tn "\Microsoft\Windows\Maintenance\WinSAT" /disable | Out-Null
schtasks /change /tn "\Microsoft\Windows\Power Efficiency Diagnostics\AnalyzeSystem" /disable | Out-Null
schtasks /change /tn "\Microsoft\Windows\RAC\RacTask" /disable | Out-Null
schtasks /change /tn "\Microsoft\Windows\Registry\RegIdleBackup" /disable | Out-Null
schtasks /change /tn "\Microsoft\Windows\Windows Error Reporting\QueueReporting" /disable | Out-Null
schtasks /change /tn "\Microsoft\Windows\Windows Media Sharing\UpdateLibrary" /disable | Out-Null
Write-Host "Creating dummy ODBC keys..." -ForegroundColor Cyan
New-Item -Path "HKCU:\SOFTWARE\ODBC\ODBC.INI\ODBC Data Sources" -Force | Out-Null
New-Item -Path "HKLM:\SOFTWARE\ODBC\ODBC.INI\ODBC Data Sources" -Force | Out-Null
if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64") { New-Item -Path "HKLM:\SOFTWARE\Wow6432Node\ODBC\ODBC.INI\ODBC Data Sources" -Force | Out-Null }
if (-not ($SeqVersion = (Get-command "C:\Program Files\Microsoft Application Virtualization\Sequencer\Sequencer.exe" -ErrorAction SilentlyContinue).FileVersionInfo.ProductVersion))
{
try
{
$Sequencer = (Join-Path $PSScriptRoot "appv_sequencer_setup.exe")
if(-not (Test-Path $Sequencer))
{
Write-Host "Sequencer source not found, please download and run the script again..." -ForegroundColor Red
}
else
{
Write-Host "Installing sequencer..." -ForegroundColor Cyan
Start-Process -FilePath `"$Sequencer`" -ArgumentList "/ACCEPTEULA /q" -Wait | Out-Null
Write-host "Sequencer installed." -ForegroundColor Green
Install-PinnedTaskBarItem "$env:ProgramFiles\Microsoft Application Virtualization\Sequencer\Sequencer.exe"
}
}
catch
{
Write-Warning "Sequencer failed to install."
}
} else {write-host "Sequencer $SeqVersion allready installed." -ForegroundColor Green}
if (-not (Test-Path "HKLM:\SOFTWARE\$Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{A79B1F5E-FEC1-4DD7-86F3-7EB2ED1D28F2}"))
{
try
{
$ACE = (Join-Path $PSScriptRoot "ACE.msi")
if(-not (Test-Path $ACE))
{
Write-Host "ACE source not found, let's download..." -ForegroundColor Yellow
Download-File -url "http://virtualengine.co.uk/?wpdmdl=3800" -localFile $ACE
}
Write-Host "Installing ACE ..." -ForegroundColor Cyan
Start-Process -FilePath msiexec.exe -ArgumentList /i, `"$ACE`", RUNAPPLICATION=0, AI_QUICKLAUNCH_SH=1, AI_STARTMENU_SH=0, AI_STARTUP_SH=0, /qn -Wait | Out-Null
Install-PinnedTaskBarItem "${env:ProgramFiles(x86)}\Virtual Engine\ACE\Ace.exe"
Write-host "ACE installed." -ForegroundColor Green
}
catch
{
Write-Warning "ACE failed to install."
}
} else {write-host "ACE allready installed." -ForegroundColor Green}
Write-Host "Add 'Copy as Path' Shellhandler..." -ForegroundColor Cyan
Set-Location -LiteralPath "HKLM:\SOFTWARE\Classes\*\shell" | Out-Null
New-Item . -Name "Copy as Path" -Force | Out-Null
Set-Location -LiteralPath "HKLM:\SOFTWARE\Classes\*\shell\Copy as Path\" | Out-Null
New-Item . -Name "command" -Value 'cmd.exe /c echo \"%1\"|clip' -Force | Out-Null
New-Item -Path "HKLM:\SOFTWARE\Classes\Directory\shell" -Name "Copy as Path" -Force | Out-Null
New-Item -Path "HKLM:\SOFTWARE\Classes\Directory\shell\Copy as Path" -Name "command" -Value 'cmd.exe /c echo \"%1\"|clip' -Force | Out-Null
Set-Location $PSScriptRoot
Write-Host "Optimising .NET assemblies..." -ForegroundColor Cyan
Start-Process -FilePath "C:\Windows\Microsoft.NET\Framework\v4.0.30319\ngen.exe" -ArgumentList "executeQueuedItems /nologo /silent" -Wait
if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64") { Start-Process -FilePath "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\ngen.exe" -ArgumentList "executeQueuedItems /nologo /silent" -Wait }
Write-Host "Performing disk clean up..." -ForegroundColor Cyan
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Recycle Bin" -Name "StateFlags0001" -PropertyType dword -Value 2 -Force | Out-Null
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Update Cleanup" -Name "StateFlags0001" -PropertyType dword -Value 2 -Force | Out-Null
Start-Process cleanmgr.exe -ArgumentList "/sagerun:1" -Wait
Remove-Item $env:temp\* -Force -Recurse
Remove-Item C:\Windows\Temp\* -Force -Recurse
#Zero unused sectors to allow VHD to be compacted efficiently with Optimize-VHD cmdlet
$env:SEE_MASK_NOZONECHECKS = 1
if (Test-Path (Join-Path $PSScriptRoot "sdelete.exe"))
{
Start-Process -FilePath (Join-Path $PSScriptRoot "sdelete.exe") -ArgumentList "-AcceptEula -s -z c:" -Wait
}
elseif (Test-Path "\\live.sysinternals.com\tools\sdelete.exe")
{
Start-Process -FilePath "\\live.sysinternals.com\tools\sdelete.exe" -ArgumentList "-AcceptEula -s -z c:" -Wait
}
else
{
Write-Warning "Unable to run sysinternals sdelete.exe..."
}
}
catch
{
Write-Warning $($_.Exception.Message)
throw
}
do
{
$choice = Read-Host -Prompt "Reboot now? [y/n]"
If ($choice -eq "y") { Write-Host "restarting now..." -ForegroundColor Green;Restart-Computer -Force}
If ($choice -eq "n") { Write-Host "Reboot first, and start your Sequence." -ForegroundColor Green}
}
until ($choice)