SCCM 2012 – Enabling the Endpoint Protection Role

Endpoint protection is pretty sweet, and the integration with SCCM Console is very well done. Nice work MS guys!

This is basically a dump from my internal documentation. It might be a little unpolished, but sometimes it’s better to ship a product then continue fretting about perfection…

Install EPP Role on the CAS

  1. Navigate to Administration -> Sites -> Site Server and System Roles
  2. Right-click the CAS and choose “Add site server role.”
  3. On the System Role Selection screen choose ‘Endpoint Protection Point’ and click Next.
  4. On the Endpoint Protection screen accept the license terms.
  5. On the Microsoft Active Protection screen review the information and make a choice.

Create EPP Collectons

  1. Download the script named prep-site-server-wsus.ps1 from my Github page.
  2. Modify the variables at the top of the script to match your Org’s name and site code.
  3. Run the script ON THE SITE SERVER, using your admin account.

Configure and Deploy Custom Anti-Malware Policies

  1. Navigate to Assets and Compliance -> Endpoint Protection -> Antimalware Policies.
  2. Right-click -> Import.
  3. In the import dialog, navigate to “C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\XmlStorage\EPTemplates”.
  4. For each Endpoint Collection that was created, there should be a custom anti-malware setting found in the folder that can be imported. Choose to import an .xml template that matches a collection you’re interested in working on.
  5. Right-click the imported EPP Policy and choose ‘Deploy’.
  6. Deploy the policy to the collection that matches the policy name best.
  7. Repeat this process for all of your EPP collections.

Configure Client Settings

  1. Navigate to Administration -> Client Settings.
  2. Right-click and choose to create a new client settings package.
  3. On the ‘General’ screen, name the new package and check the box labeled ‘Endpoint Protection’.
  4. On the Endpoint Protection screen, set the following settings
    Manage Endpoint Protection client on client computers: True
    Install Endpoint Portection client on client computers: True
    Automatically remove previously installed antimalware software: True
    Suppress any required computer restarts: True
    Disable alternate sources for the initial update: False
  5. Click OK to save changes.
  6. Right-click the new client settings package and choose ‘deploy’.
  7. Deploy the client settings to a collection of your computers.

Endpoint should now be uninstalling your previous virus scanner, and installing the EPP. Wooo!

Secunia – Patching Java

This post contains the steps necessary steps to patching Java with Secunia since things don’t seem to work out of the gate. The main problem seems to be that Secunia didn’t provision for having both x64 and x86 java on x64 systems. I was able to create 3 custom patches using Applicability Rules to get the right patch to the right place.

Overview:

  • Create an install script.
  • Extract the msi files.
  • Create a custom package for installing x64 Java on x64 machines.
  • Create a custom package for installing x86 Java on x64 machines.
  • Create a custom package for installing x86 Java on x86 machines.
  • Test the packages with an SPS.exe file.
  • Deploy the packages.

The Process

Create an Install Script

I received the following script from Secunia support. Save this script as ‘PatchJava.xml’ on a system with Secunia CSI Console installed.

<!--?xml version="1.0" encoding="UTF-8" standalone="yes"?-->
	<![CDATA[Update Sun Java JRE 1.6.x to 6u31 (x64 for 64-bit systems)]]>

	<![CDATA[var Title = "Custom Update Sun Java JRE 1.6.x - 4";
var GUID = "3c16e659-3c45-49ef-897e-4a8b8c22a0fd";
var userSpecficParams = "ADDLOCAL=ALL JAVAUPDATE=0 AUTOUPDATECHECK=0 JU=0 REBOOT=ReallySupress /qn";

// NOTE - keep the GUID and title variables already set up in the default script.

// Note - this assumes that the file order of the included files, whether they be // local files or dynamically downloaded files, is as follows:

//Data1.cab
//jre1.6.0_31.msi

function main() {

	if ( !GUID ) {
		server.logMessage("No GUID supplied for package " + Title);
		return 1;
	}

	server.logMessage("Running package " + Title);
	server.logMessage("GUID : " + GUID);

	// There must be at least 3 embedded files (this script is the first one)
	var numFiles = server.numberOfFiles;
	if ( numFiles < 3 ) {
		server.logMessage("Incorrect number of embedded files. Aborting.");
		return 1;
	}

	var filename, shell, sys, temp, tempPath, outdir; // Declare variables we use below

	// Set up the directory the files will be extracted to and run from
	shell = new ActiveXObject( "WScript.Shell" );
	temp = shell.ExpandEnvironmentStrings( "%TEMP%" );
	sys = new ActiveXObject( "Scripting.FileSystemObject" );
	tempPath = temp + "\\\\" + GUID;
	try {
		if ( sys.FolderExists( tempPath ) ) {
			outdir = sys.GetFolder( tempPath );
		} else {
			outdir = sys.CreateFolder( tempPath );
		}
	} catch ( ex ) {
		server.logMessage( "Exception with get/create temporary directory " + ex.number + " : " + ex.message );
		return 1;
	}

	// First, extract all the files into the outdir created/found above and get the full path names into
	// an array that we can reference later
	var extractedFileNamesWithPath = [];
	for ( var i=1; i <= 2; i++ ) {
		filename = server.getFilename( i );
		if ( !filename ) {
			server.logMessage( "Cannot read filename: " + filename + "  from file. Corrupted file." );
			return 1;
		}

		tempFileWithPath = outdir.Path + "\\\\" + filename;

		// Check integrity of file
		sha1Sum = server.getSHA1Sum( i ); // file at index i
		if ( !sha1Sum ) {
			server.logMessage( "Cannot read SHA1SUM from file. Corrupted file." );
			return 1;
		}
		try {
			server.extractFile( i, tempFileWithPath ); // file at index i
		} catch ( ex ) {
			server.logMessage( "Error when extracting file " + ex.number + " : " + ex.message + "File may already exist." );
		}
		sha1SumCalc = server.getSHA1Sum( tempFileWithPath );
		if ( sha1SumCalc !== sha1Sum ) {
			server.logMessage( "Wrong SHA1SUM. Corrupted file." );
			return 1;
		}

		// File is ok - store the tempFileWithPath into our array
		extractedFileNamesWithPath[ extractedFileNamesWithPath.length ] = tempFileWithPath;
	}

	// We need to the appropriate command on the 5 extracted files.  i.e. if they were called: File0, File1, ..., File4
	//

	 var commandLine = "%WINDIR%\\SYSTEM32\\msiexec.exe /package " + extractedFileNamesWithPath[1] + " " + userSpecficParams;
	server.logMessage("Executing: " + commandLine);
	var exec = shell.Exec( commandLine );

	wait( exec, 3 * 3600 * 1000 ); // timeout in 3 hours

	if ( !exec.Status ) {
		server.logMessage("Executed " + commandLine + ", but failed to complete. Abandoning.");
		exec.Terminate();
		wait( exec, 300 * 1000 ); // timeout in 5 mins
		sys.DeleteFolder( outdir.Path );
		return 1;
	} else {
		server.logMessage("Executed " + commandLine + ", return status is " + exec.ExitCode);
		shell.RegWrite( "HKLM\\Software\\Secunia\\Updates\\Installed\\" + GUID + "\\", Title );
		sys.DeleteFolder( outdir.Path );
	}
}

// The function waits for the command to complete its execution or timeout
function wait( execObject, timeout ) {
    var start = ( new Date() ).valueOf();
    while ( 0 === execObject.Status && (new Date()).valueOf()-start < timeout ) { 		server.sleep(1000);     } }   main();]]>
	<source /><![CDATA[JScript]]>

		<![CDATA[C:\workingtemp\jre1.6.0_31_x64\Data1.cab]]>
		<![CDATA[C:\workingtemp\jre1.6.0_31_x64\jre1.6.0_31.msi]]>

		<![CDATA[C:\Program Files\Java\jre6\bin\java.exe]]>

		<![CDATA[false]]>

	<![CDATA[only64]]>
	<![CDATA[false]]>
	<![CDATA[false]]>
	<![CDATA[false]]>
	<![CDATA[false]]>

Extract the msi files.

To extract the msi files from the Java downloads, follow the instructions on Oracle’s site here: How do I deploy Java using Active Directory across a network?.

Custom Package – x64 Java on x64 machines

  1. Open Secunia CSI and Navigate to Patch -> Secure Package System (SPS)
  2. Click ‘New Custom Package’
  3. Click the button ‘Import Package’ and select the xml update package created for Java.
  4. On the ‘Import Package Content’ dialog box click “OK”.
  5. Click ‘Next’ once the package is imported.
  6. On the ‘Step 2 of 4: Package Contents’ screen, right-click to remove both files under the ‘Files to Include’ Frame.
  7. Click ‘Add local file’ and select “Data1.cab” from your java installation source. Note: it’s important that the files be deleted and re-imported even if the current paths seem correct. Also, it’s important that data1.cab be imported first and the msi file imported second.
  8. Click ‘Add local file’ and select “jre1.6.0_31.msi’ from your java installation source.
  9. Click ‘Create SPS File’, and run the file on a target system. It should update your x64 java!
  10. Click ‘Next’.
  11. On the ‘Step 3 of 4: Applicability Criteria – Paths’ screen, un-check the ‘Mark Package as “Always Installable”‘ checkbox.
  12. Click ‘Next’.
  13. On the ‘Step 4 of 4: Applicability Criteria – Rules’ screen, under the “System Applicability” frame select “64-Bit Systems Only”.
  14. Un-Check the “Do not include Step 3 applicability Paths in XML File” checkbox, then click “Export Package Content”. Save the package file as “Java Package – x64 for x64.xml”.
  15. Click “Publish” to publish your package.

Custom Package – x86 Java on x64 Machines

Use the same general process as the first package, but with the following modifications:

  • On the ‘Step 1 of 4: Package Configuration’ screen, rename the package according to the architecture.
  • On steps 7-8, import the x86 versions of data1.cab and jre1.6.0_31.msi.
  • On the ‘Step 3 of 4: Applicability Criteria’ screen, remove all the applicability paths, then add the following: “C:\Program Files (x86)\Java\jre6\bin\java.exe”.

Custom Package – x86 Java on x86 Machines

Use the same general process as the first package, but with the following modifications:

  • On the ‘Step 1 of 4: Package Configuration’ screen, rename the package according to the architecture.
  • On steps 7-8, import the x86 versions of data1.cab and jre1.6.0_31.msi.
  • On step 13, select “32-Bit Systems Only”.

Grats! You should now have a working Java update.

Extending LANDesk Inventory for Bitlocker (Part 1)

The LANDesk Inventory engine is easy to extend with custom scripts.

The basic process:

  1. Create a script to gather powershell data in a landesk-compatible format.
  2. Create a batch file to run the powershell script if the system has powershell installed.
  3. Modify the LANDesk Inventory Scanner’s ini file ( LDSCNHLP.INI ).
  4. Force a full system scan.
  5. Modify the database settings to allow unknown items.

Step 1 – PowerShell Bitlocker Script

BitLocker provides WMI classes to query for BDE information. In my previous post, a script is available.
  1. Download the script from my previous post ( See: BitLocker Info with PowerShell ) by clicking ‘view source’, then copy\pasting into your favorite text editor.
  2. Then, save the file as bitlocker-info.ps1 in your LDClient folder (by default %programfiles(x86)%\LANDesk\LDClient).

Step 2 – Batch File PS Launcher

It’s possible to launch PowerShell without a batch file, but I wanted to add some logic in case the script was inadvertently pushed to an XP system without PowerShell installed.
  1. Download the following batch file.
  2. Name it as ‘bitlocker-info.cmd’.
  3. Put it in your LDClient folder.
@ECHO OFF
REM Check Windows Version
REM reference: http://www.grimadmin.com/article.php/batchfile-easy-way-to-detect-os-version
ver | findstr /i "5\.0\." > nul
IF %ERRORLEVEL% EQU 0 goto warn_OSOld
ver | findstr /i "5\.1\." > nul
IF %ERRORLEVEL% EQU 0 goto warn_OSOld
ver | findstr /i "5\.2\." > nul
IF %ERRORLEVEL% EQU 0 goto warn_OSOld

cd %SystemRoot%\system32\WindowsPowerShell\v1.0
powershell Set-ExecutionPolicy Unrestricted
powershell C:\PROGRA~2\LANDesk\LDClient\Bitlocker-Info.ps1
goto end

:warn_OSOld
IF EXIST "%programfiles(x86)%\LANDesk\LDClient\bitlocker.dat" DEL /F /Q "%programfiles(x86)%\LANDesk\LDClient\bitlocker.dat"
echo Bitlocker Info - Bitlocker Rollup = N\A for this OS > "%programfiles(x86)%\LANDesk\LDClient\bitlocker.dat"

:end
cd %programfiles(x86)%\Landesk\ldclient

Step 3 – LANDesk LDSCNHLP.INI File

The heart of the LANDesk scanning extension happens in the ‘ldscnhlp.ini’ file. Edit your file so that it matches mine. For now, ignore the fact that this will fail to run the script on a x32 Vista\Win7 system. That will be addressed in the next post.

[EXECUTE WIN16]

[EXECUTE WIN32]
LAUNCH1=C:\progra~2\landesk\ldclient\bitlocker-info.cmd
TIMEOUT1=600

[DATA FILES]
DATA1=%programfiles%\landesk\ldclient\bitlocker.dat
DATA2=%programfiles(x86)%\landesk\ldclient\bitlocker.dat

Step 4 – Force a Full Scan

By default, LANDesk will run a ‘full scan’ once per day. The BitLocker script will not run unless you force a full scan. Here is the easiest way:

  1. On the client, edit your inventory scanner program files shortcut and append “/F /SYNC” to the end of the ‘target’ field.
  2. Run a scan with the program files shortcut.
  3. Check your LDClient directory for a newly created bitlocker.dat file, written by the ps1 script.

Step 5 – Configure LANDesk Database

  1. On your LANDesk core, run ‘Configure Services’ from the Start Menu.
  2. Click the ‘Inventory’ tab.
  3. Click ‘Unknown Items’
  4. You should see the custom bitlocker data; click each item and choose ‘allow’.

References

Troubleshooting

  • I had to reboot my landesk core several times to get the scan working. I’m not sure why. Also, I had to run the scan twice to get the custom data to appear in the ‘unknown items’ tab, and again to get the inventory populated after the items were approved.

Limitations and Taking it Farther

In its current form, this process will not work uniformly on x86 and x64 machines. I haven’t found a way to use environment variables in the LDSCNHLP.ini file, which is the only restriction. However, it’s possible to edit your agent configuration package to make this happen. That will be the focus of part 2.

BitLocker Info with PowerShell

Our department recently purchased LANDesk, and I needed to get BitLocker information into the LANDesk inventory. This is the first of three posts focused on this project. This post will go over the basics of getting BitLocker information with a powershell script. The next couple posts will go into details on how to integrate this script with LANDesk.

WMI Queries

There are two basic WMI classes being used in the script below. Win32_Volume, and Win32_EncryptableVolume. I use Win32_Volume to get a list of all volumes on the device, then Win32_EncryptableVolume to see if all volumes except those labeled, “System Reserved” or “BDEDrive” are encrypted.

The Output

By default, the script writes a LANDesk-compatible .dat file in the location specified in the variable at the top of the script. The output should look something like this if you set debug to true:

PS U:\hg\winscripts\dev> .\BitLocker-Info.ps1
Volume 0 label: System Reserved
Volume 0 name: \\?\Volume{87148646-0973-11df-9733-806e6f6e6963}\
Volume 0 driveLetter:
Volume 0 fileSystem: NTFS
Volume 0 capacity: 104853504
Volume 0 deviceID: \\?\Volume{87148646-0973-11df-9733-806e6f6e6963}\
Volume 0 serialNumber: 750140681
Volume 0 bootVolume: False
Volume 0 systemVolume: True
Volume 0 BitlockerEnabled: False
Volume 1 label:
Volume 1 name: C:\
Volume 1 driveLetter: C:
Volume 1 fileSystem: NTFS
Volume 1 capacity: 69686259712
Volume 1 deviceID: \\?\Volume{87148647-0973-11df-9733-806e6f6e6963}\
Volume 1 serialNumber: 547212969
Volume 1 bootVolume: True
Volume 1 systemVolume: False
Volume 1 BitlockerEnabled: False
Bitlocker Rollup: Not Protected

References

I used parts of the script at this site as the framework, and it deserves due credit.

The Script

#Bitlocker-Info.ps1
#John Puskar, Department of Chemistry, The Ohio State University, 07/23/11
#johnpuskar (gmail)
#build 025
#reference: http://www.buit.org/2010/08/18/howto-bitlocker-status-reporting-in-sccm-2007/

#GLOBAL VARS
$script:gWriteOut = $null
$script:gWriteOut = $false			#true = write debug output to screen

#choose log file path
$logFileName = "bitlocker.dat"
$progx86 = ${ENV:\PROGRAMFILES(X86)}
If($progx86 -eq $null -or $progx86 -eq "")
	{$ldClientPath = ${ENV:\PROGRAMFILES} + "\LANDesk\LDClient\"}
Else
	{$ldClientPath = ${ENV:\PROGRAMFILES(X86)} + "\LANDesk\LDClient\"}
$script:gLogFile = $ldClientPath  + $logFileName

#Skip if not Vista or higher
$blnSkip = $null
$blnSkip = $true
$objOS = Get-WmiObject Win32_OperatingSystem
If($objOS.BuildNumber -ge 6000)
	{$blnSkip = $false}

Function Get-BLAttribute($objBDEDrive,$BLAttrib)
	{
		$strAttribVal = $null

		Switch($BLAttrib)
			{
				Default {}
				"ProtectionStatus"
					{
						$protectionStatus = $null
						$protectionStatus = ($objBDEDrive.GetProtectionStatus()).ProtectionStatus
						$strProtectionStatus = $null
						Switch ($ProtectionStatus)
							{
								0 { $strProtectionStatus = "PROTECTION OFF" }
								1 { $strProtectionStatus = "PROTECTION ON" }
								2 { $strProtectionStatus = "PROTECTION UNKNOWN"}
							}
						$strAttribVal = $strProtectionStatus
					}
				"EncryptionMethod"
					{
						$encryptionMethod = $null
						$encryptionMethod = ($objBDEDrive.GetEncryptionMethod()).EncryptionMethod
						$strEncryptionMethod = $null
						Switch ($encryptionMethod)
							{
								-1 { $strEncryptionMethod = "The volume has been fully or partially encrypted with an unknown algorithm and key size." }
								0 { $strEncryptionMethod = "The volume is not encrypted." }
								1 { $strEncryptionMethod = "AES 128 WITH DIFFUSER" }
								2 { $strEncryptionMethod = "AES 256 WITH DIFFUSER" }
								3 { $strEncryptionMethod = "AES 128" }
								4 { $strEncryptionMethod = "AES 256" }
							}
						$strAttribVal = $strEncryptionMethod
					}
				"VolumeKeyProtectorID"
					{
						$VolumeKeyProtectorID = $null
						$VolumeKeyProtectorID = ($objBDEDrive.GetKeyProtectors($i)).VolumeKeyProtectorID
						If ($VolumeKeyProtectorID -ne $Null)
							{
								$KeyProtectorIDTypes = $null
								Switch ($i)
									{
										1 {$KeyProtectorIDTypes = "Trusted Platform Module (TPM)"}
										2 {$KeyProtectorIDTypes += ",External key"}
										3 {$KeyProtectorIDTypes += ",Numeric password"}
										4 {$KeyProtectorIDTypes += ",TPM And PIN"}
										5 {$KeyProtectorIDTypes += ",TPM And Startup Key"}
										6 {$KeyProtectorIDTypes += ",TPM And PIN And Startup Key"}
										7 {$KeyProtectorIDTypes += ",Public Key"}
										8 {$KeyProtectorIDTypes += ",Passphrase"}
										Default {$KeyProtectorIDTypes = "None"}
									}
							}
						$strAttribVal = $KeyProtectorIDTypes
					}
				"Version"
					{
						$version = $null
						$version = ($objBDEDrive.GetVersion()).Version
						$strVersion = $null
						Switch ($Version)
							{
								0 { $strVersion = "UNKNOWN" }
								1 { $strVersion = "VISTA" }
								2 { $strVersion = "Windows 7" }
							}
						$strAttribVal = $strVersion
					}
			}

		Return $strAttribVal

	}

Function Get-BLInfo
	{
		$arrAttributes = @()
		$arrAttributes += "label"
		$arrAttributes += "name"
		$arrAttributes += "driveLetter"
		$arrAttributes += "fileSystem"
		$arrAttributes += "capacity"
		$arrAttributes += "deviceID"
		$arrAttributes += "serialNumber"
		$arrAttributes += "bootVolume"
		$arrAttributes += "systemVolume"

		$arrBLAttributes = @()
		$arrBLAttributes += "ProtectionStatus"
		$arrBLAttributes += "EncryptionMethod"
		$arrBLAttributes += "VolumeKeyProtectorID"
		$arrBLAttributes += "Version"

		$i = 0
		$msgs = @()

		$blnBitlockerOn = $null
		$blnBitlockerOn = $false
		$arrEncryptedVols = $null
		$arrEncryptedVols = Get-WmiObject win32_EncryptableVolume -Namespace root\CIMv2\Security\MicrosoftVolumeEncryption -ErrorAction SilentlyContinue
		If($arrEncryptedVols -eq $null -or $arrEncryptedVols -eq "")
			{$blnBitlockerOn = $false}
		Else
			{
				$blnBitlockerOn = $false
				$arrEncryptedVols | % {
					If($_.ProtectionStatus -eq 1)
						{$blnBitlockerOn = $true}
				}
			}

		#write-host -f red "DEBUG: bitlocker on: $blnbitlockerOn"
		$intBitlockerRollup = $null
		$intBitlockerRollup = 1

		$arrLocalVolumes = @()
		$arrLocalVolumes = Get-WmiObject Win32_Volume | where-object {$_.DriveType -eq 3}
		$arrLocalVolumes | % {
			$objVolume = $_
			#gather regular info
			$arrAttributes | % {
				$strAttribute = $null
				$strAttribute = $_
				$strAttribValue = $null
				$strAttribValue = $objVolume.$strAttribute
				#write messages
				$userMsg = $null
				$userMsg = "Volume " + $i + " " + $strAttribute + ": " + $strAttribValue
				If($script:gWriteOut -eq $true){Write-Host -f green $userMsg}
				$LANDeskMsg = $null
				$LANDeskMsg = "Bitlocker Info - Volume" + $i + " - " + $strAttribute + " = " + $strAttribValue
				$msgs += $LANDeskMsg
			}

			#bitlocker enabled?
			$blnVolumeBitlocked = $null
			$blnVolumeBitlocked = $false
			If($blnBitlockerOn -eq $true)
				{
					$objBLVol = $null
					$objBLVol = $arrEncryptedVols | Where-Object {$_.Driveletter -eq $objVolume.driveLetter}
					If($objBLVol -eq $null)
						{
							$blnVolumeBitlocked = $false
							#write messages
							$userMsg = $null
							$userMsg = "Volume " + $i + " BitlockerEnabled: False"
							If($script:gWriteOut -eq $true){Write-Host -f green $userMsg}
							$LANDeskMsg = $null
							$LANDeskMsg = "Bitlocker Info - Volume" + $i + " - BitlockerEnabled = False"
							$msgs += $LANDeskMsg
						}
					Else
						{
							$blnVolumeBitlocked = $true
							$arrBLAttributes | % {
								$strBLAttribute = $_
								$strBLAttributeVal = Get-BLAttribute $objBLVol $strBLattribute
								#write messages
								$userMsg = $null
								$userMsg = "Volume " + $i + " " + $strAttribute + ": " + $strAttribValue
								If($script:gWriteOut -eq $true){Write-Host -f green $userMsg}
								$LANDeskMsg = $null
								$LANDeskMsg = "Bitlocker Info - Volume" + $i + " - BL_" + $strBLAttribute + " = " + $strBLAttributeVal
								$msgs += $LANDeskMsg
							}
						}
					If($blnVolumeBitlocked -ne $true -and `
						$objVolume.Label -ne "BDEDrive" -and `
						$objVolume.Label -ne "System Reserved" -and `
						$intBitlockerRollup -ne 0)
						{$intBitlockerRollup = 0}
				}
			Else
				{
					#write messages
					$userMsg = $null
					$userMsg = "Volume " + $i + " BitlockerEnabled: False"
					If($script:gWriteOut -eq $true){Write-Host -f green $userMsg}
					$LANDeskMsg = $null
					$LANDeskMsg = "Bitlocker Info - Volume" + $i + " - BitlockerEnabled = False"
					$msgs += $LANDeskMsg
					$intBitlockerRollup = 0
				}

			$i++
		}

		$strBLRollup = $null
		$strBLRollup = ""
		If($blnBitlockerOn -eq $true)
			{
				If($intBitlockerRollup -eq 0)
					{$strBLRollup = "Insufficiently Protected"}
				Else
					{$strBLRollup = "Fully Protected"}
			}
		Else
			{$strBLRollup = "Not Protected"}

		#write messages
		$userMsg = $null
		$userMsg = "Bitlocker Rollup: " + $strBLRollup
		If($script:gWriteOut -eq $true){Write-Host -f green $userMsg}
		$LANDeskMsg = $null
		$LANDeskMsg = "Bitlocker Info - Bitlocker Rollup = " + $strBLRollup
		$msgs += $LANDeskMsg

		Return $msgs
	}

#Get bitlocker info (main loop)
$msgs = $null
If($blnSkip -eq $false)
	{
		$msgs = $null
		$msgs = Get-BLInfo
	}
Else
	{
		If($script:gWriteOut -eq $true){Write-Host -f yellow "Bitlocker is not available on this Operating System."}
		$msgs += "Bitlocker Info - Bitlocker Rollup = NA"
	}

#compile messages
If(($msgs -is [array]) -eq $false)
	{[array]$msgs = @($msgs)}

#write output
If((Test-Path $script:gLogFile) -eq $true)
	{remove-item $script:gLogFile -force | out-null}
New-Item -ItemType file $script:gLogFile | out-null
$msgs | %{
	$msg = $null
	$msg = $_
	If($gWriteOut -eq $true){Write-host -f yellow $msg}
	Add-Content $script:gLogFile $msg
}