You are browsing the archive for App-V 5.0.

MMS 2015 unplugged: Unable to publish application globally if targeted user-based within Configmgr workaround

2:55 pm in App-V, App-V 5.0, Application Model, applications, AppV, ConfigMgr 2012, configmgr 2012 R2, ConfigMgr 2012 R2 SP1, ConfigMgr 2012 SP1, ConfigMgr SP2, ConfigMgr, sccm, sccm 2012 R2, SCCM 2012 R2, SCCM 2012 R2 SP1, SCCM 2012 SP1, SCCM v.Next by Kenny Buntinx [MVP]


If you have been to our session called App-V standalone compared to CM12 Integrated: The Good, The Bad and The Ugly at MMS 2015 , we have showed you that some things by default cannot be done in Configmgr .

We showed a strong business case around the Application model in CM12 SP1 and using App-V 5.0 to do user-based software targeting. As most people are doing App-V integration in Configuration Manager and exploring the possibilities , they ran into some challenges I believe are critical and needs to be solved in a certain way . What the correct way is , I leave that up to the smart engineering guy’s in Redmond .

One of the great promises of application virtualization is dynamic delivery of software to end-users; however delivering plug-ins or add-ons to installed (i.e. not virtualized) software has thus far been a stumbling block. Internet Explorer has been particularly challenging due to the inability to separate the browser from the OS in a supported manner. So using App-V to deploy plug-ins like Flash or Java has meant changing the user experience with virtualization or falling back to standard install methods. Since App-V 5.0 SP2 this is very good news though, with the ability to seamlessly run an installed application inside a specified virtual environment. This means that the Flash plug-in can be delivered as a virtual package and made available to Internet Explorer without resorting to hacks or changing the user experience by providing a special shortcut.

The only requirement for specific Virtual Extensions (like the flash add-in) is that the package needs to be published Globally… only it doesn’t work great when deploying all your virtualized apps to users with System Center Configuration Manager and App-V 5.x. The table below will explain in what cases you will have to use Global publishing.


We can overcome that hurdle with a sort of workaround that we are not going to explain in absolute detail as every customer has specific needs. See the steps below as a guide to think outside the box.

Workaround :

1. We are going to create a scheduled task which triggers on a eventID action 1003 from the eventlog “Microsoft-AppV-Client/Operational”

The script to create the scheduled task :

param( [Parameter(ParameterSetName='Register')] [switch]$Register, [Parameter(ParameterSetName='UnRegister')] [switch]$UnRegister ) switch($PsCmdlet.ParameterSetName){ "Register"{ $Xml = Get-Content (Join-Path $PSScriptRoot "ScheduledTaskTemplate_Publish.xml") $hReplace = @{ _Date_=(Get-Date -Format s) _Author_="Publish_User2Global_1.0_EN" _WorkingDirectory_=$PSScriptRoot } foreach($key in $hReplace.Keys) { $Xml = $Xml -creplace $key, $hReplace.$key } Register-ScheduledTask -Xml ($Xml | Out-String) -TaskName "1_user_Publish_User2Global" -TaskPath "Microsoft\AppV\Publishing" -Force | Out-Null $Xml = Get-Content (Join-Path $PSScriptRoot "ScheduledTaskTemplate_UnPublish.xml") $hReplace = @{ _Date_=(Get-Date -Format s) _Author_="UnPublish_User2Global_1.0_EN" _WorkingDirectory_=$PSScriptRoot } foreach($key in $hReplace.Keys) { $Xml = $Xml -creplace $key, $hReplace.$key } #Register-ScheduledTask -Xml ($Xml | Out-String) -TaskName "1_user_UnPublish_User2Global" -TaskPath "Microsoft\AppV\Publishing" -Force | Out-Null } "UnRegister"{ Get-ScheduledTask -TaskPath "\Microsoft\AppV\Publishing\" -TaskName "1_user_Publish_User2Global" | Unregister-ScheduledTask -Confirm:$False | Out-Null #Get-ScheduledTask -TaskPath "\Microsoft\AppV\Publishing\" -TaskName "1_user_UnPublish_User2Global" | Unregister-ScheduledTask -Confirm:$False | Out-Null } }

The script is based on the following templates :

<?xml version="1.0" encoding="UTF-16"?> <Task version="1.4" xmlns=""> <RegistrationInfo> <Date>_Date_</Date> <Author>_Author_</Author> <URI>\Microsoft\AppV\Publishing\1_user_Publish_User2Global</URI> </RegistrationInfo> <Triggers> <EventTrigger> <Enabled>true</Enabled> <Subscription>&lt;QueryList&gt;&lt;Query Id="0" Path="Microsoft-AppV-Client/Operational"&gt;&lt;Select Path="Microsoft-AppV-Client/Operational"&gt;*[System[Provider[@Name='Microsoft-AppV-Client'] and EventID=1003]]&lt;/Select&gt;&lt;/Query&gt;&lt;/QueryList&gt;</Subscription> <ValueQueries> <Value name="ThePackageId">Event/EventData/Data[@Name='Package']</Value> <Value name="TheVersionId">Event/EventData/Data[@Name='Version']</Value> <Value name="UserSid">Event/System/Security/@UserID</Value> </ValueQueries> </EventTrigger> </Triggers> <Principals> <Principal id="Author"> <GroupId>S-1-5-18</GroupId> <RunLevel>LeastPrivilege</RunLevel> </Principal> </Principals> <Settings> <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy> <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries> <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries> <AllowHardTerminate>true</AllowHardTerminate> <StartWhenAvailable>false</StartWhenAvailable> <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable> <IdleSettings> <StopOnIdleEnd>true</StopOnIdleEnd> <RestartOnIdle>false</RestartOnIdle> </IdleSettings> <AllowStartOnDemand>false</AllowStartOnDemand> <Enabled>true</Enabled> <Hidden>true</Hidden> <RunOnlyIfIdle>false</RunOnlyIfIdle> <DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession> <UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine> <WakeToRun>false</WakeToRun> <ExecutionTimeLimit>P3D</ExecutionTimeLimit> <Priority>7</Priority> <RestartOnFailure> <Interval>PT1M</Interval> <Count>3</Count> </RestartOnFailure> </Settings> <Actions Context="Author"> <Exec> <Command>powershell.exe</Command> <Arguments>-NonInteractive -ExecutionPolicy RemoteSigned -WindowStyle Hidden -File User2Global.ps1 -Publish -PackageId $(ThePackageId) -VersionId $(TheVersionId) -UserSid $(UserSid)</Arguments> <WorkingDirectory>_WorkingDirectory_</WorkingDirectory> </Exec> </Actions> </Task>

2. When the scheduled task is triggered by event-ID action 1003 from the eventlog “Microsoft-AppV-Client/Operational” , we kick-off the following Powershell script (see below). It will unpublished the  package from the user and will publish the package globally instead.

param( [Parameter(ParameterSetName='Publish')] [switch]$Publish, [Parameter(ParameterSetName='UnPublish')] [switch]$UnPublish, [guid]$PackageId, [guid]$VersionId, [string]$UserSid ) Function New-BurntToastNotification{ <# This function will show a BurnToastNotification #> [CmdletBinding(SupportsShouldProcess = $True)] Param ( [Parameter(Mandatory=$True)] $Text, [Parameter(Mandatory=$True)] $Title ) # create toast template TO xml [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $null $toastXml = ([Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastImageAndText02)).GetXml() # message to show on toast $stringElements = $toastXml.GetElementsByTagName("text") | select -First 1 $stringElements.AppendChild($toastXml.CreateTextNode($Title)) > $null $stringElements = $toastXml.GetElementsByTagName("text") | select -Last 1 $stringElements.AppendChild($toastXml.CreateTextNode($Text)) > $null # image $imageElements = $toastXml.GetElementsByTagName("image") $imageElements[0].src = "file:///" + "$PSScriptRoot\appv.png" # convert from System.Xml.XmlDocument to Windows.Data.Xml.Dom.XmlDocument $windowsXml = New-Object Windows.Data.Xml.Dom.XmlDocument $windowsXml.LoadXml($toastXml.OuterXml) # send toast notification $toast = New-Object Windows.UI.Notifications.ToastNotification ($windowsXml) [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("App-V").Show($toast) } Import-Module "$env:ProgramFiles\Microsoft Application Virtualization\Client\AppvClient\AppvClient.psd1" $package = Get-AppVClientPackage -PackageId $PackageId -VersionId $VersionId -All switch($PsCmdlet.ParameterSetName) { "Publish" { try { Unpublish-AppvClientPackage $package -UserSID $UserSid if (! $package.IsPublishedGlobally) { Publish-AppVClientPackage $package -Global New-BurntToastNotification -Text "$($`nSuccesfully Published Globally." -Title "App-V User2Global" } } catch { New-BurntToastNotification -Text "Something went wrong while Publishing: `n$($" -Title "App-V User2Global" } } "UnPublish" { try { if (($package = Get-AppVClientPackage -PackageId $PackageId -VersionId $VersionId -All).IsPublishedGlobally) { $package | stop-AppVClientPackage -Global -ErrorAction SilentlyContinue | Unpublish-AppvClientPackage -Global New-BurntToastNotification -Text "$($`nSuccesfully UnPublished Globally." -Title "App-V User2Global" } } catch { New-BurntToastNotification -Text "Something went wrong while unpublishing: `n$($" -Title "App-V User2Global" } } }

You can choose how to deploy the script. You can create an App-V bubble or simply deploy this with Configmgr or GPO ….

Disclaimer : The script are delivered AS-IS and are not the complete solution to this story. It is an example on how to think outside the box and make a potential solution that will fit your specific company issue.

Hope it Helps ,

Kenny Buntinx & Roy Essers .

MMS 2015 unplugged : Preparing your App-V sequencer the right “automated” way

1:18 pm in App-V, App-V 5.0, applications, AppV, powershell, sequencing by Kenny Buntinx [MVP]


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.

You will find the script below and we provide it AS-IS with no support :

# 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:\\\' .EXAMPLE .\Download-File.ps1 'http:\\\' 'C:\Temp\' .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 #> [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 #> [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 #> # 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 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 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 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 #> 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 "" -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 "" -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 "" -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 "" -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 "" -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 "\\\tools\sdelete.exe") { Start-Process -FilePath "\\\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)

Hope it Helps ,

Kenny Buntinx & Roy Essers

App-V 5.0 standalone / CM12 Sp1 and Citrix integration overview – White Paper published!

5:03 pm in App-V, App-V 5.0, AppV, CM12, ConfigMgr 2012, ConfigMgr 2012 SP1, SCCM 2012, SCCM 2012 SP1 by Kenny Buntinx [MVP]


Hi Everyone,

Microsoft has just published a new App-V 5.0 and Citrix integration overview white paper. This paper will help you in your customer conversations, giving you a deeper understanding of the integration between the MS App-V standlone or integrated with System Center Configuration Manger 2012 SP1 and the Citrix solution. We strongly recommend you take the time to review.

This whitepaper is designed to provide administrators with guidance for combining Microsoft’s App-V 5.0 and Citrix solutions. It discusses the benefits of an App-V 5.0 and Citrix combined solution, and includes recommendations for Citrix images, App-V cache management, App-V management with Citrix, and other factors that impact user experience and administrative effort.

Whitepaper Title:  “App-V 5.0 and Citrix Integration overview” and can be downloaded here

Hope it Helps ,

Kenny Buntinx

MVP Enterprise Client Management

App-V 5.0 SP2 Beta enhancements and download links

9:40 am in App-V, App-V 5.0, AppV, windows 8, windows 8.1 by Kenny Buntinx [MVP]


This morning at Teched Europe they announced the beta availability of two Microsoft Desktop Optimization Pack products that will support the Windows 8.1 preview, User Experience Virtualization (UE-V) 2.0 and Application Virtualization (App-V) 5.0 SP2.

For those of your who are using App-V 4.6 we are also making available today a public beta of App-V 4.6 SP3 which will also support Windows 8.1

More details on Microsoft Application Virtualization (App-V) 5.0 SP2 features and improvements below :

UE-V is not the only critical component of our virtualization story, we would now like to discuss a second component, Microsoft Application Virtualization (App-V). App-V enables anywhere access to virtualized applications on any authorized PC without application installs. Microsoft’s has a long history of providing flexible application virtualization with powerful management, and making the virtual application behave as if it was locally installed. App-V 5.0 SP2 builds on these solutions through the following new features:

  1. Shell extension support, enriching the user experience.
  2. Sequencing enhancements, simplifying the virtual application creation process.

Let’s now go into some more detail on these new features in App-V.

Shell extension support

By building on App-V 5.0 investments which delivered you the ability to virtualize highly integrated applications, App-V 5.0 SP2 is providing support for a frequently requested extension point into the operating system, shell extensions. For example if you virtualize a compression program or a backup program that integrated with the Windows shell, you will now see the contextual options that these apps have like send to zip or backup to archive. Essentially this enables the virtual application to behave more like if it was installed locally on your device.

Sequencing enhancements

Powerful and flexible management is a key component of Microsoft’s application virtualization solution, allowing IT to deploy, track and service virtual applications. Continuing the approach, we have made improvements to the sequencer enabling it to automatically detect with the option to include, run time dependencies like MSXML and Visual C++ libraries in the App-V package during the sequencing process. Other additions in App-V 5.0 SP2 beta include general user experience improvements during the application publishing and refresh process, including a new publishing progress bar, to improve the overall experience of deploying a set of applications to a user.

Read the full story here :

Hope it Helps ,

Kenny Buntinx

Citrix Provisioning Services and Microsoft System Center Configuration Manager 2012 SP1

12:54 pm in App-V, App-V 5.0, ConfigMgr 2012, ConfigMgr 2012 SP1, ConfigMgr, MP, PVS, SCCM 2012 SP1, XEN, Xenapp, Xendestop, XENSERVER by Kenny Buntinx [MVP]


Hi ,

Today colleague Frank Vandenbergh at my customer responsible for Citrix was fighting to get there Citrix Provisioning services up and running with clients registering into System center Configuration Manager 2012 SP1. He has written already a blog post about it here , but I want to share some background info with you and dig a little deeper.

We had tried this in ConfigMgr 2007 out of the box without great succes resulting in a blog post :”ConfigMgr on xendesktop with the usage of provisioning server : Unique GUID issue and the smscfg.ini

A little background :

Managing Virtual Desktops Created with PVS


Citrix Provisioning Services allows for multiple servers to stream their boot disk from the same master image (vDisk). During the boot process, PVS will make sure each server has a unique SID and dynamically apply the computername together with some other tasks to make those systems unique.

If you tried installing the SCCM client on a PVS image, you will notice that SCCM shows new machines with the same name every time a PVS target device reboots in standard mode. This is because the SCCM client changes the GUID when an image is pushed to new hardware. ConfigMgr uses the GUID to keep track inside his database.

ConfigMgr uses an ID that is generated on the Client to identify a machine inside the ConfigMgr hierarchy. This ID, also known as SMS GUID is generated during ConfigMgr Client installation.
An Algorithm, which combines the Timestamp (Time of ConfigMgr Client Installation) and the Universally Unique Identifier (UUID) is used to generate a unique Identifier.
A Client generates a new SMS GUID if the following things change

  • the SMBIOS serial number
  • the Machine SID
  • the Hardware ID (see appendix)

When VM is provisioned for the first time, the client will create a new GUID to register with server. If this client was discovered earlier by AD system discovery it can merge based on machine SID if previous history is present in SCCM site server DB. If server finds a match for this GUID in system_disc with exact AD machine account, the GUID and resource ID assigned will be same. If server finds a match based on AD machine SID in SID0, server will assign the GUID associated with the AD machine account and resource ID assigned will be the record that is the AD machine account. If it cannot find previous history using either of these methods, the server will assign the client passed in GUID and a new resource ID will be assigned.

Once new GUID is established for the VM, it will retain the same GUID for that VM based on machine SID as well as identity information restored locally on client from VDI persistent store. From the logs the client starts with GUID:78F1CF6F-814B-4E44-A2AE-729FB2C4F725 for reregistration that was established earlier and if cert changes it will associate the new cert with the GUID in re-registration. For any identity changes, client will retain the same GUID and same resource ID.

If resource ID changes (ItemKey in system_disc), it means there is no match in previous history for either client GUID or AD machine SID or client cert.

The heartbeat DDR will be sent when VM is provisioned for the first time i.e. when new ID is created. Thereafter it will depend on heartbeat discovery schedule set in the policy. Once DDR is sent by the client and gets processed by MP and site server, you should see all attributes in Admin console.

To ensure any desktops created with Provisioning Services operate correctly with ConfigMgr 2012, you must set the write cache to the target device’s hard drive. Using the Provisioning Services Console, in vDisk Properties, select Cache on device hard drive as the Cache Type. If you do not configure the cache this way, data required by ConfigMgr 2012 is not persisted when the desktops are restarted, which may result in unexpected behavior such as duplicate GUID’s or invalid inventory etc.

Unique Machine IDs for Shared Image Desktops

Virtual desktops built on the shared image provisioning solutions provided by XenDesktop (Provisioning Services and Machine Creation Services) presented a bit of a challenge for Configuration Manager 2007. 

With XenDesktop 5.6 and Configuration Manager 2012 / SP1 that problem is now history.  When you create your master image with either MCS or PVS simply install the SCCM agent and forget about it.  When you create cloned/streamed machines from that master image, the SCCM agent will automatically generate and store machine IDs that persist for the life of the VM.  Your virtual desktops will register and behave exactly like their physical counterparts.  One record per machine and that machine will continue to use the same ID across reboots.  This capability will also be available for XenApp servers streamed with Provisioning Services 6.1.

However it is not that simple …

Step 1 : Extending ConfigMgr Inventory for XenDesktop

XenDesktop makes available to ConfigMgr 2012 so that virtual desktops can be managed using this tool. The properties are available for the Citrix_virtualDesktopInfo class in the Root\Citrix\DesktopInformation namespace. See official info here :

The following properties are available. Property names are those used in the Windows Management Instrumentation (WMI) provider:

  • BrokerSiteName – The name of your XenDesktop site; returns the same value as HostIdentifier
  • DesktopCatalogName – The name of the catalog associated with the desktop
  • DesktopGroupName – The name of the desktop group associated with the desktop
  • HostIdentifier – The name of your XenDesktop site; returns the same value as BrokerSiteName
  • IsAssigned – False for a pooled-random desktop, otherwise true
  • IsVirtualMachine – True for a virtual machine, false for a physical machine
  • OSChangesPersist – False if the desktop operating system image is reset to a clean state every time it is restarted, otherwise true
  • PersistentDataLocation – The location where Configuration Manager stores persistent data. This is not accessible to users.
  • PersonalvDiskDriveLetter – For a desktop with a personal vDisk, the drive letter you assign to the personal vDisk

The properties BrokerSiteName, DesktopCatalogName, DesktopGroupName, and HostIdentifier are determined when the desktop registers with the controller, so they are null for a desktop that has not fully registered.

You can display the properties using the hardware inventory in Configuration Manager or using attributes of Configuration Manager objects. When you do, the names may include spaces or vary slightly in other ways.

On how to extend the HW inventory , Marius Sandbu has written an excellent acticle about that here :

Step 2 : Create your Master VM with care !


Keep in mind that your Master VM is a fully configured and running VM. You allow SCCM to install the client as normal and so the SCCM server is aware of the machine so I guess you can say the reverse it true as well. You do this install before you do create the catalog from the VM image of course. From there it should just work.

  • Install the Configuration Manager client software on the golden image as part of your automated Configmgr Task Sequence
  • Stop the SMS Agent Host service (CCMExec.exe) on the reference computer (net stop ccmexec).
  • Delete the C:\Windows\SMSCFG.INI file
  • Delete the current certificates in the "SMS" certificate store. ( open an MMC.exe)
  • Change the provisioning image from private to standard.
  • Stream the vdisk to target computers.

If you do not remove the certificates , you will get into the following problem that registration of the client will not succeed successfully. What will happen is :

1. System booting up in “private mode”, the master image. Hostname is TEST1.

2. Same disk is now booted in standard “readonly” mode. Hostname is TEST1. SCCM is correctly getting the persistent disk location from WMI . SCCM restores everything from CCMCFG.BAK, except the correct SMBIOS value. It reports SID unchanged, HWID unchanged, SMBIOS changed (it is still reading the SMBIOS value from the ‘master’ device in the SMSCFG.INI file in the default location.)

3. ClientIDManagerStartup reports: Detected hardware identity change, generating new certificates.The Client is re-registering with the SCCM server.

4. The SCCM service seems to be restarted following the registration witch is normal .The ccmexec service restart is expected even on non-VDI systems if any of the policy configuration require service to be restarted. The heartbeat DDR will be sent when VM is provisioned for the first time i.e. when new ID is created.

5. In the SCCM console the record is recreated and as a result we loose the software metering information / direct collection memberships. We did the test with removing the SMSCFG.ini file in the “master” disk. On next startup SCCM is reading everything correctly from the CCMCFG.BAK and reports “SMBIOS unchanged”.

6.The computer object is not recreated, but we have a feeling the client is still not registered correctly because the console is not updating its last hardbeat time etc.  .This is due the faulty SMS Client certificates being stuck in the “Master Images” . Remove the Certificates as said before and you’ll be fine .


Hope it Helps ,

Kenny Buntinx

How to create an application for deploying the App-V 5.0 Client with Configmgr 2012

9:09 pm in App-V, App-V 5.0, Application Model, AppV, CM12, ConfigMgr 2012 SP1, Deployment, deployment types, detection methods, Operating System Deployment, OSD, sccm, SCCM 2012 SP1, vbscript by Kenny Buntinx [MVP]


In this blog post we will show you how to create the application for deploying the App-V 5.0 client which is part of the MDOP ( Microsoft Desktop Optimization Pack )suite.

App-V 5.0 client is supported on the following platforms (As you can see , there is NO XP support) :


First we need to have a look on the exact prerequisites needed to run the App-V 5.0 client . You will find that kind of information here :

However the information on what version you need on the Microsoft Visual C++ is vague. We will clarify that below :

  1. Microsoft Windows .NET Framework 4 (Full Package) (
  2. Windows PowerShell 3.0 (
  3. Download and install KB2533623 (
  4. The Microsoft Visual C++ 2010 SP1 x64 Redistributable –> v10.0.40219 (


  1. The Microsoft Visual C++ 2005 SP1 x86 Redistributable –> v8.0.61001


Now we need to create for each component an application . Lets start :

Windows .NET Framework 4 (Full Package)




Full installation code : dotNetFx40_Full_x86_x64.exe /q /norestart /ChainingPackage ADMINDEPLOYMENT


Create 2 detection methods as shown below


Detection Method 1 : {8E34682C-8118-31F1-BC4C-98CD9675E1C2}


Detection Method 2 : {F5B09CFD-F0B2-36AF-8DF4-1DF6B63FC7B4}

Windows PowerShell 3.0


Full installation code : c:\windows\System32\wusa.exe "Windows6.1-KB2506143-x64.msu" /quiet /norestart


Use a script as detection method :


Full detection script :

KB2533623 –> Apply hotfix in your base image or use CBS updates with offline updates

Microsoft Visual C++ 2010 SP1 –> Use download link specified above and use MSI detection methods

Microsoft Visual C++ 2005 SP1 –> Use download link specified above and use MSI detection methods

App-V 5.0 Client itself

When done , we going to create the App-V 5.0 Client Application . Deploy one of the following Windows Installer files to the target computer. The Windows Installer file you specify must match the configuration of the target computer.

  • If the target computer is running a 32-bit Microsoft Windows operating system, deploy the appv_client_MSI_x86.msi.
  • If the target computer is running a 64-bit Microsoft Windows operating system, deploy the appv_client_MSI_x64.msi.
  • If you are deploying the App-V 5.0 Remote Desktop Services client, deploy the appv_client_rds_MSI_x64.msi.


Create your application.


Create 2 deployment types : x64 and x86


For the X64 deployment type


Use the regular msi install parameters


Use the regular msi detection method


Now you must create dependencies . Make sure you select AUTO Install !


First create the Visual C++ 2005 SP1 x86 and select the deployment type you created earlier .


Then create a WMF 3.0 one and select the deployment type you created earlier .


Then create the Visual C++ 2010 SP1 x86 and select the deployment type you created earlier .


Then as the last one , create the Visual C++ 2010 SP1 x64 and select the deployment type you created earlier .



Your al set , your App-V 5.0 install is ready to be used .

Hope it Helps ,

Kenny Buntinx

How to sequence the Microsoft System Center 2012 Configuration Manager Admin Console with Microsoft App-V

6:28 am in App-V, App-V 5.0, AppV, ConfigMgr, ConfigMgr 2012, ConfigMgr 2012 SP1, SCCM 2012, SCCM 2012 SP1 by Kenny Buntinx [MVP]

Did you ever felt like sequencing your Configmgr 2012 console with App-V ? Somaning Turwale , a Support Engineer from Management and Security Division has written these steps on how to do this :


1. Follow the best practices for the Microsoft Application Virtualization Sequencer:

2. Build your clean sequencing system with the 64-bit version of Windows 7 Service Pack 1 and join it to the domain. 

3. Install .NET 4 full version on the Sequencer machine (

4. Copy the Tools folder from your ConfigMgr 2012 central site server to the sequencing machine and place it in the following path:

C:\Program files\Microsoft Configuration Manager\Tools

NOTE This is used for sequencing ConfigMgr 2012 Version 5.00.7711.0000 and build 7711

5. Install the App-V 4.6 SP1 Sequencer with Hotfix 8 (

6. Launch the Microsoft Application Virtualization Sequencer and select Create New Virtual Application Package.

7. Select the Create Package (Default) option and click Next.  Note that Prepare the Computer for Creating a Virtual Package may give the Warning “Windows Defender is active”.  If so, launch the Services.msc snap-in and stop the Windows Defender service.  After doing so, click Refresh and then click Next.

7. Select the Standard Application (default) option and click Next.

8. On the Select Installer screen, browse to the Tools folder and select ConsoleSetup.exe, then click Next.

9. Name the package ConfigMgr2012Console, leave other options as the defaults and click Next.

10. Once the ConfigMgr installation screen appears, click Install Configuration Manager 2012 and click Next.

11. Type the FQDN of the Central Site server name and click Next.

12. Leave the Destination Folder as the default (e.g. C:\Program Files(x86)\Microsoft Configuration Manager Console\) and click Next.

13. Click Install and wait for the installation to complete, then click Finish.

14. Now the ConfigMgr 2012 console will launch. Verify that everything functions properly and exit from the console.

15. Select the I am finished installing option and click Next.

16. Select the ConfigMgr console and run it. After launching the console close it.  Click Next to review the installation report and click Next when you’re done.

17. Select the Customize option and click Next.

18. If you would like to remove the "Remote Control View" then right-click and remove it.

19. Click Next and then Run All. Once the console launches completely, verify the settings and exit the console.

20. Select the Target OS, click Next and then select Create the Package. The package will be saved as ConfigMgr2012Console.

21. Copy the package to the App-V management content share.

22. Follow the normal procedure to import the package into your App-V Management server.

23. Verify that the console is published to the App-V users.

24. Ensure that the .NET 4 full version is installed on the App-V clients as it is a prerequisite for console.

25. Make sure that the App-V clients have the latest hotfix installed (e.g.

26. Ensure that you have the required permissions and connectivity to the Site server.

If all steps are followed correctly then the client should be able to launch the Configuration Manager console successfully.

For the full article on how to sequence the console , please visit

Hope it Helps ,

Kenny Buntinx

Great Blog on App-V 5.0 : Sequencing for Connection Groups

6:34 am in App-V, App-V 5.0, AppV, ConfigMgr, ConfigMgr 2012, ConfigMgr 2012 SP1, SCCM 2012, SCCM 2012 SP1 by Kenny Buntinx [MVP]


Hi ,

If you are searching more info on how connection groups are working in App-V 5.0 , read on :

Connection Group is comprised of two or more individual packages. Which means those packages are Sequenced individually, they are edited individually, and they are upgraded individually. This poses a challenge as how to ensure that individually sequenced packages would work together seamlessly when put together in a Connection Group, and when they don’t work seamlessly together how and what to troubleshoot. This blog touches on these challenges and provides a guide to troubleshooting issues that can be taken care of while sequencing these packages.

In Configmgr 2012 we call a connection group “ Virtual Environments” where we “connect” multiple App-V 5.0 Bubble’s to each other .

If you want to read more , please go to this excellent blog post written by Naveed Ahmad, Senior SDE at

Hope it helps ,

Kenny Buntinx