Deployment question - run hidden if no user logged in?

I understand that when deploying an application via this toolkit with SCCM or PDQ Deploy, it will prompt the user about closing programs or deferring.
But, if there is no user logged in, will the package continue to run/install?

Or do I have to create two packages (toolkits): one for computers with users not logged in, one for computers for users that are not?

Also if using SCCM, should I use Package or Application deployment?

If no user is logged on, then why would any app be open?

Or are you specifically looking for some other process that may be running without an actual interactive user session being active?

I mean if you really wanted to, then you could do something like this in the ‘Deploy-Application.ps1’ file (in the installation section) I suppose:

<code>If ($usersLoggedOn)
{
    Show-InstallationWelcome -CloseApps &#039;App1, App2, App3&#039; -DeferTimes &#039;3&#039;
}

Else
{
    Show-InstallationWelcome -CloseApps &#039;App1, App2, App3&#039; -Silent
}</code>

You will want to use the Application Model, not the standard package deployment. I prefer the Application Model for obvious reasons, but mainly because I use the same deployment for both users and systems. This way users can simply go to the Application Catalog and download/install the app themselves and if I NEED to deploy the app to systems for whatever reason, then I have that option as well without having to create a separate application. All I would need to do is simply create a separate deployment instead. The Application Model is MUCH better in this respect than the older standard package deployment and using PS App Deploy makes it even easier.

Then in the SCCM application in the Deployment Type section and on the User Experience tab, just configure the following:

 Installation behavior: Install for system
 Logon requirement: Whether or not a user is logged on
 Installation program visibility: Normal

You will need to ensure that the Detection Method is configured as well. If the app you want to deploy is an MSI, then that would be easier as all you would need to do is to navigate to the MSI file and then it will find the GUID automatically. Then copy that GUID (include the brackets) and paste that in the ‘Uninstallation’ section of the ‘Deploy-Application.ps1’ file like in the following example:

Execute-MSI -Action Uninstall -Path “{42E870AA-8C2E-4764-9340-31A693D2E033}”

Wow, that code block sux not showing the single quotes.

Anyway, this is what I was trying to show:

If ($usersLoggedOn)
{
Show-InstallationWelcome -CloseApps ‘App1, App2, App3’ -DeferTimes ‘3’
}

Else
{
Show-InstallationWelcome -CloseApps ‘App1, App2, App3’ -Silent
}

Ah ok, got it, yes I think that’s what I was looking for. I’ll give it some testing.
Didn’t know if I was allowed to do that. I just figured that if a user is logged in, they’d get Windows to interact with to close applications, and remember to save their work before a reboot was imposed.
Otherwise, if no one was logged in, I wasn’t sure how to force a reboot with Show-InstallationRestartPrompt

Also this package is for Internet Explorer 11 so I’m not sure a method of uninstall would be applicable. I guess since it’s an “update”.

This is my script here:

<pre class=“brush: powershell; gutter: true; first-line: 1; highlight: []; html-script: false”>

[CmdletBinding()]
Param (
[Parameter(Mandatory=$false)]
[ValidateSet('Install','Uninstall')]
[string]$DeploymentType = 'Install',
[Parameter(Mandatory=$false)]
[ValidateSet('Interactive','Silent','NonInteractive')]
[string]$DeployMode = 'Interactive',
[Parameter(Mandatory=$false)]
[switch]$AllowRebootPassThru = $false,
[Parameter(Mandatory=$false)]
[switch]$TerminalServerMode = $false,
[Parameter(Mandatory=$false)]
[switch]$DisableLogging = $false
)

Try {
## Set the script execution policy for this process
Try { Set-ExecutionPolicy -ExecutionPolicy 'ByPass' -Scope 'Process' -Force -ErrorAction 'Stop' } Catch {}

##*===============================================
##* VARIABLE DECLARATION
##*===============================================
## Variables: Application
[string]$appVendor = &#039;Microsoft&#039;
[string]$appName = &#039;Internet Explorer&#039;
[string]$appVersion = &#039;11&#039;
[string]$appArch = &#039;&#039;
[string]$appLang = &#039;EN&#039;
[string]$appRevision = &#039;01&#039;
[string]$appScriptVersion = &#039;1.0.0&#039;
[string]$appScriptDate = &#039;09/08/2016&#039;
[string]$appScriptAuthor = &#039;IT&#039;
##*===============================================
## Variables: Install Titles (Only set here to override defaults set by the toolkit)
[string]$installName = &#039;&#039;
[string]$installTitle = &#039;&#039;

##* Do not modify section below
#region DoNotModify

## Variables: Exit Code
[int32]$mainExitCode = 0

## Variables: Script
[string]$deployAppScriptFriendlyName = &#039;Deploy Application&#039;
[version]$deployAppScriptVersion = [version]&#039;3.6.8&#039;
[string]$deployAppScriptDate = &#039;02/06/2016&#039;
[hashtable]$deployAppScriptParameters = $psBoundParameters

## Variables: Environment
If (Test-Path -LiteralPath &#039;variable:HostInvocation&#039;) { $InvocationInfo = $HostInvocation } Else { $InvocationInfo = $MyInvocation }
[string]$scriptDirectory = Split-Path -Path $InvocationInfo.MyCommand.Definition -Parent

## Dot source the required App Deploy Toolkit Functions
Try {
	[string]$moduleAppDeployToolkitMain = &quot;$scriptDirectory\AppDeployToolkit\AppDeployToolkitMain.ps1&quot;
	If (-not (Test-Path -LiteralPath $moduleAppDeployToolkitMain -PathType &#039;Leaf&#039;)) { Throw &quot;Module does not exist at the specified location [$moduleAppDeployToolkitMain].&quot; }
	If ($DisableLogging) { . $moduleAppDeployToolkitMain -DisableLogging } Else { . $moduleAppDeployToolkitMain }
}
Catch {
	If ($mainExitCode -eq 0){ [int32]$mainExitCode = 60008 }
	Write-Error -Message &quot;Module [$moduleAppDeployToolkitMain] failed to load: <code>n$($_.Exception.Message)</code>n `n$($_.InvocationInfo.PositionMessage)&quot; -ErrorAction &#039;Continue&#039;
	## Exit the script, returning the exit code to SCCM
	If (Test-Path -LiteralPath &#039;variable:HostInvocation&#039;) { $script:ExitCode = $mainExitCode; Exit } Else { Exit $mainExitCode }
}

#endregion
##* Do not modify section above
##*===============================================
##* END VARIABLE DECLARATION
##*===============================================
	
If ($deploymentType -ine &#039;Uninstall&#039;) {
	##*===============================================
	##* PRE-INSTALLATION
	##*===============================================
	[string]$installPhase = &#039;Pre-Installation&#039;
	
	## Show Welcome Message, close Internet Explorer if required, allow up to 3 deferrals, verify there is enough disk space to complete the install, and persist the prompt
	Show-InstallationWelcome -CloseApps &#039;iexplore&#039; -AllowDefer -DeferTimes 3 -CheckDiskSpace -PersistPrompt
	
	## Show Progress Message (with the default message)
	Show-InstallationProgress -StatusMessage &quot;Internet Explorer 11 installation in Progress...<code>nThe installation should take fewer than 10 minutes to complete.</code>nA reboot will be required, so please save any work you have open at this time.&quot;
	
	## &lt;Perform Pre-Installation tasks here&gt;

function IsInstalled($hotfixid)
{
$ret = $false
Get-WmiObject -Query "Select * from Win32_QuickFixEngineering WHERE HotfixID = '$hotfixid'" -Namespace "root\CIMV2" | ForEach-Object {
$ret = $true
}

            return $ret
      }

##===============================================##===============================================

##===============================================
##
Prerequisite 1
##*===============================================

$update1 = "KB2834140"
If (IsInstalled($update1))
{
write-log -Message "$update1 is already installed, skipping." -source 'Isinstalled' -Logtype 'Legacy'
}
Else
{
Write-log -Message "Installing $update1…" -source 'Isinstalled' -Logtype 'Legacy'
cmd.exe /c "C:\Windows\System32\dism.exe /online /add-package /packagepath:$dirfiles\Windows6.1-KB2834140-v2-x86.cab /quiet /norestart"
}

##===============================================
##
Prerequisite #2
##*===============================================

$update2 = "KB2670838"
If (IsInstalled($update2))
{
write-log -Message "$update2 is already installed, skipping." -source 'Isinstalled' -Logtype 'Legacy'
}
Else
{
write-log -Message "Installing $update2…" -source 'Isinstalled' -Logtype 'Legacy'
cmd.exe /c "C:\Windows\System32\dism.exe /online /add-package /packagepath:$dirfiles\KB2670838_x86.CAB /quiet /norestart"
}

##===============================================
##
Prerequisite #3
##*===============================================

$update3 = "KB2639308"
If (IsInstalled($update3))
{
write-log -Message "$update2 is already installed, skipping." -source 'Isinstalled' -Logtype 'Legacy'
}
Else
{
write-log -Message "Installing $update2…" -source 'Isinstalled' -Logtype 'Legacy'
cmd.exe /c "C:\Windows\System32\dism.exe /online /add-package /packagepath:$dirfiles\KB2639308_x86.CAB /quiet /norestart"
}

##===============================================
##
Prerequisite 4
##*===============================================

$update4 = "KB2731771"
If (IsInstalled($update4))
{
write-log -Message "$update4 is already installed, skipping." -source 'Isinstalled' -Logtype 'Legacy'
}
Else
{
write-log -Message "Installing $update4…" -source 'Isinstalled' -Logtype 'Legacy'
cmd.exe /c "C:\Windows\System32\dism.exe /online /add-package /packagepath:$dirfiles\KB2731771_x86.CAB /quiet /norestart"
}

##===============================================
##
Prerequisite #5
##*===============================================

$update5 = "KB2729094"
If (IsInstalled($update5))
{
write-log -Message "$update5 is already installed, skipping." -source 'Isinstalled' -Logtype 'Legacy'
}
Else
{
write-log -Message "Installing $update5…" -source 'Isinstalled' -Logtype 'Legacy'
cmd.exe /c " C:\Windows\System32\dism.exe /online /add-package /packagepath:$dirfiles\Windows6.1-KB2729094-v2-x86.cab /quiet /norestart"
}

##===============================================
##
Prerequisite #6
##*===============================================

$update6 = "KB2786081"
If (IsInstalled($update6))
{
write-log -Message "$update6 is already installed, skipping." -source 'Isinstalled' -Logtype 'Legacy'
}
Else
{
write-log -Message "Installing $update6…" -source 'Isinstalled' -Logtype 'Legacy'
cmd.exe /c "C:\Windows\System32\dism.exe /online /add-package /packagepath:$dirfiles\KB2786081_x86.CAB /quiet /norestart"
}

##===============================================
##
Prerequisite 7
##*===============================================

$update7 = "KB2888049"
If (IsInstalled($update7))
{
write-log -Message "$update7 is already installed, skipping." -source 'Isinstalled' -Logtype 'Legacy'
}
Else
{
write-log -Message "Installing $update7…" -source 'Isinstalled' -Logtype 'Legacy'
cmd.exe /c "C:\Windows\System32\dism.exe /online /add-package /packagepath:$dirfiles\Windows6.1-KB2888049-x86.cab /quiet /norestart"
}

##===============================================
##
Prerequisite 8
##*===============================================

$update8 = "KB2882822"
If (IsInstalled($update8))
{
write-log -Message "$update8 is already installed, skipping." -source 'Isinstalled' -Logtype 'Legacy'
}
Else
{
write-log -Message "Installing $update8…" -source 'Isinstalled' -Logtype 'Legacy'
cmd.exe /c "C:\Windows\System32\dism.exe /online /add-package /packagepath:$dirfiles\Windows6.1-KB2882822-x86.cab /quiet /norestart"
}
##===============================================##===============================================

	##*===============================================
	##* INSTALLATION 
	##*===============================================
	[string]$installPhase = &#039;Installation&#039;
	
	## Handle Zero-Config MSI Installations
	If ($useDefaultMsi) {
		[hashtable]$ExecuteDefaultMSISplat =  @{ Action = &#039;Install&#039;; Path = $defaultMsiFile }; If ($defaultMstFile) { $ExecuteDefaultMSISplat.Add(&#039;Transform&#039;, $defaultMstFile) }
		Execute-MSI @ExecuteDefaultMSISplat; If ($defaultMspFiles) { $defaultMspFiles | ForEach-Object { Execute-MSI -Action &#039;Patch&#039; -Path $_ } }
	}
	
	## &lt;Perform Installation tasks here&gt;
	
write-log -Message &quot;Installing IE11&quot; -source &#039;Isinstalled&#039; -Logtype &#039;Legacy&#039;
cmd.exe /c &quot;C:\Windows\System32\dism.exe /online /add-package /packagepath:$dirfiles\IE-Win7.cab /quiet /norestart&quot;

write-log -Message &quot;Installing Spelling package&quot; -source &#039;Isinstalled&#039; -Logtype &#039;Legacy&#039;
cmd.exe /c &quot;C:\Windows\System32\dism.exe /online /add-package /packagepath:$dirfiles\Windows6.3-KB2849696-x86.cab /quiet /norestart&quot;

write-log -Message &quot;Installing Hyphenation package&quot; -source &#039;Isinstalled&#039; -Logtype &#039;Legacy&#039;
cmd.exe /c &quot;C:\Windows\System32\dism.exe /online /add-package /packagepath:$dirfiles\Windows6.3-KB2849697-x86.cab /quiet /norestart&quot;

	
	##*===============================================
	##* POST-INSTALLATION
	##*===============================================
	[string]$installPhase = &#039;Post-Installation&#039;
	
	## &lt;Perform Post-Installation tasks here&gt;

write-log -Message &quot;Installing IE11 CU Aug Update&quot; -source &#039;Isinstalled&#039; -Logtype &#039;Legacy&#039;		
cmd.exe /c &quot;C:\Windows\System32\dism.exe /online /add-package /packagepath:$dirfiles\IE11-Windows6.1-KB3175443-x64.cab /quiet /norestart&quot;


	## Display a message at the end of the install
	# If (-not $useDefaultMsi) { Show-InstallationPrompt -Message &#039;Internet Explorer 11 upgrade is complete.&#039; -ButtonRightText &#039;OK&#039; -Icon Information -NoWait }
	Show-DialogBox -Title &#039;Internet Explorer 11 installation Complete&#039; -Text &#039;Internet Explorer 11 installation has completed. Please click OK and restart your computer.&#039; -Icon &#039;Information&#039;
	Show-InstallationRestartPrompt -Countdownseconds 600 -CountdownNoHideSeconds 60
}
ElseIf ($deploymentType -ieq &#039;Uninstall&#039;)
{
	##*===============================================
	##* PRE-UNINSTALLATION
	##*===============================================
	[string]$installPhase = &#039;Pre-Uninstallation&#039;
	
	## Show Welcome Message, close Internet Explorer with a 60 second countdown before automatically closing
	Show-InstallationWelcome -CloseApps &#039;iexplore&#039; -CloseAppsCountdown 60
	
	## Show Progress Message (with the default message)
	Show-InstallationProgress
	
	## &lt;Perform Pre-Uninstallation tasks here&gt;
	
	
	##*===============================================
	##* UNINSTALLATION
	##*===============================================
	[string]$installPhase = &#039;Uninstallation&#039;
	
	## Handle Zero-Config MSI Uninstallations
	If ($useDefaultMsi) {
		[hashtable]$ExecuteDefaultMSISplat =  @{ Action = &#039;Uninstall&#039;; Path = $defaultMsiFile }; If ($defaultMstFile) { $ExecuteDefaultMSISplat.Add(&#039;Transform&#039;, $defaultMstFile) }
		Execute-MSI @ExecuteDefaultMSISplat
	}
	
	# &lt;Perform Uninstallation tasks here&gt;
	
	
	##*===============================================
	##* POST-UNINSTALLATION
	##*===============================================
	[string]$installPhase = &#039;Post-Uninstallation&#039;
	
	## &lt;Perform Post-Uninstallation tasks here&gt;
	
	
}



##*===============================================
##* END SCRIPT BODY
##*===============================================

## Call the Exit-Script function to perform final cleanup operations
Exit-Script -ExitCode $mainExitCode

}
Catch {
[int32]$mainExitCode = 60001
[string]$mainErrorMessage = "$(Resolve-Error)"
Write-Log -Message $mainErrorMessage -Severity 3 -Source $deployAppScriptFriendlyName
Show-DialogBox -Text $mainErrorMessage -Icon 'Stop'
Exit-Script -ExitCode $mainExitCode
}

I think for SCCM I will have to change “dism” command to “C:\WINDOWS\SYSNATIVE\DISM.EXE” since target computers are 64-bit Windows 7, and I understand the ConfigMgr client is 32-bit

Well you can force a reboot using the SCCM client as well in the ‘User Experience’ tab within ‘Deployment Types’. It’s located in the bottom section as ‘Should Configuration Manager enforce specific behavior regardless of the application’s intended behavior?’. You can select the ‘Configuration Manager client will force a mandatory device restart’ option from the drop down. You can do this if you ALWAYS want the deployment to restart the computer. Depending on how you have your Computer Agent restart notices configured in Client Settings, the user will receive a notice in the System Tray that a computer restart will occur. The default I believe is 90 minutes. I usually like to have SCCM restart, but that is just me.

But if you do not want SCCM to restart the system for all deployments and just let PS App Deployment Toolkit do it for you, then you could use the same method as above (I am pretty sure) - something like this:

If ($usersLoggedOn)
{
Show-InstallationRestartPrompt -Countdownseconds ‘600’ -CountdownNoHideSeconds ‘60’
}

Else
{
Show-InstallationRestartPrompt -Countdownseconds ‘0’
}

You could use the ‘Restart-Computer -Force’ as well, I believe.

Possibly the best way to do this is to use the ‘AllowRebootPassThru’ parameter with ‘Deploy-Application.exe’ or ‘Deploy-Application.ps1’ (whichever one you prefer to use) and in the SCCM console just set the ‘Should Configuration Manager enforce specific behavior regardless of the application’s intended behavior?’ drop down option as ‘Determine behavior based on return codes’. This way computer restarts will just be dependent on the restart return code (3010) and you won’t have to worry about taking care of the restarts yourself.

-AllowRebootPassThru $true | $false (default is false)
Specify whether to allow the 3010 exit code (reboot required) to be passed back to the parent process (e.g. SCCM) if detected during an installation. If a 3010 code is passed to SCCM, the SCCM client will display a reboot prompt. If set to false, the 3010 return code will be replaced by a “0” (successful, no restart required).

Hi,

The short answer is no. PSADT will identify is someone is logged on. So you just have to code consequently.

Thanks,

My SCCM Site Server is being worked on that moment, but I believe I already deployed IE11 in my org. When it is back up, I can shoot you over what I did.

Scratch that, I did not use PSADT to deploy IE11, but rather relied on Software Update Deployment instead.

Is there a reason why you are not using a software update deployment for this?

I wasn’t sure if WSUS or SCCM Update could deploy it.
Right now I use WSUS only for updates (Windows, Office) and will switch to SCCM soon because it’s complicated having all these GPOs and registry settings just to get it to work right and hopefully not interrupt and end-user.

With WSUS I deployed all the pre-requisites for IE11 over the past few months. It’s taken the environment a while to get them all, but they’re almost done.

So the reason for creating this deployment of IE11 was to first get a grasp of how PS App Deployment toolkit works. Packaging Adobe Flash for example isn’t going to give me the same learning experience as IE11.
The other reason is that I have to meet a deadline to get 50 specific computers updated to IE11 according to a vendor, and it’s become an “urgent” request. The rest of the 1,000 computers or so can wait around on WSUS I suppose.

Thank you again for your help!

Using SCCM for updates is MUCH better by far than using just WSUS, though you are still using WSUS under the hood. SCCM simply manages the WSUS environment. All you really need to do for those 50 systems is to simply put them in an AD group, then disable your current WSUS GPO on them using their AD group on the Delegation tab of that GPO (I think you would deny that policy on the group to disable it). Then create an SCCM Software Updates GPO by configuring the “Specify Intranet Microsoft Update Service Location“ setting that is located in Group Policy Management Editor, Navigate to Computer Configuration, Policies, Administrative templates, Windows Components, Windows Update. Then enter the necessary information in the fields such as the SCCM server (http://SCCMServer). All you have to do then, from what I recall (it’s been a while) is to enable that GPO to run on those 50 computers in the AD group that you created by giving them Read permissions on the Delegation tab. Then in SCCM, go to Software Library > All Software Updates and click the “Synchronize Software Updates” button at the top in the ribbon. Check your wsyncmgr.log file in your default SCCM installation directory (usually in ‘%ProgramFiles%\Microsoft Configuration Manager\Logs’), or you can check the SMS_WSUS_SYNC_MANAGER component status message. After the sync runs, then simply search for the IE11 KB in the All Software Updates section. Right click that update, download it and then create a software update group to store it. After that is done, then just create a collection to contain the 50 computers by using the AD group name. After that, then simply deploy the software update group to that collection.

That’s it!

Pretty simple stuff.

You really need to utilize the power of SCCM here. I love PSADT as much as the next person, but it really is unnecessary in this instance. SCCM will do it all for you and it will monitor the deployment as well and even provides software update reports that you can run.

I am an SCCM consultant and have done this quite a bit, so if you need any help with anything let me know.

You can reach me at: savvyprosolutionsllc@gmail.com

I don’t mind helping a brother out.

I forgot, when you download the update, you need to specify a package as well. You will then need to distribute that package out to your distribution points (DPs).

Also, my name is Ron and you can checkout my TechNet Gallery PowerShell scripts’ page here: https://gallery.technet.microsoft.com/scriptcenter/site/search?f[0].Type=User&f[0].Value=RonRatzlaff

LinkedIn: https://www.linkedin.com/in/ronratz

Anyway, if you need help, hit me up.

Have a good one man and good luck.

Awesome thanks so much.

Yeah I am still on the testing the deployment thing of user logged on vs user not logged on.

So with SCCM application, I have two deployment types: 1 run with user logged on, 1 when user is not logged on – both “install for system”.
Is that not the way I should do it?

My end goal is really to just deploy the application silently, but, if a user is logged in, let’s show them the prompt and messages because they need to proceed at will and save their work for the impending reboot to come.

For install part I’m using:

<code>$UserLoggedIn = [bool](query user)
		if ($UserLoggedIn = $true)
		{
		Show-InstallationWelcome -CloseApps &#039;iexplore=Internet Explorer&#039; -BlockExecution 
		Show-InstallationProgress -StatusMessage &quot;Internet Explorer 11 installation is in progress...&lt;code&gt;nThe installation should take fewer than 10 minutes to complete.&lt;/code&gt;nA reboot will be required, so please SAVE your documents.&quot;

 		}
		else 
		{
		Show-InstallationWelcome -CloseApps &#039;iexplore&#039; -BlockExecution -Silent
		
		}</code>

For post install I’m using:

<code>$UserLoggedIn = [bool](query user)
		if ($UserLoggedIn = $true)
		{
		Show-InstallationRestartPrompt -Countdownseconds 120 -CountdownNoHideSeconds 60
		}
		else 
		{ 
		cmd.exe /c &quot;shutdown /f /r /t 0&quot;
		}</code>

Hi,

PSADT will detect if someone is logged in or not. So don’t worry with popup and interactive. Just run the script silent and everything will be fine.

Thanks,

Ok thanks I guess I’m just confused now on the Deployment Type priority/order.
If I have DT of user logged on at priority 1, application deployment will fail saying it couldn’t detect application after install completed.
So I go log into the computer targetted and the PSADT show install welcome screen is popped up on the screen.

I guess I’m supposed to make “user not logged on” the #1 priority DT?

How do you have your detection method configured?