Archiv

Posts Tagged ‘iis-remote-Administration-powershell’

Automatische Applikationsverteilung mit Powershell

Herausforderung

Damit wir zukünftig Script-basiert unsere Applikation und die damit verbundenen Produkte automatisch verteilen wollen, habe ich, auftrund der Flexibilität der Powershell ein entsprechendes Script erstellt.

Dieses Script verteilt unter anderem:

  • Dienste
  • Anwendungen
  • Webseiten

Ziele

Folgende Ziele sollen dafür erreicht werden:

  • Die Dienste werden konfiguriert und mit der entsprechenden Datenbankverbindung gestartet (Entwicklungs-, Integrations- oder Produktionsumgebung).
  • Gleiches Ziel gilt für die Anwendung
  • Gleische Ziel gilt auch für die Webseite

Durchführung

Die Anwendungen und die Webseiten übrprüfen beim Start in welchem Ordner sie sich befinden und wählen automatisch die korrekte Konfiguration aus. Dies gilt nicht für die Dienste, also müssen die .config Dateien nach dem Verteilen angepasst werden, sodass der Dienst auf die richtige Datenbank verbinden kann.

Hierzu wird das folgende Powershell Script erstellt, welches Schritt für Schritt erläutert wird.

Vorbereitung

Das SetUp des Scriptes sieht wie folgt aus:

param([Parameter(Mandatory=$true)][string]$environment, [Parameter(Mandatory=$true)][string]$buildPath)

$server = [string]::Empty;
$deployPath = [string]::Empty;
$dbServer = [string]::Empty;
$global:session = $null;

if($environment.Equals('Entwicklung')){
    $server = "Entwicklungsserver-01";  
    $deployPath = "\\EntwicklungsZielServer-01\D$\Produkt";      
    $dbServer = "EntwicklungsDbserver-01";
    $session = New-PSSession -ComputerName $server
}elseif($environment.Equals('Integration')){
    $server = "Integrationsserver-01";  
    $deployPath = "\\Integrationsserver-01\D$\Produkt";      
    $dbServer = "Integrationsserver-01";
    $session = New-PSSession -ComputerName $server;
}elseif($environment.Equals('Production')){
    $server = "Produktionsserver-01";  
    $deployPath = "\\Produktionsserver-01\D$\Produkt";      
    $dbServer = "Produktionsserver-01";
    $session = New-PSSession -ComputerName $server;
}

Das SetUp überprüft an der Stelle, welcher Parameter am Anfang mit gegeben worden ist und stellt die Werte dementsprechend zusammen.

Verteilung der Benutzerapplikation

In unserem Fall ist diese Applikation eine Desktop-Applikation, die vom entsprechenden Share aus gestartet wird. Aufgrund in welchem Ordner diese liegt, wird dann die richtige Verbindungszeichenfolge genommen. Die Funktion sieht so aus:

Function DeployProduct(){   
        
    Write-Host('Deploying ' + $buildPath + ' in ' + $environment);

    Write-Host('Deploy Produkt 01');

    if($environment.Equals('Entwicklung'))
    {
        copy-Item -Path (Get-Item -Path (Join-Path -Path $buildPath -ChildPath "Produkt-01")) -Recurse -Force -Destination "\\Share-01\DEVELOPMENT";
    }
    elseif($environment.Equals('Integration'))
    {
        copy-Item -Path (Get-Item -Path (Join-Path -Path $buildPath -ChildPath "Produkt-01")) -Recurse -Force -Destination "\\Share-01\INTEGRATION";
        Write-Host('CleanUp xml and pdb files');
        Get-ChildItem -Path "\\Share-01\Produkt-01" -Recurse -Force -Include '*.pdb', '*.xml', '*sccm*exe', '*.nupkg', '*.nuspec' | Remove-Item -Force;
    }
    elseif($environment.Equals('Production'))
    {
        copy-Item -Path (Get-Item -Path (Join-Path -Path $buildPath -ChildPath "Produkt-01")) -Recurse -Force -Destination "\\Share-01\PRODUCTION";
        Write-Host('CleanUp xml and pdb files');
        Get-Item -Path "\\Share-01\PRODUCTION\Produkt-01" -Recurse;
    }
}

Somit hätten wir die Applikation unseres Produktes verteilt.

Dienstverteilung

Bei der Dienstverteilung gibt es einen Fallstrickt, den ich hier vorenthalten möchte und zwar MUSS man, wenn man die .config Datei anpassen will, diese wie folgt speichern.

(Get-Content -Path $_.FullName).Replace('SAIFC1-AISQLD-03', $dbServer) | Out-File -FilePath $_.FullName -Force -Encoding utf8;

Ganz wichtig hierbei ist, dass man die Datei im UTF-8 Format speichert. Ansonsten kann es sein, dass man den Fehler ‚Side-by-Side‘ erhält.

Nun die ganze Verteilung der Dienste, wie ich sie vorgenommen habe:

Stoppen der Dienste

Function StopServices(){
    Get-Service -ComputerName $server -Include "*%DienstName%*" | Stop-Service -Force;    
}

Kopieren des Dienstes

Function ServiceDeployment(){      
    if($environment.Equals('Entwicklung'))
    {        
        copy-Item -Path (Get-Item -Path (Join-Path -Path $buildPath -ChildPath "DailyService")) -Recurse -Force -Destination $deployPath;
    }
    elseif($environment.Equals('Integration'))
    {
        copy-Item -Path (Get-Item -Path (Join-Path -Path $buildPath -ChildPath "DailyService")) -Recurse -Force -Destination $deployPath;
        Write-Host('CleanUp xml and pdb files');
        Get-ChildItem -Path $deployPath -Recurse -Force -Include '*.pdb', '*.xml', '*sccm*exe', '*.nupkg', '*.nuspec' | Remove-Item -Force;
    }
    elseif($environment.Equals('Production'))
    {
        copy-Item -Path (Get-Item -Path (Join-Path -Path $buildPath -ChildPath "DailyService")) -Recurse -Force -Destination $deployPath;
        Write-Host('CleanUp xml and pdb files');
        Get-Item -Path $deployPath -Recurse -Force -Include '*.pdb', '*.xml', '*sccm*exe', '*.nupkg', '*.nuspec' | Remove-Item -Force;
    }    
}

Änderung der Verbindungszeichenfolge

Function ChangeConnectionStrings(){
    
    Get-ChildItem -Path $deployPath -Filter "*.config*" -Recurse | ForEach-Object{                     
        (Get-Content -Path $_.FullName).Replace('EntwicklungsDbServer-01', $dbServer) | Out-File -FilePath $_.FullName -Force -Encoding utf8;        
    }
}

Starten des Dienstes

Function StartServices(){
    Get-Service -ComputerName $server -Include "*%DienstName%*" | Start-Service;    
}

Fast haben wir es geschafft.

Verteilung der Webseite

Da die Webseite wie auch die Applikation weiss mit welchem Datenbankserver sie sich verbinden muss, wenn sie in einem bestimmten Ordner liegt, muss man hier nur folgende Aktivitäten durchführen:

  • Stoppen der jeweiligen Webseite
  • Kopieren des Inhaltes der Webseite
  • Starten der Webseite
Function DeployWebSite(){
$session = New-PSSession -ComputerName InternetServer-01;
    if($environment.Equals('Entwicklung'))
{
    Invoke-Command -Session $session -ScriptBlock { Stop-Website -Name Web-Entwicklung };
    copy-Item -Path (Get-Item -Path (Join-Path -Path $buildPath -ChildPath "Web-Entwicklung\_PublishedWebsites\Web-Entwicklung")) -Recurse -Force -Destination "\\InternetServer-01\D$\WebSites\Development";
    Invoke-Command -Session $session -ScriptBlock { Start-Website -Name Web-Entwicklung };
}
elseif($environment.Equals('Integration'))
{
    Invoke-Command -Session $session -ScriptBlock { Stop-Website -Name Web-Integration };
    copy-Item -Path (Get-Item -Path (Join-Path -Path $buildPath -ChildPath "Web-Integration\_PublishedWebsites\Web-Integration")) -Recurse -Force -Destination "\\InternetServer-01\D$\WebSites\Integration";
    Write-Host('CleanUp xml and pdb files');
    Get-ChildItem -Path "\\InternetServer-01\D$\WebSites\Integration" -Recurse -Force -Include '*.pdb', '*.xml' | Remove-Item -Force;
    Invoke-Command -Session $session -ScriptBlock { Start-Website -Name Web-Integration };
}
elseif($environment.Equals('Production'))
{    
    Invoke-Command -Session $session -ScriptBlock { Stop-Website -Name Web-Produktion };
    copy-Item -Path (Get-Item -Path (Join-Path -Path $buildPath -ChildPath "Web-Produktion\_PublishedWebsites\Web-Produktion")) -Recurse -Force -Destination "\\InternetServer-01\D$\WebSites\Production";
    Write-Host('CleanUp xml and pdb files');
    Get-ChildItem -Path "\\InternetServer-01\D$\WebSites\Integration" -Recurse -Force -Include '*.pdb', '*.xml' | Remove-Item -Force;
    Get-Item -Path "\\InternetServer-01\D$\WebSites\Production" -Recurse;
    Invoke-Command -Session $session -ScriptBlock { Start-Website -Name Web-Produktion };
}
}

Aufgrung dessen, dass der Windows Server 2012R2 bereits die Module für die Administration des IIS mittels Powershell instaliert hat, wird hier eine Remote-Session eröffnet, damit dann das Kommando remote ausgelöst werden kann. (ACHTUNG: Der Benutzer muss hierfür und auch für alle anderen Aktionen die entsprechende Berechtigung auf dem Server haben).

Fazit

Mit Powershell lassen sich mehr oder weniger einfache Verteilmechanismen etablieren, die einem zum Anderen eine gewisse Flexibilität geben und zum Anderen sehr gut an die eigenen Bedürfnisse angepasst werden können.