A little corner of the Empire on the web.

Showing posts with label scripting. Show all posts
Showing posts with label scripting. Show all posts

16 April, 2019

Retrieving Package Source Path/Application Content Locations from SCCM

We've been running with an old file server and a new file server for our SCCM package sources for a while now, with all the new software being put on the new server, and obsolete software being deleted from the old server as it goes out of use. Over time the old server should be used less and less as a package source, until it falls out of use and is decommissioned, but we really need to get a shift on with getting everything off the old hardware, and onto the new one. To make things easier for future moves the new one's referenced in all our systems with a fairly generic DNS name (rather than an old and obscure machine naming scheme), which should also make future server changes easier to manage.

So the first thing that I need to do is trundle through SCCM and work out what software is still using the old server as its source. There are a few main categories of bulk content that could be referencing files on that old server:

  1. Programs (using the Package Model)
  2. Driver Packages
  3. Applications (using the Application model)

Any of those could have their content files stored on either server. Packages are fairly easy as you can add a column to the console called "Package Source Path" and then sort by that. Applications are not so easy; each Deployment Type within an app has its own individual source path, and you have to drill right into the Deployment Type's properties to see it, there's no quick and easy way to see a list. (Obviously things like OS Images, Boot Images, etc could also be on the old server but we've only got single digit numbers of each of those, so they're just as quick to check one by one).

Packages (and Programs)

First the easy options, Packages and Driver Packages. Packages have one Package Source Path for the whole Package, which is used by each Program in the Package. All we need to do is use a cmdlet to retrieve all of the packages, then just pull out the properties from each that we're interested in, and finally dump that to a file that we can open in Excel and work our way through:

(All of the below PowerShell commands to be executed from a Config Manager PowerShell window, or a normal PowerShell command prompt with the ConfigurationManager module loaded and the current location changed to the Config Manager PSDrive for your site).


PS ABC:\> $Packages = Get-CMPackage
PS ABC:\> $Packages | Select-Object -Property PackageID, Name, PkgSourcePath | Export-Csv -Path C:\temp\PkgSrc.csv -NoTypeInformation

Driver Packages

These are very, very similar to normal packages. So similar that you may not even notice there's six extra letters in the initial cmdlet.


PS ABC:\> $DriverPackages = Get-CMDriverPackage
PS ABC:\> $DriverPackages | Select-Object -Property PackageId, Name, PkgSourcePath | Export-Csv -Path c:\Temp\DriverPkgSrc.csv -NoTypeInformation

Both of the above (Get-CMPackage and Get-CMDriverPackage) are relatively quick, they took less than a minute even running them from a remote machine against a site with a large number of [driver] packages .

Application model applications

Ok, here we go, here's the big one. Each Application can have multiple Deployment Types, each of those deployment types (which can each use a different deployment style) has its own, individual Content Location. To add to this, these queries are slower than their equivalents in the Package model (we're talking multiple minutes rather than seconds), long enough that there's some progress text output during this one, just to let you know that it's actually doing something. Then finally details of the app's Deployment Type(s) are all listed in a big XML property of the Application object, rather than each being their own object properties.

Here we are. Firstly we get the full list of apps from the site (which is the part that I've seen take 10 to 15 minutes on a site with around 800 app model apps), then iterate over them, pulling the relevant properties out of the XML (including iterating over each Deployment Type in that app), then finally output all of that as an object which we write to a CSV file:


$CSVOutPath = 'c:\temp\AppSrc.csv'
#Change to your site's ConfigMgr PSDrive before running this script

#Get each Application, then get the source path from each Applications's Deployment Types
Write-Host "Fetching application details - this takes a few minutes, please wait...`n"

$Applications = Get-CMApplication
$AppCount = $Applications.Count
Write-Host "$AppCount applications found.`n"

#Iterate over the apps list pulling out the details for each app. Takes a couple of minutes.
$i = 1
$AppSourceList = ForEach($App in $Applications)
{
    Write-Progress -Activity 'Checking apps and deployment types' -Id 1 -PercentComplete $(($i / $AppCount) * 100) -CurrentOperation "app $i / $AppCount"
    $PackageXml = [xml]$App.SDMPackageXML
    #An app can have multiple Deployment Types, each with their own source location. DT details are stored in the XML properties
    ForEach($DT in $PackageXml.AppMgmtDigest.DeploymentType) {
        $DtTitle = $DT.Title.'#text'    #need to quote property names with hashes in them, normal backtick escaping doesn't work
        $DtTech = $DT.Technology
        $DtLocation = $DT.Installer.Contents.Content.Location
        New-Object -TypeName psobject -Property (@{AppDisplayName = $App.LocalizedDisplayName; PackageID = $App.PackageID;
        CiId = $app.CI_ID; Enabled = $app.IsEnabled; Superseded = $app.IsSuperseded; HasContent = $app.HasContent;
        DepTitle = $DtTitle; DepTypeTech = $DtTech; DepTypeSrcLocation = $DtLocation})
    }
    $i++
}

#$AppSourceList | Out-GridView
$AppSourceList | Export-Csv -Path $CSVOutPath -NoTypeInformation

Download the Get-ApplicationSources.ps1 here: GitHub Get-ApplicationSources.ps1

(No download for the Package and Driver Package sources, as they're basically just one-liners).

09 April, 2019

SCCM Detection Rules for Dell BIOS Updates

I've been doing a bit of work lately on deploying Dell BIOS updates using SCCM. This is using SCCM's Application Model to deploy the updates out to live machines, and also using those to update BIOS firmware during OSD Task Sequences. I've been a through a few versions of SCCM app detection rules for these jobs. The problem is that WMI's SMBIOSBIOSVersion is a text field, so it's returned as and then treated as a string type. There's also nothing that standardises how manufacturers fill in that text field, so they can use whatever versioning scheme they want, with whatever text they want around it. So different manufacturers do it different ways and they often change it between models.

Dell (at least for their Optiplex and Latitude business products) seem to use two different BIOS version number schemes at the moment, they seem to use one on older models, and the other for newer models, and they each have their own challenges. The first scheme uses an "A" followed by a two digit integer, counting up from zero, eg "A00" followed by "A01" then "A02" with "A09", "A10", "A11" being later in the series. The second scheme is a three part version number separated by dots (full stops, periods whatever your region calls them), eg "1.0.0", then "1.1.0" and later releases may be "1.6.2", "1.9.3", "1.11.2".

Taking them one by one, the problem with the "A00" scheme is that it's a mix of letters and numbers, so you can't do a purely numeric comparison to get the larger number, but at least a string greaterThanEqualTo (-ge) comparison of them does work, and you can always remove the leading "A" and then just do a numerical comparison.

Unfortunately the "1.0.0" type scheme works perfectly with a string greaterThanEqualTo (-ge) comparison right up to point where any of the numbers in it become double digits, so '1.2.0' -ge '1.0.5' returns True, whereas '1.12.0' -ge '1.3.5' unexpectedly returns False. This is because it's comparing each digit in the number one by one, so the first digit "1" is the same in both, then the dot is the same in both, then "1" is less than "3", so the greater than test fails, even though 12 is bigger than 3 it only looks at the digits individually.

The end result of this is that I'm now using the following PowerShell script in a script detection rule for Dell's BIOS updates. You just put the version number of the BIOS version that you're deploying in the variable in the first line then it parses it all correctly from that.

$NewBiosVer = '1.13.0'
$CurrentBIOSVer = (Get-WmiObject -Class Win32_BIOS).SMBIOSBIOSVersion
#Check if this is old-style Dell BIOS versioning "Ann" or new-style "n.n.n"
If ($NewBIOSVer.StartsWith('A') -and $CurrentBIOSVer.StartsWith('A')) {
    If ($CurrentBIOSVer -ge $NewBIOSVer) {
        #To indicate software is found, return something in STDOUT
        Write-Host "BIOS Version $CurrentBIOSVer found"
    } else {
        #To indicate software not found, don't return anything to STDOUT or STDERR
    }
} ElseIf (($NewBiosVer -match "^[\d\.]+$") -and ($CurrentBIOSVer -match "^[\d\.]+$")) {
    #Check if we've got a problem with two digit decimals (where 1.9 is less than 1.10 in version numbers, but not numerically)
    $NewBiosVerArray = $NewBiosVer.Split('.')
    $CurrentBIOSVerArray = $CurrentBIOSVer.Split('.')
    If ([int]$CurrentBIOSVerArray[0] -ge [int]$NewBiosVerArray[0]) {
        If ([int]$CurrentBIOSVerArray[1] -ge [int]$NewBiosVerArray[1]) {
            #To indicate software is found, return something in STDOUT
            Write-Host "BIOS Version $CurrentBIOSVer found"
        }
        Else {
            #To indicate software not found, don't return anything to STDOUT or STDERR
        }
    } else {
        #To indicate software not found, don't return anything to STDOUT or STDERR
    }
} Else {
    #Doesn't fit either BIOS version format???
    #To indicate software not found, don't return anything to STDOUT or STDERR
}

For the task sequence step's run conditions I'm doing two WQL queries, one for the device's model, and the second a simple check that the BIOS version is not the one that the update's looking for (so that machines on our nominated version skip the step) and then the Application's detection rules check fully whether the currently installed version is older or newer than the one we're installing (using the above script).

04 July, 2017

Retrieve LAPS (Microsoft's Local Admin Password Solution) details using VBScript

History

We've been Microsoft's LAPS (Local Admin Password Solution) as a solution to manage the local administrator passwords on the majority of our servers (excepting domain controllers, for obvious reasons) for a while now. The fact that we'd been living with all of our servers having one or two incredibly simplistic passwords that hadn't changed for the best part of a decade, was giving us so many potential problems, from the ex-employee knowing the all the keys to the kingdom problem, to the straightforward pass-the-hash attack, and LAPS has worked well to mitigate those problems. For our server admins the options of reading the raw password from ADU&C, or using the PowerShell module, or even using the barebones LAPS GUI were fairly straightforward.


The Present

Now we're expanding our LAPS usage to manage the local admin passwords on all of our desktops and laptops. I know that most companies roll LAPS out to their laptops first and their servers last, but the fact that we'd been regularly changing the admin passwords on our laptops and desktops roughly twice a year for years meant that it wasn't needed so badly there.


For our desktop admins, and our helpdesk staff, we can't just point them at the installer for the PoSh module and leave them to it, so we're giving them the LAPS GUI client, but also integrating LAPS password lookup into some of the existing tools that we provide them. This means that I needed to retrieve the LAPS info using VBScript, rather than PoSh, to integrate into one of these tools. All of the examples in the LAPS documentation, and in everyone else's web write-ups of LAPS, use PoSh, so I had to pull this together from a few other scripts and tools that I'd used.




Download


Download the script here: GitHub: LAPS-Password.vbs



11 July, 2016

Find objects within folders in the SCCM Console hierarchy using PowerShell

Frustration


Ever since Microsoft's SCCM got the new-style System Centre console with SCCM 2012, I've been massively frustrated about how difficult it is to find items in the console once you've organised it with folders. Probably not too much of a problem if you're a lone SCCM admin, but as someone who works as part of a larger team who all use SCCM for slightly different reasons it can be a nightmare finding things like collections buried deep in the folder hierarchy.


Yes, I know that the console has a search function, and I know that there's a subtle "All Subfolders" button that magically appears on the Search ribbon bar sometimes. However that "All Subfolders" button has a few major flaws: 1) it doesn't always appear when it ought to, leaving you in a dance of clicking in the search text box, clicking away, clicking back in there, clicking away to another part of the console, clicking back, all the while crossing your fingers that the button will show up; 2) annoyingly clicking the "All Subfolders" button clears out any text that you may have foolishly typed into the search bar already before you remembered that you wanted to search the whole tree; 3) most importantly, the search results don't give you any information about the folder path that it found each item on, so it doesn't help you find that item (or similar items that may be in the same folder) quickly next time.


SCCM console rant over.


Journey


So over time I started casually looking around for ways to access this folder hierarchy info. These days there's a regularly updated set of SCCM PowerShell Cmdlets provided by the ConfgMgr team, this seemed an obvious place to start. Unfortunately, none of them even acknowledge that console folders exist. Next I set off on a hunt through WMI on the site server (using one of my favourite tools WMI Explorer (WMI Explorer downloads), but I couldn't any folder related properties on the collections/packages/etc themselves and couldn't find any obvious looking WMI classes. Real desperation now, open up SQL Management Studio and hunt through the Views in the site database. Nothing obvious. Brick wall hit. I went away and worked on other stuff for a while.


Enlightenment

Then early last week I came across this blog post by Peter van der Woude, that my previous searches had somehow missed: More than just ConfigMgr: Get the folder location of an object in ConfigMgr 2012 via PowerShell. Finally, answers - not only did that post contain this magic phrase: A good thing to know is that all folder information is stored in two classes in WMI, SMS_ObjectContainerNode and SMS_ObjectContainerItem. These two classes are the same for every object type., but it also had a working script linked, find his script in the TechNet gallery here: TechNet Gallery: Get the location of an object in the console via PowerShell


Moving Beyond


As great and useful as Peter's script was, it does have a minor flaw, and doesn't handle some edge cases. So I set off to use his information to write my own. The minor flaw was that his script requires hard coding your SMS Site Code and Site Server names into it. I can see the reason for that, especially as it means that his script has no external dependencies (other than the ConfigMgr infrastructure, obviously) but it still bugged me. More problematically, I hit an edge case that it didn't handle within my first few uses of it. Some objects (of different types) in SCCM can share the same SCCM IDs. So a Collection, a Query and a Package could have the same 8 character ([site code][5 hex digits]) ID, as all of those objects have their own numbering starting from XXX00001 and counting upwards. So using Peter's script, you don't always find the object that you're actually looking for, and, as his doesn't echo the name or type of the object that it's showing the hierarchy for, it can get quite confusing if it's not showing the info for the item that you're expecting.


Nirvana (or Farvana)


So, I've written my own PowerShell function to do this: Get-SCCMObjectLocation, find the code on GitHub, here GitHub: Get-SCCMObjectLocation.ps1. There's a bit more code here than Peter's versions, but this now outputs the object's name and type (so that you know you haven't made any typos, and you really have found the droids object you were looking for), and if there are multiple objects with the same ID, it lists them one by one. It also gets the site code and site server name from the ConfigurationManager PowerShell provider, though this does mean that you need the SCCM console installed on your machine, and you need to change to ConfigurationManager's PSDrive before running it.


Usage:
PS C:\>Get-SCCMObjectLocation -SMSId "ABC001BB"
root\Racked Server Drivers\Dell\PE1950-Microsoft Windows 2008 R2 SP1-OM7.3  [SMS_DriverPackage]
PS C:\>"ABC000CC" | Get-SCCMObjectLocation
WARNING: Multiple objects with ID: ABC000CC
root\x64\Test\Win 7 Ent x64 with Office 2010  [SMS_ImagePackage]
root\Servers\Server roles\All Domain Controllers  [SMS_Collection_Device]
PS C:\>Get-SCCMObjectLocation -SMSId "ABC00166" -SiteCode ABC -SiteServerName sms01.example.com
root\Application Deployment\MS Access App-V  [SMS_Collection_User]

Download

Download the script here: GitHub: Get-SCCMObjectLocation.ps1



Music to get through this with: Faithless - Faithless 2.0

12 June, 2013

Kindle Weather pt2 Setting up the server

Back to the first post in this series

Setting up the server


Matthew Petroff's original scripts assume that you're using a Linux server, they're made up of a BASH shell script (that won't do anything on a Windows box) and a Python script (that needs an extra install of 3rd party software to get it working on Windows), as well as relying on a couple of small tools that either aren't available to us, or don't work as well on Windows.

So to get things compatible with pretty much any Windows box, I've rewritten the shell script as a batch file and the Python script as VBScript, both of those should just work on anything from Windows 2000 upwards, and I've found some replacements for the small tools.

What you'll need for this section:

Create a new folder somewhere on your server (preferably outside of your web server's root) and download and extract the zip file into it. This should give you three folders, one of which is called "Server-VBScript", and in there are three folders called "ServerNOAA", "ServerWU" and "ServerMet" inside each of these you'll find an SVG, CMD and a VBS file. Now you need to decide which weather service you'll be getting your forecasts from, they all make their data freely available for non-commercial usage but have different coverage, may differ slightly in their forecasts of different areas and provide slightly different info, whichever one you want copy that service's folder into the new folder on your server:

  • NOAA - the US Government's NOAA's National Weather Service. The major downside (for me) is that they only provide forecasts for the USA.
  • WU - Weather Underground, run from a US University, they provide worldwide coverage, and in multiple languages
  • Met - Her Majesty's Government's Meteorological Office (The Met Office) forecasts. They only cover the UK (this is what I'm using on my Kindle)

Pick whichever weather service you want, and feel free to delete the other folders if you don't want them.

Weather Services


Depending which weather forecast service you pick, you'll need to find your location code, and possibly sign up for a (free) API key to use it, I'll go though these:

NOAA
All you need is to find the latitude and longitude of your location, and change these in the long URL inside the ServerNOAA\weather-script.vbs file using a text editor, also note that the final parameter in that URL "&Unit=e" sets it to the temperature in Fahrenheit, changing it to "&Unit=m" will output the temperatures in Centigrade.

WUnderground
You will need to sign up for a developer account at Weather Underground, and then you can create a (free for the usage level that we'll be putting it to) API key to access the weather reports. Once you have that you just need to work out your city's code, acceptable formats are:
CA/San_Francisco | US state/city
60290             | US zipcode
Australia/Sydney  | country/city
37.8,-122.4       | latitude,longitude

Then open up ServerWU\weather-script.vbs file using a text editor, find the line with the long URL in it and replace "APIKEY", "COUNTRY" and "CITY" with your API key and location.

Met Office
As with WUnderground, you'll need to sign up for a free API Key to use the Met Office's forecasts, once you have that you can visit the following URL to get a list of the location codes that you can use:
http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/xml/sitelist?key=API_KEY
Take the 6-digit location code from there, and your API key, and plug them into the long URL in the ServerMet\weather-script.vbs file using a text editor.

If you've got everything set up right, then when you double-click the VBS file it should create an SVG file in the same folder, showing your graphical weather forecast. You can view this SVG file with most web browsers.

Getting the Image Tools

(Side-story, looking for a suitable replacement for the RSVGconvert *nix tool to do command-line SVG to PNG conversions I went straight to my old, faithful ImageMagick, but every version of that and every option I tried seemed to choke on the embedded icons, eventually I came across Inkscape and discovered it had a decent command line interface, so here we are)

Next download Inkscape SVG editor and extract it into an Inkscape folder in your server's weather folder.

Then download PNGCrush and save the file in your server's weather folder.

Your folder structure should now look a bit like this (the "public" folder won't have been created yet):


Then go into the folder for the weather provider you're using, open the CMD file and edit it to reflect the paths of all these tools, and change the references to the "public" folder to point to the location of the folder within your web server's root that will be serving up the PNG image.

Now try running the CMD file in your chosen folder by double-clicking it, and with luck after a few seconds your 'public' folder should now contain a PNG image that looks a bit like this:

typical English weather

Scheduled Tasks


Finally you'll need to decide how often you want your weather image to update and set up a Scheduled Task in Windows to run the relevant CMD file as often as you want it. Personally I'm going to have my Kindle updating its weather display 4 times a day, so I'm setting my scheduled task to run every 6 hours.


Download the Scripts

  • GitHub project: all files for the project browsable online, and individually downloadable
  • Entire project master.zip (72KB zip compressed file): if you don't know what a GitHub project is, use this link to download everything needed in one go.


Update 07/2016: files and download moved to GitHub, all previous updates merged in.



Next: Setting up the Kindle.


  1. Introduction
  2. Setting up the server
    • Weather Services
    • Getting the Image Tools
    • Scheduled Tasks
    • Download the Scripts
  3. Setting up the Kindle
    • Jailbreak your Kindle
    • Setting up USB Networking (1)
    • Setting up USB Networking (2)
    • Installing Kite on the Kindle
  4. Putting it all together
    • Setting up the Weather Display on the Kindle
    • Setting up Cron on the Kindle
    • Useful References and acknowledgments
    • Todo