Friday, December 8, 2017

Query to search for MAC addresses with wildcard

Sometimes a machine doesn't want to PXE boot for imaging, doesn't show up in a hostname search, or has stale records in SCCM and we need to delete the object(s) to do what we want to do.  That's where a MAC address query can come in.  This query can also utilize wildcard searches using a "%" sign.  This can help if you are given a blurry phone picture of a MAC address and aren't able to determine if that's an '8' or a 'B', or any other characters.

select distinct SMS_R_System.MACAddresses, SMS_R_System.Name from  SMS_R_System where SMS_R_System.MACAddresses like ##PRM:SMS_R_System.MACAddresses## order by SMS_R_System.MACAddresses
Add this to your queries under Monitoring > Overview > Queries and run it as needed.


Managing IP Boundaries using IP Ranges

In our environment we consistently had IP boundary issues utilizing IP subnets, and recently discovered (via our network admins) that IP ranges would be considerably better than IP Subnets.  I had already been in the midst of converting the environment from IP Subnets to IP Ranges when I came across this article which proved the theory.  Therefore, here are some basic methods to start using IP ranges in your environment.

Convert SCCM Boundaries from IP Subnets to IP Ranges

First, you'll want to find a basic IP Calculator - there are many on Google although the one linked is what I used.  You'll also need access to the basic network layout of your environment - preferably acquired from your network admin.  The network layout should include either IP subnets with subnet masks (e.g. 255.255.240.0), or subnets with bit length (e.g. 10.81.64.0/20).  If you are given only subnets with subnet masks and not bit length, use a Subnet Mask Cheat Sheet to determine the bit length with subnets.  Then use the IP Calculator to determine the IP Range.  In SCCM the easiest way to convert is to go to the Properties of the Subnet Mask (under Administration > Hierarchy Configuration > Boundaries), change the Type dropdown to IP Address Range, then enter your IP ranges there. (If you create new IP Range entries alongside the IP Subnet entries, you'd still need to modify the new IP Ranges to add your Boundary Groups).

You will notice a pattern and probably remember the pattern as you do more subnets.  For me, it was that a /20 mask meant I would simply add 15 to the 3rd octet, so, for example, the complete range for a 10.50.32.0 subnet would be 10.50.32.1 to 10.50.47.254 (32+15=47).  Note that if you are doing /24 masks, it's pretty much pointless to convert since the range will be only that subnet anyway (e.g. 10.50.32.1 to 10.50.32.254).

Create a Device Collection based on IP Ranges in SCCM

If you'd like to create a collection based on IP Ranges, the best method is to use the subnet method with a range.  I have seen methods (i.e. here) which actually do not always work correctly since the way WQL works, it calculates only single digit numbers when sorting.  For instance, if your IP range is 10.50.96.1 to 10.50.111.254, and you setup the query as "greater than or equal to 10.50.96.1" and "less than or equal to 10.50.111.254", it will return 0 results, because WQL only reads the first digit in the 3rd octet (9) and compares that with the first number in the 3rd octet comparison (1).  So the method in that link will only work for specific IP ranges and not all of them.

Here's some examples on the correct way to use subnets with a range in your WQL query:

For a given IP subnet of 10.177.160.0/20:
select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.IPSubnets like "10.177.16[0-9].0" or   SMS_R_System.IPSubnets like "10.177.17[0-5].0"
The actual range in the example is 10.177.160.1 through 10.177.175.254.  Since WQL works with single-digit numbers, this method must be used and can get a bit complicated.  

Here's another example for a multiple subnet entry of 10.124.0.0/20 and 10.124.16.0/21:
select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.IPSubnets like "10.124.[0-9].0" or   SMS_R_System.IPSubnets like "10.124.1[0-9].0" or   SMS_R_System.IPSubnets like "10.124.2[0-3].0"
The ranges for this example are 10.124.0.1 through 10.124.15.254 and 10.124.16.1 through 10.124.23.254.  This can be combined to a range of 10.124.0.1 through 10.124.23.254, then is converted into the WQL above.

Note that it's not possible to just use 10.124.[10-19].0 in the above WQL query because of WQL's design to use single digits. 

Furthermore, if you have multiple ranges, you can go a bit further:
select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.IPSubnets like "10.172.9[6-9].0" or SMS_R_System.IPSubnets like "10.172.1[0-1][0-9].0" or SMS_R_System.IPSubnets like "10.172.12[0-7].0"
With subnets of 10.172.96.0/20, 10.172.112.0/21, and 10.172.120.0/21, the subnets are converted and then shown in the above WQL.  

Thursday, November 2, 2017

SQL Query logic - Report on Collections based on Non-compliance of Configuration Baselines

Here is a quick trick on how to create a report based on a Configuration Baseline's Non-compliance utilizing only a collection ID.  This assumes the Config Item and Baseline are already created, as well as the Collection based on Non-Compliance of the baseline.  The report can then be generated based on the below query, then subscribed to, to check results over time.  The only thing needing editing in the query is the CollectionID in bold.

select all dbo.v_Collection.Name AS Location,
collection.Name AS [Computer Name],
    dbo.v_GS_SYSTEM_ENCLOSURE.SerialNumber0 AS [Serial Number],
    dbo.v_GS_COMPUTER_SYSTEM.Manufacturer0 AS [Manufacturer],
    dbo.v_GS_COMPUTER_SYSTEM.Model0 AS [Computer Model],
"Client" = CASE
WHEN collection.ClientType = 1 THEN 'Yes'
WHEN collection.ClientType = 0 THEN 'No'
ELSE ''
END,
    "Operating_System" = CASE
    WHEN collection.DeviceOS LIKE 'Microsoft Windows NT Workstation 6.1%' THEN 'Windows 7'
    WHEN collection.DeviceOS LIKE 'Microsoft Windows NT Workstation 10.0%' THEN 'Windows 10'
    WHEN collection.DeviceOS LIKE 'Microsoft Windows NT Workstation 6.3%' THEN 'Windows 8.1'
WHEN collection.DeviceOS LIKE 'Microsoft Windows NT Advanced Server 6.0%' THEN 'Windows Server 2008'
WHEN collection.DeviceOS LIKE 'Microsoft Windows NT Advanced Server 5.2%' THEN 'Windows Server 2003'
    WHEN collection.DeviceOS LIKE 'Microsoft Windows NT%Server 6.1%' THEN 'Windows Server 2008 R2'
    WHEN collection.DeviceOS LIKE 'Microsoft Windows NT%Server 6.3%' THEN 'Windows Server 2012 R2'
    WHEN collection.DeviceOS LIKE 'Microsoft Windows NT Workstation 5.1%' THEN 'Windows XP'
    WHEN collection.DeviceOS LIKE 'Mac OS X%' THEN 'Mac OS X'
    end,
    collection.Domain AS [Domain],
    dbo.v_CH_ClientSummary.LastPolicyRequest AS [Last Checked-in],
    dbo.v_CH_ClientSummary.LastHW AS [Last HW Inventory],
    collection.UserName AS [Last Logged User],
    dbo.v_FullCollectionMembership.CollectionID AS [CollID]

from _RES_COLL_COL00001 AS collection
left JOIN dbo.v_GS_COMPUTER_SYSTEM ON collection.MachineID = dbo.v_GS_COMPUTER_SYSTEM.ResourceID
left JOIN dbo.v_GS_SYSTEM_ENCLOSURE ON collection.MachineID = dbo.v_GS_SYSTEM_ENCLOSURE.ResourceID
left JOIN dbo.v_FullCollectionMembership ON collection.MachineID = dbo.v_FullCollectionMembership.ResourceID
left JOIN dbo.v_Collection ON dbo.v_FullCollectionMembership.CollectionID = dbo.v_Collection.CollectionID
left JOIN dbo.v_CH_ClientSummary ON collection.MachineID = dbo.v_CH_ClientSummary.ResourceID
where
(dbo.v_FullCollectionMembership.CollectionID IS null
or dbo.v_FullCollectionMembership.CollectionID = 'CAS00002'
or dbo.v_FullCollectionMembership.CollectionID = 'CAS00003')
ORDER BY [Computer Name]

The 'where' clause includes extra Collections to which the results can be further filtered down - this was required in my situation since we didn't want to have all endpoints included.

Thursday, October 26, 2017

SQL Reports - Building custom expressions

Just another entry for my own personal reference, but here is how I built expressions to accumulate total counts of machines running Windows 10 based on their hostname naming format.

The report was custom built utilizing basic text boxes aligned with the fields I desired. 

Notice the differentiation between 'Records' and 'Clients'.  These were different expressions since at the time we had a somewhat different count of clients vs actual records in SCCM since we had just finished the migration over the summer. 

The 'Records' expression was built like so:

=Sum(IIF(Fields!Computer_name.Value LIKE "L*",1,0),"DataSet1")

Whereas Clients were built like:

=Sum(IIF(Fields!Computer_name.Value LIKE "L*" AND Fields!Client0.Value = 1,1,0),"DataSet1")

The query for the dataset:

select  all SMS_R_System.DiscArchKey,
SMS_R_System.Name0 as 'Computer Name',
SMS_R_System.Operating_System_Name_and0 as 'Operating System',
SMS_R_System.Resource_Domain_OR_Workgr0,
SMS_R_System.Client0
from vSMS_R_System AS SMS_R_System where SMS_R_System.Operating_System_Name_and0 like 'Microsoft Windows NT Workstation 10%'

Monday, October 16, 2017

Powershellist - Using Config Baselines with Certificate Detection

This is mainly a post to jog my memory, so I won't go into too much detail, but here is how I handled a recent issue with a certificate that was accidentally deployed to the entire enterprise (not by me 😛).  It was up to me to remedy this utilizing a combination of a Configuration Baseline and Application deployment.  I decided Powershell would best handle this since there was not a way to remedy it in the Config Baseline with certutil.exe.

The Configuration Item's Powershell script was setup as follows:
$str1 = Get-ChildItem Cert:\LocalMachine\My | ? {$_.Extensions | ? {$_.oid.friendlyname -match "Template" -and $_.Format(0) -match "1.3.6.1.4.1.311.21.8.9698164.1945666.12076471.14939724.7091849.139.2709251.9725632"}}
if ($str1) { write-host 'Found'}
else {write-host 'Not found'}
 The Compliance Rule was then configured to be 'Equals: Not found', since we wanted the cert to be removed (and thus would be compliant if so).

I then created a collection based on the Config Baseline, and deployed the 'Cert Removal' application to the collection as Required.  The Cert Removal application was setup as follows:

RemoveCert.bat as command line.

RemoveCert.bat contents:
certutil -delstore MY "1.3.6.1.4.1.311.21.8.9698164.1945666.12076471.14939724.7091849.139.2709251.9725632"
'Cert Removal' Application Detection Method (Powershell):
$str1 = Get-ChildItem Cert:\LocalMachine\My | ? {$_.Extensions | ? {$_.oid.friendlyname -match "Template" -and $_.Format(0) -match "1.3.6.1.4.1.311.21.8.9698164.1945666.12076471.14939724.7091849.139.2709251.9725632"}}
if ($str1) { }
else{write-host 'Installed'}

Wednesday, October 11, 2017

Bookmark-worthy: Allow normal users to run a specific program as admin using ACT

This post from the Performance Team Blog depicts how to utilize Microsoft's Application Compatibility Toolkit to enable a standard user to run a pre-specified program as an Administrator.  This is useful for those programs that assume all users have local admin rights.  This can be deployed in SCCM either via a batch command after the program is installed, or as a separate application with the application itself as a dependency (since the program must be installed first).

Tuesday, October 10, 2017

SQL Query logic - avoiding duplicates with a multi-table query

Here's how to avoid duplicates in a SQL query/report utilizing multiple table joins.  The GROUP BY does the trick, and I also had to ensure I was only using the table columns I was choosing from the original 'system' table, as shown below:

select DISTINCT MAX(dbo.v_Collection.Name) AS Location,
system.Name0 AS [Computer Name],
max(dbo.v_GS_SYSTEM_ENCLOSURE.SerialNumber0) AS [Serial Number],
max(dbo.v_GS_COMPUTER_SYSTEM.Manufacturer0) AS [Manufacturer],
max(dbo.v_GS_COMPUTER_SYSTEM.Model0) AS [Computer Model],
"Operating_System" = CASE
WHEN system.Operating_System_Name_and0 LIKE 'Microsoft Windows NT Workstation 6.1%' THEN 'Windows 7'
WHEN system.Operating_System_Name_and0 LIKE 'Microsoft Windows NT Workstation 10.0%' THEN 'Windows 10'
WHEN system.Operating_System_Name_and0 LIKE 'Microsoft Windows NT Workstation 6.3%' THEN 'Windows 8.1'
WHEN system.Operating_System_Name_and0 LIKE 'Microsoft Windows NT Workstation 5.1%' THEN 'Windows XP'
end,
system.Resource_Domain_OR_Workgr0 AS [Domain],
max(dbo.v_CH_ClientSummary.LastPolicyRequest) AS [Last Checked-in],
max(v_RA_System_IPSubnets.IP_Subnets0) AS [IP Subnet],
max(dbo.v_CH_ClientSummary.LastHW) AS [Last HW Inventory],
system.User_Name0 AS [Last Logged User]
from vSMS_R_System AS system
left JOIN dbo.v_GS_COMPUTER_SYSTEM ON system.ItemKey = dbo.v_GS_COMPUTER_SYSTEM.ResourceID
left JOIN dbo.v_GS_SYSTEM_ENCLOSURE ON system.ItemKey = dbo.v_GS_SYSTEM_ENCLOSURE.ResourceID
left JOIN dbo.v_FullCollectionMembership ON system.ItemKey = dbo.v_FullCollectionMembership.ResourceID
left JOIN dbo.v_Collection ON dbo.v_FullCollectionMembership.CollectionID = dbo.v_Collection.CollectionID
left JOIN dbo.v_CH_ClientSummary ON system.ItemKey = dbo.v_CH_ClientSummary.ResourceID
inner join v_RA_System_IPSubnets on v_RA_System_IPSubnets.ResourceID = System.ItemKey
where
(dbo.v_FullCollectionMembership.CollectionID = 'COL11111'
or dbo.v_FullCollectionMembership.CollectionID = 'COL22222') AND system.Client0 = '1' AND
(system.Operating_System_Name_and0 LIKE 'Microsoft Windows NT Workstation 6.1%' OR
system.Operating_System_Name_and0 LIKE 'Microsoft Windows NT Workstation 10.0%' OR
system.Operating_System_Name_and0 LIKE 'Microsoft Windows NT Workstation 6.3%' OR
system.Operating_System_Name_and0 LIKE 'Microsoft Windows NT Workstation 5.1%')
GROUP BY system.Name0, system.Operating_System_Name_and0, system.Resource_Domain_OR_Workgr0, system.User_Name0
ORDER BY [Computer Name]

FYI, the query grabs all clients in the SCCM system table, based on workstation OS (Windows XP through 10), shows all IP Subnets they are assigned to, as well as specifying the collections.

Tuesday, September 12, 2017

SQL query - List of all Applications referenced in Task Sequences

Since I was unable to find this query online, I decided to make it myself.  This SQL query lists all referenced applications in all Task Sequences in SCCM.

select app.DisplayName [Application],TS.Name [TS Name],TS.Description [TSDescription] From dbo.fn_ListLatestApplicationCIs(1033) app
inner join v_TaskSequenceAppReferencesInfo TSApp on app.ModelName=TSApp.RefAppModelName
inner join v_TaskSequencePackage TS on TS.PackageID=TSApp.PackageID
order by [Application]

This can be useful when cleaning up old applications and you want a quick way to show what Task Sequences are using what applications.

Monday, September 11, 2017

Powershellist - Remove a package from all distribution points

DexterPOSH originally created this script - however I feel it wasn't quite explained correctly (not to mention typed out for an easy copy/paste), so I'm re-blogging it here.  This Powershell script removes a package from all Distribution Points easily.  This is much easier than removing the package content from each individual distribution point from the Content Locations tab of the Package Properties (especially since I am currently working with 250 DP's):

Get-CimInstance -ClassName SMS_DistributionPoint -Namespace root/sms/site_A01 -ComputerName SCCMA01 | Where {$_.PackageID -eq "A01002C9"} | Remove-CimInstance -Verbose
 Be sure to edit the values in bold to fit your own environment (Site Code, Primary Site Server, and PackageID).

I have found this helpful when removing expired/old packages to free up space on all DP's.

Friday, April 28, 2017

Using Process Monitor

Sometimes a Google search on a complex issue just won't find the solution.  That's where Process Monitor comes in.  I have had to use Process Monitor a couple of times to fix issues that I knew weren't on the web (and maybe some that were).  In one case, one of my company's in-house apps wasn't allowing a user to use "Run as different user" for the app (which was a requirement to use it).  After using Process Monitor to pinpoint what exactly was happening at the point of failure, Process Monitor helped determine the issue where two files that resided in the Public profile needed to be moved/removed.  Without Process Monitor, I would have never figured this out (besides maybe weeks/months of trial and error) - and worse, even reimaging the machine wouldn't have fixed the issue.  Process Monitor can also help you quickly figure out where exactly in the registry a specific setting is in Windows, which helps with scripting/automation.

At first glance, Process Monitor is not an easy egg to crack.  You can easily get information overload from a couple of clicks in the program.  The power resides in how to filter and utilize this information at the point of failure and/or as you change settings in Windows.

We will disable the 'Location service' in Windows 10 settings as an example to find out where exactly this setting is in the registry.  First, obtain Process Monitor from here in case you missed the first link.  Open procmon.exe (no installation required) from the downloaded zip file.  The first thing you'll want to do is ensure Capture mode is off.  Click the magnifying glass icon (or press Ctrl-E) to disable the active process search.  The magnifying glass icon will show a red X over it when disabled.

Also, disable the AutoScroll option next to the Capture mode icon (shown disabled in the above picture).

Click the Clear icon (or Ctrl-X) to erase all processes on the list.

Open the desired settings window in Windows where you want to find the registry setting - in our case, it's under Settings - Location tab.  This can also be found by doing a Windows 10 search for Location privacy settings.  Look for the Location service option and ensure it's set to On.  (Because we want to turn it off, we want it in its original position.)

Here's the slightly tricky part - In Process Monitor, click the Capture (magnifying glass) icon to enable Capture mode, then, as quickly as you can, click the Location service option to Off, then switch to Process Monitor again and disable the Capture icon again.  You may notice other processes/functions appear besides the one we are looking for, but that's OK.  As it stands, we are looking at every little change on the system from the point we pressed the Capture icon on, then off.

This is a lot of information, even if only captured for a few seconds.  We want to filter this information by clicking Filter - Filter... (or Ctrl-L).  On the Filter menu, change the first dropdown box from Architecture to Operation, then the second dropdown to "is", then the third to "RegSetValue".  Make sure the last dropdown is set to 'Include'.  Click the Add button to add it to the list of Filters below, and ensure it's checked on the list, then click OK.

We now have a more manageable list of settings to pinpoint.  We know that one of these options is what we are looking for.
Generally we know that one registry change is only required, and since we see multiple "Explorer.EXE" values (under Process Name), we can rule those out.  This leaves us with svchost.exe and SystemSettings.exe (x2).  At this point, we can open RegEdit.exe to navigate to the shown key/data for the first SystemSettings.exe value.

The Value we are looking for is 'Deny', which looks like our winner.  We can further validate this by going back to the Location settings window and clicking it to the On position again.  Then we refresh the registry data (click another key, then click back to the original), At this point, we notice the Value field has changed to Allow (the original setting).  Therefore, we can now manually change the Value key to Deny (double-click it to change) in Regedit, then refresh the Location settings window.  The option is back to 'Off' again.  We have a winner!

Now, we can right-click the main key (on the left-hand side of Regedit) and click Export to save the file as a ".reg" file.  Give it a worthwhile name (such as LocationSetting.reg).

Since this exports the entire key, we want to remove any unneeded settings from the reg file we just saved.  Open the .reg file in Notepad, then delete the unneeded options.  Your .reg file should now show as the following:
Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\DeviceAccess\Global\{BFA794E4-F964-4FDB-90F6-51056BFE4B44}]
"Value"="Deny"

Save it, then test it by setting the Location Settings to On again, then open the reg file to import the info to the registry.  The setting should turn Off again.

That's it.  You are now one step closer to becoming a Windows troubleshooting master, despite how complex the issue at hand could be.

Thursday, April 20, 2017

CreateShortcut.vbs - now with icons!

Per the last post, I expanded the CreateShortcut VBScript to include icon specification, in case you don't like the default icon (to make it look 'prettier' if needed). This adds a parameter for the icon location as well as icon index. Here is the code:

Set oWS = WScript.CreateObject("WScript.Shell")
sLinkFile = WScript.Arguments(0)
Set oLink = oWS.CreateShortcut(sLinkFile)
oLink.TargetPath = WScript.Arguments(1)
if WScript.Arguments.count = 3 then
oLink.IconLocation = WScript.Arguments(2)
end if
oLink.Save


And the usage:

CreateShortcut.vbs "C:\Users\JoeSmith\Desktop\Excel.lnk" "C:\Program Files (x86)\Microsoft Office\Office16\EXCEL.EXE" "C:\Program Files (x86)\Microsoft Office\Office16\protocolhandler.exe,0"

This example creates an icon on JoeSmith's desktop for Excel, but replaces the icon with the default 'Office' icon instead of the Excel icon.  

The icon specification parameter is completely optional - you can still use the default icon by not specifying the 3rd parameter for icon location in the script usage and it will work fine.

I've found this particularly useful for batch files or internet shorcuts to replace those ugly default icons with prettier ones.

Saturday, April 15, 2017

Tackling the HP Audio Driver Keylogger issue with SCCM

Here's a rundown of how I tackled the HP Audio Driver Keylogger issue.

1. Created a Config Item called "Conexant Log File Check", and used the below settings.

2. Created a Config Baseline, added the Config Item to it, then deployed to the Clients collection.
3. Created a collection off of the resultant collection based on the Non-compliance count, then deployed the Conexant Audio Driver application as Required to this collection.
4. The Conexant Audio Driver application was based on two Detection Method rules:

Since both of these driver versions were documented as being keyloggers, the driver could be used to automatically upgrade both versions.

Monday, March 13, 2017

Create a desktop shortcut the VBScript way

Here is how to create a shortcut (on the Desktop or anywhere else) using VBScript.  This method allows creation from just one .VBS file and saves space by not having to worry about copying multiple shortcuts to the user's Desktop or elsewhere.  I used this VBScript extensively when I realized I wanted to create desktop shortcuts to commonly used applications but did not want to create multiple shortcut.lnk files to copy to the user's Desktop folder (or other folders) manually.

The contents of the CreateShortcut.vbs file:

Set oWS = WScript.CreateObject("WScript.Shell")
sLinkFile = WScript.Arguments(0)
Set oLink = oWS.CreateShortcut(sLinkFile)
oLink.TargetPath = WScript.Arguments(1)
oLink.Save

Usage:

CreateShortcut.vbs <path to shortcut with .lnk extension> <path/filename of file/folder>

For example - to create a shortcut to MS Excel on the user's desktop:

CreateShortcut.vbs "C:\Users\JoeUser\Desktop\Microsoft Excel.lnk" "C:\Program Files (x86)\Microsoft Office\Office16\EXCEL.EXE"

The icon for the shortcut uses the default icon of the file/folder (e.g. it will use Excel's default icon in the above example).

Powershellist - My backup script


Not really SCCM related, but still useful.  :)

This Powershell script is what I wrote awhile back to backup the entire C: drive to a specified network share.  It prompts to perform a disk check (reboot required) before backing up to ensure there are no bad sectors.  It creates folders based on make, model, and hostname of the computer and outputs the general backup information (hostname, backup date, network location, make, model) to a CSV file on the network share.

Clear-Host
Write-Host "This performs a full system backup using Windows Backup.  Please verify you are logged in as an account that has write access to \\server\share before continuing."
$diskchfile = "C:\windows\temp\disk_check.tag"
if (-not (Test-Path $diskchfile)) { 
$diskch = read-host "Disk check has not been run yet.  Do you want to run a Disk Check now? [y/n] "
IF($diskch -eq "y") {
$text = 'y'
$text | Out-File 'C:\windows\temp\disk_check.tag'
Write-Host "NOTE: Please run this script again to continue backup after the Disk Check."
$text | chkdsk C: /r
Write-Host "Rebooting to run Disk check..."
shutdown /r /t 5
cmd /c pause | out-null
exit
} else {
write-host "Disk check aborted."
}
} else {
Write-Host "Disk check has been performed."
}
$machine = (Get-WmiObject -Class:Win32_ComputerSystem).Model
$make,$model = $machine -Split(" "),2

write-host "Make: $make"
write-host "Model: $model"

$backuppath = "\\server\share\$make\$model\$env:computername"
if ((Test-Path $backuppath)) { 
write-host "WARNING: This machine may have previously been backed up.  Please check the open Explorer window to verify VHD file exists."
Start-Process -filepath explorer.exe -argumentlist "\\server\share\$make\$model\$env:computername"
cmd /c pause | out-null
}

$modelpath = "\\server\share\$make\$model"
if (-not (Test-Path $modelpath)) { 
New-Item -ItemType directory -Path "\\server\share\$make\$model"
}

write-host "Running DontSleep to keep machine awake during backup operation..."
Start-Process -filepath DontSleep.exe

New-Item -ItemType directory -Path "\\server\share\$make\$model\$env:computername"
$date = Get-Date
"$env:computername,$date,\\server\share\$make\$model\$env:computername" | Out-File -Append "\\server\share\Recent backups.csv"
wbadmin start backup "-backupTarget:\\server\share\$make\$model\$env:computername" -include:c: -vssFull
cmd /c pause | out-null
exit

Using certificates as applications in SCCM

This is more for my own benefit, although some may find this useful.

Here is how to import certificates using the Application model in SCCM.  This also includes some basic errorlevel handling.

First, create an Application as a Script Installer, then use the following batch script as the installation command (e.g. save as InstallCertificate.cmd).


@echo off
setlocal
if exist "\\server\share\%COMPUTERNAME%.pfx" (
echo Cert exists for this endpoint. >> C:\logs\cert_install.log
certutil.exe -f –p CertPassword1 –importpfx "%~dp0\%COMPUTERNAME%.pfx" NoExport >> C:\logs\cert_install.log | findstr "ERROR_INVALID_PASSWORD"
if '%ERRORLEVEL%'=='0' goto password2
exit /b
:password2
echo First password invalid - trying second password. >> C:\logs\cert_install.log
certutil -f –p CertPassword2 –importpfx "%~dp0\%COMPUTERNAME%.pfx" NoExport >> C:\logs\cert_install.log
)


(In this example, there were two passwords defined depending on who made the certificate in our environment, hence the errorlevel checking to verify passwords)

The detection method of the Application is then defined as a Powershell script:

Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Subject -like "*$env:computername*"}

This will check to ensure the hostname of the computer exists in the Cert store (which is how this particular certificate is installed).  Another variant would be this install script (InstallCertificate.cmd example):

@echo off
if exist "\\server\share\CertName.cer" (
echo Certificate found >> C:\logs\cert_install.log
certutil -addstore root "%~dp0\CertName.cer" >> C:\logs\cert_install.log
)

The detection method for this example would be this Powershell script:

Get-ChildItem -Path Cert:\LocalMachine\Root | Where-Object {$_.Subject -like "*CertName*"}

The "CertName" in this example would be the exact name of the certificate listed in the store.

I have yet to make an uninstall script for this Application since in our environment it wasn't necessarily needed (and I didn't want users to have the power to remove the cert in the first place).  However, the script would most likely need a minor change in InstallCertificate.cmd from "-addstore" to "-delstore" in the second example.

Tuesday, February 14, 2017

Silent install of Installshield applications in SCCM

I have had Installshield applications come up time and again, and not much documentation (or few and far between) based on how to silently install them.  Therefore here is my tutorial on how to silently install them properly with ConfigMgr.

1. Identify if the application is InstallShield-based or not.  You can do this usually by running the setup or installation exe file manually.  If the setup wizard says InstallShield, you're golden.


2. Copy the Installshield application source file(s) to your SCCM file share.  Most of the time, they will have a setup.exe and no MSI file, although there are sometimes MSI files alongside the setup executable - it's usually better to follow this tutorial than add the MSI file due to extra requirements needed by the application's setup.
3. Install the application in "Record" mode.  This is done to generate a setup.iss file.  Open a command prompt in the folder of the application's source files.  To do this, open an Explorer window, navigate to your SCCM server's file/application share, then to the folder of the application source files.  Hold Shift, then right-click a blank area of the folder, then click Open command window here.   Type the following at the cmd prompt:
setup.exe -r
4. Install the application as you would normally for every user in Record mode.  When the application setup finishes, the setup.iss file is generated and placed in the C:\Windows folder of the computer it installed on.  Copy this file to your application's source files folder on the SCCM share.
5. Create an installation bat file with the following (general) contents:
setup.exe -s -f1".\setup.iss" -f2"%TEMP%\Appinstall.log"
Save the file as install.cmd (or install.bat) into your SCCM's application share folder.  This install.bat can be run normally to perform a silent (unattended) install of the application.
6. Add the application using the SCCM Console.  Be sure to specify it as a Script installer when adding the application.  For the command line, enter install.cmd (or install.bat).
7. Determine the Detection Method.  This can be based on MSI (if there was an optional MSI with the application), or on Registry key.  I have had to do a few Registry detection methods loosely based on the following:
8. Finish the Add Application wizard, then deploy to a Collection.  I usually deploy to a test collection to install from Software Center before deploying to any others.

If there are any install errors in Software Center, check the log file on the endpoint by opening the %TEMP%\Appinstall.log file generated from the installation (specified in Step 5).