RSS

Monthly Archives: December 2014

Ending in style and looking back to 2014…

Today is the last day of the year 2014, I ended this year in style by passing exam 70-533: Implementing Microsoft Azure Infrastructure Solutions. I have to admit, this one is hard but I got it and nobody will take that one away from me J

Sitting at my laptop looking back at 2014, it was quite a tremendous year for me. I changed employer and I started to notice that I needed to expand my knowledge. I also receive my MCT certification making me eligible to provide training courses. 2014 is what I consider a breakthrough year for cloud computing. Since I’m a Microsoft minded consultant I’m talking about Azure of course…

But I see quite a split in what technology is available for organizations and what organizations are able or willing to use. 2014 marked the end for Windows XP and many organizations who still use(d) Windows XP are still in the process of migrating to a new Windows Client. At some of my customers I notice some reluctance to directly jump to Windows 8.1 which excludes some cloud related functionality completely. So I spent most my time working on projects which required my skills on Configuration Manager…

Hopefully this will change in 2015…

 

 

 

ConfigMgr 2012: Completing a workflow to add, distribute and deploy applications…

In one of my previous posts I wrote about using a PowerShell cmdlet to detect applications. This allows me to create a unified script for a complete workflow to add, distribute and deploy applications. Since I treat everything as a scripted installation it doesn’t matter anymore what input is provided to create the application.

After some digging and research I used the following approach:

  • The application’s specific parameters are imported from a .csv file
  • I define a set of ‘fixed’ variables in the script itself, these variables are the same for each application
  • I add the PowerShell detection script as part of the script too. While this may be not the most elegant solutions (especially when the detection script is signed), this allows me to create a foundation to work on later

Once the information is in, I use the following steps for what I consider a complete workflow:

  • Create an Application
  • Create a Deployment Type
  • Distribute the Application
  • Deploy the Application to a User Collection

NOTE: In my example I used the All Users collection, Since I already have something to create User Collections as well I might merge that part with this one.

The most difficult part was having the PowerShell script for detection put in correctly. I used the following post as a reference that helped me out:

http://www.dexterposh.com/2014/04/powershell-sccm-2012-r2-deploy.html

For the sake of this post, I used Adobe Reader XI as a reference. Here’s a sample .csv file I used for running this script:

ApplicationName,DeploymentTypeName,InstallationProgram,ContentLocation,UninstallProgram

Adobe Reader XI,Adobe Reader XI,msiexec /i AdbeRdr11000_en_US.msi /qn,\\cm01\Source\Apps\Adobe Reader XI,msiexec /x{AC76BA86-7AD7-1033-7B44-AB0000000001} /qn

 

After some extensive trying, failing and testing, I was able to get to the following script that allowed me to reach my goal:

 

#

# Script name: Add_Application.ps1

#

# Purpose: Creates an application, distributes it and deploys it to a user collection

#

# Author: Marc Westerink

#

# Reference: http://technet.microsoft.com/library/jj821860(v=sc.20).aspx

# http://technet.microsoft.com/library/jj870953(v=sc.20).aspx

# http://technet.microsoft.com/library/jj850100(v=sc.20).aspx

# http://technet.microsoft.com/library/jj821911(v=sc.20).aspx

#

#

#Create the required ‘global’ variables

$ConfigMgrModulePath=“D:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1”

$ConfigMgrSiteCode=“PS1:”

#Connecting to site

Import-Module $ConfigMgrModulePath

Set-Location $ConfigMgrSiteCode

#’Fixed’ variables

$InstallationBehaviorType=“InstallForSystem”

$LogonRequirementType=“WhereOrNotUserLoggedOn”

$ScriptType=“Powershell”

$DistributionPointGroupName=“All DPs”

$CollectionName=“All Users”

$DeployAction=“Install”

$DeployPurpose=“Required”

$App = Import-CSV D:\App.csv

$ScriptContent = @’
$DetectKey=Get-Item HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*

$ProductID =”{AC76BA86-7AD7-1033-7B44-AB0000000001}”

$ProductCode=”HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$productID”

ForEach ($Element in $DetectKey.name)

{if ($ProductCode -contains $Element)

    {

    return $TRUE

    }

}

‘@

 New-CMApplication -Name $App.ApplicationName

Add-CMDeploymentType -ApplicationName $App.ApplicationName -ScriptInstaller -DeploymentTypeName $App.DeploymentTypeName -InstallationProgram $App.InstallationProgram -InstallationBehaviorType $InstallationBehaviorType -LogonRequirementType $LogonRequirementType -UninstallProgram $App.UninstallProgram -ContentLocation $App.ContentLocation -ManualSpecifyDeploymentType -DetectDeploymentTypeByCustomScript -ScriptType $ScriptType
-ScriptContent $ScriptContent

Start-CMContentDistribution -ApplicationName $App.ApplicationName -DistributionPointGroupName $DistributionPointGroupName

Start-CMApplicationDeployment -CollectionName $CollectionName -Name $App.ApplicationName -DeployAction $DeployAction -DeployPurpose $DeployPurpose

 

So, the next step would be finding a more elegant way of getting the detection script and having it signed. But that’s for another post…

Feel free to use it if you like. As always, try this out in a test environment

 

ConfigMgr: use PowerShell to detect applications…

Recently, in one of my projects I’m heavily involved in adding applications in a System Center 2012 R2 Configuration Manager environment. One of the biggest challenges I have is that no ‘standard’ or ‘preferred’ method is available to deploy applications. Since this customer doesn’t have Software Assurance they’re not able to use any App-V technology for their applications, so the following types of installations exist:

  • .msi
  • .msi with a transform
  • Scripted install using a .vbs or .cmd that does something which may include an .msi.

The tricky thing for me is that when I want to automate adding applications, it becomes difficult to create a uniform script that will cover all deployment types without creating a lengthy script with a lot of different options or creating multiple scripts for each type of deployment. In order to prevent that I need to redefine my approach. Basically this means that each deployment type will be configured as a scripted install. However, creating scripted installs using PowerShell has a limitation that I consider a pretty big one: only PowerShell scripts can be filled in to detect the application.

This requirement means we need to make a PowerShell script to detect the application. I’ve decided to focus on an .msi file since an .msi has something that is unique (or should be): the Product Code.

We also know that, unless it has been taken out which is a big no-no for me, a registry key with the Product Code will be written in the HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall and/or HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall hive.

Since we know Product Code if the application that needs to be installed, we can simply get all the keys from that hive and compare it with the one we want to install. If the Product Code is found we need to return the $True value. If we don’t, we do nothing.

I had some help with a friend of mine. His name is Daan Weda (his blog is available http://daanweda.wordpress.com ) and he helped me deliver a sample script that can be used for this purpose. Daan is more skilled in PowerShell than me, so I’d like to thank Daan for his effort.

Eventually, we came to this sample script that can be used to detect applications:

$DetectKey = Get-Item HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*

$ProductID = “{F8136D8B-7B8B-3FC6-BF42-EEAF643C5C4F}”

$ProductCode = “HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$productID

ForEach ($Element in $DetectKey.name)

{

if ($ProductCode -contains $Element)

              {

              return $TRUE

              }

}

 

I tested this script to deploy Google Chrome using this PowerShell script to detect Google Chrome’s presence. To test if this works a few requirements had to be met:

  • I needed to configure the Configuration Manager client to set the Execution Policy to Bypass. While this may is fine in lab environment, I don’t recommend doing this in production environments
  • I didn’t sign my PowerShell script with a Code Signing certificate. This is something for another blog post
  • I limited my testing to deploy Google Chrome to Windows 8.1 x64 to keep things simple

After configuring the Deployment Type using this script to detect the application I created a required deployment to my machine. The result was the application was detected and installed successfully, and I didn’t receive any detection errors afterwards.

I’ve decided to analyze the AppEnforce.log and this is what I got (the relevant part is bold):

<![LOG[+++ Starting Install enforcement for App DT “Google Chrome” ApplicationDeliveryType – ScopeId_61488BDA-5732-4832-BD85-C5886AE52D03/DeploymentType_e50a3a65-2a41-4bca-b2f0-a57334590f9b, Revision – 4, ContentPath – C:\Windows\ccmcache\1, Execution Context – System]LOG]!><time=”01:48:43.169+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appprovider.cpp:1702″>

<![LOG[    A user is logged on to the system.]LOG]!><time=”01:48:43.189+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appprovider.cpp:2083″>

<![LOG[    Performing detection of app deployment type Google Chrome(ScopeId_61488BDA-5732-4832-BD85-C5886AE52D03/DeploymentType_e50a3a65-2a41-4bca-b2f0-a57334590f9b, revision 4) for user.]LOG]!><time=”01:48:43.192+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appprovider.cpp:2148″>

<![LOG[+++ Application not discovered with script detection. [AppDT Id: ScopeId_61488BDA-5732-4832-BD85-C5886AE52D03/DeploymentType_e50a3a65-2a41-4bca-b2f0-a57334590f9b, Revision: 4]]LOG]!><time=”01:48:44.254+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”scripthandler.cpp:500″>

<![LOG[    App enforcement environment:

              Context: Machine

              Command line: msiexec /i “googlechromestandaloneenterprise.msi” /qn

              Allow user interaction: No

              UI mode: 1

              User token: not null

              Session Id: 2

              Content path: C:\Windows\ccmcache\1

              Working directory: ]LOG]!><time=”01:48:44.255+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appcontext.cpp:85″>

<![LOG[    Prepared working directory: C:\Windows\ccmcache\1]LOG]!><time=”01:48:44.256+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appcontext.cpp:189″>

<![LOG[Found executable file msiexec with complete path C:\Windows\system32\msiexec.exe]LOG]!><time=”01:48:44.257+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”executility.cpp:188″>

<![LOG[    Prepared command line: “C:\Windows\system32\msiexec.exe” /i “googlechromestandaloneenterprise.msi” /qn /qn]LOG]!><time=”01:48:44.260+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appcontext.cpp:338″>

<![LOG[Valid MSI Package path = C:\Windows\ccmcache\1\googlechromestandaloneenterprise.msi]LOG]!><time=”01:48:44.267+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appcontext.cpp:765″>

<![LOG[    Advertising MSI package [C:\Windows\ccmcache\1\googlechromestandaloneenterprise.msi] to the system.]LOG]!><time=”01:48:44.267+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appcommon.cpp:125″>

<![LOG[    Executing Command line: “C:\Windows\system32\msiexec.exe” /i “googlechromestandaloneenterprise.msi” /qn /qn with user context]LOG]!><time=”01:48:44.974+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appexcnlib.cpp:201″>

<![LOG[    Working directory C:\Windows\ccmcache\1]LOG]!><time=”01:48:44.974+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appexcnlib.cpp:215″>

<![LOG[    Post install behavior is BasedOnExitCode]LOG]!><time=”01:48:44.984+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appcommon.cpp:1094″>

<![LOG[    Waiting for process 1076 to finish.  Timeout = 120 minutes.]LOG]!><time=”01:48:44.987+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appexcnlib.cpp:1958″>

<![LOG[    Process 1076 terminated with exitcode: 0]LOG]!><time=”01:49:23.325+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appexcnlib.cpp:1967″>

<![LOG[    Looking for exit code 0 in exit codes table…]LOG]!><time=”01:49:23.325+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appexcnlib.cpp:505″>

<![LOG[    Matched exit code 0 to a Success entry in exit codes table.]LOG]!><time=”01:49:23.325+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appexcnlib.cpp:584″>

<![LOG[    Performing detection of app deployment type Google Chrome(ScopeId_61488BDA-5732-4832-BD85-C5886AE52D03/DeploymentType_e50a3a65-2a41-4bca-b2f0-a57334590f9b, revision 4) for user.]LOG]!><time=”01:49:23.330+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appprovider.cpp:2148″>

<![LOG[+++ Discovered application [AppDT Id: ScopeId_61488BDA-5732-4832-BD85-C5886AE52D03/DeploymentType_e50a3a65-2a41-4bca-b2f0-a57334590f9b, Revision: 4]]LOG]!><time=”01:49:23.863+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”scripthandler.cpp:491″>

<![LOG[++++++ App enforcement completed (40 seconds) for App DT “Google Chrome” [ScopeId_61488BDA-5732-4832-BD85-C5886AE52D03/DeploymentType_e50a3a65-2a41-4bca-b2f0-a57334590f9b], Revision: 4, User SID: S-1-5-21-1857953727-1504300629-3046737139-500] ++++++]LOG]!><time=”01:49:23.863+480″ date=”12-17-2014″ component=”AppEnforce” context=”” type=”1″ thread=”3308″ file=”appprovider.cpp:2448″>

 

So this works like a charm (oops one /qn too many, need to remove)…

Next step is signing the script and try again, but that is for a future post.

You can create something similar if you want to detect application presence by looking for a file.

 

Feel free to try this out yourself, as long as it’s a test environment…

 

ConfigMgr: Notes from the field regarding content source…

I’ve been designing and building ConfigMgr environments for years now. Most of these environments are built from scratch, in some scenarios existing environments were either upgraded or completely replaced with a newer version. At any situation, one of the biggest challenges is determining where to store the content source and how to manage it. The content source is also known as the Definite Software Library (DSL) or Definite Media Library (DML).

The most common scenarios I’ve seen before I get started are:

  • Stored locally on a site server, the share is presented as a standard UNC path or a DFS Namespace
  • Stored on a file server, the share is presented as a standard UNC path
  • Stored on a file server, the presented as a DFS Namespace

It may be obvious that I recommend using the 3rd scenario, I have the following reasons why I recommend using a file server for the content source and present the share name as DFS Namespace:

  • It is a separate entity that is not part of the ConfigMgr site infrastructure
  • It simplifies migration, export or upgrade scenarios
  • If the content needs to be moved to a different server, then all that needs to be done is modifying the DFS Namespace itself
  • Data deduplication is used commonly at file servers, some content (especially images) is very suitable to be deduplicated but requires some configuration
  • This share can be used for the ConfigMgr Backup Task which will take the backup files outside the ConfigMgr site infrastructure

Looking at the buildup of the content source location, I always try to keep it as ‘flat’ as possible. For a typical content source location, the following folders may be present:

  • Applications
  • Backup
  • Drivers
  • Driver Packages
  • Images
  • Packages
  • Updates

In those folders I don’t believe more depth is required to know to where the actual content belongs to. The main reason is to minimize the risk of exceeding the 260 character limit filename paths are supported to have. Everything above that amount will simply be cut off. However, we depend on software vendors as well to deliver us applications without almost ridiculously long filenames. In some of my projects I had to distribute Autodesk DWG TrueView 2015 (I believe Autodesk needs to reconsider their naming conventions). This application has some unusually long filenames for some critical .dll files and I ended up exceeding the 260 character limit when storing them. It’s not hard to imagine how much time it takes to figure that one out, especially if the setup fails with error 1603 only…

To summarize: store the content source on a file server and present the location as a DFS Namespace. Keep the folder structure as flat as possible.

 
 
Steve Thompson [MVP]

The automation specialist

Boudewijn Plomp

Cloud and related stuff...

Anything about IT

by Alex Verboon

MDTGuy.WordPress.com

Deployment Made Simple

Modern Workplace

Azure, Hybrid Identity & Enterprise Mobility + Security

Daan Weda

This WordPress.com site is all about System Center and PowerShell

IT And Management by Abheek

Microsoft certified Trainer -Abheek

Heading To The Clouds

by Marthijn van Rheenen