I won't go into much detail about all of the steps in the runbook, but I will detail some of the bits I've added. My plan is to create some other blog posts about running the SCCM client on Linux to provide some more context.
A brief overview of the environment:
- Multiple Windows forests and logical network locations. (Prod, PreProd, & SystemTest)
- An SCCM Management Point serving each forest.
- A SCORCH Runbook Server in each domain.
- SCCM Management Points communicate on HTTPS.
- A Windows CA in each forest.
I needed to add the following functionality to this work flow:
- Execute the SSH commands from an Orchestrator server situated logically close to the target Linux server.
- Request and export a certificate from the local CA to be used to secure communication from the target Linux server SCCM client and the HTTPS SCCM Management Point.
- Append the hostname / IP address of the local SCCM management point to /etc/hosts on each Linux system.
Initialize Data
- Linux Username: Input a user with root privileges on the target Linux system
- Linux Password: User password.
- Target IP Address: IP address of target Linux system.
- Domain: The domain / environment the target Linux system is situated in.
- System Name: The hostname of the target Linux system
Runbooks
The parent runbook is used to determine which Orchestrator Runbook Server executes the 'ConfigMgr - Install Universal Client' runbook by using the 'Domain' Initialize Data.
The runbook server is defined in the Invoke Runbook activity property 'Runbook Server'.
The 'ConfigMgr - Install Universal Client' runbook (below) contains the following activities.
The 'Request PFX Certificate' runbook contains a simple PowerShell script, as described below.
Request and Export a PFX for Linux SCCM Client.
The 'Domain' and 'System Name' (shown as CN) Initialize Data values are parsed into the script which ultimately exports a PFX file to D:\SCORCH ready to be transferred to the target Linux system and used during SCCM client installation.
This is the contents of the PowerShell 'Run .Net Script' activity.
This is the contents of the PowerShell 'Run .Net Script' activity.
- Determine the issuing CA server and Certificate Template name using the 'Domain' Initialize Data value.
if ($Domain -like "PREPROD")
{
$CAName = "#SERVERNAME#\PreProd CA"
$TemplateName = "ConfigMgrClientCertificateforExport"
}
elseif ($Domain -like "PROD")
{
$CAName = "#SERVERNAME#\IssuingCA"
$TemplateName = "ConfigMgrClientCertificateforExport"
}
elseif ($Domain -like "ST")
{
$CAName = "#SERVERNAME#\ST CA"
$TemplateName = "ConfigMgrClientCertificateforExport"
}
- Define a function to remove temporary certificate request files.
function Remove-ReqTempfiles()
{
param(
[String[]]$tempfiles
)
Write-Verbose "Cleanup temp files and pending requests"
#delete pending request (if a request exists for the CN)
$certstore = new-object system.security.cryptography.x509certificates.x509Store('REQUEST', 'LocalMachine')
$certstore.Open('ReadWrite')
foreach($certreq in $($certstore.Certificates))
{
if($certreq.Subject -eq "CN=$CN")
{
$certstore.Remove($certreq)
}
}
$certstore.close()
foreach($file in $tempfiles){remove-item ".\$file" -ErrorAction silentlycontinue}
}
- Remove temporary request files (in case left over from previous requests)
Remove-ReqTempfiles -tempfiles "certreq.inf","certreq.req","$CN.cer","$CN.rsp"
- Build certificate request file
$file = @"
[NewRequest]
Subject = "CN=$CN"
MachineKeySet = TRUE
KeyLength = 2048
Exportable = TRUE
[RequestAttributes]
CertificateTemplate = "$TemplateName"
"@
Set-Content .\certreq.inf $file
- Submit certificate request and add to local cert store.
Invoke-Expression -Command "certreq -new certreq.inf certreq.req"
Invoke-Expression -Command "certreq -submit -config `"$CAName`" certreq.req $CN.cer"
Invoke-Expression -Command "certreq -accept $CN.cer"
- Export PFX file to D:\SCORCH directory
$certpath = "d:\SCORCH\$CN.pfx"
Invoke-Expression -Command "certutil -privatekey -exportPFX -p 'Password' my $CN $certpath"
- Remove certificate from local cert store, and remove temporary files.
$cert = Get-Childitem "cert:\LocalMachine\My" | where-object {$_.Thumbprint -eq (New-Object System.Security.Cryptography.X509Certificates.X509Certificate2((Get-Item "$CN.cer").FullName,"")).Thumbprint}
$certstore = new-object system.security.cryptography.x509certificates.x509Store('My', 'LocalMachine')
$certstore.Open('ReadWrite')
$certstore.Remove($cert)
$certstore.close()
Remove-ReqTempfiles -tempfiles "certreq.inf","certreq.req","$CN.cer","$CN.rsp"
The resulting PFX file is transferred to the target Linux system in the 'Transfer Client Files' activity.
I added the following lines to the existing PowerShell script.
$certpath = "{certpath from "Request PFX Certificate"}"
$p = Start-Process cmd.exe -ArgumentList "/c D:\SCORCH\pscp.exe -l $linuxUsername -pw $linuxPassword -C -scp $certpath $linuxUsername`@$Target`:/tmp/" -wait -WindowStyle Hidden -PassThru
Append /etc/hosts with SCCM Management Point
Using an SSH activity, the /etc/hosts file is appended with the hostname and IP address of the corresponding SCCM Management Point for the target Linux system.
The hostname and IP is determined using PowerShell ('Set MP' activity) and the 'Domain' Initialized Data.
$Domain = "#INITIALIZE-DATA#"
if ($Domain -like "PREPROD")
{
$MP= "#PREPROD-MP-FQDN#"
$MPNetbios = "#PREPROD-MP#"
$IP = "#IP ADDRESS#"
}
elseif ($Domain -like "PROD")
{
$MP= "#PROD-MP-FQDN#"
$MPNetbios = "#PROD-MP#"
$IP = "#IP ADDRESS#"
}
elseif ($Domain -like "ST")
{
$MP= "ST-MP-FQDN#"
$MPNetbios = "#ST-MP#"
$IP = "#IP-ADDRESS#"
}
The published data from this script is as follows:
The SSH command to use this data looks like this:
echo '{IP from "Set MP"} {MPNetBios from "Set MP"} {MP from "Set MP"}' >> /etc/hosts
SCCM Client Installation
Using the resulting PFX file and Published Data, the final installation command looks something like the following:
-t sudo /tmp/./install -mp {MP from "Set MP"} /tmp/ccm-Universalx64.tar -sitecode ### -UsePKICert /tmp/{CN from "Request PFX Certificate"}.pfx -NoCRLCheck -certpw #Password# -keepdb -fsp #FSP-Hostname#
Conclusion
The fundamental functions in this runbook were kindly put together by someone else, but with a few extra PowerShell scripts and some clever runbook invocation I was able to achieve my end goal.
0 comments:
Post a Comment