Quantcast
Channel: Chad Simmons – Catapult Systems
Viewing all 28 articles
Browse latest View live

ConfigMgr Content Source Path migration

$
0
0

Several ConfigMgr scenarios require that the content Source Path be changed.  This typically includes migrating to a new ConfigMgr environment (2007 to 2012, 2012 to Current Branch, etc.), and simply moving the source content to a new location such as a DFS Share or low-speed NAS device.

Updating the Source Path can be done manually via the ConfigMgr console.  For Packages, Software Update Deployment Packages, Drivers, Driver Packages, Operating System Images, Operating System Upgrade Packages, Boot Images, and Virtual Hard Disks, just add the Pkg Source Path or Package Source Path column to the console view to review the paths, then edit the object’s Source Folder in the Data Source tab.

However, for Applications, you’ll have to step through each Deployment Type on each Application, view the properties and modify the Content Location in the Content tab.

This is all painfully slow if you have more then a handful to deal with.  So, automate it!

image image

The community has developed at least 5 solutions to this including

CoreTech and Nickalaj have the slickest solutions.  Sometimes a GUI gives the visual feedback you need to be confident in the final outcome.

158_1 image

Either of these two tools should effectively handle the changes.  As a bonus, both will actually copy the content files from the old to the new path.  Awesome!

Happy migrating!


ConfigMgr and SQL – NTFS allocation unit size

$
0
0

It’s been many years since I read that SQL databases should use an NTFS volume formatted with at 64KB file allocation unit size (block size). So long that I didn’t remember why or if it is still considered best/good practice. It appears that it is according to Microsoft and the foremost authority on SQL with ConfigMgr.

Microsoft recommends this for User Databases and the TempDB: https://docs.microsoft.com/en-gb/azure/virtual-machines/virtual-machines-windows-sql-performance?toc=%2fazure%2fvirtual-machines%2fwindows%2ftoc.json

Steve Thompson [MVP]: https://stevethompsonmvp.wordpress.com/2014/07/25/sql-server-ntfs-cluster-size

Steve explains, “The reason that SQL Server prefers 64KB NTFS cluster size, this happens to correspond to the way SQL Server allocates storage. One page = 8K, and SQL Server allocates an Extent, which is 8 pages in size. 8 pages x 8KB = 64KB/extent.”

To check the Block Size per drive/volume/partition…

From PowerShell, execute

Get-WmiObject -Namespace 'root\CIMv2' -Class Win32_Volume -Filter "DriveType = 3 and Label <> 'Recovery'" | Select Name, Label, BlockSize

image

OR from a Command Prompt

fsutil fsinfo ntfsinfo s: | findstr "Bytes per"

clip_image001

OR create a small file on the drive(s) and check the file properties.  This can easily be done by…

  • open Notepad, hold any key for about 30 seconds, then save the file
  • open a Command Prompt and type 
    FOR /L %I (1,1,200) DO @echo %I>> %temp%\test.file
      (or similar)

Using Windows Explorer, right-click the test file and notice the Size and Size on Disk info.

From testing, the file had to be more than 500 bytes on a 4K block size volume to register any size on the disk.  On the 64K block size disk it took about 800 bytes to register.  You mileage may vary though.

image

In addition, if the SQL PowerShell module installed or there is some other method to query the existing SQL server, PowerShell can check if the block size on all drives where a SQL files exist or will exist if using the default file locations.

Import-Module SQLPS
$SQLDrivesInUse = @(Invoke-Sqlcmd -Query 'SELECT DISTINCT left(physical_name,3) [Drive] from master.sys.master_files')
Write-Output 'SQL Drives In Use'
$SQLDrivesInUse | Format-Table -AutoSize

$SQLDriveDefaults = @(Invoke-Sqlcmd -Query "SELECT Left(Convert(varchar(255), SERVERPROPERTY('instancedefaultdatapath')),3) [Drive] UNION SELECT Left(Convert(varchar(255), SERVERPROPERTY('instancedefaultlogpath')),3)")
Write-Output 'SQL Drive Defaults'
$SQLDriveDefaults | Format-Table -AutoSize

$DiskDrives = @(Get-WmiObject -Namespace 'root\CIMv2' -Class Win32_Volume -Filter "DriveType = 3 and Label <> 'Recovery'" | Select Name, Label, BlockSize, ExpectedBlockSize, IsOK)
ForEach ($DiskDrive in $DiskDrives) {
   $SQLDrivesInUse.Drive | ForEach-Object {
      If ($_ -eq $DiskDrive.Name) { $DiskDrive.ExpectedBlockSize = 8*8*1024 }
   }
   $SQLDriveDefaults.Drive | ForEach-Object {
      If ($_ -eq $DiskDrive.Name) { $DiskDrive.ExpectedBlockSize = 8*8*1024 }
   }
   If ($DiskDrive.ExpectedBlockSize -eq $null) {
      $DiskDrive.ExpectedBlockSize = 4*1024
   }
   If ($DiskDrive.BlockSize -eq $DiskDrive.ExpectedBlockSize) {
      $DiskDrive.IsOK = $true
   } else {
     $DiskDrive.IsOK = $false
   }
}
$DiskDrives | Select Name, Label, BlockSize, ExpectedBlockSize, IsOK | Format-Table -AutoSize

image

The many ways to import the ConfigMgr cmdlet Library module

$
0
0

Over the years I’ve seen and used a variety of PowerShell commands to get the

ConfigurationManager.psd1
  file location to pass to
Import-Module

Just for fun, I compiled a list of the different methods.  It’s all about string manipulation.

Import-Module "$env:SMS_ADMIN_UI_PATH\..\configurationmanager.psd1" # shortest code
Import-Module $env:SMS_ADMIN_UI_PATH.Replace('\bin\i386','\bin\configurationmanager.psd1') # most readable code
Import-Module $env:SMS_ADMIN_UI_PATH.Replace('i386','configurationmanager.psd1')
Import-Module $env:SMS_ADMIN_UI_PATH -replace 'i386$','configurationmanager.psd1'
Import-Module "$($env:SMS_ADMIN_UI_PATH.TrimEnd('i386'))configurationmanager.psd1"
Import-Module ((Split-Path $env:SMS_ADMIN_UI_PATH)+'\configurationmanager.psd1')
Import-Module ($Env:SMS_ADMIN_UI_PATH.Substring(0,$Env:SMS_ADMIN_UI_PATH.Length-5)+'\configurationmanager.psd1')

When using a ConfigMgr cmdlet it is necessary to work from the ConfigMgr site’s drive.  I’ve also seen a variety of ways to accomplish this as detailed below.

$SiteCode = Get-PSDrive -PSProvider CMSITE
Push-Location "$($SiteCode.Name):\"

$SiteCode = (Get-PSDrive -PSProvider CMSITE).Name
Push-Location "$($SiteCode):\"
Push-Location "$SiteCode`:\"

Push-Location "$(Get-PSDrive -PSProvider CMSITE).Name):\"

When more than one ConfigMgr site is registered on a computer, it may be necessary to more specifically identify it.  This is one example.

$SiteCode = (Get-PSDrive -PSProvider CMSITE | Where {$_.Root -eq 'ConfigMgr.contoso.com'}).Name

Lastly, note the difference between the

*-Location
  cmdlets.  Using Push/Pop-Location allows changing to the ConfigMgr site drive before running the cmdlets then returning to the previous location afterward.
Push-Location
and the alias
pushd
.
Pop-Location
  and the alias
popd
.
Set-Location
  and the aliases
cd
 ,
chdir
 , and 
sl
.

Downgrading SQL Enterprise to Standard for ConfigMgr

$
0
0

At a recent client engagement we discovered that Microsoft SQL Server Enterprise edition was installed on the ConfigMgr Primary Site Server.  Technically this is not a problem, but it is only needed if you expect to have more than 50,000 clients*.  As this environment wasn’t anywhere close to the limit so there was no need to pay the extra licensing cost of Enterprise edition (Standard edition comes with the ConfigMgr licenses).

 

These steps are largely based on the Jonathan Kehayias approach as documented by Brady Upton at MSSQLTips.

Steps for SQL Enterprise to Standard downgrade

  • On each database verify that no Enterprise features are utilized (SELECT * FROM sys.dm_db_persisted_sku_features)

select 'Master' as [Database], * from [master].[sys].[dm_db_persisted_sku_features]
select 'Model' as [Database], * from Model.[sys].[dm_db_persisted_sku_features]
select 'msdb' as [Database], * from msdb.[sys].[dm_db_persisted_sku_features]
select 'tempdb' as [Database], * from tempdb.[sys].[dm_db_persisted_sku_features]
select 'CM_P01' as [Database], * from CM_P01.[sys].[dm_db_persisted_sku_features]
select 'SUSDB' as [Database], * from SUSDB.[sys].[dm_db_persisted_sku_features]
select 'ReportServer' as [Database], * from ReportServer.[sys].[dm_db_persisted_sku_features]
select 'ReportServerTempDB' as [Database], * from ReportServerTempDB.[sys].[dm_db_persisted_sku_features]

  • Document databases, security, maintenance plans, and jobs
  • Verify the SQL version number and ensure install files are available (SELECT @@VERSION)
  • Stop and disable backup software
  • Stop ConfigMgr, IIS, and Windows Update services (set to disabled if desired)
  • Backup databases (system and user)
  • Stop SQL services
  • Copy the master, model and msdb database files (.mdf and .ldf) to another location
  • Uninstall SQL Enterprise instance (all features)
    • The Shared tools do not have to be uninstalled; however, if they are not then reporting the SQL edition in the future will be confusing
  • Reboot
  • Install new SQL Standard instance as required by ConfigMgr being sure to keep the same instance name and file/folder paths.
    • Review the Required and Optional configurations for SQL server (64-bit, SQL_Latin1_General_CP1_CI_AS, Database Engine, Windows Authentication, min/max Memory, nested triggers, CLR integration, static TCP ports, etc.)
    • If the original SQL ConfigurationFile.ini is still around, installing based on this file can make all of the configurations fool proof.
  • Patch SQL to the same version as before
  • Verify the SQL version and edition (SELECT @@VERSION)
  • Stop SQL Server and copy/restore the system databases
  • Configure Trace flags (see section below)
  • Start SQL server and verify databases, security, and jobs are as before
    • If login fails, use PSEXEC to start SQL Management Studio as the SYSTEM account, then recreate any SQL Logins needed
  • Enable common language runtime (CLR) integration (sp_configure ‘clr enabled’,1; reconfigure)
  • Enable and start IIS, and Windows Update services… verify WSUS is working
  • Enable and start ConfigMgr services
  • Verify event Viewer and ConfigMgr logs and monitoring to ensure ConfigMgr is healthy
  • Re-enable and start backup software

 

 

SQL Trace Flags

Using this method is simple and easy, but there is one additional thing to keep in mind… SQL Trace flags (thanks Allen for pointing this out).  When installing SQL, trace flags are not enabled / added by default; this is taken care of by the ConfigMgr installation.  Since we are not doing a ConfigMgr installation or a site reset, etc. these options need to be added manually.

  • Open SQL Server Configuration Manager
  • Navigate to SQL Server Services -> SQL Server… -> Properties
  • Add “-T8295”
  • Add “-T4199”
  • Apply, stand on one foot, OK, Close

image_thumb1

Executing DBCC TRACEON (4199,-1) and DBCC TRACEON (8295,-1) in SQL Server Management Studio will enable these flags as seen by executing DBCC TRACESTATUS (-1).  However, this only affects the current session and they need to be added as startup flags.

 

SQL and ConfigMgr References

Additional / Related References

Quick Post: ConfigMgr Status Filter Rule with alternate credentials

$
0
0

A recent customer wanted an automated SSRS Report Email Subscription anytime a new deployment was created.  I’d been pondering how do accomplish that for some time, but finally got a catalyst and a bit of time.

I found a PowerShell script by George Walkey to get started with which is hosted on his GitHub repo at https://github.com/gwalkey/SSRS_Subscriptions/blob/master/New-SSRS_Subscription.ps1.  The script needs to be tweaked a bit to accept all of the required parameters for the various deployments (Application, Package/Program, Task Sequence, Software Update Group, Compliance Baseline), but the core functionality is there.

After tweaking the script I created a ConfigMgr Status Filter Rule to run the script with the right parameters.  The script runs; however, it throws an error

Exception: Exception calling “CreateSubscription” with “6” argument(s): “System.Web.Services.Protocols.SoapException: The DefaultValue expression for the report parameter ‘UserTokenSIDs’ contains an error: Server names cannot contain a space character.

Attempting to hard code the UserTokenSIDs to “disable” or “disabled” only produced another error:

Exception: Exception calling “CreateSubscription” with “6” argument(s): “System.Web.Services.Protocols.SoapException: The report parameter ‘UserTokenSIDs’ is read-only and cannot be modified. —> Microsoft.ReportingServices.Diagnostics.Utilities.ReadOnlyReportParameterException: The report parameter ‘UserTokenSIDs’ is read-only and cannot be modified.

So the issue is that the script is executed as the local SYSTEM account by the Status Filter Rule and SYSTEM doesn’t have any RBAC credentials.  I considered trying to grant it some rights, but decided this wouldn’t be a good idea.

I could only find one reference on BinGoogle related to the situation and comments are closed. https://www.reddit.com/r/SCCM/comments/4cvb7j/status_filter_rule_run_as_account/

So, how to get the SSRS subscription created with a user that has proper RBAC?   RunAs…

There are a few ways to accomplish this by having the primary script run the SSRS CreateSubscription function with alternate credentials.

  1. Have the Status Filter Rule call a bootstrap script.  The bootstrap script saves the parameters to an XML then calls a Scheduled Task that runs a valid user which consumes the XML to create the subscription.
  2. Using a Scheduled Task with saved credentials, trigger the PowerShell script on the proper Application / Source / Event IDs.  The script will needed to query ConfigMgr to find the associated Audit Status Message then parse out all of the required parameters.
  3. Use Lee Holmes method to save PowerShell credentials to disk and have the bootstrap script utilize the credentials to call the CreateSubscription script/function.  The credentials will need to be created while running PowerShell as the SYSTEM account.  PSEXEC -S -D -I PowerShell.exe will get that part going.
  4. Dig into the CreateSubscription function in SSRS to see if it will accept alternate credentials.  If so, then no bootstrap script will be required when using Lee Holmes’ method.

I chose option 1 for the moment and my rough code is working for Packages.

I hope to post the full solution soon including any script(s) for each deployment type.

Parse env:Path with PowerShell

$
0
0

The environment variable PATH (combined SYSTEM PATH and USER PATH) can be tricky to parse if you want to check for each folder it contains.  This is due to the support of various formatting styles.  For example, this is a valid PATH statement:

C:\WINDOWS\;"C:\Path with semicolon; in the middle";"E:\Path with semicolon at the end;";;C:\Program Files;

Notice the differences

  • Folder with spaces
  • Folder without spaces
  • Folder with an ending backslash (
    \
     )
  • Folder without an ending backslash (
    \
     )
  • Folder with a semicolon (
    ;
     )  in the middle
  • Folder with a semicolon (
    ;
     ) at the end
  • Blank folder (
    ;;
     )

 

A few references can be found…

But, I could find no fully working method to parse this complexity so I wrote a PowerShell Function to handle it.

Function Get-ENVPathFolders {     
   #.Synopsis Split $env:Path into an array
   #.Notes      
   #  - Handle 1) folders ending in a backslash 2) double-quoted folders 3) folders with semicolons 4) folders with spaces 5) double-semicolons I.e. blanks
   #  - Example path: 'C:\WINDOWS\;"C:\Path with semicolon; in the middle";"E:\Path with semicolon at the end;";;C:\Program Files;'
   #  - 2018/01/30 by Chad.Simmons@CatapultSystems.com - Created
   $PathArray = @()
   $env:Path.ToString().TrimEnd(';') -split '(?=["])' | ForEach-Object { #remove a trailing semicolon from the path then split it into an array using a double-quote as the delimiter keeping the delimiter
      If ($_ -eq '";') { # throw away a blank line
      } ElseIf ($_.ToString().StartsWith('";')) { # if line starts with "; remove the "; and any trailing backslash
         $PathArray += ($_.ToString().TrimStart('";')).TrimEnd('\')
      } ElseIf ($_.ToString().StartsWith('"')) {  # if line starts with " remove the " and any trailing backslash
         $PathArray += ($_.ToString().TrimStart('"')).TrimEnd('\') #$_ + '"'
      } Else {                                    # split by semicolon and remove any trailing backslash
         $_.ToString().Split(';') | ForEach-Object { If ($_.Length -gt 0) { $PathArray += $_.TrimEnd('\') } }
      }
   }
   Return $PathArray
}

To prove it out:

#output the array of PATH folders
Get-ENVPathFolders

#create a test file in half of the folders
$i=0
Get-ENVPathFolders | ForEach-Object { 
   $i++
   If ($i%2 -eq 0) { New-Item -Path "$_" -Name $myFile -ItemType File } 
}
#output the PATH folders with the test file
Get-ENVPathFolders | ForEach-Object { 
   If (Test-Path -Path $_\$myFile) { Write-Output "Found [$_\$myFile]" }
}

Happy coding!

ConfigMgr Package/Program… will retry later

$
0
0

Jason Sandy’s has a great blog explaining ConfigMgr Package/Program retry behavior including the Retry return/exit/error codes and how often the retry occurs.  One bit of info missing from the blog that I couldn’t find anywhere else (documentation, forums, blogs, etc.) was how long or how many retry attempts will occur before ConfigMgr gives up.

The answer is… 1008 times!  This equates to every 10 minutes for an entire week; however, if the computer is restarted, turned off, goes to sleep, etc. the duration will be extended.

To verify this I created a Package/Program that simply exited with an error code in the FailureRetry list.  Something like

image

Monitoring the ConfigMgr Client Execution Manager log (execmgr.log) will show the sequence of events.

image

Here is a filtered archive of the log as well.

Program Exit Code 4 has been tried 1 times, will retry later    execmgr    1/11/2018 2:30:56 PM    11212 (0x2BCC)
Program Exit Code 4 has been tried 2 times, will retry later    execmgr    1/11/2018 2:40:57 PM    11212 (0x2BCC)
Program Exit Code 4 has been tried 3 times, will retry later    execmgr    1/11/2018 2:50:57 PM    10972 (0x2ADC)
Program Exit Code 4 has been tried 4 times, will retry later    execmgr    1/11/2018 3:00:58 PM    6320 (0x18B0)
Program Exit Code 4 has been tried 5 times, will retry later    execmgr    1/11/2018 3:10:58 PM    9644 (0x25AC)
Program Exit Code 4 has been tried 6 times, will retry later    execmgr    1/11/2018 3:20:59 PM    12792 (0x31F8)
Program Exit Code 4 has been tried 7 times, will retry later    execmgr    1/11/2018 3:30:59 PM    3640 (0x0E38)
Program Exit Code 4 has been tried 8 times, will retry later    execmgr    1/11/2018 3:40:59 PM    10372 (0x2884)
Program Exit Code 4 has been tried 9 times, will retry later    execmgr    1/11/2018 3:51:00 PM    3640 (0x0E38)
Program Exit Code 4 has been tried 10 times, will retry later    execmgr    1/11/2018 4:01:00 PM    11208 (0x2BC8)
Program Exit Code 4 has been tried 11 times, will retry later    execmgr    1/11/2018 4:11:00 PM    10304 (0x2840)
Program Exit Code 4 has been tried 20 times, will retry later    execmgr    1/11/2018 5:41:04 PM    7232 (0x1C40)
Program Exit Code 4 has been tried 30 times, will retry later    execmgr    1/11/2018 9:40:30 PM    13020 (0x32DC)
Program Exit Code 4 has been tried 50 times, will retry later    execmgr    1/12/2018 11:22:05 AM    12300 (0x300C)
Program Exit Code 4 has been tried 80 times, will retry later    execmgr    1/12/2018 6:57:52 PM    12360 (0x3048)
Program Exit Code 4 has been tried 150 times, will retry later    execmgr    1/13/2018 6:38:25 AM    10400 (0x28A0)
Program Exit Code 4 has been tried 200 times, will retry later    execmgr    1/13/2018 2:58:47 PM    11076 (0x2B44)
Program Exit Code 4 has been tried 250 times, will retry later    execmgr    1/13/2018 11:19:09 PM    7588 (0x1DA4)
Program Exit Code 4 has been tried 300 times, will retry later    execmgr    1/14/2018 7:39:31 AM    10752 (0x2A0)
Program Exit Code 4 has been tried 400 times, will retry later    execmgr    1/15/2018 12:20:17 AM    6132 (0x17F4)
Program Exit Code 4 has been tried 450 times, will retry later    execmgr    1/15/2018 9:33:10 AM    7728 (0x1E30)
Program Exit Code 4 has been tried 500 times, will retry later    execmgr    1/15/2018 10:42:39 PM    2464 (0x09A0)
Program Exit Code 4 has been tried 550 times, will retry later    execmgr    1/16/2018 7:02:57 AM    8564 (0x2174)
Program Exit Code 4 has been tried 600 times, will retry later    execmgr    1/16/2018 4:10:44 PM    2292 (0x08F4)
Program Exit Code 4 has been tried 650 times, will retry later    execmgr    1/17/2018 2:35:55 AM    2284 (0x08EC)
Program Exit Code 4 has been tried 700 times, will retry later    execmgr    1/17/2018 12:03:37 PM    2324 (0x0914)
Program Exit Code 4 has been tried 750 times, will retry later    execmgr    1/17/2018 8:26:40 PM    7172 (0x1C04)
Program Exit Code 4 has been tried 800 times, will retry later    execmgr    1/18/2018 4:01:25 PM    6420 (0x1914)
Program Exit Code 4 has been tried 850 times, will retry later    execmgr    1/19/2018 12:05:56 PM    8136 (0x1FC8)
Program Exit Code 4 has been tried 900 times, will retry later    execmgr    1/19/2018 9:20:29 PM    10880 (0x2A80)
Program Exit Code 4 has been tried 950 times, will retry later    execmgr    1/20/2018 5:40:49 AM    8136 (0x1FC8)
Program Exit Code 4 has been tried 1000 times, will retry later    execmgr    1/20/2018 2:01:08 PM    8136 (0x1FC8)
Program Exit Code 4 has been tried 1008 times, will retry later    execmgr    1/20/2018 3:21:11 PM    10848 (0x2A60)
The maximum retry count has been reached for program Exit Code 4. This program will not retry.    execmgr    1/20/2018 3:31:11 PM    2036 (0x07F4)

The progress can obviously be monitored through ConfigMgr Status Messages… all 2016+ of them!

image

While the retries are ongoing, the Deployment Status in the ConfigMgr console will show as In Progress but will eventually become an Error after the retries conclude.

image

Where is this information stored?  Can it be changed?

The configuration values are stored in the CCM_SoftwareDistributionClientConfig WMI class within the root\ccm\Policy\Machine namespace.  MSDN has some good info on it.

While the article details out the ExecutionFailureRetryErrorCodes it does not list the default value of the ExecutionFailureRetryCount.

A quick WMI query with PowerShell will expose that though.

Get-WMIObject -Namespace root\ccm\Policy\Machine -Class CCM_SoftwareDistributionClientConfig -Property CacheContentTimeout, CacheSpaceFailureRetryCount, CacheSpaceFailureRetryInterval, SuccessReturnCodes, RebootReturnCodes, ExecutionFailureRetryCount, ExecutionFailureRetryErrorCodes, ExecutionFailureRetryInterval | Format-List Cache*,Ex*,Reboot*,Success*

$a = Get-WMIObject -Namespace root\ccm\Policy\Machine -Class CCM_SoftwareDistributionClientConfig -Property ExecutionFailureRetryErrorCodes | Select -ExpandProperty ExecutionFailureRetryErrorCodes; $a -join ','

image

Check out some of the other properties here.  There may be a good reason to tweak content download timeouts or retries, and success & reboot return codes (like you can with Applications and Task Sequences).

I haven’t gotten around to changing these values yet and verifying they stick.  It should be possible to change these for the entire ConfigMgr Site and for individual computers.  Chris Nackers and an old MSDN article give a good starting point for injecting ConfigMgr Client Local Policies.

Happy Retry (times 1008!)

ConfigMgr Chassis Type Global Condition / Requirement

$
0
0

Working with a customer recently we wanted to deploy a ConfigMgr Application to all all laptops in the organization without creating a new collection of just laptops.  Using the

ChassisTypes
property of the
Win32_SystemEnclosure
WMI namespace is a great way to do this; however, it can get a bit complicated, especially when there is non-normalized data in the inventory.  An example would be a wrongly coded ChassisType.

Additionally, if you want to target only desktops and exclude virtual computers the details can get tricky if you have a diverse environment.

The script

Get-ChassisTypeName.vbs
(no, it isn’t PowerShell… gasp!) will return
IsLaptop
,
IsDesktop
,
IsServer
, and
IsVirtual
based on the ChassisType and an array of computer model names.  For example, every Dell OptiPlex is a desktop, regardless of what the
ChassisTypes
  property returns.  Determining
IsVirtual
in ConfigMgr is tricky because of some underlying issues.  This script removes the confusion and gets the job done.

I recommend reading Brandon Linton’s ConfigMgr 2012 Chassis Type Global Condition blog for implementing this as a ConfigMgr Global Condition.

Global Conditions don’t work in a Task Sequence, but it would be trivial to add code to populate a custom TSVariable with the result.  You can rely on the source script from MDT if your TS is MDT integrated, but it doesn’t handle the edge cases and it’s a ton of overhead just to determine the Chassis Type.

Grab the script from GitHub.

image

IsLaptop
,
IsDesktop
,
IsServer
, and
IsVirtual

image


Viewing all 28 articles
Browse latest View live