Wie kann die Erstellung virtueller Maschinen automatisiert werden? Wir erzählen im Detail

Das Erstellen einer neuen virtuellen Maschine ist eine zeitaufwändige Routine. Und je mehr Infrastruktur und Organisation vorhanden sind, desto mehr Verfahren sind mit diesem Prozess verbunden. Wir haben diesen Prozess mit PowerShell automatisiert.

Willkommen bei kat, wenn Sie interessiert sind.




Programmierer arbeiten nicht gerne doppelt, auch Systemadministratoren.

Nachfolgend finden Sie ein Beispiel für die Automatisierung eines unserer Kunden.

Wir wollten sicherstellen, dass jeder Ingenieur oder Projektmanager mit minimalem Aufwand und für einen minimalen Zeitraum eine neue virtuelle Maschine erstellen kann. Unser Kunde hat ein ITSM-System, in diesem Beispiel ist es ServiceNow, wir haben das entsprechende Webformular im Servicekatalog erstellt. Um eine neue Maschine zu „bestellen“, muss der Manager die Felder ausfüllen und die „Bestellung“ bestätigen, nachdem die Prozesskette gestartet wurde, und am Ausgang wird die Maschine betriebsbereit gemacht.

Schauen wir uns also an, was ein Manager definieren muss, um eine neue virtuelle Maschine zu erstellen:



VM-Beschreibung: Beschreibung der virtuellen Maschine
Hier sind einige Klarstellungen erforderlich. In unserer Lösung wird PowerShell 5.1 aktiv verwendet. Während wir nur Windows verwenden, werden wir in Zukunft versuchen, Unterstützung für Unix-Computer hinzuzufügen und auf PowerShell Core zu wechseln.

Betriebssystem , Betriebssystem. Es gibt keine besonderen Hindernisse für die Verwendung von Windows 2008 (R2), aber wir verwenden 2012R2 oder 2016.

VM-Größe , Größe der virtuellen Maschine. Für jeden kann dies auf seine eigene Weise bestimmt werden, in diesem Beispiel Small 1CPU-4Gb Ram, Medium 2CPU-8Gb, Large 4-16.

VM-Speicher , Datenträger 0 (C: \) hat eine feste Größe, die Sie nicht ändern können. Es ist nur die Auswahl für den schnellen / langsamen Speicher verfügbar. "Schnell" kann Speicherebene mit SSD sein, und "Langsam" kann Speicher auf "normalen" Festplatten (natürlich SAN) sein. Disk1 (ab Disk2) verfügt außerdem über einen Selektor zur Auswahl von Speicher sowie Felder zur Eingabe der gewünschten Größe in Gigabyte, Buchstaben für die Partition und Clustergröße (wichtig für SQL Server).

Vertrauen , wir stellen fest, dass der Computer einer Domäne beitreten muss oder nicht, mit Zugriff über das öffentliche Netzwerk oder nicht.

Typ , Maschinentyp. Fast jede Maschine kann in allen anderen Fällen als Front-End- oder Back-End-Anwendung oder als andere definiert werden. Anhand des ausgewählten Typs können wir das für die Maschine am besten geeignete Subnetz ermitteln.

Umgebung , in der Infrastruktur des Kunden gibt es zwei Rechenzentren: Primär (Produktion) und Sekundär (Entwicklung / Test), DC sind über einen schnellen Kommunikationskanal verbunden und bieten Fehlertoleranz. Nach Vereinbarung haben alle virtuellen Maschinen im primären DC eine IP-Adresse ab 10.230 und im sekundären DC ab 10.231.

(SLA) Service Level Agreement . Dieser Parameter wirkt sich auf die Servicequalität dieser Maschine aus.

Anwendungen Wir haben die Möglichkeit hinzugefügt, SQL Server zu installieren und zu konfigurieren. Sie müssen die Edition, den Instanznamen und die Sortierung auswählen. Es ist auch möglich, sowohl die Webserverrolle als auch vieles mehr zu konfigurieren.

Jetzt müssen wir bestimmen, wie die ausgewählten Werte gespeichert werden sollen. Wir haben entschieden, dass das bequemste Format eine JSON-Datei ist. Wie bereits erwähnt, verwendet die Kundenumgebung ITSM ServiceNow. Nachdem der Manager alle erforderlichen Werte ausgewählt hat, klickt er auf die Schaltfläche "Bestellen". Danach übergibt ServiceNow alle Parameter an unser PowerShell-Skript (an das Back-End-ServiceNow), das die JSON-Datei erstellt. Es sieht ungefähr so ​​aus:

.\CreateConfiguration.ps1 -SecurityZone trusted -VMDescription "VM for CRM System" -Requestor "evgeniy.vpro" -OSVersion 2k16 -OSEdition Standard -BuildNewVM -VMEnvironment Prod -VMServiceLevel GOLD -VMSize Medium -Disk0Tier Fast -Disk1Size 50 -Disk1Tier Eco -Disk1Letter D -MSSQLServer -MSSQLInstanceName "Instance1" -SQLCollation Latin1_General_CI_AS -SQLEdition Standard -Disk2Size 35 -Disk3Size 65 


Im Hauptteil des Skripts CreateConfiguration .ps1:

 # PowerShell- $config = [ordered]@{} #    . $config.SecurityZone=$SecurityZone 


Exportieren Sie am Ende unser Objekt in eine JSON-Datei:

 $ServerConfig = New-Object –TypeName PSObject $config ConvertTo-Json -InputObject $ServerConfig -Depth 100 | Out-File "C:\Configs\TargetNodes\Build\$($Hostname.ToLower()).json" -Force 


Beispielkonfiguration:

 { "Hostname": "dsctest552", "SecurityZone": "trusted", "Domain": "testdomain", "Requestor": "evgeniy.vpro", "VM": { "Size": "Medium", "Environment": "Prod", "SLA": "GOLD", "DbEngine": "MSSQL", "RAM": 8, "Storage": [ { "Id": 0, "Tier": "Fast", "Size": "100", "Allocation": 4, "Letter": "C" }, { "Id": 1, "Tier": "Eco", "Size": 50, "Label": "Data", "Allocation": 64, "Letter": "D" }, { "Id": 2, "Tier": "Fast", "Size": 35, "Label": "Data", "Allocation": 64, "Letter": "E" }, { "Id": 3, "Tier": "Fast", "Size": 65, "Label": "Data", "Allocation": 64, "Letter": "F" } ] }, "Network": { "MAC": "", "IP": "10.230.168.50", "Gateway": "10.230.168.1", "VLAN": “VLAN168” }, "OS": { "Version": "2k16", "Edition": "Standard", "Administrators": [ "LocaAdmin", "testdomain\\ Security-LocalAdmins" ] }, "OU": "OU=Servers,OU=Staging,DC=testdomain", "Applications": [ { "Application": "Microsoft SQL Server 2016", "InstanceName": "vd", "Collation": "Latin1_General_CI_AS", "Edition": "Standard", "Features": "SQLENGINE", "Folders": { "DataRoot": "E:\\MSSQL", "UserDB": "E:\\MSSQL\\MSSQL11.vd\\MSSQL\\Data", "UserLog": "E:\\MSSQL\\MSSQL11.vd\\MSSQL\\Log", "TempDB": "D:\\MSSQL\\MSSQL11.vd\\MSSQL\\TempDB", "TempDBLog": "D:\\MSSQL\\MSSQL11.vd\\MSSQL\\TempDB", "Backup": "E:\\MSSQL\\MSSQL11.vd\\MSSQL\\Backup" }, "MaxMemory": 2147483647 } ], "Description": "VM for CRM", "Certificate": { "File": null, "Thumbprint": null }, "Version": 0 } 


Möglicherweise haben Sie bemerkt, dass das Webformular keinen Namen und keine IP-Adresse der virtuellen Maschine hatte. Wir erhalten diese Werte automatisch wie folgt:

Der Name der Maschine , ITSM ServiceNow, hat einen speziellen Abschnitt: CMDB (Configuration Management Data Base). In dieser Datenbank werden alle Datensätze über vorhandene virtuelle Maschinen, deren Status, Support-Team und mehr gespeichert. Wir haben ungefähr 200 Sicherungsdatensätze mit dem Status "Zugewiesen" erstellt. Um einen Namen für die virtuelle Maschine zu erhalten, senden wir eine REST-Anfrage an CMDB, rufen den ersten "freien" Datensatz ab und ändern seinen Status von "Zugewiesen" in "Ausstehende Installation".

IP-Adresse und VLAN haben wir IPAM in unserem Netzwerk bereitgestellt. Dies ist eine in Windows Server 2016 integrierte Funktion, mit der Sie IP-Adressen in Ihrem Netzwerk verwalten können. Es ist überhaupt nicht erforderlich, alle Funktionen von IPAM (DHCP, DNS, AD) zu verwenden, sondern nur als Datenbank mit IP-Adressen mit einer möglichen Erweiterung der Funktionalität. Das Skript, das die JSON-Datei erstellt, fordert IPAM nach der ersten freien IP-Adresse im Subnetz an. Das VLAN-Subnetz (x / 24-Subnetz) wird basierend auf den ausgewählten Werten für SLA, Umgebung, Vertrauen und Typ bestimmt.
Die Konfigurationsdatei ist fertig, alle Felder sind vorhanden, Sie können eine Maschine erstellen. Die Frage lautet: "Wie werden Anmeldeinformationen für alle unsere Skripte gespeichert?". Wir verwenden das CredentialManager- Paket. Dieses Paket funktioniert mit der integrierten Windows Credential Manager-API zum Speichern von Kennwörtern. Beispiel für die Erstellung eines Passworts:

 New-StoredCredential -Target "ESXi" -UserName "testdomain.eu\vmwareadm" -Password "veryultraP@ssw00rd." -Type Generic -Persist LocalMachine 


Das Passwort kann auf diesem Computer und in diesem Konto gelesen werden.

 $ESXiAdmin = Get-StoredCredential -Type Generic -Target ESXi 


Wir haben einen Server, auf dem alle Konfigurationen mit GIT gespeichert sind. Jetzt können wir alle Konfigurationsänderungen zuverlässig verfolgen: wer, was, wo und wann.

Die geplante Aufgabe wird auf diesem Server konfiguriert: Überprüfen Sie den Ordner mit den Konfigurationen und schreiben Sie alle Änderungen in das Windows-Ereignisprotokoll.

Nach 15 Minuten schreibt die geplante Aufgabe in Windows EventLog, dass eine neue Konfigurationsdatei erkannt wurde.

Es ist Zeit, diese Konfiguration zu überprüfen. Zunächst müssen wir sicherstellen, dass die Datei die richtige Formatierung hat:

 $Configuration=(Get-Content -Raw $File | Out-String | ConvertFrom-Json) 


Wenn alles in Ordnung ist, ist es Zeit, mit der Erstellung des Computers zu beginnen und das Skript BuildVM.ps1 auszuführen.

In BuildVM.ps1 überprüfen wir, ob die Konfigurationsdatei eine Beschreibung aller Merkmale der virtuellen Maschine enthält: Größe, Umgebung, Sla, Typ, Speicher, RAM, Netzwerk.

Stellen Sie sicher, dass in der Infrastruktur ein Computer mit demselben Namen vorhanden ist (CheckVM.ps1).
Wir stellen über die VMWare PowerShell-CLI eine Verbindung zu unserer vSphere her:

 $VmWareAdmin = Get-StoredCredential -Type Generic -Target ESXi Connect-VIServer -Server "vSphereSrv" -Credential $VmWareAdmin | Out-Null 


Überprüfen Sie, ob sich in der Infrastruktur ein Computer mit demselben Namen befindet

 $VM=Get-VM $server -ErrorAction SilentlyContinue 


Und ausschalten:

 Disconnect-VIServer * -Force -Confirm:$false 


Stellen Sie sicher, dass der Computer auch unter WinRM nicht verfügbar ist

 $ping=Test-NetConnection -ComputerName $Configuration.Hostname -CommonTCPPort WINRM -InformationLevel Quiet -ErrorAction SilentlyContinue 


Wenn $ VM und $ ping leer sind, können Sie einen neuen Computer erstellen. (Wir behandeln Situationen, in denen ein Computer bereits manuell in ESXi erstellt wurde oder sich dieser Computer in einem anderen Rechenzentrum befindet.)

Ein paar Worte zum Auto. Dies ist ein vorbereitetes Image der virtuellen Maschine, das von sysprep finalisiert und in unserer vSphere in eine Vorlage konvertiert wurde. Der lokale Administrator mit dem uns bekannten Kennwort wird im Image gespeichert. Dieses Konto stürzt nach sysprep nicht ab, sodass wir über diese Vorlage auf jeden Computer zugreifen können. Später können wir dieses Kennwort aus Sicherheitsgründen ersetzen.


Erstellen einer virtuellen Maschine


Suchen Sie den entsprechenden SLR-Cluster:

 $Cluster=Get-Cluster -Name $Configuration.VM.SLA 


Überprüfen Sie, ob im Datenspeicher genügend Speicherplatz vorhanden ist:

 $DatastoreCluster = Get-DatastoreCluster |Where-Object {$_.Name -like $Datastore1Name} $Datastore1 = Get-Datastore -Location $DatastoreCluster |sort -Property "FreeSpaceGB" |select -Last 1 IF ($Datastore1.FreeSpaceGB -le "200"){ Write-Host -foreground red "STOP: Not enough datastore capacity for DISK" $vdisk.Id Break } 


Und genug Speicher:

 $VMHost = Get-VMHost -Location $Cluster |sort -Property "MemoryUsageGB" |select -First 1 IF ($VMHost.MemoryUsageGB -le "20"){ Write-Host -foreground red "STOP: No enough ESXi host capacity" Break } 


Wir nehmen unsere Vorlage

 $VMTemplate = Get-Template -Name 'Win2016_Std_x64_Template' 


Und erstellen Sie eine neue virtuelle Maschine

 New-VM -Name $Configuration.Hostname.ToUpper() -VMHost $VMHost -ResourcePool $ResourcePool -Datastore $Datastore -Template $VMTemplate -Location "AutoDeployed VMs" 


Es ist wichtig, die Netzwerkschnittstelle mit einem Subnetz mit aktiviertem DHCP zu verbinden.

Wir starten die virtuelle Maschine

 Start-VM $VM 


Speichern Sie die Beschreibung des Computers, damit Sie den Computer später auf VMWare-Ebene ermitteln können.

 Set-Annotation -Entity $VM -CustomAttribute "Change request" -Value $Configuration.Request -Confirm:$false Set-VM $VM -Notes $Configuration.Description -Confirm:$false 


Die Maschine wurde gestartet und jetzt können wir die empfangene MAC-Adresse herausfinden:

 $vMAC = (($VM | Get-NetworkAdapter | Select-Object -Property "MacAddress").MacAddress).Replace(':','') 


Speichern Sie diesen Wert in unserer JSON-Datei.

 $Configuration.Network.MAC=$VMAC ConvertTo-Json -InputObject $Configuration -Depth 100 | Out-File "C:\Configs\TargetNodes\Build\$Hostname.json" -Force 


Hier ist die Zeit, um unserem Git zu verpflichten, dass die Maschine erstellt wurde und einen eigenen einzigartigen MAC hat.

Das Gerät beginnt mit der Initialisierung (nach Sysprep), der Einrichtung des Geräts und der Erstkonfiguration.

Warten wir, bis unser WinRM-Computer mit dem Skript EstablishConnection.ps1 verfügbar ist.

Zuerst finden wir heraus, welche IP der Computer von DHCP erhalten hat:

 # $MAC = $vMAC while($isOnline -ne $true){ if((Get-DhcpServerv4Lease -ClientId $MAC -ScopeId $StagingDHCPScope -ComputerName $DHCPServer -ErrorAction Ignore).IPAddress.IPAddressToString){ $tempIP=(Get-DhcpServerv4Lease -ClientId $MAC -ScopeId $StagingDHCPScope -ComputerName $DHCPServer).IPAddress.IPAddressToString break } else{ if($isOnline -ne $true){ Write-Host "`r$i`t" -NoNewline $i++ } } } 


Und jetzt warten wir, bis die Maschine auf WinRM verfügbar ist:

 $LocalAdmin = Get-StoredCredential -Type Generic -Target LocalAdmin $i=0 $isOnline=$false while($isOnline -ne $true){ if(Invoke-Command -ComputerName $tempIP -ScriptBlock{ Get-ItemProperty -Path "Registry::\HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing" } -Credential $LocalAdmin -ErrorAction SilentlyContinue){ $isOnline=$true break } else{ if($isOnline -ne $true){ Write-Host "`r$i" -NoNewline $i++ Start-Sleep -Seconds 1 } } } 


Die Maschine ist fahrbereit.

Gewünschte Zustandskonfiguration


Um die gewünschte Konfiguration zu konfigurieren, verwenden wir den PowerShell-Teil - DSC (Desired State Configuration). Im Netzwerk befindet sich ein konfigurierter DSC-Pull-Server: dscpull.testdomain.eu.
Unten finden Sie die Konfiguration unseres DSC Pull Servers. Guter Artikel zur Konfiguration von DSC Pull.

 Node $NodeName { WindowsFeature DSCServiceFeature { Ensure = "Present" Name = "DSC-Service" } xDscWebService PSDSCPullServer { Ensure = "Present" EndpointName = "PSDSCPullServer" Port = 8080 PhysicalPath = "$env:SystemDrive\inetpub\PSDSCPullServer" CertificateThumbPrint = $certificateThumbPrint ModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules" ConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration" State = "Started" DependsOn = "[WindowsFeature]DSCServiceFeature" RegistrationKeyPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService" AcceptSelfSignedCertificates = $true UseSecurityBestPractices = $true } File RegistrationKeyFile { Ensure = 'Present' Type = 'File' DestinationPath = "$env:ProgramFiles\WindowsPowerShell\DscService\RegistrationKeys.txt" Contents = $RegistrationKey } } 


Es ist verfügbar unter: https://dscpull.testdomain.eu:8080

Sein Endpunkt: https://dscpull.testdomain.eu:8080/PSDSCPullserver.svc

Auf allen Pull-Server-Clients muss PowerShell 5.1 installiert sein
Wenn nicht PowerShell 5.1 installiert ist:

 $PSVersionTable.PSVersion.Major –lt 5 


Installieren Sie PowerShell 5.1:

 Write-Host "Download PowerShell 5.1" Invoke-Command -ComputerName $Node -ScriptBlock { [System.Net.ServicePointManager]::SecurityProtocol=[System.Net.SecurityProtocolType]::Tls12;Invoke-WebRequest -Uri "https://dscpull.testdomain.eu:8080/Files/Updates/WMF.msu" -OutFile C:\TEMP\WMF.MSU } Write-Host "Extract PowerShell 5.1" Invoke-Command -ComputerName $Node -ScriptBlock {Start-Process -FilePath 'wusa.exe' -ArgumentList "C:\temp\WMF.msu /extract:C:\temp\" -Wait -PassThru } Write-Host "Apply PowerShell 5.1" Invoke-Command -ComputerName $Node -ScriptBlock {Start-Process -FilePath 'dism.exe' -ArgumentList "/online /add-package /PackagePath:C:\temp\WindowsBlue-KB3191564-x64.cab /Quiet" -Wait -PassThru } Write-Host "PowerShell 5.1 has been installed" 


Ein PKI-Server wird auch in unserem Netzwerk bereitgestellt. Dies ist eine Bedingung für die sichere Verschlüsselung von Anmeldeinformationen, die in DSC-Mof-Dateien gespeichert sind (Mof-Dateien sind die „Sprache“, in der Pull Server und seine Clients kommunizieren). Wenn ein Client versucht, sich auf dem Pull-Server zu registrieren, muss ein Fingerabdruck-Zertifikat angegeben werden. Später verwendet der Pull-Server dieses Zertifikat zum Verschlüsseln von Kennwörtern. Unten sehen wir, wie es funktioniert.

Importieren Sie die Stammzertifizierungsstelle auf unseren neuen Computer:

  Invoke-Command -ComputerName $server -ScriptBlock{ $PKI="-----BEGIN CERTIFICATE----- MIIF2TCCA8GgAwIBAgIQSPIjcff9rotNdxbg3+ygqDANBgkqhkiG9w0BAQUFADAe **************************************************************** znafMvVx0B4tGEz2PFss/FviGdC3RohBHG0rF5jO50J4nS/3cGGm+HGdn1w/tZd0 a0FWpn9VCOSmXM2It+tSW1f4nZVt6T2kr1ZlTxkDhT7HMSGsrX/XJswzCkDGe3dE qrVVjNUkhVTaeeBWdujB5J6mcx7YkNsAUhODiS9Cf7FnYnxLFA72M0pijI48P5F0 ShM9HWAAUIrLkv13ug== -----END CERTIFICATE-----" $PKI | Out-File RootCA.cer Import-Certificate RootCA.cer -CertStoreLocation Cert:\LocalMachine\Root | select Thumbprint | Out-Null } -Credential $LocalAdmin | Out-Null 


Für die weitere Arbeit benötigen wir ein Paar RSA-Schlüssel. Wir werden ein selbstsigniertes Zertifikat erstellen und vorübergehend damit arbeiten.

Jetzt können wir uns auf dem Pull Server registrieren:

 $DscHostFQDN = [System.Net.Dns]::GetHostEntry([string]$env:computername).HostName $DscPullServerURL = "https://$($DscHostFQDN):8080/PSDSCPullserver.svc" $DscWebConfigChildPath = '\inetpub\psdscpullserver\web.config' $DscWebConfigPath = Join-Path -Path $env:SystemDrive -ChildPath $DscWebConfigChildPath $DscWebConfigXML = [xml](Get-Content $DscWebConfigPath) $DscRegKeyName = 'RegistrationKeys.txt' $DscRegKeyXMLNode = "//appSettings/add[@key = 'RegistrationKeyPath']" $DscRegKeyParentPath = ($DscWebConfigXML.SelectNodes($DscRegKeyXMLNode)).value $DscRegKeyPath = Join-Path -Path $DscRegKeyParentPath -ChildPath $DscRegKeyName $DscRegKey = Get-Content $DscRegKeyPath [DSCLocalConfigurationManager()] configuration RegisterOnPull { Node $Node { Settings { ConfigurationModeFrequencyMins = 1440 CertificateID = $Thumbprint RefreshMode ='Pull' RefreshFrequencyMins = 1440 RebootNodeIfNeeded = $true ConfigurationMode ='ApplyAndAutoCorrect' AllowModuleOverwrite = $true DebugMode = 'None' StatusRetentionTimeInDays = 1 } ConfigurationRepositoryWeb $([string]$env:computername) { ServerURL = $DscPullServerURL RegistrationKey = $DscRegKey CertificateID = $Thumbprint ConfigurationNames = @("$hostx") } } } RegisterOnPull -OutputPath $MetaConfigsStorage Set-DscLocalConfigurationManager -ComputerName $Node -Path $MetaConfigsStorage -Verbose -Force -Credential $LocalAdmin 


Senden Sie die erste Konfiguration an unsere Maschine

 Configuration Rename { param ( [Parameter()] [System.String[]] $Node, $hostname ) Import-DscResource -ModuleName xComputerManagement Import-DscResource –ModuleName PSDesiredStateConfiguration Node $Node { xComputer JoinDomain { Name = $hostname } } } Rename -Node $Node -OutputPath $DscConfigPath -hostname $hostname New-DscChecksum $DscConfigPath -Force Invoke-Command -ComputerName $Node -ScriptBlock{Update-DscConfiguration -Verbose -Wait } -Credential $LocalAdmin -Verbose 


Der Server wird automatisch umbenannt und neu gestartet. Jetzt können wir Join Domain ausführen.

 Configuration JoinAD { param ( [Parameter()] [System.String[]] $Node, [Parameter(Mandatory = $true)] [ValidateNotNullorEmpty()] [System.Management.Automation.PSCredential] $DomainAdmin, $hostname, $domain ) Import-DscResource -ModuleName xComputerManagement Import-DscResource –ModuleName PSDesiredStateConfiguration Node $Node { xComputer JoinDomain { Name = $hostname DomainName = $domain Credential = $DomainAdmin JoinOU = "OU=Servers,OU=Staging,DC=testdomain,DC=eu" } GroupSet LocalAdmins { GroupName = @( 'Administrators') Ensure = 'Present' MembersToInclude = @( 'testdomain-eu\dscstaging' ) } } } $cd = @{ AllNodes = @( @{ NodeName = $Node PSDscAllowPlainTextPassword = $false PSDscAllowDomainUser=$true Certificatefile = $CertFile Thumbprint = $Certificate.ToString() } ) } JoinAD -Node $Node -OutputPath $DscConfigPath -DomainAdmin $DomainAdmin -hostname $hostname -ConfigurationData $cd -domain $domain New-DscChecksum $DscConfigPath -Force Invoke-Command -ComputerName $Node -ScriptBlock{Update-DscConfiguration -Verbose -Wait } -Credential $LocalAdmin -Verbose 


So sieht unsere mof-Datei aus:

 instance of MSFT_Credential as $MSFT_Credential1ref { Password = "-----BEGIN CMS-----\nMIIBsgYJKoZIhvcNAQcDoIIBozCCAZ8CAQAxggFKMIIBRgIBADAuMBoxGDAWBgNVBAMMD1dJTi1H\nNFFKTFFQME4xNQIQOQN77pxew75HU6l7GPn99TANBgkqhkiG9w0BAQcwAASCAQAlhFf7Zs2gJbJEnc1DEK2yWbKcO+BEyD2cr6vKHdn\nQ9TrjvbysEOvYjT15o6MccwkMEwGCSqGSIb3DQEHATAdBglghkgBZQMEASoEEEdKJT+GX4IkPezR\nwYncyQiAIAFKxwJocH4ufRsq9L2Ipkp+VQCx2ljlwif6ac4X/PqG\n-----END CMS-----"; UserName = "testdomain.eu\\service_DomainJoin_001"; }; instance of MSFT_xComputer as $MSFT_xComputer1ref { ResourceID = "[xComputer]JoinDomain"; Credential = $MSFT_Credential1ref; DomainName = "testdomain.eu"; SourceInfo = "C:\\Program Files\\WindowsPowerShell\\Scripts\\JoinAD.ps1::34::9::xComputer"; Name = "dsctest51"; JoinOU = "OU=Servers,OU=Staging,DC=testdomain,DC=eu"; ModuleName = "xComputerManagement"; ModuleVersion = "4.1.0.0"; ConfigurationName = "JoinAD"; }; nMIIBsgYJKoZIhvcNAQcDoIIBozCCAZ8CAQAxggFKMIIBRgIBADAuMBoxGDAWBgNVBAMMD1dJTi1H \ nNFFKTFFQME4xNQIQOQN77pxew75HU6l7GPn99TANBgkqhkiG9w0BAQcwAASCAQAlhFf7Zs2gJbJEnc1DEK2yWbKcO + BEyD2cr6vKHdn \ nQ9TrjvbysEOvYjT15o6MccwkMEwGCSqGSIb3DQEHATAdBglghkgBZQMEASoEEEdKJT + GX4IkPezR \ nwYncyQiAIAFKxwJocH4ufRsq9L2Ipkp + VQCx2ljlwif6ac4X / PQG \ n ----- END CMS -----"; instance of MSFT_Credential as $MSFT_Credential1ref { Password = "-----BEGIN CMS-----\nMIIBsgYJKoZIhvcNAQcDoIIBozCCAZ8CAQAxggFKMIIBRgIBADAuMBoxGDAWBgNVBAMMD1dJTi1H\nNFFKTFFQME4xNQIQOQN77pxew75HU6l7GPn99TANBgkqhkiG9w0BAQcwAASCAQAlhFf7Zs2gJbJEnc1DEK2yWbKcO+BEyD2cr6vKHdn\nQ9TrjvbysEOvYjT15o6MccwkMEwGCSqGSIb3DQEHATAdBglghkgBZQMEASoEEEdKJT+GX4IkPezR\nwYncyQiAIAFKxwJocH4ufRsq9L2Ipkp+VQCx2ljlwif6ac4X/PqG\n-----END CMS-----"; UserName = "testdomain.eu\\service_DomainJoin_001"; }; instance of MSFT_xComputer as $MSFT_xComputer1ref { ResourceID = "[xComputer]JoinDomain"; Credential = $MSFT_Credential1ref; DomainName = "testdomain.eu"; SourceInfo = "C:\\Program Files\\WindowsPowerShell\\Scripts\\JoinAD.ps1::34::9::xComputer"; Name = "dsctest51"; JoinOU = "OU=Servers,OU=Staging,DC=testdomain,DC=eu"; ModuleName = "xComputerManagement"; ModuleVersion = "4.1.0.0"; ConfigurationName = "JoinAD"; }; 


DSC-verschlüsselte Anmeldeinformationen von einem Dienstkonto mit Domänenadministratorrechten: testdomain.eu \\ service_DomainJoin_001 mit einem selbstsignierten Zertifikat. Der DSC-Client mit seinem privaten Schlüssel entschlüsselt die Anmeldeinformationen und wendet alle Konfigurationsmodule mit den angegebenen Domänenanmeldeinformationen an. In diesem Fall wird der Domänenbeitritt in der angegebenen Organisationseinheit ausgeführt.

 GroupSet LocalAdmins { GroupName = @( 'Administrators') Ensure = 'Present' MembersToInclude = @( testdomain-eu\dscstaging' ) } 


Dieses Modul fügt lokalen Administratoren dscstaging zur weiteren Konfiguration hinzu.

Nach dem Neustart können wir den Computer mit Domänenanmeldeinformationen eingeben.

Wir warten darauf, dass der Server ein Zertifikat von unserer PKI erhält (wir haben die automatische Registrierung konfiguriert) und werden in Zukunft mit dem von unserer PKI ausgestellten Zertifikat arbeiten.

 $vmcert=Invoke-Command -ComputerName $server -ScriptBlock{ return Get-ChildItem -Path cert:\LocalMachine\My | where {$_.EnhancedKeyUsageList.FriendlyName -eq "Document Encryption"-and $_.Issuer -eq "CN=TestDomain Issuing CA, DC=testdomain, DC=eu"} } -ErrorAction Ignore 


Melden Sie sich jetzt erneut mit dem aktualisierten Fingerabdruck bei Pull Server an.

Das war's, der Computer mit Domänenbeitritt, und wir können ihn so verwenden, wie es uns passt.

Installieren Sie SQL Server


Die JSON-Datei beschreibt die Anforderungen für MS SQL Server. Wir verwenden DSC auch zum Installieren und Konfigurieren von SQL Server. So sieht die Konfiguration aus:

 Configuration $Node{ WindowsFeature "NetFramework35"{ Name = "NET-Framework-Core" Ensure = "Present" Source = "\\$DscHostFQDN\Files\Updates" } WindowsFeature "NetFramework45"{ Name = "NET-Framework-45-Core" Ensure= "Present" } SqlSetup "MSSQL2012NamedInstance"{ InstanceName = $MSSQL.InstanceName Features = $MSSQL.Features ProductKey = $ProductKey SQLCollation = $MSSQL.Collation SQLSysAdminAccounts = @('testdomain-EU\SQLAdmins',' testdomain-EU\Backup') InstallSharedDir = "C:\Program Files\Microsoft SQL Server" InstallSharedWOWDir = "C:\Program Files (x86)\Microsoft SQL Server" InstallSQLDataDir = $MSSQL.DataRoot SQLUserDBDir = $MSSQL.UserDBDir SQLUserDBLogDir = $MSSQL.UserLogDir SQLTempDBDir = $MSSQL.TempDBDir SQLTempDBLogDir = $MSSQL.TempDBLogDir SQLBackupDir = $MSSQL.BackupDir SourcePath = $SQLSource SAPwd = $SA SecurityMode = 'SQL' UpdateSource = ".\Updates" Action = "Install" ForceReboot = $True SQLSvcAccount = $SqlServiceCredential AgtSvcAccount = $SqlServiceCredential ISSvcAccount = $SqlServiceCredential BrowserSvcStartupType = "Automatic" DependsOn = '[WindowsFeature]NetFramework35', '[WindowsFeature]NetFramework45' } 

Wo $ MSSQL definiert ist:
 $MSSQL=$Configuration.Applications | where {$_.Application -eq "Microsoft SQL Server 2012"} 


$ MSSQL.InstanceName - all dies ist in unserer Json-Datei angegeben. Durch Anwenden dieser Konfiguration wird MS SQL Server mit allen Updates im Ordner "Updates" installiert und der Server bei Bedarf neu gestartet.

Das Auto ist fertig.

Service jetzt


In Service-Now sind mehrere APIs verfügbar . Wir verwenden die Rest-API.
Um eine Liste der Computer mit dem Status "Zugewiesen" zu erhalten, wird eine Abfrage des Formulars verwendet:
instance.service-now.com/cmdb_ci_server_list.do?sysparm_query=install_status=16 ^ u_subtype = ^ ORDERBYname
In PowerShell sieht es folgendermaßen aus:
 $url="https://instance.service-now.com/api/now/table/cmdb_ci_server?sysparm_query=install_status=16^u_subtype=^ORDERBYname" $uri= new-object System.Uri("https://instance.service-now.com/") #       $credentials = (Get-StoredCredential -Type Generic -Target DSC).GetNetworkCredential() $credentials = new-object System.Net.NetworkCredential $credentials.UserName, $credentials.SecurePassword Add-Type -AssemblyName System.Net.Http $handler = New-Object System.Net.Http.HttpClientHandler $handler.CookieContainer = New-Object System.Net.CookieContainer $handler.UseCookies=$true $handler.Credentials=$credentials $HttpClient = New-Object System.Net.Http.HttpClient($handler) $HttpClient.BaseAddress= $uri $Header = New-Object System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json") $HttpClient.DefaultRequestHeaders.Accept.Clear() $HttpClient.DefaultRequestHeaders.Accept.Add($Header); $response=$HttpClient.GetAsync($url) $respStream=$response.Result.Content.ReadAsStringAsync() $Servers = $respStream.Result | ConvertFrom-Json #   Configuration Items  $ServersCI=$Servers.result 

Das erste Array-Objekt ist der Hostname, den wir benötigen.
Wenn der Computer bereit ist, können Sie den Status des Computers in Service-Now ändern. Dazu das Skript UpdateCI.ps1:
 param( $CI, [ValidateSet("Allocated","In use","Pending install")] $NewStatus='In use' ) $url="https://instance.service-now.com/api/now/table/cmdb_ci_server?sysparm_query=name=$CI" $uri= new-object System.Uri("https://instance.service-now.com/") $credentials = (Get-StoredCredential -Type Generic -Target DSC).GetNetworkCredential() $credentials = new-object System.Net.NetworkCredential $credentials.UserName, $credentials.SecurePassword Add-Type -AssemblyName System.Net.Http $handler = New-Object System.Net.Http.HttpClientHandler $handler.CookieContainer = New-Object System.Net.CookieContainer $handler.UseCookies=$true $handler.Credentials=$credentials $HttpClient = New-Object System.Net.Http.HttpClient($handler) $HttpClient.BaseAddress= $uri $Header = New-Object System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json") $HttpClient.DefaultRequestHeaders.Accept.Clear() $HttpClient.DefaultRequestHeaders.Accept.Add($Header); $response=$HttpClient.GetAsync($url) $respStream=$response.Result.Content.ReadAsStringAsync() $Servers = $respStream.Result | ConvertFrom-Json $ServerCI=$Servers.result[0] $update=@{} if($NewStatus -eq "In use"){ $update.install_status=1 } if($NewStatus -eq "Pending install"){ $update.install_status=4 } $stringcontent = New-Object System.Net.Http.StringContent((ConvertTo-Json -InputObject $update -Depth 100),[System.Text.Encoding]::UTF8, "application/json"); $result=$HttpClient.PutAsync("https://instance.service-now.com/api/now/table/cmdb_ci_server/$($ServerCI.sys_id)", $stringcontent) 

Um die Tabelle und die Datensätze abzurufen, werden REST-API-GET-Anforderungen verwendet, um die Datensatz-PUT / POST-Anforderungen zu ändern, in deren Hauptteil die Felder geändert werden müssen.

Wir haben ein praktisches Tool mit einem grafischen Tool wie Azure Portal erstellt, mit dem wir die lokale Infrastruktur für uns und unsere Kunden so bequem wie möglich verwalten können.
PS 12.24.2018. Scheint alles veraltet zu sein? Es ist Zeit, Azure DevOps zu verwenden. Im nächsten Artikel werde ich Ihnen zeigen, wie Sie dies alles mit der Azure DevOps-Pipeline tun.

Source: https://habr.com/ru/post/de425129/


All Articles