Certificate Distribution – Deploying Software Inventory Logger
Overview
I developed this tool to help you deploy Software Inventory Logger Servers. Software Inventory Logger collects Microsoft software inventory data on a per server basis and reports them to a central Software Inventory Aggregator. This utility can be deployed through SCCM and will setup a Software Inventory Logger Server. Because s Software Inventory Logger requires a trusted key to be distributed to the server, this tool will allow you to automatically deploy the same certificate and store the password in an encypted AES key that can either be deleted after deployment or hidden with ACL’s. This will allow you to automate the deploymenet of Software Inventory Logger Servers in an environment.
How To Use This Tool
Follow the procedures below to begin deploying Software Inventory Logger Servers in your environment.
Creating the AES Key / Secure Password Pair Files
Load the functions below and run this one time to create the AES Key and Secure password files that will be included with your deployment package. The password that you will supply will be the password to your SLI certificate. In our example below, this is the PFX certificate that can be included with the other files for easy distribution.
$cred = Get-Credential
New-DGMAESKey -Path .\ -credentialFile credentialFile -AESKeyFile AESKeyFile -passwordSecureString $cred.Password
This will generate the following two files. Along with the script below, this can be put into an SCCM or other deployment package:
Deploying Via SCCM
This can easily be turned into an SCCM Application. When creating the application, call it as:
Powershell.exe -ExecutionPolicy ByPass -File "Invoke-DGMSILloggingServer.ps1"
Sample Output and Logging
The utility will create a log file which is compatible with the CMTrace.exe tool.
PowerShell Code
<#
.SYNOPSIS
Enable SIL on Servers
.NOTES
Version: 1.0
Author: David Maiolo
Creation Date: 2018-02-01
Purpose/Change: Initial script development
Enable SIL on Servers
There are several ways to enable SIL in a distributed server infrastructure, such as in a private cloud of virtual machines.
You will need a valid client SSL certificate in .pfx format to use these steps.
The thumbprint of this certificate will need to be added to your SIL Aggregator using the Set-SILAggregator –AddCertificateThumbprint cmdlet.
This client certificate does not need to match the name of your SIL Aggregator.
#>
function New-DGMCMTraceLog{
param (
[Parameter(Mandatory=$true)]
$ScriptName,
[Parameter(Mandatory=$true)]
$LogFile,
[Parameter(Mandatory=$true)]
$ScriptFile,
[Parameter(Mandatory=$true)]
$message,
[Parameter(Mandatory=$true)]
$component,
[Parameter(Mandatory=$true)]
$type )
$VerboseLogging = "true"
[bool]$Verbose = [System.Convert]::ToBoolean($VerboseLogging)
$MaxLogSizeInKB = 10240
$ScriptStatus = 'Success'
switch ($type)
{
1 { $type = "Info" }
2 { $type = "Warning" }
3 { $type = "Error" }
4 { $type = "Verbose" }
}
if (($type -eq "Verbose") -and ($Verbose))
{
$toLog = "{0} `$$<{1}><{2} {3}>" -f ($type + ":" + $message), ($ScriptName + ":" + $component), (Get-Date -Format "MM-dd-yyyy"), (Get-Date -Format "HH:mm:ss.ffffff"), $pid
$toLog | Out-File -Append -Encoding UTF8 -FilePath ("filesystem::{0}" -f $LogFile)
Write-Host $message
}
elseif ($type -ne "Verbose")
{
$toLog = "{0} `$$<{1}><{2} {3}>" -f ($type + ":" + $message), ($ScriptName + ":" + $component), (Get-Date -Format "MM-dd-yyyy"), (Get-Date -Format "HH:mm:ss.ffffff"), $pid
$toLog | Out-File -Append -Encoding UTF8 -FilePath ("filesystem::{0}" -f $LogFile)
if ($type -eq 'Info') { Write-Host $message }
if ($type -eq 'Warning') { Write-Host $message -ForegroundColor Yellow}
if ($type -eq 'Error') { Write-Host $message -ForegroundColor Red}
}
if (($type -eq 'Warning') -and ($ScriptStatus -ne 'Error')) { $ScriptStatus = $type }
if ($type -eq 'Error') { $ScriptStatus = $type }
if ((Get-Item $LogFile -ErrorAction SilentlyContinue).Length/1KB -gt $MaxLogSizeInKB)
{
$log = $LogFile
Remove-Item ($log.Replace(".log", ".lo_"))
Rename-Item $LogFile ($log.Replace(".log", ".lo_")) -Force
}
}
function Set-DGMSILLoggingServer{
param(
[Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
[String]$pfxcertificateserverpath,
[Parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true)]
[String]$pfxcertificatename,
[Parameter(Position=2,Mandatory=$true,ValueFromPipeline=$true)]
[String]$pdxcertificatethumbprint,
[Parameter(Position=3,Mandatory=$true,ValueFromPipeline=$true)]
[String]$pfxcertificatelocalpath,
[Parameter(Position=4,Mandatory=$true,ValueFromPipeline=$true)]
[String]$SILAggregatorURI,
[Parameter(Position=5,Mandatory=$false,ValueFromPipeline=$true)]
$pfxcertificatelocalpathsercurepassword,
[Parameter(Position=6,Mandatory=$false,ValueFromPipeline=$true)]
[String]$pfxcertificatelocalpathusername,
[Parameter(Position=7,Mandatory=$true,ValueFromPipeline=$true)]
$pfxcertificatesecurepassword,
[Parameter(Position=8,Mandatory=$true,ValueFromPipeline=$true)]
[ValidateSet("Install","Uninstall")]
[String]$InstallationType
)
#Set Logging Varibales
$invocation = (Get-Variable MyInvocation -Scope 1).Value
#$ScriptDirectory = Split-Path $invocation.MyCommand.Path
$ScriptDirectory = "c:\admin"
$ScriptName = ($MyInvocation.MyCommand.Name)+".psm1"
$LogName = ($MyInvocation.MyCommand.Name)+".log"
$LogFile = Join-Path $ScriptDirectory $LogName
$ScriptFile = Join-Path $ScriptDirectory $ScriptName
$ReportDate = Get-Date
$WrapperScriptPath = $MyInvocation.PSCommandPath
$errorcount = 0
if ($InstallationType -eq "Install"){
try{
#Map Drive to Client PFX Certificate Path
New-DGMCMTraceLog -message ("Mapping Drive to Client PFX Certificate Path...") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
$driveLetters = ([int][char]'C')..([int][char]'Z') | % {[char]$_}
$occupiedDriveLetters = Get-Volume | % DriveLetter
$availableDriveLetters = $driveLetters | ? {$occupiedDriveLetters -notcontains $_}
$firstAvailableDriveLetter = $availableDriveLetters[0]
if ($pfxcertificatelocalpathusername -ne $null -and $pfxcertificatelocalpathsercurepassword -ne $null){
$mycreds = New-Object System.Management.Automation.PSCredential ($pfxcertificatelocalpathusername, $pfxcertificatelocalpathsercurepassword)
New-PSDrive -Name $firstAvailableDriveLetter -PSProvider filesystem -root $pfxcertificateserverpath -credential $mycreds | Out-Null
}else{
New-PSDrive -Name $firstAvailableDriveLetter -PSProvider filesystem -root $pfxcertificateserverpath | Out-Null
}
}catch{
New-DGMCMTraceLog -message ("Could Not Map Drive to Client PFX Certificate Path.") -component "Main()" -type 3 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
$errorcount++
}
try{
#Copy Client PFX Certificate to Local Path
New-DGMCMTraceLog -message ("Copying Client PFX Certificate to Local Path...") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
Copy-Item ${firstAvailableDriveLetter}:\$pfxcertificatename $pfxcertificatelocalpath
Remove-PSDrive –Name $firstAvailableDriveLetter
}catch{
New-DGMCMTraceLog -message ("Could Not Copy Client PFX Certificate to Local Path.") -component "Main()" -type 3 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
$errorcount++
}
try{
#Import Client PFX Certificate To Local Store
New-DGMCMTraceLog -message ("Importing Client PFX Certificate To Local Store...") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
Import-PfxCertificate -FilePath $pfxcertificatelocalpath\$pfxcertificatename cert:\localMachine\my -Password $pfxcertificatesecurepassword | Out-Null
}catch{
New-DGMCMTraceLog -message ("Could not Import Client PFX Certificate To Local Store.") -component "Main()" -type 3 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
$errorcount++
}
try{
#Set SIL Logging
New-DGMCMTraceLog -message ("Setting SIL Logging...") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
Set-sillogging –targeturi $SILAggregatorURI –certificatethumbprint $pdxcertificatethumbprint
}catch{
New-DGMCMTraceLog -message ("Could not set SIL Logging.") -component "Main()" -type 3 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
$errorcount++
}
try{
#Start SIL Logging
New-DGMCMTraceLog -message ("Starting SIL Logging...") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
Start-SilLogging
}catch{
New-DGMCMTraceLog -message ("Could not Start SIL Logging.") -component "Main()" -type 3 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
$errorcount++
}
if ($errorcount -le 0){
Set-DGMRegistryInstallValue -InstallationType Install
New-DGMCMTraceLog -message ("The Set-DGMSILLoggingServer has been installed.") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
}else{
New-DGMCMTraceLog -message ("The Set-DGMSILLoggingServer could not be installed. $errorcount error(s) were found.") -component "Main()" -type 3 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
Write-Host "This Window Will Close Automatically in 10 Seconds. A log of these errors can be found at $ScriptFile."
Start-Sleep -Seconds 10
}
}
elseif ($InstallationType -eq "Uninstall"){
try{
#Start SIL Logging
New-DGMCMTraceLog -message ("Stopping SIL Logging...") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
Stop-SilLogging
}catch{
New-DGMCMTraceLog -message ("Could not Stop SIL Logging.") -component "Main()" -type 3 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
$errorcount++
}
if ($errorcount -le 0){
Set-DGMRegistryInstallValue -InstallationType Uninstall
New-DGMCMTraceLog -message ("The Set-DGMSILLoggingServer has been uninstalled.") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
}else{
New-DGMCMTraceLog -message ("The Set-DGMSILLoggingServer could not be uninstalled. $errorcount error(s) were found.") -component "Main()" -type 3 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
Write-Host "This Window Will Close Automatically in 10 Seconds. A log of these errors can be found at $ScriptFile."
Start-Sleep -Seconds 10
}
}
}
function Set-DGMRegistryInstallValue{
param(
[Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
[ValidateSet("Install","Uninstall")]
[String]$InstallationType
)
#Set The Success Bit for SCCM Application Detection
$Path = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Set-DGMSILLoggingServer"
$Name = "IsInstalled"
$Name2 = "Version"
$Value2 = "1"
if(!(Test-Path $Path)){New-Item -Path $Path -Force | Out-Null}
if ($InstallationType -eq "Install"){
$Value = "1"
New-ItemProperty -Path $Path -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null
New-ItemProperty -Path $Path -Name $Name2 -Value $Value2 -PropertyType DWORD -Force | Out-Null
}elseif ($InstallationType -eq "Uninstall"){
$Value = "0"
New-ItemProperty -Path $Path -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null
New-ItemProperty -Path $Path -Name $Name2 -Value $Value2 -PropertyType DWORD -Force | Out-Null
}
}
function Invoke-DGMSILLoggingServer{
param(
[Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
[ValidateSet("Install","Uninstall")]
[String]$InstallationType
)
#Set DGM Variables
$pfxcertificateserverpath = ".\"
$pfxcertificatename = "SILClient2018.pfx"
#Set Key Variables
$AESKeyFile = "AESKeyFile"
$AESKeyFile = "AESKeyFile"
#Client PFX Certificate Thumbprint and added to your SIL Aggregator using Set-SilAggregator -AddCertificateThumbprint
#For example, the thumbprint "a9 09 50 2d d8 2a e4 14 33 e6 f8 38 86 b0 0d 42 77 a3 2a 7b" should be specified as "a909502dd82ae41433e6f83886b00d4277a32a7b"
$pdxcertificatethumbprint = "a909502dd82ae41433e6f83886b00d4277a32a7b"
$pfxcertificatelocalpath = "c:\admin"
$SILAggregatorURI = "https://VCOMSIL001PRD"
#Get Credentials to Client PFX Certificate Path
#$Cred = Get-Credential -Message "Enter DGM Account With Permissions to Client PFX File Path"
#$pfxcertificatelocalpathsercurepassword = $Cred.Password
#$pfxcertificatelocalpathusername = $Cred.UserName
#Get Credentials for Client PFX Certificate Password
#$cred2 = Get-Credential -Message "Enter Password for PFX Certificate. You Can Make Up a UserName"
#$pfxcertificatesecurepassword = $cred2.Password
$pfxcertificatesecurepassword = Get-DGMAESKey -Path $pfxcertificateserverpath -credentialFile credentialFile -AESKeyFile AESKeyFile
Set-DGMSILLoggingServer `
-pfxcertificateserverpath $pfxcertificateserverpath `
-pfxcertificatename $pfxcertificatename `
-pdxcertificatethumbprint $pdxcertificatethumbprint `
-pfxcertificatelocalpath $pfxcertificatelocalpath `
-SILAggregatorURI $SILAggregatorURI `
-pfxcertificatesecurepassword $pfxcertificatesecurepassword `
-InstallationType $InstallationType
#-pfxcertificatelocalpathsercurepassword $pfxcertificatelocalpathsercurepassword `
#-pfxcertificatelocalpathusername $pfxcertificatelocalpathusername `
}
function Set-DGMAESKey{
param(
[Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
$Path,
[Parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true)]
$credentialFile,
[Parameter(Position=2,Mandatory=$true,ValueFromPipeline=$true)]
$AESKeyFile,
[Parameter(Position=3,Mandatory=$true,ValueFromPipeline=$true)]
$passwordSecureString
)
$credentialFilePath = Join-Path $Path $credentialFile
$AESKeyFilePath = Join-Path $Path $AESKeyFile
# Generate a random AES Encryption Key.
Write-Host "Generating a random AES Encryption Key..."
$AESKey = New-Object Byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($AESKey)
# Store the AESKey into a file. This file should be protected! (e.g. ACL on the file to allow only select people to read)
Write-Host "Storing the AESKey into $AESKeyFilePath. This file should be protected!..."
Set-Content $AESKeyFilePath $AESKey # Any existing AES Key file will be overwritten
# Store the Credentials into a file
Write-Host "Storing the Credential File into $credentialFilePath..."
$password = $passwordSecureString | ConvertFrom-SecureString -Key $AESKey
Add-Content $credentialFilePath $password
}
function Get-DGMAESKey{
param(
[Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
$Path,
[Parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true)]
$credentialFile,
[Parameter(Position=2,Mandatory=$true,ValueFromPipeline=$true)]
$AESKeyFile
)
#To get a new key, run such as:
#$cred = Get-Credential
#New-DGMAESKey -Path .\ -credentialFile credentialFile -AESKeyFile AESKeyFile -passwordSecureString $cred.Password
$AESKeyFilePath = Join-Path $Path $AESKeyFile
$credentialFilePath = Join-Path $Path $credentialFile
#Retreive AES Encryption Key
Write-Host "Retrieving AES Encryption Key from $AESKeyFilePath..."
$AESKey = Get-Content $AESKeyFilePath
#Retreive Credential File
Write-Host "Retrieving AES Credential File from $credentialFilePath..."
$pwdTxt = Get-Content $credentialFilePath
#Convert and return this a secure string
Write-Host "ConvertTo-SecureString with $credentialFile and $AESKeyFile..."
$securePwd = $pwdTxt | ConvertTo-SecureString -Key $AESKey
Return $securePwd
}
Leave a Reply
Want to join the discussion?Feel free to contribute!