Certificate Distribution – Deploying Software Inventory Logger

Deploying Software Inventory Loggers with SCCM

Software Inventory Logging 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 Software Inventory Logger requires a trusted key to be distributed to the server, the 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.

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:

sli_in_view

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"

sli_software_center

Sample Output and Logging

The utility will create a log file which is compatible with the CMTrace.exe tool.
Log_File_SLI_Aggregator

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}><thread={4}>" -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}><thread={4}>" -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 Comment

Your email address will not be published.