Category Archives: DCM Script Library

DCM Scripts – Checking Windows Activation Status

My last script was, out of necessity, a rather laborious one. Using a VBScript to check a status, generating a file from its output, reading files, creating objects and properties etc. Luckily enough, checking Windows activations (and checking Office activations on Windows 8.1) is considerably easier.

There is a WMI class called SoftwareLicensingProduct which is where Windows just happens store the activation status for itself and on Windows 8, for Office as well.

To detect the activation status for Windows itself, use this:

$WindowsActivationStatus = Get-CimInstance SoftwareLicensingProduct -Filter "Description LIKE '%KMSCLIENT%' and Name LIKE '%Windows%'" | select ID, Description, LicenseStatus, Name, GenuineStatus

if ($WindowsActivationStatus.LicenseStatus -eq "1") {
 echo "Windows is Activated"
 }
 else {
 echo "Windows is not activated"
 }

And use this as a remediation script:


c:\windows\system32\cscript.exe c:\windows\system32\slmgr.vbs /skms address.of.kms.server.domain.name:1688
c:\windows\system32\cscript.exe c:\windows\system32\slmgr.vbs /ato

Strictly speaking, the first line shouldn’t be necessary if you’ve set KMS up properly but including it does at least force the machine to look at the correct server for activation.

Set up a compliance rule to look for a string which says “Windows is activated”, create a new baseline or add it to a new one and deploy to a collection.

 

 

DCM Script – Detect Office Activation Status on Windows 7 and Activate if Unactivated

This one was a lot of fun and by “fun”, I mean a complete pain.

Recently, several of my helpdesk calls have been along the lines of “When I open Word, it says that it needs activating”. As I’d hope most people with more than 20 PCs to manage do, we use a Key Management Services (KMS) Server to activate all of our Windows and Office clients. Windows and Office are supposed to activate themselves either during the build process or very soon afterwards. However, the PCs need to phone back to the KMS server every 180 days to remain activated so either the PC hasn’t activated Office during the build process or its activation ticket has expired and it hasn’t managed to get a new one. Therefore, I needed a way to detect whether Office is activated on a computer and activate it if it wasn’t. Detect a state? Remediate it if it isn’t in a desired state? Hmm, this sounds like something thats perfect for DCM! So I went a-looking, seeing what I could see.

First of all, this post is written for 64 bit machines which are running 32 bit Office. However, if you’re running 64 bit Office or 32 bit Office on 32 bit Windows, it’s just a matter of adjusting the paths for the Office VBS script accordingly.

At first, I hoped that I could use pure PowerShell to fix this. There is a very handy CIM instance called SoftwareLicensingProduct which lists the activation status for the Microsoft products installed on your computer. I thought a simple Powershell command like

Get-CimInstance SoftwareLicensingProduct -Filter "Description LIKE '%KMSCLIENT%'" | select ID, Description, LicenseStatus, Name, GenuineStatus

would give me a nice base to work from. On my Windows 8.1 machine, it does; it lists all of the KMS products on your PC and their activation statuses. However, on Windows 7, that CIM instance only lists the operating system, not Office and unfortunately Windows 7 is what is installed on the vast majority of the computers in my workplace. So that meant going back to the drawing board.

I needed another way to get the activation status for Office. From Office 2010 onwards, there is a VBS script called ospp.vbs. It needs to be run with the cscript interpreter as it’s purely command line rather than GUI driven. There are several switches for it which perform operations like attempting an activation, clearing the activation status, setting the KMS server name and port and displaying the activation status of the various Office products. Running the following command:

cscript "C:\Program Files (x86)\Microsoft Office\Office 15\ospp.vbs" /dstatus

returned the following output on my PC with Office 2013 Pro Plus, Project 2013 Standard and Visio 2013 Pro installed on it:

---Processing--------------------------
---------------------------------------
SKU ID: 427a28d1-d17c-4abf-b717-32c780ba6f07
LICENSE NAME: Office 15, OfficeProjectStdVL_KMS_Client edition
LICENSE DESCRIPTION: Office 15, VOLUME_KMSCLIENT channel
LICENSE STATUS: ---LICENSED---
REMAINING GRACE: 177 days (256304 minute(s) before expiring)
Last 5 characters of installed product key: 8QHTT
Activation Type Configuration: ALL
KMS machine name from DNS: kmsserver.domain:1688
Activation Interval: 120 minutes
Renewal Interval: 10080 minutes
KMS host caching: Enabled
---------------------------------------
SKU ID: b322da9c-a2e2-4058-9e4e-f59a6970bd69
LICENSE NAME: Office 15, OfficeProPlusVL_KMS_Client edition
LICENSE DESCRIPTION: Office 15, VOLUME_KMSCLIENT channel
LICENSE STATUS: ---LICENSED---
REMAINING GRACE: 177 days (256304 minute(s) before expiring)
Last 5 characters of installed product key: GVGXT
Activation Type Configuration: ALL
KMS machine name from DNS: kmsserver.domain:1688
Activation Interval: 120 minutes
Renewal Interval: 10080 minutes
KMS host caching: Enabled
---------------------------------------
SKU ID: e13ac10e-75d0-4aff-a0cd-764982cf541c
LICENSE NAME: Office 15, OfficeVisioProVL_KMS_Client edition
LICENSE DESCRIPTION: Office 15, VOLUME_KMSCLIENT channel
LICENSE STATUS: ---LICENSED---
REMAINING GRACE: 177 days (256304 minute(s) before expiring)
Last 5 characters of installed product key: RM3B3
Activation Type Configuration: ALL
KMS machine name from DNS: kmsserver.domain:1688
Activation Interval: 120 minutes
Renewal Interval: 10080 minutes
KMS host caching: Enabled
---------------------------------------
---------------------------------------
---Exiting-----------------------------

Apart from the KMS Server, that output is verbatim. There is some very useful information in there; the product license, the activation information, the KMS server it’s using to activate, how long the activation has left. It’s great! Unfortunately it’s also a big lump of text which isn’t especially useful by itself.

At this point, I could have just created a package which ran

cscript "C:\Program Files (x86)\Microsoft Office\Office 15\ospp.vbs" /act

and called it a day. It certainly would have worked to an extent but I still wanted to use DCM. Using DCM would have been better because:

  • I can, in theory, set it to detect whether Office needs activating and only run the activation script if it’s not whereas using a package with that command line in it will attempt activation of Office whether it needs activating or not
  • Using a package would be a set-once kind of affair, if Office decides to deactivate itself or fails reactivation after the KMS grace period expires, using a package won’t allow the script to re-run whereas using DCM, I can re-run the detection script every hour, every day, every week, every month or whatever

So I turned back to PowerShell and, eventually, came up with this:

C:\Windows\System32\cscript.exe 'C:\Program Files (x86)\Microsoft Office\Office15\OSPP.VBS' /dstatus | Out-File $env:temp\actstat.txt

$ActivationStatus = $($Things = $(Get-Content $env:temp\actstat.txt -raw) `
                            -replace ":"," =" `
                            -split "---------------------------------------" `
                            -notmatch "---Processing--------------------------" `
                            -notmatch "---Exiting-----------------------------"
                       $Things | ForEach-Object {
                       $Props = ConvertFrom-StringData -StringData ($_ -replace '\n-\s+')
                       New-Object psobject -Property $Props  | Select-Object "SKU ID", "LICENSE NAME", "LICENSE DESCRIPTION", "LICENSE STATUS"
        })

$Var = "Office Activated "
for ($i=0; $i -le $ActivationStatus.Count-2; $i++) {
    if ($ActivationStatus[$i]."LICENSE STATUS" -eq "---LICENSED---") {
        $Var = $Var + "OK "
        }

    else {
        $Var = $Var + "Bad "
        }
        }

If ($Var -like "*Bad*") {

    echo "Office Not Activated"
}
else
{
    echo "Office Activated"
}

That script runs the Office activation VBScript and saves the output to a text file in the user’s TEMP directory. It reads the created text file and dumps the entire lot into a variable called Things (I was experimenting, I couldn’t think of a better name once I had finished and hey, it worked! If it ain’t broke don’t fix it). It converts the text file into a series of PowerShell objects using the series of dashes to separate them, replaces any colons with equals signs and excludes the “Processing” and “Exiting” lines. It uses the ConvertFrom-StringData command to add and populate properties on the objects which is why the colons needed replacing. It then selects the particular properties that I’m interested in. The whole lot gets put into a array called ActivationStatus which I can now use to do what I need to do.

The script creates another object called Var and pre-populates it with a bit of random text. It runs through all but the last object in the ActivationStatus array (If you look at the text file output, you’ll see that the series of dashes appears twice at the end so my little routine creates a blank but not null object at the end of the array) and checks to see if the “LICENSE STATUS” property is equal to ‘— LICENSED —“. If so, it appends “OK ” onto the end of Var, if not it adds “Bad “. Finally, the script looks at Var and sees if the word “Bad” appears in it. If so, it echos back to ConfigMgr that Office is activated or not activated.

The remediation script looks like this:

cscript "C:\Program Files (x86)\Microsoft Office\Office 15\ospp.vbs" /act

Simple, no?

When you’ve created the Detection and Remediation scripts inside ConfigMgr, create a Compliance Rule which looks for a string called “Office Activated”. Then, as always, either create a new baseline and deploy it to a collection or add it to an existing one.

DCM Script – Detect if a Mac is a Member of the Domain and Join If Not

As I’ve said before, Macs can be a pain in the backside when you’re trying to manage a lot of them. One of the particular bugbears that I’ve found is that they have a habit of unbinding themselves from your Active Directory domain for no apparent reason. Usually this would mean a helpdesk call because someone can’t log on and disruption and annoyance and well, you get the idea.

This script is a bit of a kludge. My Bash isn’t the best by any stretch of the imagination and I’ve put detection and remediation into the same snippet as for some reason, I couldn’t get a separate remediation script to work. No matter. It’s not ideal but it still works. Anyway, the script looks like this:


DOMAIN_STATUS=$(dsconfigad -show | awk "/Active Directory Forest/" | cut -d "=" -f 2)"_Member"
if [[ ${DOMAIN_STATUS} == "{{domain.fqdn}}_Member" ]]; then

echo "OK"

exit 2 # already a domain member, exit script

fi

dsconfigad -add {{domain.fqdn}} -user {{user}} -password {{password}} -force
EXIT_CODE=$(echo $?)

if [[ ${EXIT_CODE} != 0 ]]; then

echo "There was an error. Code is " $EXIT_CODE
exit ${EXIT_CODE}

fi

echo "OK"

Change anything in dual braces to reflect your environment.

The script runs a command called dsconfigad which gets information about the Active Directory domain that the Mac belongs to. It trims out the FQDN of the domain, appends _Member onto the end of it and adds it to a variable. I’m adding _Member to the end of the string because if the Mac isn’t a member of a domain, dsconfigad returns a null value and the variable doesn’t get created.

The script compares the output with what it should be. If it matches, it returns “OK” to ConfigMgr and exits. If not, it joins the Mac to the domain and returns “OK” to ConfigMgr. If for some reason the domain join fails, the script sends the error code back to ConfigMgr.

As always, you set the detection rule to look for a string called “OK”, add the rule to a new or pre-existing baseline and deploy the baseline to a collection. After you do, any Mac which is managed by ConfigMgr but which is not a member of your domain will find itself joined.

As I say, I know that my Bash scripting skills are fairly minimal so if you see a better way for this script to work, please feel free to contact me. The usual “I’m not responsible if this script hoses your Mac and network” disclaimers apply.

DCM Script – Detect and disable Intel Graphics Card Service

As I imagine the majority of corporate PCs do these days, all of the computers at my workplace have integrated Intel graphics chipsets. And why not, for a business PC they’re perfectly adequate; their 3D acceleration is good enough for Aero on Windows 7 and for anything else the vast majority of users need.

However, there is a rather… annoying feature of the drivers which I like to suppress. The driver puts an application into the System Notification Area which makes it easy for people to mess around with graphical settings and which lets them change the orientation of the screen by pressing certain key combinations. I’m sure that for a lot of corporate settings this isn’t too much of a problem but for a school or college it generates a lot of helpdesk calls because the little sods darlings like hitting those keys and turning the screens upsidedown.

Anyway, this DCM script detects whether the service is running and kills and disables it if it is

$IntelGFXService = Get-Service | Where-Object {$_.Name -like 'igfx*'}

 if ($IntelGFXService -ne $null) {

    $IntelGFXServiceName = $IntelGFXService.Name
    $IntelFGXStartupMode = Get-CimInstance Win32_Service -Filter "Name='$IntelGFXServiceName'"
    $IntelGFXService.Status
    $IntelFGXStartupMode.StartMode

       if ($IntelGFXService.Status -eq "Running" -and $IntelFGXStartupMode.StartMode -eq "Auto")
        {
            echo "Service Started, Startmode Automatic"
        }
       elseif ($IntelGFXService.Status -eq "Stopped" -and $IntelFGXStartupMode.StartMode -eq "Auto")
        {
            echo "Service Stopped, Startmode automatic"
        }
       elseif ($IntelGFXService.Status -eq "Running" -and $IntelFGXStartupMode.StartMode -eq "Disabled")
        {
            echo "Service Started, Startmode Disabled"
        }
       else
        {
            echo "all disabled"
        }

}
else
{
    echo "all disabled"
}

That checks the status of the service and reports the status back to ConfigMgr. The remediation script looks like this:

$IntelGFXService = Get-Service | Where-Object {$_.Name -like 'igfx*'}

Set-Service -Name $IntelGFXService.Name -StartupType Disabled
Stop-Service -Name $IntelGFXService.Name
get-process igfx* | stop-process

That stops the service, disables it and kills any relevant processes running alongside the service.

Set the compliance rule to look for a string called “all disabled” and apply the rule to either a new or existing baseline. That’s it for today!

DCM Script – Disable On-Board Sound Card if USB Device is Attached

The first script in my new library is one that I am quite proud of as it was the first that I created to solve a relatively complex problem. It came to be because of the Music department at the college that I work at. The PCs in their department have external USB sound cards for students to plug MIDI instruments into and other such things (Hell, I’m not a musician, I don’t understand what they’re for exactly!). The problem was that Sonar, the music sequencing software that they use, was giving them trouble when both the on-board audio and the USB device was enabled. They therefore wanted me to disable the on-board sound card in the machines so that it wouldn’t happen again.

I could have gone to each of their PCs and just disabled the onboard sound in the BIOS or in Windows but that would be a short term fix; if the PC got replaced or rebuilt the person doing that would have to remember to disable it again. I therefore wrote this:


$SoundDevices = Get-CimInstance Win32_SoundDevice

if ($SoundDevices.DeviceID -like "USB*")
    {
         #USB Sound Card detected, will now check to see if on-board HDAUDIO is still active
         $HDAudio = Get-CimInstance Win32_SoundDevice -Filter 'DeviceID LIKE "HDAUDIO%"'
         $AudioStatus = $HDAudio.StatusInfo
         If ($AudioStatus -eq '3')
             {
                 #On-board still active, need to disable
                 echo "USB Audio detected, on-board audio needs to be disabled"
             }
         else
             {
                 #USB detected, on-board disabled
                 echo "OK"
             }
    }
else
    {
        #No USB, onboard sound to be left alone
        echo "OK"
    }

 

This script queries WMI to find out what sound devices are installed in the machine. If it detects one with USB in the device string, it goes on to see if the onboard HDAUDIO device is enabled. If it is, it sends a string back to ConfigMgr saying that remediation needs to happen. If the on-board is disabled it sends “OK” back to ConfigMgr. If there is no USB audio device installed at all, it sends “OK back to ConfigMgr.

The remediation script is quite simple. Since these are Windows 7 machines, there is no PowerShell way of managing hardware. I therefore had to use the old DEVCON command. The remediation script therefore looks like this:

%Path_to_file%\devcon.exe disable HDAUDIO\*

That disables any device with HDAUDIO at the beginning of its device ID.

Set the compliance rule on the DCM script to look for a string that says “OK” and that’s it. Then add the rule to a existing baseline or create a new one and deploy it to a collection.

%d bloggers like this: