Ausgangslage
In meinem Unternehmen setzen wir IBM Urban Code Deploy ein für die Provisionierung der Server. Nun da dies einen manuellen Mehraufwand bedeutet muss dieses Vorgehen effizienter gestaltet werden. Aus diesem Grund habe ich mit für die Konfiguration mittels Desired State Configuration (DSC) entschieden.
Ziel
- Feststellung ob JAVA bereits installiert ist
- Intallation des Agents
- Konfiguration des Dienstes für den Agent
- Setzen des Group Managed Service Account (GMSA) für den Dienst
Durchführung
Für die Durchführung orientieren wir uns an den gesteckten Zielen.
Ist JAVA instaliert?
Hat man JAVA installiert, so wird dies meistens in ein Verzeichnis wie JDK oder JAVA gemacht und im Pfad wird das entsprechende angegeben. Die Konfiguration wird nicht gestartet, sollte JAVA nicht installiert sein. Dies kann man auf einfache Art und Weise überprüfen.
if(($env:Path.Split(';') | Where-Object { Where-Object { $_ -like "*JDK*" -or $_ -like "*JAVA*" } ))
Nun gut, wenn alles korrekt verläuft dann kann die Konfiguration beginnen.
Installation des Agenten
Der Agent wird mittels XCOPY Installation auf dem System installiert. Das hat den Vorteil, dass der Agent bereits als Vorlage "gezipped" zur Verfügung steht und dann noch an die richtige Stelle kopiert wird. Um diesen Vorgang auszuführen braucht es die Resource Archive, die bereits standardmässig von Windows zur Verfügung steht. Die Resource sieht wie folgt aus:
Archive UrbanCodeBinaries
{
Destination = "C:\Program Files"
Ensure = "Present"
Path = $agentSource
Force = $true
}
Die Destination ist das Ziel in welche die binären Dateien extrahiert werden. Der Path wird durch das Aufrufen des Konfigurationsscriptes mitgegeben.
Konfiguration des Dienstes für den Agenten
Für die Konfiguration des Agenten ist die Service Resource verwendet worden, die wie folgt konfiguriert wird.
# Preconfigure the properties for the service
$agentName = ("ucd-agent-{0}" -f $env:COMPUTERNAME)
$servicePathArguments = "//RS//{0}" -f $agentName
$servicePath = Join-Path -Path $env:ProgramFiles -ChildPath "ibm-ucd-agent\native\x64\winservice.exe"
Service UrbanCodeAgentService
{
Ensure = "Present"
Name = $agentName
StartupType = "Manual"
Path = ("`"{0}`" {1}" -f $servicePath, $servicePathArguments)
Description = "IBM Urban Code Agent"
DisplayName = $agentName
State = "Stopped"
}
Vor der Ressource sind die notwendigen Parameter vorkonfiguriert worden, da dieser JAVA Agent über einen Windows-Dienst Wrappter mit Argumenten gestartet wird. Der Dienst ist nun konfiguriert nur noch nicht so wie gewünscht.
Konfiguration des Dienstes für den Agent
Die Dienst Resource lässt nur zu einen Benutzer-Account mit Passwort zu verknüpfen. Da kein Object PSCredential Objekt ohne Passwort erstellt werden kann und das Passwort für den Group Managed Service Account unbekannt ist, muss man sich mittels WMI den Dienst wie gewünscht konfigurieren. Dies geschieht mittels der Script Resource
# Third Set the Service with a script Resource
Script ServiceScript{
DependsOn = "[Service]UrbanCodeAgentService"
GetScript = {
return Get-WmiObject -Class win32_Service | Where-Object -Property Name -like ("*{0}*" -f $using:agentName)
}
TestScript = {
$service = [scriptblock]::Create($GetScript).Invoke()
if($service){
return ($service.StartName -eq $using:groupManagedServiceAccount)
}
return $false
}
SetScript = {
$service = [scriptblock]::Create($GetScript).Invoke()
if($service){
$service.Change($null, $null, $null, $null, $null, $null, $using:groupManagedServiceAccount, $null, $null, $null, $null)
}
}
}
Wichtig an dieser Stelle ist zu erwähnen, dass diese Script-Resource mit dem DependsOn versehen ist. Das bedeutet, dass diese Ressource erst durchgeführt wird, wenn die angegeben Ressource erfolgreich appliziert werden konnte.
Hier noch das ganze Script
param([Parameter(Mandatory=$true,HelpMessage="The full path to the template ucd agent zip")]
[ValidateNotNullOrEmpty()]
[string]$agentSource="C:\Temp\ibm-ucd-agent.zip",
[Parameter(Mandatory=$true,HelpMessage="The Group Managed Account that is used as service account.")]
[ValidateNotNullOrEmpty()]
[string]$groupManagedServiceAccount="IFC1\srvgp-ucd-r$"
)
Configuration UrbanCodeAgentConfiguration{
Import-DscResource -ModuleName PSDesiredStateConfiguration
if(($env:Path.Split(';') | Where-Object { $_ -like "*JDK*" -or $_ -like "*JAVA*" } )){
Node $env:COMPUTERNAME
{
# First Extract the service binaries to the Destination
Archive UrbanCodeBinaries
{
Destination = "C:\Program Files"
Ensure = "Present"
Path = $agentSource
Force = $true
}
# Preconfigure the properties for the service
$agentName = ("ucd-agent-{0}" -f $env:COMPUTERNAME)
$servicePathArguments = "//RS//{0}" -f $agentName
$servicePath = Join-Path -Path $env:ProgramFiles -ChildPath "ibm-ucd-agent\native\x64\winservice.exe"
# Second configure the service
Service UrbanCodeAgentService
{
Ensure = "Present"
Name = $agentName
StartupType = "Manual"
Path = ("`"{0}`" {1}" -f $servicePath, $servicePathArguments)
Description = "IBM Urban Code Agent"
DisplayName = $agentName
State = "Stopped"
}
# Third Set the Service with a script Resource
Script ServiceScript{
DependsOn = "[Service]UrbanCodeAgentService"
GetScript = {
return Get-WmiObject -Class win32_Service | Where-Object -Property Name -like ("*{0}*" -f $using:agentName)
}
TestScript = {
$service = [scriptblock]::Create($GetScript).Invoke()
if($service){
return ($service.StartName -eq $using:groupManagedServiceAccount)
}
return $false
}
SetScript = {
$service = [scriptblock]::Create($GetScript).Invoke()
if($service){
$service.Change($null, $null, $null, $null, $null, $null, $using:groupManagedServiceAccount, $null, $null, $null, $null)
}
}
}
}
}else {
Write-Host "Java is not installed"
}
}
UrbanCodeAgentConfiguration
Eine Bemerkung zum Node: Started man die Desired State Configuration, so ist eine MOF-Datei das Resultat. Will man diese archivieren, so würde diese standardmässig "localhost" heissen, was nicht hilfreich wäre. Aus diesem Grund verwende ich immer die Variable $env:COMPUTERNAME um so eine sprechende MOF-Datei zu erhalten.
Fazit
Nicht alles was bereits standardmässig zur Verüfung steht, kann gleich für jeden Anwendungsfall verwendet werden. Mit der Script-Ressource ist es möglich eine gewisse Flexibilität gegeben und man kann auch ohne dedizierte Scripts Aktionen ausführen, die von keiner Ressource bereitgestellt werden. Für Fragen und Anregungen bin ich offen und wenn Dir der Artikel gefallen hat, dann freue ich mich über ein like