Meine ersten Schritte mit Terraform

In diesem Artikel ist die Vorgehensweise beschrieben wie man eine Terraform Umgebung für Tests auf- und wieder abbauen kann. Hierzu habe ich Microsoft Azure verwendet. Diese ist im Rahmen der dotnet user group Seite geschehen um so die Tests mit Browserstack auf einer virtuellen Umgebung durchführen zu können.

Vorausetzungen

Ziel

Das Ziel soll sein, dass eine Testumgebung bestehend aus folgenden Komponenten

  • SQL Server
  • App Service hochfährt, die automatisierten Tests durchläuft und danach gelöscht wird.

Vorgehensweise

Die nachfolgenden Schritte beschreiben die Vorgehensweise in 3 Schritten.

  • Identifizieren der benötigten Ressourcen
  • Identifizieren der Terraform Ressourcen
  • Zusammenführen

Identifizieren der Terraform Ressourcen

Wirf man ein Auge auf die Azure Automation, so sticht die sehr grosse JSON-Datei ins Auge. Sie kann als Anhaltspunkt verwendet werden, muss aber nicht. Schaut man sich das so an, dann sind das eine Menge an Ressourcen, die da Verwendung finden. Nun stellt Terraform unter folgendem Link, die aktuellen Ressourcen für Azure zur Verfügung. Folgende Ressourcen können dafür Verwendung finden:

Zusammenführen

Nun muss das Ganze in einer Terraform Datei zusammengeführt werden. Hierzu wird ein Ordner erstellt, zum Beispiel dotnet-usergroup-bern-terraform-configuration. In diesem Ordner ist dann eine main.tf Datei zu erstellen, die die ganze Terraform Konfiguration beinhaltet. Diese Datei sieht dann wie so aus:

# Configure the dotnet-user-group test environment

terraform{
    required_providers{
        azurerm = {
            source = "hashicorp/azurerm"
            version = ">=2.26"
        }
    }
}

provider "azurerm"{
    features{}
}

resources "azurerm_resource_group" "bddtest"{
    name = "bddtest-dotnet-usergroup"
    location = "switzerlandnorth"
}

// SQL configuration
resource "azurerm_storage_account" "bddtest"{
    name = "bdd-test-dotnet-usergroup"
    resource_group_name = azurerm_resource_group.bddtest.name
    location = azurerm_resource_group.bddtest.location
    account_tier = "Free"
    account_replication_type = "GRS"
}

resource "azurerm_sql_server" "bddtest" {
  name                         = "bddtest-sqlserver"
  resource_group_name          = azurerm_resource_group.bddtest.name
  location                     = azurerm_resource_group.bddtest.location
  version                      = "12.0"
  administrator_login          = "4dm1n157r470r"
  administrator_login_password = "4-v3ry-53cr37-p455w0rd"
}

resource "azurerm_mssql_database" "bddtest" {
  name           = "bddtest-dnugberndev"
  server_id      = azurerm_sql_server.bddtest.id
  collation      = "SQL_Latin1_General_CP1_CI_AS"
  license_type   = "LicenseIncluded"
  max_size_gb    = 4
  read_scale     = false
  sku_name       = "Basic"
  zone_redundant = false

  extended_auditing_policy {
    storage_endpoint                        = azurerm_storage_account.example.primary_blob_endpoint
    storage_account_access_key              = azurerm_storage_account.example.primary_access_key
    storage_account_access_key_is_secondary = true
    retention_in_days                       = 6
  }

// App Service configuration (Web Apps)
resource azurerm_app_service_plan "bddtest"{
    name = "bddtest-appserviceplan"
    location = azurerm_resource_group.bddtest.location
    resource_groupe_name = azurerm_resource_group.bddtest.name

    sku{
        tier = "Basic"
        size = "F1"
        name = "F1"
    }
}
 

Natürlich sind hier noch nich alle definitiven Werte eingetragen, sodass während der Build-Zeit diese Konfiguration erstellt wird. Diesen Punkt werde ich in Zukunft behandeln.

Erstellung der Ressourcen
Nun ist es an der Zeit, zu sehen ob die Konfiguration, die da entstanden ist, auch ohne Fehler angewendet werden kann.
Hierzu bin ich wie folgt vorgegangen:
1. Anmelden in Azure (Diese Methode habe ich gewählt, weil bei mir der Browser oder die Browser ein wenig gezickt haben.)

az login --use-device-code

Nach erfolgter Anmeldung, sind die Azure-Abos ersichtlich.

[
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "YYYYYYYY",
    "id": "0000000-11111-2222-3333-555555555555",
    "isDefault": true,
    "managedByTenants": [],
    "name": "Pay-As-You-Go",
    "state": "Enabled",
    "tenantId": "ZZZZZZZ",
    "user": {
      "name": "hansmustter@windowslive.com",
      "type": "user"
    }
  },
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "XXXXXXX",
    "id": "0000000-11111-2222-3333-444444444444",
    "isDefault": false,
    "managedByTenants": [],
    "name": "Visual Studio Enterprise mit MSDN",
    "state": "Enabled",
    "tenantId": "XXXXXX",
    "user": {
      "name": "hansmuster@windowslive.com",
      "type": "user"
    }
  }
]

Nun, wenn man die Subscription wechseln will, muss man mit den Azure CLI-Werkzeugen folgenden Befehl ausführen:

az account list

[
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "YYYYYYYY",
    "id": "0000000-11111-2222-3333-555555555555",
    "isDefault": false,
    "managedByTenants": [],
    "name": "Pay-As-You-Go",
    "state": "Enabled",
    "tenantId": "ZZZZZZZ",
    "user": {
      "name": "hansmustter@windowslive.com",
      "type": "user"
    }
  },
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "XXXXXXX",
    "id": "0000000-11111-2222-3333-444444444444",
    "isDefault": true,
    "managedByTenants": [],
    "name": "Visual Studio Enterprise mit MSDN",
    "state": "Enabled",
    "tenantId": "XXXXXX",
    "user": {
      "name": "hansmuster@windowslive.com",
      "type": "user"
    }
  }
]

das die Default Subscription geändert hat.

  1. Nun muss der Plan von Terraform initiiert werden.
.\terraform.exe plan -out .\myplan
terraform init
  1. Anschliessend muss
.\terraform.exe plan -out .\myplan

ausgeführt werden. Wobei der Out Parameter optional ist. Bei der Ausführung speichert Terraform mit diesem Parameter den Plan als Datei im aktuellen Verzeichnis oder in dem angegeben im out-Parameter, ab. Verlief alles nach Plan, so zeigt Terraform den Plan an. Die + Zeichen deuten darauf hin, dass diese Ressourcen erstellt werden. Der nachfolgende Plan ist der Übersicht halber symbolisch dargestellt. In Wirklichkeit ist dieser um einiges länger.

------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azurerm_app_service.bddtest will be created
  + resource "azurerm_app_service" "bddtest" {
      + app_service_plan_id            = (known after apply)
      + app_settings                   = {
          + "SOME_KEY" = "some-value"
        }
      + client_affinity_enabled        = false
      + client_cert_enabled            = false
      + default_site_hostname          = (known after apply)
      + enabled                        = true
      + https_only                     = false
      + id                             = (known after apply)
      + location                       = "switzerlandnorth"
      + name                           = "bddtest-dnug-bern"
      + outbound_ip_addresses          = (known after apply)
      + possible_outbound_ip_addresses = (known after apply)
      + resource_group_name            = "bddtest-dotnet-usergroup"
      + site_credential                = (known after apply)

      + auth_settings {
          + additional_login_params        = (known after apply)
          + allowed_external_redirect_urls = (known after apply)
          + default_provider               = (known after apply)
          + enabled                        = (known after apply)
          + issuer                         = (known after apply)
          + runtime_version                = (known after apply)
          + token_refresh_extension_hours  = (known after apply)
          + token_store_enabled            = (known after apply)
          + unauthenticated_client_action  = (known after apply)

          + active_directory {
              + allowed_audiences = (known after apply)
              + client_id         = (known after apply)
              + client_secret     = (sensitive value)
            }          
....

Plan: 7 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

This plan was saved to: .\MyPlan

To perform exactly these actions, run the following command to apply:
    terraform apply ".\\MyPlan"
  1. Nun wird der Plan mit
terraform apply

zur Ausführung gebracht.

  1. Eine kurze Rechenphase und der Plan der ausgeführt werden wird, wird angezeigt. Die Bestätigung mit „Yes“ lässt Terraform nun seine Arbeit verrichten.
terraform apply
  1. Sobald die Bestätigung mit „YES“ erfolgte, schreitet Terraform zur Tat. Die Fortschritte sind dann wie folgt:
terraform progress indikator
  1. Wenn alles richtig gemacht worden ist, dann zeigt Terraform den Status der Operation an.
terraform status

Hier noch die erstellten Ressourcen im Azure Portal.

azure ressourcen nach der terraform applizierung

Löschen der erstellten Ressourcen

Der Aufbau einer Umgebung ist das Eine. Das Abbauen das Andere. In der Dotnet User Group Bern, sollt diese Umgebung für die BDD (Behavior Driven Tests) hochgefahren und dann wieder abgebaut werden. Der Abbau geht dann wie nachfolgend beschrieben, sehr schnell.

  1. Mit dem Befehl
terraform.exe destroy

werden die Ressourcen dann abgebaut.

2. Nach einer kurzen Verarbeitungszeit auf dem ausführenden Client sieht man dann Ausführungsplan des oben bereits dargestellten Ausführungsplanes. Der Unterschied hier, bei jeder Resource ist nun ein vorangestellt, dass angibt das die Ressource gelöscht werden wird. Auch hier muss mit YES die Aktion bestätigt werden.

3. Sind alle Aktionen erfolgreich verlaufen so teilt terraform mit, dass die Ressourcen zerstört sind.

terraform destory

Auch im Azure Portal sind die Ressourcen nicht mehr zu finden.

azure nach dem abbau durch terraform destroy

Fazit

Mit Terrform ist es relativ einfach eine automatisierte Infrastruktur zu instantiieren und so den Prozess der Bereitstellung zu Beschleunigen. Wenn Dir dieser Artiekl gefallen hat, dann würde ich mich über ein Like freuen und nehme gerne Verbesserungsvorschläge an. Meine Reise mit Terraform ist noch nicht zu Ende und das hier gezeigt, einfache Beispiel, kann noch weiter verbessert werden. Weiter will ich zudem die Möglichkeiten erkunden, diese Lösung auch onPremise einzusetzen, da nicht in jeder Umgebung die Cloud als definierte Zielplattform gewünscht ist.

Weiterführende Links

Desired State Configuration und Group Managed Service Accounts

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

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

Erstellen eines Windows Dienstes mit Desired State Configuration

In diesem Blog-Post soll gezeigt werden, wie man einfach einen Dienst mittels DSC (Desired State Configuration) installiert und wieder deinstalliert. Die meisten Beispiele zeigen nur kleine Schnipsel an Code, sodass kein Praxisnahes Beispiel herangezogen werden kann.

Ziel

  • Die Installation eines Windows Dienstes mittels DSC auf einem Windows Server 2019 Core

Durchführung

Um die Voraussetzungen zu erfüllen, um DSC verwenden zu können, kann hier nachgelesen werden.

Schritte zur Durchführung

  1. Die Quelldateien des Dienstes müssen von einem Quellverzeichnis in das Zielverzeichnis des Dienstes kopiert werden.
  2. Der Dienst muss dann mittels DSC installiert werden können.

Für diese Operation sind die zwei Windows Resourcen notwendig, die bereits fixfertig vom Betriebssystem geliefert werden.

Das komplette Script nachfolgend aufgeführt:

param([Parameter(Mandatory=$true)][string]$source,
      [Parameter(Mandatory=$true)][string]$destination,
      [Parameter(Mandatory=$true)][string]$servicename,
      [Parameter(Mandatory=$true)][string]$pathtoServiceExecutable)

Configuration ServiceDemo
{

    Import-DscResource -ModuleName PSDesiredStateConfiguration    

    Node $env:COMPUTERNAME
    {    
        File ServiceDirectoryCopy
        {
            Ensure = "Present"
            Type = "Directory"
            Recurse = $true
            SourcePath = $source
            DestinationPath = $destination
        }

        Service DemoService
        {
            Ensure = "Present"
             Name = $servicename
             StartupType = "Manual"
             State = "Stopped"
             Path = $pathtoServiceExecutable
        }
    }
}

Die Variable $env:COMPUTERNAME wird verwendet, damit die generierte MOF-Datei den Namen des Servers aufweist und die Konfiguration so zu anderen unterschieden werden kann.

Die ServiceDirectoryCopy Aktion führt das Kopieren in rekursiver Form der Dienst Dateien aus.

Die Operation DemoService konfiguriert und installiert den Dienst.

Nun muss mittels Aufruf der Scriptes und den Parametern die MOF-Datei erzeugt werden. In diesem Beispiel sieht der Aufruf wie folgt aus:

.\CreateService.ps1 -source "C:\_install\DemoService" -destination "C:\Program Files\DemoService" -servicename "DemoService" -pathtoServiceExecutable "C:\Program Files\DemoService\DemoService.exe"

Ist alles korrekt gelaufen, so sieht man das nachfolgende Resultat:

Erzeugte MOF-Datei
Die Erezugte MOF-Datei

Der Inhalt dieser Datei sieht dann so aus:

/*
@TargetNode='WIN-MFVO0VQ8PB7'
@GeneratedBy=Administrator
@GenerationDate=09/08/2020 12:07:47
@GenerationHost=WIN-MFVO0VQ8PB7
*/

instance of MSFT_FileDirectoryConfiguration as $MSFT_FileDirectoryConfiguration1ref
{
ResourceID = "[File]ServiceDirectoryCopy";
 Type = "Directory";
 Ensure = "Present";
 DestinationPath = "C:\\Program Files\\DemoService";
 ModuleName = "PSDesiredStateConfiguration";
 SourceInfo = "C:\\Users\\Administrator\\Documents\\DemoServiceConfiguration.ps1::14::9::File";
 Recurse = True;
 SourcePath = "C:\\_install\\DemoService\\";

ModuleVersion = "1.0";

 ConfigurationName = "ServiceDemo";

};
instance of MSFT_ServiceResource as $MSFT_ServiceResource1ref
{
ResourceID = "[Service]DemoService";
 State = "Stopped";
 SourceInfo = "C:\\Users\\Administrator\\Documents\\DemoServiceConfiguration.ps1::23::9::Service";
 Name = "DemoService";
 StartupType = "Manual";
 ModuleName = "PSDesiredStateConfiguration";
 Path = "C:\\Program Files\\DemoService\\notepad.exe";

ModuleVersion = "1.0";

 ConfigurationName = "ServiceDemo";

};
instance of OMI_ConfigurationDocument


                    {
 Version="2.0.0";
 

                        MinimumCompatibleVersion = "1.0.0"; 

                        CompatibleVersionAdditionalProperties= {"Omi_BaseResource:ConfigurationName"}; 

                        Author="Administrator"; 

                        GenerationDate="09/08/2020 12:07:47"; 

                        GenerationHost="WIN-MFVO0VQ8PB7";

                        Name="ServiceDemo";

                    };

Nun muss die Konfiguration appliziert werden. Dies geschieht mit dem folgenden Befehl:

Start-DscConfiguration .\ServiceDemo

Das Ausführen erzeugt einen Powershell Job der ausgeführt wird.

Ausführung der Konfiguration

Nun kann überprüft werden, ob die Konfiguration unseren Wünschen entspricht.

Get-Service *Demo*

liefert dann das Ergebnis, das erwartet wird.

Installierter Beispiel-Dienst

Anmerkung: Zum Demonstrationszweck dient bei mir notepad.exe als Demo-Dienst. Fragen wir die Dienst-Details mittels

Get-CimInstance -Class Win32_Service -Property * | Where-Object -Property Name -like "*Demo*"

so sehen wir alle konfigurierten Werte, dieses Dienstes und sehen, dass die Konfiguration angewendet worden ist.

Dienst Details

Fazit

Mit einfachen Mitteln lassen sich so ohne Komplexe Scripts gewünschte Zustände eines Systems festlegen und die Konfiguration ist einfacher zu lesen und zu interpretieren. Ein weiterer Vorteil liegt in der Idempotenz. Ein Nachteil dieser Methode ist, dass die Anmeldeinformationen nicht mitgegeben werden können und diese anschliessend konfiguriert werden müssen. Eine Alternative hierfür könnnte sein, dass man selber eine Resource schreibt und dies implementiert oder wie bereits erwähnt über Set-CimInstance, die entsprechende Eigenschaft des Dienst-Objektes setzt.

Falls der Artikel Gefallen gefunden hat, freue ich mich über einen Like und bin für Rückmeldungen offen.

Weiterführende Links

WindowsFeatures Export in ein Desired State Configuration Script

Ziel

Mein Ziel, das Exportieren der WindowsFeatures eines Servers und diese dann in ein Desired State Configuration .ps1 zu speichern.

Voraussetzungen

  • Windows Server 2019 Core ist installiert.
  • Es sind nur die Standard-WindowsFeatures aktiviert (Standardmässig sind hier 18 installiert. Dies kann variieren).

Durchführung

Die Nachfolgenen Schritte zeigen auf, wie man dies für einen Remote Server machen sollte:

1.) Die Anmeldeinformationen mit Get-Credential in der Powershell Sitzung speichern

2.) Um effizient mit dem /den entfernten Servern arbeiten zu können, ist es empfehlenswert dies mit einer neuen Powershell Sitzung auf den Server zu machen mit New-PSSession

3.) Nun kann mit Invoke-Command auf den gewünschten Server zugegriffen werden.

4.) In dieser Sitzung, wird mit dem Cmdlet Get-WindowsFeature die gewünschten WindowsFeatures herausgefiltert.

5.) Anschliessend wird mit einer String-Variable ein Powershell-Script erstellt.

6.) Zum Schluss kann das erstellte Script ausgeführt werden. Wenn alles erfolgreich läuft, dann sieht man eine erstellte Mof-Datei, dass für Start-DscConfiguration verwendet werden kann.

PS C:\Users\U80794990> Invoke-Command -Session $session -ScriptBlock {                                                                                                                                 $features = Get-WindowsFeature | where -Property Name -like "*Web*"                                                                                                                                $script = "Configuration AspNetCoreOnIIS { `n"                                                                                                                                                       $script += "`t Import-DscResource -ModuleName 'PSDesiredStateConfiguration' `n"
 $script += "`t `t Node 'loalhost' { `n"
 foreach($feature in $features){
 $name = $feature.Name
 $script += "`t `t `t WindowsFeature $name {`n"
 $script += "`t `t `t `t Name = '$name' `n"
 $script += "`t `t `t `t Ensure = 'Present' `n"
 $script += "`t `t `t} `n"
 }
 $script += "`t `t } `n"
 $script += "`t } `n"
 $script += "AspNetCoreOnIIS -OutPutPath:`"C:\ConfigurationPath`""
 $script | Out-File C:\ServerConfiguration.ps1 -Force -Encoding utf8
 }                                                                                                                                                                                                 

Führt man nun das Script aus, mittels Invoke-Command auf dem Server aus, das generiert worden ist, so erhält man im Erfolgsfall nachfolgende Meldung.

Generierte MOF-Datei

Das generierte Script, für die WindowsFeatures sieht dann so aus: (Nur ein kleiner Ausschnitt)

Configuration AspNetCoreOnIIS { 
	 Import-DscResource -ModuleName 'DesiredStateConfiguration' 
	 	 Node 'loalhost' { 
	 	 	 WindowsFeature ADCS-Enroll-Web-Pol {
	 	 	 	 Name = 'ADCS-Enroll-Web-Pol' 
	 	 	 	 Ensure = 'Present' 
	 	 	} 
	 	 	 WindowsFeature ADCS-Enroll-Web-Svc {
	 	 	 	 Name = 'ADCS-Enroll-Web-Svc' 
	 	 	 	 Ensure = 'Present' 
	 	 	} 
.......

Nun ist es ein leichtes, einen Server für einen bestimmten Einsatzzweck vorzubereiten, indem bei den nicht gewünschten Funktionen, einfahch der Parameter "Ensure" auf Absent gesetzt werden muss.

Fazit

Ich finde die Desired State Configuration leichtgewichtig und ich denke, dass man mit dieser Art der Konfiguration vieles erledigen kann und es nicht immer ein Ainsible oder Puppet oder sonst ein Werkzeug geben muss. So kann auf eine einfach Art und Weise eine Boilerplate Konfiguration erstellt und dann für die unterschiedlichen Zwecke in einer anderen Konfiguration gespeichert werden.

Weiterführende Links

OpenSSH Agent /Server auf mehreren Windows Server Core 2019 installieren

Ziel

In diesem Beitrag möchte ich erläutern, wie es möglich ist den OpenSSH Server und den Agenten auf mehreren Windows Servern zu installieren und dies ohne Internetverbindung

Voraussetzungen

Um das Ziel zu erfüllen, ist es notwendig, dass die Features On Demand für Windows 2019 Server und Windows 10 (denn nur hier sind die beiden Komponenten vorhanden) zur Verfügung stehen. Diesen Artikel habe ich mir zu Hilfe genommen um das zu bewerkstelligen. Weiter gilt zu beachten dass:

  • Alle Server über WinRM /WSMan erreichbar sind
  • Der Administrator berechtigt ist, auf den Server zuzugreifen
  • Windows Server 2019 Core
  • Die Features On Demand liegen auf dem lokalen Rechner bereit um diese zu kopieren (bei mir sind diese gezipped).

Durchführung

Um sich mit den Servern zu verbinden bin ich wie folgt vorgegangen:

  1. Erstellen eines Arrays in der Powershell
$servers = "ISRV-01","ISRV-02","ISRV-03","ISRV-04"
  1. Zwischenspeichern der Anmeldeinformationen für den Zugriff auf die Server. Dies kann wie folgt bewerkstelligt werden:
$cred = Get-Credential
  1. Anschliessend wird durch das Array wie folgt iterriert:
$servers | Foreach-Object -Process {
	
}
  1. In diesem Iterationsblock wird zuerst das Features On Demand ZIP kopiert, dann eine Session auf den Zielserver erstellt und annschliessend die gewünschten Funktionen installiert.
Kopiervorgang Fortschrittsanzeige
Kopiervorgang Fortschrittsanzeige
$session = New-PSSession -ComuputerName $_ -Credential $cred
	Copy-Item C:\Temp\_FeaturesOnDemand.zip -Destination C:\ -ToSession $session
	Invoke-Command -Session $session -ScriptBlock{
		Expand-Archive C:\_FeaturesOnDemand.zip -DestinationPath C:\ -Force
		Add-WindowsCapability -Online -Name OpenSSH.Agent~~~~0.0.1.0 -Source C:\_FeaturesOnDemand
		Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 -Source C:\_FeaturesOnDemand
		Remove-Item C:\_FeaturesOnDemand.zip -Force
		Remove-Item C:\_FeaturesOnDemand -Recurse -Froce
}
Remove-PSSession -Id $session.Id

Den Status der Installation sieht man dann wie folgt:

Installationsfortschritt und Status
Installationsfortschritt und Status

Fazit

Mit einfachen Mitteln lassen sich ganze Serverfarmen administrieren. Natürlich könnte man nun auch die Active Directory nach gewünschten Servern Abfragen und diese dann administrieren. Zum Abschluss noch das Ganze Script.

$servers | Foreach-Object -Process {
$session = New-PSSession -ComuputerName $_ -Credential $cred
Copy-Item C:\Temp\_FeaturesOnDemand.zip -Destination C:\ -ToSession $session
	Invoke-Command -Session $session -ScriptBlock{
		Expand-Archive C:\_FeaturesOnDemand.zip -DestinationPath C:\ -Force
		Add-WindowsCapability -Online -Name OpenSSH.Agent~~~~0.0.1.0 -Source C:\_FeaturesOnDemand
		Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 -Source C:\_FeaturesOnDemand
		Remove-Item C:\_FeaturesOnDemand.zip -Force
		Remove-Item C:\_FeaturesOnDemand -Recurse -Froce
	}
Remove-PSSession -Id $session.Id
}

App Deployment mit Minikube /kubectl

Ziel

In diesem Beitrag möchte ich in meiner Umgebung die Applikations Verteilungen (Deployments) lernen. Weiter möchte ich eine .NET Applikation auf den Cluster mit kubectl verteilen.

Ich folge den hier beschriebenen Scrhitte und verwende eine einfache .NET Core Applikation für das Deployment.

Voraussetzungen

Um diese Aufgabe zu erfüllen sind folgende Voraussetzungen gegeben:

  • Minikube ist installiert
  • Eine .NET Core App ist vorhanden um auf einen Container verteilt werden zu können.
  • Docker für Windows muss installiert sein
  • Unter Umständen muss das LINUX Subsystem für Windows installiert sein, wenn ihr die entsprechende Option im Docker-Installer ausgewählt habt.

Durchführung

Um das oben genannte Ziel zu erreichen muss ein Kubernetes Deployment erstellt werden. Für die Erstellung eines Deploments wird das CLI kubectl verwendet, dass in meinem Fall im C:\Program Files\kubectl installiert worden ist.

  1. Starten der Powershell als Administrator
  2. Anschliessend den minikube starten, wenn er noch nicht gestartet worden ist.

Der erfolgreiche Start sieht dann so aus:

Erfolgreicher Minikube Start in der Powershell

Werden beim Starten Fehler festgestellt, so sollte zuerst mit minikube stop versucht werden das Ganze zu stoppen und wieder zu starten.

Den Status kann man dann damit überprüfen:

minikube status

Un erhält dann die folgende Meldung:

Minikube Status – Powershell
  1. Nun wird der Befehl
kubectl version
kubectl get nodes

ausgeführt.

Das sieht dann so aus:

kubectl version und nodes in der Powershell

Es ist ersichtlich, dass der Client und der Server verfügbar sind. Weiter ist ersichtlich, dass ein verfügbarer Knoten (Node) da ist. Kubernetes verwendet für die Applikationsverteilung die verfügbaren Ressourcen auf Basis der Knoten (Nodes).

  1. Nun kommt der spannende Teil lokal in der Powershell. Hier muss die .NET Core Applikation angegeben werden, die verteilt werden soll. Hierzu habe ich eine Beispiel-Applikation analog dem hier beschriebenen Vorgehen erstellt.

Das Docker-Image ist mit dem Befehl

docker build -t app-demo -f Dockerfile .

erstellt worden.

Anschliessend kann man überprüfen ob das Image in der lokalen Registry vorhanden ist mit:

docker images
App-Demo in der lokalen Registry – Powershell
  1. Anschliessend habe ich dann mit kubectl ein Deplyoment erstellt, das auf die lokale Docker Registry geht.
kubectl create deployment app-demo --image=app-demo:latest

kubectl teil mir dann mit, dass das Deployment erfolgreich erstellt worden ist.

Deployment ist erstellt worden – Powershell

Fazit

Das Ganze, sieht einfach aus aber der eine oder andere Fallstrick ist dann doch vorhanden. Wenn zum Beispiel das .NET Core Image nicht von einer zentralen Registry runtergeladen werden kann, sondern selber gehostet werden muss, aus Sicherheitsgründen oder politischen Vorgaben. Falls der Artikel Gefallen gefunden hat, freue ich mich über einen Like und bin für Rückmeldungen offen.

Kubernetes Cluster erstellen mit Minikube

Ziel

Mein Ziel in diesem Beitrag ist, einen Kubernetes Cluster, mit meinem SetUp zu erstellen und mich mehr und mehr vertraut mit Containern und Kubernetes zu machen. Dieser Artikel beschreibt die Vorgehensweise mit meinem Setup und folgt dem Tutorial hier

Voraussetzungen

Damit ich einen Cluster installieren kann, muss zuerst Minikube installiert sein, welches eine leichtgewichtige Implementierung von Kubernetes darstellt. Dies kreiiert eine virutelle Machine (nachfolgend VM genannt) auf dem lokalen Computer.

Durchführung

In diesem Abschnitt gehe ich Schritt für Schritt das interaktive Tutorial lokal nach denselben Schritten durch.

Version und Starten von Minikube

Natürlich kann auch mit dem interaktiven Tutorial auf gearbeitet werden. Da ich nun mein eigenes Setup habe, starte ich zuerst die Powershell als Administrator und gehe wie folgt vor:

  1. Powershell als Administrator starten
  2. Minikube version anzeigen und starten
Minikube Version und Start mit Powershell

Zum Vergleich, im interaktiven Terminal sieht dass dan so aus:

Minikube Version und Start im interaktiven Terminal auf kubernetes.io

Cluster Version

Um mit Kubernetes zu interagieren wird das Kommandzeilen Werkzeug kubectl verwendet. Zuerst schauen wir uns ein paar Cluster informationen an. Zu diesem Zweck geben wir das Kommando

kubectl version
kubectl version mit Powershell

Die interaktive Konsole zeigt diese Informationen ein wenig übersichtlicher an.

kubectl version im interaktiven Terminal auf kubernetes.io

Cluster Details

Um die Cluster Details anzuzeigen geben wir folgende Kommandos ein:

kubectl cluster-info
kubectl get nodes

Mit dem Nodes-Kommando werden uns alle Nodes (Knoten) gezeigt die wir für unsere Applikationen verwenden können.

kubectl cluster und node informationen in der powershell

Das interaktive Terminal zeigt uns die Informationen wie folgt an:

kubectl cluster und node informationen im interaktiven Terminal von kubernetes.io

Fazit

Ich hätte nie gedacht, dass dies so einfach sein wird und bin mir bewusst, dass die Herausforderungen noch kommen werden. Nun freue ich mich auf die weiteren Module und werde diese mit dem Fokus einer .NET Applikation durcharbeiten. Weiter geht es dann mit dem nächsten Modul mit meiner Installation.

Meine ersten Schritte mit Kubernetes /Minikube

Ziel

Das Ziel, ist es einen Minikube auf meinem Windows Rechner zu installieren um mich in die Welt von Container und Kubernetes einzuarbeiten. Das soll eine Artikelserie geben, die basierend auf den folgenden Tutorials ist Kubernetes.io

Voraussetzungen

Zuerst müssen die folgenden Voraussetzungen erfüllt sein, damit Minikube installiert werden kann:

  • kubectl muss installiert sein

Ich habe dies mit Powershell durchgeführt. Hierzu muss diese als Administrator ausgeführt werden. Anschliessend kann mit dem folgenden Befehl die Installation initiiert werden.

Install-Script Name 'install-kubectl' -Scope CurrentUser -Force
install-kubectl.ps1 -DownloadLocation "C:\Program Files\kubectl"

Es kann sein, dass der NuGet Provider, wie im nachfolgenden Bild, aktualisiert werden muss. Hier mit „Y“ bestätigen und den Befehl noch einmal ausführen, wie oben beschrieben.

Installation von Minikube mit Powershell

Ich selber hatte Probleme, die mit dem Bit-Filetransfer die Installation verhinderten. Damit ich trotzdem weiterkam, habe ich die Binary manuell heruntergeladen, diese in den „C:\Program Files\kubectl“ Ordner kopiert und den Pfad angegeben.

 Die Version ist mir mit folgendem Befehl angezeigt worden:

kubectl client --version
Kubectl Client version

Nun sind alle Voraussetzungen für die Minikube Installation, wie [hier](https://kubernetes.io/docs/tasks/tools/install-kubectl/) beschrieben, durchgeführt worden.

Durchführung

Für die Installation des Minikube, habe ich mich für den Windows Installer entschieden. Dieser muss dann als Administrator installiert werden.

Minikube Installation als Administrator

Minikube Setup

Ist die ausführbare Datei als Administrator gestartet worden, so werden die Installationsschritte durchgegangen:

  1. Als erstes wählen wir die Sprache "Englisch"
Setup Sprache wählen
  1. Anschliessend sind die Lizenvereinbarungen zu bestätigen.

  2. Danach den Installationsort wählen. Ich habe diesen auf dem Standardwert wie unten dargestellt belassen.

Installationsziel für Minikube

Minikube Installationbestätigung

Mein Setup ist Windows 10 mit Hyper-V. So muss Minikube gestartet werden. Eine Liste der Treiber kann hier gefunden werden.

Zu disem Zweck muss die Powershell wieder mit Administratorenrechten und dem folgenden Befehl ausgeführt werden:

minikube start --driver=hyperv

Ist alles korrekt eingegeben worden, dann startet Minikube und setzt seine Umgebung zur Verwendung mit Hyper-V auf, wie im nachfolgenden Bild dargestellt.

Minikube mit Hyper-V initialisieren

Fazit

So kann ich mir, ohne grosse Kosten auf meinem private Azure Portal einmal Kubernetes lokal in Verbindung mit Hyper-V anschauen und mich vertraut machen. In weiteren Artikeln werde ich mich analog den hier hier durchhangeln und mir dann später eine Umgebung aufzubauen, die in etwa bei den meisten Deployments vorkommt.

Dokumentieren ohne Microsoft Word

Voraussetzungen

Ich bin als System Engineer bei der Schweizerischen Eidgenossenschaft tätig und wir haben die tägliche Herausforderung, dass wir Artefakte zum Beispiel für die Betriebsübergabe gemäss HERMES verpflichtet sind zu erstellen.Als Beispiel in diesem Blogpost habe ich das Betriebshandbuch und dessen Vorlage genommen, die in diesem Blogpost als Markdown verwendet werden soll.

Die Struktur des Dokumentes ist nachfolgend aufgeführt:

  • Systemübersicht
  • Aufnahme des Betriebes
    • Voraussetzungen für die Betriebsaufnahme
      • Ablauf der Betriebsaufnahme
      • Qualitätssicherung nach Betriebsaufnahme
      • Vorgaben zur Abnahme des Systems
  • Durchführung und Überwachung des Betriebes
    • Betriebsüberwachung
    • Datensicherung
    • Kontrolle zum Datenschutz
    • Statistisken, Kennzahlen, Messzahlen
    • Vorgehen im Fehlerfall
    • Hindweise auf Betriebsprozesse
  • Unterbrechung oder Beendigung des Betriebes
    • System stoppen
    • Ablauf für die Wiederinbetriebnahme
    • Qualitätssicherung nach Wiederinbetriebnahme
    • Abbau des System, Archivierung, Übergabe
  • Supportorganisation
    • Supportprozesse
    • Organisation mit Rollen
  • Changemanagement
    • Changemanagement Prozess
      • Changemanagement mit Rollen, Kontaktinformationen
  • Sicherheitsbestimmungen

Hierzu verwende ich die folgenden Visual Studio Code Extensions:

Eine sehr hilfreiche Quelle für die Anwendung des Authoring Packs findet man hier

Duchführung

Hierfür erstelle ich mir die notwendige Struktur des Betriebsdokumentes auf dem Dateisystem. Dies sieht dann bei mir so aus:

  • Systemübersicht
  • Aufnahme des Betriebes
  • Durchführung des Betriebes
  • Unterbrechung oder Beendigung des Betriebes
  • Supportorganisation
  • Changemanagement
  • Sicherheitbestimmungen

Als Beispiel die Ordnerstruktur für die Ordnerübersicht.

In diesem Ordner sind alle relevanten Artefakte für dieses Kapitel integriert. Bedauerlicherweise ist es aktuell nicht möglich mit dem Microsoft Authoring Pack SVG Grafiken zu referenzieren.

Jedoch kann mit der INCLUDE Direktive, Mardown Dateien zusammen geführt werden, sodass zum Schluss dann ein gesamthaftes Betriebshandbuch erstellt werden kann. Diese sieht dann folgendermassen aus:

[!INCLUDE [Systemoverview](Link zum Kapitel)]
[!INCLUDE [Betriebsaufnahme](Link zum Kapitel)]
[!INCLUDE [Durchführung des Betriebes](Link zum Kapitel)]
[!INCLUDE [Unterbrechung des Betriebes](Link zum Kapitel)]

Fazit

Das Microsoft Authoring Pack hilf viel beim Dokumentieren nur wäre es wünschenswert, wenn die Grafiken nicht nur als jpg/png unterstützt werden würden, sondern auch als SVG, das ja von yUML automatisch generiert wird. Für hilfreiche Rückmeldungen bin ich dankbar.

Dokumentieren mit Markdown und Visual Studio Code

Ausgangslage

Für die Erstellung von Code bieten sich verschiedene Werkzeuge an. Eines davon ist Visual Studio Code, welches mit seiner Plug-In Vielfalt extrem erweitert werden kann.Viele Code-Dokumentationen bauen auf Markdown auf.

Ziel

Das Ziel ist, mit Visual Studio Code eine einfache Dokumentation, inklusive Grafiken zu erstellen und diese dann generieren zu lassen. Folgender Fall soll abgedeckt werden.

  • Eine Klassendiagramm als Bild in einem Markdown anzeigen.

Natürlich sind noch weitere Diagramme möglich. Eine Hilfestellung bietet sich beim Plug-In Ersteller an (https://marketplace.visualstudio.com/_apis/public/gallery/publishers/ms-vscode-remote/vsextensions/remote-ssh/0.51.0/vspackage) wo viele weitere Beispiele zu finden sind.

Vorbereitungen

Für meine Installation habe ich foglende Komponenten verwendet:

Somit können wir starten zu dokumentieren.

Ausführung

Als ersters wird eine yuml Datei erstellt.

yuml Datei erstellen Erstellte yuml-Datei

Es ist ersichtlich, dass auf der rechten Seite bereits eine Vorschau aktiv ist. Fängt man in der Datei mit den vorgsehenen Tags an, so wird einem mit „class“ Autovervollständigung angeboten, die mit Tab bestätig werden kann.

Autofill

Anschliessend ist der „Stub-Code“ bereits generiert.

Stub code

Auch das bereits generierte Bild lässt sich sehen und kann anschliessend im Readme, mittels Markdown Syntax, eingebettet werden. 

SVG Referenz im Markdown

Markdown Referenz auf das SVG-Bild

Das SVG-Bild, wird mit der Option

{generate:true}

forciert.

Einbinden lässt es sich dann wie nachfolgend dargestellt. 

SVG Referenz im Markdown

Markdown Referenz auf das SVG-Bild

Auch die Vorschau, auch wenn nicht Word konkurrenzfähig, lässt sich sehen. 

Doc Preview

Dokument-Vorschau

Um einenen Export zu starten drückt man CTRL+P und gibt dann Export ein.

Fazit

Mit einfachen Mitteln, kann eine Dokumentation erstellt und exportiert werden, die sich sehen lassen kann. Konstruktive Kritik und Anregungen werden gerne entgegen genommen.