A Hammer to Turn Screws

20161120_160743

Script: Check-Readiness.ps1

Purpose: Keeps me busy and away from drinking too much coffee.  Okay, seriously, it’s just another flavor of “check for Windows 10 upgrade readiness using PowerShell”. It can be used within SCCM or while standing naked in a WalMart, your choice.

<#
.DESCRIPTION
.PARAMETER SourcePath
Why didn't they use PARAMETRE, hmm? So American. Anyhow, SourcePath is wherever the Windows 10 media is located.
.PARAMETER OutputPath
If not "" then it will dump a file named <computer>-<result>.txt in that location.
.NOTES
I was only half awake when I wrote this. Use at your own risk.
#>
[CmdletBinding()]
param (
    [parameter(Mandatory=$True, HelpMessage="Path to setup.exe")]
    [ValidateNotNullOrEmpty()]
    [string] $SourcePath,
    [parameter(Mandatory=$False, HelpMessage="Path to dump output data")]
    [string] $OutputPath = ""
)
$setup = Join-Path -Path $SourcePath -ChildPath "setup.exe"
Write-Verbose "setup source is $setup"
if (!(Test-Path $setup)) {Write-Error "$setup not found"; break}
Write-Verbose "starting assessment"
$p = Start-Process -FilePath $setup -Wait -ArgumentList "/Auto Upgrade /Quiet /NoReboot /Compat ScanOnly" -PassThru
Write-Verbose "exit code: $($p.ExitCode)"
switch ($p.ExitCode) {
    -1047526896 { $result = "UpgradeReady"; break } 
    -1047526912 { $result = "SysReqtsFault"; break } 
    -1047526904 { $result = "CompatIssues"; break } 
    -1047526898 { $result = "InsuffDiskSpace"; break } 
    default { $result = "InvalidProductSku"; break }
}
if ($OutputPath -ne "") { 
    $filename = "$($env:COMPUTERNAME)-$result.txt"
    $filepath = Join-Path -Path $OutputPath -ChildPath $filename 
    $result | Out-File -FilePath $filepath -Append -NoClobber -Encoding Default
}
Write-Output $result

Shove it out as a Package, or make it a Script object you can spray all over helpless devices via Run Script.  Or run it directly (just add hot water and PSRemoting), or print it out, hang it on the wall, and laugh hysterically at it.  No matter what, it beats whatever your next staff meeting has to offer.

Cheers!

Advertisements

Dr. Skatterbrainz Answers Reader Mail

Warning: The following text may contain adult-ish offensive language which may cause unwanted side effects.  Read at your own risk.

“I was just wondering what you think about going into IT consulting for someone who’s never done it before, but who’s been working in IT direct for about ten years?” – Brad

It’s not for everyone, but some really enjoy it.  It also depends on whether you’re working from home/remote or on-site mostly.  If you’re used to being in a room full of people and a bustling office environment, then switch to being alone all the time, even with online communication tools, it’s sometimes lonely.  If you have pets or someone at home to talk to it helps, otherwise you should get outside frequently and mix with other people.  Coffee shop, park, etc.  It also requires you to impose your own control over scheduling, sleep, eating, exercise, etc.

If you prefer keeping hands-on with things after you build them, it might be tough letting go of each project and moving on to the next.  If you like having a steady office environment, that too may be a tough adjustment.  If you don’t like traveling a lot, or meeting strangers and getting used to strange places, accents, rules, customs, and so on, it may be a tough adjustment.

Other things to consider are how well you adjust to working alone, or with different teams from one day/week/month to the next, as opposed to being with the same group of people for months/years.

I would suggest that if you’re really curious/interested in consulting to give it a try.  You will be exposed to more variety and more ideas than you typically get with a steady office role.  But, no matter how it turns out, it will still be more experience, and more experience is good no matter what (as long as it doesn’t kill you or leave you brain-damaged). And you may get to rack up lots of flyer miles and hotel rewards points.

“Why do CIOs so often turn down requests from their own IT staff to improve tools and processes?” – Jim

Because most technical people suck at communicating things in terms of money.  Remember that old book “Women are from Venus. Men are from Mars”?.  CxO’s like numbers and charts.  The more colors and spiffiness (I made that term up) the better.  The situations where I’ve seen (or done by myself) a proposal laid out in terms of what it will provide in terms of the following, it got a positive result:

  • Cost savings
  • New revenue (not always welcome, unless it’s in a core competency)
  • Added capability (e.g. competitive advantage)

Any idea you have to improve things needs to be distilled down to what “improve” really means.  Improves what?  How?  For whom?  Keep walking that question back until it comes to a dollar figure.  And one other aspect I find that helps is to focus more on the repeat financial benefit, rather than a one-time benefit.  A simple one-time cost-savings doesn’t usually get them excited enough to set down the Martini and whip out the credit card, but a pay-off that keeps getting better every quarter/year is hard to ignore.  In the end, if you have to fluff the numbers to make it work, you need to ask yourself if you really have the best idea.  If it really makes sense you shouldn’t need to oversell it, but make sure you present it in the language the suit-clad folks really love to hear.

“Some of my siblings and cousins have a condescending view of the IT profession.  They’re all lawyers and marketing people, but somehow think IT is like dishwashing.  What’s the best thing I can do for that?” – Charles

Hi Charles.  I can completely sympathize.  I have a few of those people in my family as well.  You can either hold a grudge, or let it go. I prefer to let it go.  Time is your most valuable asset.  Don’t waste it.  The time you would spend on debating them could be better used on learning new skills or finding more projects to grow your experience.  Changing someone’s mind about things is almost impossible without proper firearms and pharmaceuticals.

“I’ve seen you pick on Microsoft Access a few times.  What do you hate about it?” – Chris

I don’t hate Access itself.  It’s a great product, especially for small scale needs.  But it’s not built for large-scale, shared use, and it couples the application (forms, reports, logic) with the data (tables, views, etc.) which doesn’t scale or lend itself to flexible maintenance.  It’s also very dependent on the version of Office installed, so upgrading the rest of Office then becomes hostage to it.

The other issue is that shared-use problems often lead to proliferation of multiple, standalone copies throughout the enterprise.  Maintaining consistency and centralized reporting becomes increasingly difficult.

Then when IT wants (or needs) to roll out a new version of Office, it turns into “Now hold on a minute!  That’ll break our precious Access DB ‘application’!”   The longer the Access app remains in production, the more intrinsic it becomes to business operations, making it more sensitive to disruption.  And the longer is remains in production, the more likely staff will have quit/retired/died/joined a cult, whatever, and now nobody is left who knows how to maintain or modify the code.

This often leads to the following discussion playbook scenarios, complete with the eye-rolling, mumbling, and drooling package…

Version 1

IT: “You guys in Finance need to upgrade this Access thing of yours so it works with 2016!”

Finance guys: “The guy who wrote it left months/years ago and we don’t have anyone who can update it”

IT: “Not our problem. Make it happen.”

Finance guys: “Then fuck you.”

Version 2

IT: “You guys in Finance need to upgrade this Access thing of yours so it works with 2016!”

Finance guys: “Okay, but since this is YOUR requirement, then IT should pay for all that work.”

IT: “No!”

Finance guys: “Then fuck you.”

Version 3

IT: “You guys in Finance need to upgrade this Access thing of yours so it works with 2016!”

Finance guys: “Okay, but we don’t have anyone who can do it due to schedules and other work.  Can IT do it for us?”

IT: “No!”

Finance guys: “Then fuck you.”

Version 4

IT: “You guys in Finance need to…”

Finance guys: “Just fuck you.”

Then we break for lunch, listen to IT complain about how <insert department name here> are a bunch of a-holes to work with.  We come back to the office, fighting off the carb-coma sleep monster, and repeat the same discussions again.  Someone will suggest the usual workarounds…

  • “Let’s install the Access runtime for 2007 or 2010 along with Access 2016!”
    • “Adding more complexity to our environment is not the answer.”
  • “Move it to Citrix or RDS!”
    • Citrix/RDS guy: “No!  I need funding to buy more hardware.”
  • “Let’s App-V or ThinApp it!”
    • “Carl – Do YOU know how to sequence that?”
      • “Ummm no.  But I hear it’s really cool.”
    • “You thought Justin Bieber was cool.”
      • “What’s wrong with Justin?!”
    • (continues on until some smacks the table to break it up)
  • “Let’s rent an unmarked van and kidnap that old guy that wrote this!”
    • “I have $53 on me.  Can we rent one that cheap?”
      • “I have duct tape, maybe a gift card too, hold on…”

In the end, the most expensive, least efficient and most painful “solution” will be chosen and everyone will be unhappy.  After a few months they will have left for other jobs and a new staff will be looking at it, wash rinse and repeat.  Or, in some cases, they hire someone to rewrite the app using modern tools that support shared use, are easy to maintain and even move to the cloud.

“I’d like to use Chocolatey at my company, but management won’t allow it and won’t pay for the business version.  Can I still leverage pieces of it somehow?” – Larry

There are several things you can “leverage” without using the public repository, or buying the business license features.

  • Set up an internal repository.  If there’s no objection to internal sourcing of packages.
  • Crack open Chocolatey packages for the silent installation and configuration syntax to use elsewhere.  Scripts, SCCM, etc.
  • Apply for a new job elsewhere.

Option 1 – Setting up an internal repo…

  1. Read this – https://chocolatey.org/docs/how-to-host-feed
  2. Test, test, and test some more
  3. Pilot deployment
  4. Production domination and ultimate anihilation

Option 2 – Cracking open a warm one…

  1. Locate the desired package (e.g. Microsoft Teams desktop app)
  2. Click on the “Package source” link along the left (opens the Github repo)
  3. Inspect the .nuspec file for some general details
  4. Inspect the xxxinstall.ps1 file for the code and tasty stuff
  5. Copy / adapt when you can into whatever else you’re using

Notes:

  • If you intend to “keep up” with the latest releases of a given package, you may need to repeat the above steps, or monitor the vendor source location(s) to react as they post new versions.
  • If you want to expand on this, you can post your own packages with internal modification requirements (icacls, registry hacks, etc.) as needed, or adapt them into your own deployment scripts or task sequences.

Cheers!

Send more questions via Twitter DM.  If you follow me, and your account doesn’t smell like a bot, or a weird cats-for-kids black market thing, then I will usually follow you back.

ConfigMgr Device Naming, Part 2: The Electric Boogaloo

 

1f3be4a21f5823b4516b458ba2f89a87--drink-coffee-coffee-cafe

So the previous blog post infuriated a few people, so I thought “I can do better than that. I can infuriate more people!” and came up with this:

I combined several PowerShell scripts, some oatmeal, hot water, chopped walnuts and a fresh cup of coffee into a single, mono-nuclear, monolithic uber-script that can be awoken (that’s a real word, I checked) from within a task sequence to rope the legs of the device, hold it down, and burn a new name on it.

You can view and laugh at it here.

Note: If you wish to ignore and infuriate people who strongly advise against using location names as part of device names, you will also want the associated “locations.txt” file to go with this steaming pile of electrons.  This is a poor man’s version of using an MDT CustomSettings.ini with DefaultGateway mappings, only poorer, and it hasn’t had anything to eat in a long, long time.

How to make this cat say “moo”…

  1. Drop the script files in a folder
    1. If you’re using location names, be sure to edit the locations.txt file to suit your environment.
  2. Create a new (or update an existing, your choice) Package in Configuration Manager.
  3. The Package contains source files, but has no Program entries, at least not for this script.
  4. Distribute that mess out into your unsuspecting environment.
  5. Add a task sequence step (Add > General > Run PowerShell Script)
    1. Insert somewhere above the “Apply Operating System” step
    2. Name: Set Computer Name
    3. Select the Package with the the script
    4. Script name: Set-ComputerName.ps1
    5. Parameters: (see notes)
    6. PowerShell execution policy: Bypass
  6. Click OK
  7. Deploy
  8. Run like hell

Figure 1 shows an example using the -Interactive parameter.  Figure 2 shows an example of using the fully-automatic, belt-fed, multi-rotational, liquid-cooled location-based naming option, which implies the -InfuriatesTheShitOutOfSomeAdmins parameter.

set-name1Figure 1 – Comes before Figure 2 🙂

set-name2Figure 2 – Comes right after Figure 1

Notes:

  • The -Interactive [switch] parameter ignores all other parameters except -DefaultName.  This parameter displays a really fancy, super-complex, highly-sophisticated dialog form for entering the computer name.  It looks like this…
    set-name3
  • -DefaultName is a [string] parameter which is set to “” by default.  If a value is assigned, it overrides everything like a car crashing through a drug store front window at 50 mph.
    Example: -DefaultName “DOUCHEBAG”
  • -UseLocation is a [switch] parameter which references the default IP gateway, at the time of imaging, to determine the location code to add to the device name.  If no matching gateway is found, you can force a default using the -DefaultLocation parameter.  You can also use multiple location.txt files (with different filenames of course) by using the -LocationFile parameter.  This might be useful when you have a CAS environment and want to segregate different sub-groups of IP gateways for some crazy, glue-sniffing reason. The locations.txt file uses a strict format which took decades to refine:  GATEWAYADDRESS=FULLNAME,SHORTNAME
    Example: -UseLocation -DefaultLocation “LON” -LocationFile “en-gb.txt”
    Example: -UseLocation -DefaultLocation “CLT”
  • -UseHyphens is a [switch] parameter that will concatenate the sub-atomic naming particles into a cohesive pile of dung using 100% organic gluten-free hyphen characters.
    Example: (without) “D1234”
    Example: (with) “D-1234”
  • -SnMaxLen controls the maximum length of the BIOS serial number values to allow when concatenating the final name value.  In most cases, a serial number won’t cause a problem, but within some virtual environments, the serial number can be almost as long as a fillibuster speech on Capitol Hill.  The default is 8 characters.  The truncation is from the left, so this fetches the right-most characters.
    Example: -SnMaxLen 10
  • -Testing is a [switch] parameter for running the script outside of a task sequence for testing and validation only.  If you don’t use -Testing and not running within a task sequence session, it will throw some ugly red errors at you because it can’t invoke the Microsoft.SMS.TSEnvironment interface.  Can be combined with any or all other parameters.
    Example: -Testing
  • If you wish to change the “form factor” codes (“D”, “L”, etc.) you will need to edit the {switch} block code within function Get-FormFactorCode. Between lines 170 and 185 or so.

So, for those of you that find this useful: glad I could help in some small way.  For those who are even more angry: Decaf is on sale at Trader Joe’s this week.  Stock up.

😀

ConfigMgr OSD Device Naming

For whatever reasons, this still seems to be an interesting and ongoing topic of discussion/debate/argument/angst/drinking, etc.  I’m talking about assigning names to computers while they’re being imaged or refreshed.  There are quite a few common scenarios, and an unknown variety of less-common scenarios, I’m sure.  Some embrace this and some despise it.  The views are all over the place it seems.  But regardless, as a consultant, I’m very often tasked with advising (okay: consulting) customers on options for either fully or partially automating the process.

Personally, I approach all automation scenarios with the same view: Explain why you need it.  Very often, automation is simply taking over from a poorly designed manual process.  As an old colleague of mine once said (and I repeat ad nauseum) “If you automate a broken process, you can only get an automated broken process“.  It’s true.  If the customer is receptive to a discussion about alternatives, it always produces a more positive outcome.  So far, at least.

Anyhow, I wanted to lay out some common scenarios and examples for handling them.  These are based on the use of either Microsoft System Center Configuration Manager (aka ConfigMgr, aka SCCM), or Microsoft Deployment Toolkit (aka MDT).  I am by no means implying these are the ONLY way to accomplish each scenario.  They are provided simply as ONE way out of many, but which I’ve found offer the best benefit for the least effort and risk.  You are free to roll your eyes and call me a dumbass, that’s fine, because I plug my ears anyway.

Manual: Stick shift (The Exploding Space Shuttle Method)

This method requires a human to manually enter the desired name to assign to the device.  It’s the easiest/quickest method to implement.

For ConfigMgr environments, the quickest implementation is by assigning “OSDComputerName” Collection Variable to a device collection.  For bare metal (e.g. new) machines, that is often the “Unknown Computers” collection, but for refresh/reimage scenarios it’s whatever device collection is being targeted by the relevant task sequence.

Pro – easy, fast, cheap

Con – prone to human error

Fully-Automatic: Serial Number

Since Windows 10 still imposes a limit of 15 characters, with restrictions on certain alphanumeric characters, this method often includes some checking/filtering to insure that the process doesn’t swallow a grenade and make a mess.  This is less common today with physical machines, but often occurs with virtualized environments due to differing serial number formats.  This example uses the BIOS serial number as the machine name.  If the serial number is longer than 15 characters, only the last 15 characters are used.  So “CNU1234567891234” would become “NU1234567891234”.

# Set-ComputerNameAutoSN.ps1
$snmax = 15
$csn = (Get-WmiObject -Class Win32_SystemEnclosure).SerialNumber
if ($csn.Count -gt 1) { $csn = $csn[0] }
if ($csn.Length -gt $snmax) {
  $csn = $csn.Substring($csn.Length - $snmax)
}
try {
  $tsenv = New-Object -ComObject Microsoft.SMS.TSEnvironment
  $tsenv.Value("OSDComputerName") = $csn
  Write-Output 0
}
catch {
  Write-Output "not running in a task sequence environment"
}

Note: You might be wondering why the “if ($csn.Count -gt 1)…” stuff.  That’s in case the device is sitting in a dock or connected via an e-dock or dongle, which often causes WMI queries to return an array instead of a single integer for things like ChassisTypes and so on.  Checking the count of values assigned to the variable allows for retrieving just the first element in the array.

Full-Automatic: Form-Factor + Serial Number

This is a variation on the preceding example, whereby a prefix (or sometimes a suffix) is added to the name to identify a general form, such as “D” for desktop, and “L” for laptop, and so on.  This example would take a ChassisType value of 3 and the last 10 characters (arbitrary, not required) of the Serial Number of “CNU1234567891234” and concatenate these into “D4567891234”

# Set-ComputerNameAutoFormSN.ps1
$snmax = 10
$csn = (Get-WmiObject -Class Win32_SystemEnclosure).SerialNumber
$cff = (Get-WmiObject -Class Win32_SystemEnclosure).ChassisTypes
if ($csn.Count -gt 1) { $csn = $csn[0] }
if ($cff.Count -gt 1) { $cff = $cff[0] }
if ($csn.Length -gt $snmax) {
  $csn = $csn.Substring($csn.Length - $snmax)
}
switch ($cff) {
   3 { $ff = 'D'; break }
   4 { $ff = 'D'; break }
   5 { $ff = 'D'; break }
   6 { $ff = 'D'; break }
   7 { $ff = 'D'; break }
   8 { $ff = 'L'; break }
   9 { $ff = 'L'; break }
  10 { $ff = 'L'; break }
  11 { $ff = 'L'; break }
  14 { $ff = 'L'; break }
  default { $ff = 'X'; break }
}
$newname = $ff+'-'+$csn
try {
  $tsenv = New-Object -ComObject Microsoft.SMS.TSEnvironment
  $tsenv.Value("OSDComputerName") = $newname
  Write-Output 0
}
catch {
  Write-Output "you forgot to flush the toilet"
}

Semi-Automatic: Location Code + Form Factor + Serial Number

This one is based on a scenario where all devices are imaged at a single location, and shipped out to other locations, whereby the customer wishes to place the AD computer account into an associated location-based Organizational Unit (OU) during the imaging process.  This can be done using User-Driven Interface (UDI) forms, or by script.  I prefer script because I’m more comfortable with it and there are far fewer moving parts to contend with.  That said, there are several ways to do this:

Option 1 – SCCM Collection Variable

A collection variable can be assigned to the target collection in order to prompt for a location identifier.  This could be a number, a name, or an abbreviation, it doesn’t really matter since it’s just a textbox form input.

Option 2 – MDT / SCCM Task Sequence Script

Some flavors of this involve copying the ServiceUI.exe component from the MDT installation into the script package, so that ServiceUI.exe can be invoked to suppress the task sequence progress UI and allow custom script GUI forms to display properly.  I did it this way for a long time, until I ran across this really nice script from John Warnken *here*.  After some (minor) tweaking it can easily be adapted to almost any need.  The example below prompts the technician for a 3-character Location Code and then concatenates the device name using [Location]+[FormFactor]+[SerialNumber] up to 15 characters, so the [SerialNumber] value is truncated to the last 10 characters (max).

<#
Set-ComputerNameFormLocSN.ps1
.SYNOPSIS 
Displays a gui prompt for a computername usable in a SCCM OSD Task Sequence. 
 
.PARAMETER testing 
The is a switch parameter that is used to test the script outside of a SCCM Task Sequence. 
If this -testing is used the script will not load the OSD objects that are only present while a task sequence is running. 
instead it will use write-output to display the selection. 
 
.EXAMPLE 
powershell -executionpolicy Bypass -file .\Set-ComputerNameLocationPrompt.ps1 
 
.EXAMPLE 
powershell -file .\Set-ComputerNameLocationPrompt.ps1 -testing 
 
.NOTES 
This is a very simple version of a OSD prompt for a computername. You can add extra validation to the computer name, for example a regular expression test 
to ensure it meets standard form used in your environment. Addtional form object can be added to other options that you may want to set 
task sequence variables for. Also as a simple example, I just added the xaml for the wpf form as a variable in the script. You have the option of storing it in 
a external file if your form gets complex.

.Author 
Jonathan Warnken - jon.warnken (at) gmail (dot) com 
https://gallery.technet.microsoft.com/scriptcenter/Prompt-for-a-computername-6f99fa67
.Hallucinator
Skatterbrainz - @skatterbrainzz
#> 
[CmdletBinding()] 
param( 
  [parameter(Mandatory=$False)]
    [switch] $Testing
)
$snmax = 10
if(!$Testing){
  # this section provides the gluten-free, high-protein alternative to ServiceUI.exe
  $tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment  
  $tsui  = New-Object -COMObject Microsoft.SMS.TSProgressUI  
  $OSDComputername = $tsenv.Value("OSDComputername")
  $tsui.CloseProgressDialog() 
}

# query machine serial number and chassis type from WMI
$csn = Get-WmiObject -Class Win32_SystemEnclosure | Select-Object -ExpandProperty SerialNumber
$cff = Get-WmiObject -Class Win32_SystemEnclosure | Select-Object -ExpandProperty ChassisTypes

# check if return values are an array and if true, then only use the first element
if ($csn.Count -gt 1) { $csn = $csn[0] }
if ($cff.Count -gt 1) { $cff = $cff[0] }

# if serial number is longer than 10 chars, get last 10 chars only
if ($csn.Length -gt $snmax) {
  $csn = $csn.Substring($csn.Length - $snmax)
}
# derive form factor code from chassis type code
switch ($cff) {
   3 { $ff = 'D'; break }
   4 { $ff = 'D'; break }
   5 { $ff = 'D'; break }
   6 { $ff = 'D'; break }
   7 { $ff = 'D'; break }
   8 { $ff = 'L'; break }
   9 { $ff = 'L'; break }
  10 { $ff = 'L'; break }
  11 { $ff = 'L'; break }
  14 { $ff = 'L'; break }
  default { $ff = 'X'; break }
}
[xml]$XAML = @' 
<Window 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
  Title="SCCM OSD Computername" Height="154" Width="425" Topmost="True" WindowStyle="ToolWindow"> 
  <Grid> 
    <Label Name="Computername_label" Content="Location:" HorizontalAlignment="Left" Height="27" Margin="0,10,0,0" VerticalAlignment="Top" Width="241"/> 
    <TextBox Name="LocationCode_text" HorizontalAlignment="Left" Height="27" Margin="146,10,0,0" TextWrapping="Wrap" Text=" " VerticalAlignment="Top" Width="220"/> 
    <Button Name="Continue_button" Content="Continue" HorizontalAlignment="Left" Margin="201,62,0,0" VerticalAlignment="Top" Width="75"/> 
  </Grid> 
</Window> 
'@ 
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework') 
#Read XAML 
$reader=(New-Object System.Xml.XmlNodeReader $xaml) 
$Form=[Windows.Markup.XamlReader]::Load( $reader )

# add form objects as script variables 
$xaml.SelectNodes("//*[@Name]") | %{Set-Variable -Name ($_.Name) -Value $Form.FindName($_.Name)}

# set the default value for the form textbox
$LocationCode_text.Text = 'STL'

# assign event handler to the "Continue" button
$Continue_button.add_Click({ 
  $Script:LocationCode = $LocationCode_text.Text.ToString()
  $Form.Close() 
})
# display the form for user input
$Form.ShowDialog() | Out-Null

# after form is closed, concatenate computer name from input values and other variables
$computername = $Script:LocationCode+$ff+$csn

if (!$Testing) {
  $tsenv.Value("OSDComputername") = $computername 
  Write-Output " OSDComputername set to $($tsenv.value("OSDComputername"))" 
}
else { 
  Write-Output " OSDComputername would be set to $computername" 
}

Pro – works with PXE and offline media

Con – some scripting and testing required

Fully Automatic: Location Code + Form Factor + Serial Number

This method relies on something available from the environment upon which to determine the “location”.  For MDT this is often using the CustomSettings.ini with the IP Gateway address.  Using a logical if/then you can construct a logic mapping of IP gateway = Location or location Code.

Option 1 – MDT Deployment Share Configuration

This one uses a CustomSettings.ini which reads the IPv4 Gateway address and the Serial Number to concatenate the resultant device name.  For a better example of this and a much, much better explanation by the one-and-only Mikael Nystrom, please read this first.

[Settings]
Priority=Init, ByDesktop, ByLaptop, DefaultGateway, Default 
Properties=ComputerLocationName, ComputerTypeName, ComputerSerialNumber

[Init]
ComputerSerialNumber=#Right("%SerialNumber%",10)#

[ByLaptop]
SubSection=Laptop-%IsLaptop%
ComputerTypeName=L

[ByDesktop]
SubSection=Desktop-%IsDesktop%
ComputerTypeName=D

[DefaultGateway]
192.168.1.1=Norfolk
192.168.2.1=Chicago
192.168.3.1=NewYork

[Norfolk]
ComputerLocationName=NOR

[Chicago]
ComputerLocationName=CHI

[NewYork]
ComputerLocationName=NYC

[Default]
OSInstall=Y
ComputerLocationName=XXX
ComputerTypeName=X
OSDComputerName=%ComputerLocationName%%ComputerTypeName%%ComputerSerialNumber%
SkipCapture=YES
SkipAdminPassword=YES
SkipProductKey=YES
SkipComputerBackup=YES
SkipBitLocker=YES

Using this sample INI configuration, a laptop device with Serial Number “CNU1234567891234” connected to IP gateway 192.168.3.1 would end up with an assigned name of “NYCL4567891234”.  If the DefaultGateway is not defined in the list, it will default to location code “XXX”.  If the form factor cannot determine Desktop or Laptop it will default to “X”.  So, using the same serial number value it would end up as “XXXX4567891234”.

Option 2 – SCCM Task Sequence

This example adapts the semi-automatic example above, but replaces the GUI prompt with a check for the IP gateway.

# Set-ComputerNameAutoLocIPFormFactorSN.ps1
$csn = (Get-WmiObject -Class Win32_SystemEnclosure).SerialNumber
$cff = (Get-WmiObject -Class Win32_SystemEnclosure).ChassisTypes
if ($csn.Count -gt 1) { $csn = $csn[0] }
if ($cff.Count -gt 1) { $cff = $cff[0] }
if ($csn.Length -gt 15) {
  $csn = $csn.Substring($csn.Length - 15)
}
$gwa = (Get-WmiObject -Class Win32_NetworkAdapterConfiguration | Where {$_.IPEnabled -eq $True}).DefaultIPGateway
switch ($gwa) {
  '192.168.1.1' {
    $location = 'Norfolk'
    $loccode = 'NOR'
    break
  }
  '192.168.2.1' {
    $location = 'Chicago'
    $loccode = 'CHI'
    break
  }
  default {
    $location = 'NewYork'
    $loccode = 'NYC'
    break
  }
}
switch ($cff) {
   3 { $ff = 'D'; break }
   4 { $ff = 'D'; break }
   5 { $ff = 'D'; break }
   6 { $ff = 'D'; break }
   7 { $ff = 'D'; break }
   8 { $ff = 'L'; break }
   9 { $ff = 'L'; break }
  10 { $ff = 'L'; break }
  11 { $ff = 'L'; break }
  14 { $ff = 'L'; break }
  default { $ff = 'X'; break }
}
$newName = $loccode+$ff+$csn
try {
  $tsenv = New-Object -ComObject Microsoft.SMS.TSEnvironment
  $tsenv.Value("OSDComputerName") = $newName
  Write-Output 0
}
catch {
  Write-Output "not running in a task sequence environment"
}

Option 3 – SCCM Task Sequence + Collection Variable

This example came from a project where the customer had created a device collection for each branch location, which had a Collection Variable “DeviceLoc” assigned, and having deployed a refresh task sequence to each.  So in this scenario, they wanted to merge the script with the collection variable to derive the new name.  Is this ideal?  Not for most people, but due to time constraints it had to work.

$csn = (Get-WmiObject -Class Win32_SystemEnclosure).SerialNumber
$cff = (Get-WmiObject -Class Win32_SystemEnclosure).ChassisTypes
if ($csn.Count -gt 1) { $csn = $csn[0] }
if ($cff.Count -gt 1) { $cff = $cff[0] }
if ($csn.Length -gt 15) {
  $csn = $csn.Substring($csn.Length - 15)
}
$gwa = (Get-WmiObject -Class Win32_NetworkAdapterConfiguration | Where {$_.IPEnabled -eq $True}).DefaultIPGateway
switch ($gwa) {
  '192.168.1.1' {
    $location = 'Norfolk'
    $loccode = 'NOR'
    break
  }
  '192.168.2.1' {
    $location = 'Chicago'
    $loccode = 'CHI'
    break
  }
  default {
    $location = 'NewYork'
    $loccode = 'NYC'
    break
  }
}
switch ($cff) {
   3 { $ff = 'D'; break }
   4 { $ff = 'D'; break }
   5 { $ff = 'D'; break }
   6 { $ff = 'D'; break }
   7 { $ff = 'D'; break }
   8 { $ff = 'L'; break }
   9 { $ff = 'L'; break }
  10 { $ff = 'L'; break }
  11 { $ff = 'L'; break }
  14 { $ff = 'L'; break }
  default { $ff = 'X'; $ousub = ''; break }
}
try {
  $tsenv = New-Object -ComObject Microsoft.SMS.TSEnvironment
  $loc = $tsenv.Value("DeviceLoc")
  if ([string]::IsNullOrEmpty($loc)) {
    $newName = 'CRP'+$ff+$csn
  }
  else {
    $newName = $loc+$ff+$csn
  }
  $tsenv.Value("OSDComputerName") = $newName
  Write-Output 0
}
catch {
  Write-Output "not running in a task sequence environment"
}

Conclusion / Summary

This is only a small sample of all the variations and scenarios many of us encounter.  Other variants include setting the Organizational Unit (OU) path, adding the device to domain security groups, and so on.  I would like to thank John Warnken for the script code he posted on TechNet, which makes it so much easier to deal with the progress bar UI when using custom forms.

I’d also like to say that you shouldn’t look at these and think you need to immediately put them into use.  Test labs are one thing.  But production requires serious review and planning to determine what really makes the most sense.  For example, a lot of customers I work with have persistently used a location code for device names, but when they really assess the business environment, they find many devices roam to different locations, or don’t really stay in any “official” location (home, hotel, on the road, etc.)  And when pressed to explain how the location code provides real value, many don’t have a rationale besides, “it’s how we’ve always done it”.  So think it through.  Ask if you really need to do something like this and if so, why.  Then work on the how aspects.

Do you have other/better examples you’d like to share?  Post a comment below.  Thank you for reading!

ConfigMgr – 2 Minute Microwave Style

Genesis – I posted a tweet about someone I know getting stressed at learning Configuration Manager in order to manage 50 Windows devices.  All desktops.  The background is basically that his company had planned on 1000 devices running Windows.  But the end-users, who wield more purchasing power, opted to buy mostly Macbooks.  So the total Windows device count was capped at 50, BUT…. they already approved the purchase of ConfigMgr.  It’s worth noting that the end-users also purchases JAMF (formerly Casper) and set it up in their own secret underground lab, complete with a diabolical German scientist in a white lab coat.  Ok.  That last part isn’t really true, but the JAMF part is true.

So, the “discussion” slid into “okay mr. smarty-pants skatter-turd-brainz, what would you want in a ‘perfect’ ConfigMgr world to address such a scenario?” (again, I’m paraphrasing a bit here)

MC DJam, aka DJammer, aka David the Master ConfigMaster Meister of ConfigMgr, popped some thermal verbals in front of the house and the room went Helen Keller (that means quiet and dark, but please don’t be offended, just stay with me I promise this will make sense soon…)

Yes, I’ve had a few beers.  Full disclosure.  I had to switch to water and allow time for the electric shock paddles to bring my puny brain back online.  That was followed by a brief gasp,”oh shit?! what have I started now?”  Then some breathing exercises and knuckle crackings and now, back to the program…

So, Ryan Ephgrave (aka @EphingPosh) stepped in and dropped some mic bombs of his own.

And just like having kids, the whole thing got out ahead of me way too quick.

So, I agree with Ryan, who also added a few other suggestions like IIS logs, Chocolatey package deployments (dammit – I was hoping to beat him to that one).

So the main thing about this was that this person (no names) is entirely new to ConfigMgr.  Never seen it before, and only gets to spend a small portion of their daily/weekly time with it, due to concurrent job functions.  This is becoming more and more common everywhere I go, and I’ve blogged ad nauseum about it many times (e.g. “role compression”)

What do most small shop admins complain about?

  1. Inventory reporting
  2. Remote management tools
  3. Deploy applications
  4. Deploy updates
  5. Imaging
  6. Customizable / Extendable

These are the top (6) regardless of being ConfigMgr, LANdesk, Kace, Altiris, Solarwinds, or any other product.  All of them seem to handle most of the first 4 pretty well, with varying levels of learning and effort.  But Imaging is entirely more flexible and capable with ConfigMgr (or MDT) than any of the others I’ve seen (Acronis, Ghost, etc. etc. etc.)

ConfigMgr does an outstanding job of all 6 (even though I might bitch about number 6 in private sometimes, it is improving).  ConfigMgr is also old as dirt and battle-tested.  It scales to very large demands, and has a strong community base to back it up in all kinds of ways.  In some respects it reminds me of the years I spent with AutoCAD and Autodesk communities and the ecosystems that developed around that, but that’s another story for another time.

The challenge tends to come from just a few areas:

  1. Cost and Licensing – ConfigMgr is still aimed at medium-to-large scale customers.  The EA folks with Software Assurance, are most often interested and courted into buying it.  Some would disagree, but I set my beer mug down and calmly say “Walk into any major corporate IT office and ask who knows about ConfigMgr.  Then walk into a dentist office, car dealership, or small school system and ask that same question.”  I bet you get a different response.
  2. Complexity – ConfigMgr makes no bones about what it aims to do.  The product sprung from years of “Microsoft never lets me do what I want to manage my devices” (say that with a nasally whiny tone for optimum effect).  Microsoft responded “Here you go bitch.  A million miles of rope to hang yourself.  Enjoy!”  It’s an adjustable wrench filled with adjustable wrenches, because it was designed to be the go-to toolset for almost any environment.  And it’s still evolving today (faster than ever by the way)
  3. Administration – Anyone who’s worked with ConfigMgr knows it’s not really a “part-time” job.  But that’s okay.  It’s part of the “complexity” side-effect.  And rarely are two environments identical enough to make it cookie cutter.  That’s okay too.  Microsoft didn’t try to shoehorn us into “one way”, but said “here’s fifty ways, you choose“.  The more devices you manage with it, the more time and staff it often demands in order to do it justice.  I know plenty of environments that have scaled to the point of having dedicated staff for parts of it like App deployments, Patch Management, Imaging and even Reporting.

None of these are noted with the intention of being negative.  They are realities.  It’s like saying an NHRA dragster is loud and fast.  It’s supposed to be.

Now, add those three areas up and it makes that small office budget person lose control of their bowels and start munching bottles of Xanax.  So they start searching Google for “deploy apps to small office computers” or “patching small office computers cheap as hell” and things like that.

So, ConfigMgr already does the top 6 functions pretty darn well.  So what could be done to spin off a new sitcom version of this hit TV show for the younger generation?

  1. Simpler – It needs to be stupid-simple to install/deploy and manage.  This reaches into the UI as well.  Let’s face it, as much as I love the product, the console needs a makeover.  Simplify age-old cumbersome tasks like making queries and Collections, ADRs and so on.
  2. Lightweight – Less on-prem infrastructure requirements: DPs, MPs, SUPs, RPs, etc.  Move that into cloud roles if possible.
  3. Integrate/Refactor – Move anything which is mature (and I mean really mature) in Intune, out of ConfigMgr.  Get rid of Packages AND Applications, make a hybrid out of both.  Consider splitting some features off as premium add-ons or extensions, like Compliance Rules (or move that to Intune), OSD, Custom Reporting, Endpoint Protection, Metering, etc.
  4. Cheaper – Offer a per-node pricing model that scales down as well as up.  Users should be able to get onboard within the cost range of Office 365 models, or lower.

Basically, this sounds like Intune 3.0, which I’ve also blabbered about like some Kevin Kelly wanna-be futurist guy, but without the real ability to predict anything.

Some of the other responses on Twitter focused on ways to streamline the current “enterprise” realm, with things like automating many of the (currently) manual tasks involved with installation and initial configuration (SQL, AD, service accounts, IIS, WSUS, dependencies, etc. etc.), all of which are extremely valid points.  I’m still trying to focus on this “small shop” challenge though.

It’s really easy to stare at the ConfigMgr console and start extrapolating “what would the most basic features I could live with really come down to?” and end up picking the entire feature set in the end.  But pragmatically, it’s built to go 500 mph and slow down to push a baby stroller.  That’s a lot of range for a small shop to deal with, and they really shouldn’t.  That would be like complaining that the Gravedigger 4×4 monster truck makes for a terrible family vehicle, but it’s not supposed to be that.  And ConfigMgr really isn’t supposed to be the go-to solution for a group of 10-20 machines on a small budget.  Intune COULD be, but it’s still not there yet.  And even it is already wandering off the mud trail of simplicity.  It needs to be designed with a different mindset, but borrowing from the engine parts under the ConfigMgr hood.

Maybe, like how App-V was boiled down and strained into a bowl of Windows 10 component insertions for Office 365 enablement, and dayam that was a weird string of nouns and verbs, they could do something similar with a baked-in “device management client” in a future build of Windows 10.  Why not?  Why have to deploy anything?  They have the target product AND the management tool under the same umbrella (sort of, but I heard someone unnamed recently moved from the MDT world into the Windows 10 dev world, so I’m not that far off).

Does any of this make sense?  Let me know.

 

The IT Professional’s PlayBook

cant_crash

So, you’re growing tired of trying to convince clueless managers about approving your requests to improve IT operations.  Maybe you’ve been doing it like this…

You: “Good morning sir/ma’am.  If we spend some money on upgrading our WAN links, we can get ahead of our backlog of projects by moving all our deployment processes out of the slow lane.”

Them: “I don’t know who WAN links is, but it sounds like Chinese food.  Go away.”

Maybe you should try rehearsing these tried-and-tested proven methods:

You: “Good morning sir/ma’am.  I ran the numbers and found we could save money by upgrading our WAN links.  A one-time cost of $14k would eliminate our need for additional infrastructure, license upgrades, controlled spaces, and lower power and cooling costs at all our remote facilities.  That alone would reduce our infrastructure costs by $5,000 per year, and cut our deployment times from weeks to hours.  The cost could be a tax deduction and we’d recoup that in less than three years.  And, are you losing weight?”

Them: “Yes, I’ve been working on my chip shot all weekend and I think it’s getting me in shape again.  I like you Bob….”

You: “It’s Ben.  Sir.”

Them: “Right, Bill, anyhow, it sounds like you think this WAN links guy is really that good?  Ok. I’ll approve him, if you think he can help with our taxes.”

Another example…

You: “Good morning sir/ma’am. I ran the numbers and it turns out we’re spending 150 hours per week installing apps by hand.  That’s 5 technicians over 150 hours at $7.50 per hour, oops, I mean $5.50 per hour.  That comes to $8,250 per week, and a backlog on other support requests in the queue.  We could spend a quarter of that packaging or wrapping the installers and procuring a product to help deploy them remotely.”

Them: “I like that idea.  We can then cut 3 of those technician’s jobs and reduce our burden rate at the same time!  Great work Bill!  You can call me Mike.”

You: “Actually, uh, no disrespect, but I don’t think we should cut…”

Them: “Consider it done Bobby!” (strong pat on the back)

And another…

You: “Good morning Ma’am.  I would like to request approval to replace Acronis and Ghost and all our other imaging tools with Microsoft Deployment Toolkit.  It’s free.  It’s very customizable.  It would allow us to reduce our image library from 43 individual images to 1 with a task sequence.  And it’s been around for years and battle tested.”

Them: “That sounds interesting.  But I spoke with Sam, who dates my daughter, and he says it’s better to maintain 43 image files every month because the extra care and feeding makes it an important job.  And he graduated from a 2 year tech school.  And he dates my daughter, so you know how that goes.  But really, Bobby, I appreciate your concern.”

You: “It’s Ben.  But thank you.”

And finally…

You: “Good morning Ma’am.  I heard about the big tax changes and how we’re going to save $20 million this year alone.  I was wondering if you had a few minutes to discuss some ideas I have about infrastructure improvements to help streamline our operations and save money?”

Them: “I’m glad you asked.  Yes, but it’s actually around $22 million.  And we already have plans to apply that towards automation, to reduce our dependency on human labor.  Oh, and what was your name again?”

Or, you could just consider a career in the legal or medical field.

ConfigMgr Script Deployments

Introduction

The following caffeine-induced mess was the result of a quick demo session conducted with a customer about the use of the new “Scripts” feature in Configuration Manager 1710+.  There are other examples floating about the Internet which are equally good, if not better, but just finished unpacking, doing laundry, walking the dog, and needed something to do.

What is it?

The new “Scripts” feature allows you to perform “real-time” execution of PowerShell scripts against a Device Collection or individual members of a Device Collection.  It is worth noting that you cannot deploy to individual Devices from within the Devices node of the console, it only works from within, and beneath, the Device Collections node.  The script is executed on the client remotely, so the shell context is local to the remote client.  This means if you instruct the code to look at C:, it will be looking for C: on the remote device(s).

What Can You Use This For?

The answer to this question depends on your intentions and personality.  If you’re an eager workaholic, the sky is the limit.  If you’re a diabolical evil bastard with malicious thoughts, the sky is also the limit.  Is this potentially dangerous?  Yes.  But EVERYTHING in life is potentially dangerous, even brushing your teeth and going for a walk.  So weigh your risks and proceed accordingly.  I’ve provided a few examples below to illustrate some possible use cases.  Read the disclaimer before attempting to use any of them.

Preliminary Stuff

The first thing you need is to have Configuration Manager 1710 or later.  The second thing you need is to check the box to “Consent to use Pre-Release features” (Administration / Site Configuration / Sites / Hierarchy Settings / General tab).  The third thing you need (for testing anyway) is to un-check the box right below it that says “Do not allow script authors to approve their own scripts”.  If you do not un-check that option, you will be able to create script items, but you won’t be able to deploy them.

The next step is to enable the pre-release feature “Create and run scripts”:  Administration / Updates and Servicing / Features.  Right-click “Create and run scripts” and select “Turn on”. Once you’ve enabled the feature, the first time at least, you may need to close and re-open the console.  This is not always the case it seems, but I have seen this most of the time.

The Process

Create the Script

Once everything is enabled and ready to go, you should be ready to destroy, I mean, ready to begin.

  1. Select “Software Library”
  2. Select “Scripts”
  3. Select “Create Script” on the ribbon menu at top-left (or right-click and choose “Create Script”)
  4. Provide a Name
  5. Import or Paste the script code (only PowerShell is supported as of now)
  6. Tip: Make sure your script code returns an exit code of some sort to indicate success/fail to ConfigMgr (example: Write-Output 0)
  7. Click Next, Next, and Close

Approve the Script

  1. Right-click on the Script item
  2. Select Approve/Deny
  3. Click Next (I still don’t know why, but you have to, at least for now)
  4. Choose “Approve” or “Deny” and enter an “Approver comment”
    NOTE: Many organizations have procedures that require documenting approval authorization directly on the change items involved with a given change.  And to change that would require changing the way you manage change, which would require change management to effect the change and change the way you’re changing things.
  5. Click Next, Next and Close

Deploy the Script

  1. Select Assets and Compliance
  2. Select Device Collections
  3. Navigate to an appropriate device collection
    1. To deploy the script to all members, right-click on the Collection and select “Run Script”
    2. To deploy the script to individual members, select ‘Show Members’, right-click on each member (resource) and select “Run Script”
  4. Choose the approved Script from the library listbox, and click Next
  5. Click Next again (safety switch, good idea!)
  6. Watch the green bar thing slide across the progress banner a few times
  7. When it’s done, review the pretty Bar Chart.

    Select the “Bar Chart” drop-down to change reports to “Pie Chart” or “Data Table” display.
  8. Change the “Script Output” selection to “Script Exit Code” to view results by exit code values.

Parameters

You can include parameter inputs within a script by including the param(…) block at the very top.  As soon as you type in param ( and then enter a variable name, like $MyParam, you should notice the ‘Script Parameters’ node appear in the left-hand panel below “Script”.  Remember to close the parentheses on param ().  This adds a new set of options that you’ll see when you click Next in the Create Script form.

This allows you to make scripts more flexible at runtime, so you can provide specific inputs as needed, rather than making a bunch of duplicate scripts with only minor variations between them.

Examples

So, here are just a few basic examples for using this feature.  You can obviously apply more brain juice to this and concoct way-more amazing awesomeness than this stuff, but here’s a taste.  These are provided “as-is” without any warranty or guarantee of fitness or function for any purposes whatsoever.  The author assumes no liability or responsibility for use,  or derivative use, of any kind in any environment on any planet in any universe for any reason whatsoever, notwithstanding, hereinafter, forthwith, batteries not included, actual results may vary, void where prohibited or taxed, past results do not indicate future performance.

Collect Client Log Files

# Collect-ClientLogs.ps1
# Modify $TargetPath to suit your needs
$SourcePath = 'C:\Windows\CCM\Logs'
$TargetPath = '\\CM01.contoso.com\ClientLogs$\'+$($env:COMPUTERNAME)
if (!(Test-Path $TargetPath)) { mkdir $TargetPath }
robocopy $SourcePath $TargetPath *.log /R:2 /W:2 /XO /MT:16
if (Test-Path $TargetPath) {
  Write-Output 0
}
else {
  Write-Output -1
}

Refresh Group Policy

# Refresh-GroupPolicy.ps1
GPUPDATE /FORCE
Write-Output 0

Modify Folder Permissions

# Set-FolderPermissions.ps1
param (
  [parameter(Mandatory=$True)]
  [ValidateNotNullOrEmpty()]
  [string] $FolderPath
)
if (Test-Path $FolderPath) {
  ICACLS "$FolderPath" /grant 'USERS:(OI)(CI)(M)' /T /C /Q
  Write-Output 0
}
else {
  Write-Output -1
}

Summary

If you haven’t looked into this feature yet, I strongly recommend you give it a try IN A TESTING ENVIRONMENT.

How’s my driving?

Did I miss anything?  Did you find any bugs?  Let me know!

Thank you for reading!