La création d'une nouvelle machine virtuelle prend du temps. Et plus il y a d'infrastructure et d'organisation, plus il y a de procédures associées à ce processus. Nous avons automatisé ce processus à l'aide de PowerShell.
Bienvenue à kat si vous êtes intéressé.

Les programmeurs n'aiment pas faire un double travail, les administrateurs système aussi.
Ci-dessous, un exemple d'automatisation d'un de nos clients.
Nous voulions nous assurer que tout ingénieur ou chef de projet pouvait créer une nouvelle machine virtuelle avec un minimum d'effort et pour une période minimale. Notre client dispose d'un système ITSM, dans cet exemple c'est ServiceNow, nous avons créé le formulaire web correspondant dans le catalogue de service. Pour «commander» une nouvelle machine, le gestionnaire doit remplir les champs et confirmer la «commande», après que la chaîne de processus est lancée, et à la sortie, nous préparons la machine pour l'utilisation.
Examinons donc ce qu'un gestionnaire doit définir pour créer une nouvelle machine virtuelle:
Description de la machine virtuelle: description de la machine virtuelle
Quelques clarifications sont nécessaires ici. Dans notre solution, PowerShell 5.1 est activement utilisé, donc bien que sous Windows uniquement, nous essaierons à l'avenir d'ajouter la prise en charge des machines Unix et de passer à PowerShell Core.
OS , système d'exploitation. Il n'y a pas d'obstacles particuliers à l'utilisation de Windows 2008 (R2), mais nous utilisons 2012R2 ou 2016.
Taille de la machine virtuelle, taille de la machine virtuelle. Pour chacun, cela peut être déterminé à sa manière, dans cet exemple Petit Ram 1CPU-4Gb, Moyen 2CPU-8Gb, Grand 4-16.
VM Storage , Disk 0 (C: \) a une taille fixe que vous ne pouvez pas modifier, seul le sélecteur de stockage rapide / lent est disponible. «Fast» peut être un niveau de stockage avec SSD, et «Slow» peut être un stockage sur des disques durs «ordinaires» (bien sûr, SAN). Disk1 (Disk2 en avant) a également un sélecteur pour sélectionner le stockage, ainsi que des champs pour entrer la taille souhaitée en gigaoctets, Lettre pour la partition et la taille du cluster (ce qui est important pour SQL Server).
Confiance , nous déterminons que la machine doit être jointe au domaine ou non, avec accès depuis le réseau public ou non.
Type , type de machine. Presque chaque machine peut être définie comme une application frontale ou principale ou autre dans tous les autres cas. En fonction du type sélectionné, nous pouvons en outre déterminer le sous-réseau le plus approprié pour la machine.
Environnement , dans l'infrastructure du client, il existe deux centres de données: primaire (production) et secondaire (développement / test), les CC sont connectés par un canal de communication rapide et offrent une tolérance aux pannes. Par accord, toutes les machines virtuelles dans Primary DC ont une adresse IP commençant à 10.230 et dans Secondary DC à 10.231.
(SLA) Service Level Agreement , ce paramètre affecte la qualité de service de cette machine.
Les applications Nous avons ajouté la possibilité d'installer et de configurer SQL Server. Vous devez sélectionner l'édition, le nom de l'instance et le classement. Il est également possible de configurer à la fois le rôle de serveur Web et bien plus encore.
Nous devons maintenant déterminer comment stocker les valeurs sélectionnées. Nous avons décidé que le format le plus pratique était un fichier JSON. Comme je l'ai dit plus tôt, l'environnement du client utilise ITSM ServiceNow; le gestionnaire, après avoir sélectionné toutes les valeurs nécessaires, clique sur le bouton «commander» et après cela, ServiceNow passe tous les paramètres à notre script PowerShell (au back-end ServiceNow), qui créera le fichier JSON. Cela ressemble à ceci:
.\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
Dans le corps du script CreateConfiguration .ps1:
# PowerShell- $config = [ordered]@{} # . $config.SecurityZone=$SecurityZone
À la fin, exportez notre objet dans un fichier JSON:
$ServerConfig = New-Object –TypeName PSObject $config ConvertTo-Json -InputObject $ServerConfig -Depth 100 | Out-File "C:\Configs\TargetNodes\Build\$($Hostname.ToLower()).json" -Force
Exemple de configuration:
{ "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 }
Vous avez peut-être remarqué que le formulaire Web n'avait pas de nom de machine virtuelle ni d'adresse IP. Nous obtenons ces valeurs automatiquement comme suit:
Le nom de la machine , ITSM ServiceNow a une section spéciale: CMDB (Configuration Management Data Base), cette base de données stocke tous les enregistrements sur les machines virtuelles existantes, leur état, l'équipe d'assistance, etc. Nous avons créé environ 200 enregistrements de sauvegarde avec le statut Alloué. Pour obtenir un nom pour la machine virtuelle, nous faisons une demande REST à CMDB et obtenons le premier enregistrement «gratuit» et passons son statut d'Alloué à l'installation en attente.
Adresse IP et VLAN , nous avons déployé IPAM sur notre réseau - il s'agit d'une fonctionnalité intégrée à Windows Server 2016 qui vous permet de gérer les adresses IP sur votre réseau. Il n'est pas du tout nécessaire d'utiliser toutes les fonctionnalités d'IPAM (DHCP, DNS, AD), mais de ne l'utiliser que comme base de données d'adresses IP avec une extension potentielle des fonctionnalités. Le script qui crée le fichier JSON fait une demande à IPAM pour la première adresse IP libre sur le sous-réseau. Et le sous-réseau VLAN (sous-réseau x / 24) est déterminé en fonction des valeurs SLA, Environment, Trust et Type sélectionnées.
Le fichier de configuration est prêt, tous les champs sont en place, vous pouvez créer une machine. La question est: «comment stocker les informations d'identification pour tous nos scripts?». Nous utilisons le package
CredentialManager . Ce package fonctionne avec l'API Windows Credential Manager intégrée pour stocker les mots de passe. Exemple de création d'un mot de passe:
New-StoredCredential -Target "ESXi" -UserName "testdomain.eu\vmwareadm" -Password "veryultraP@ssw00rd." -Type Generic -Persist LocalMachine
Le mot de passe sera disponible pour lecture sur cette machine et ce compte.
$ESXiAdmin = Get-StoredCredential -Type Generic -Target ESXi
Nous avons un serveur sur lequel toutes les configurations avec GIT sont stockées, nous pouvons maintenant suivre de manière fiable tous les changements de configurations: qui, quoi, où et quand.
La tâche planifiée est configurée sur ce serveur: vérifiez le dossier avec les configurations et écrivez dans le journal des événements Windows toutes les modifications.
Après 15 minutes, la tâche planifiée écrira dans Windows EventLog qu'un nouveau fichier de configuration a été détecté.
Il est temps de vérifier cette configuration. Tout d'abord, nous devons nous assurer que le fichier a la bonne mise en forme:
$Configuration=(Get-Content -Raw $File | Out-String | ConvertFrom-Json)
Si tout va bien, il est temps de commencer à créer la machine et d'exécuter le script BuildVM.ps1.
Dans BuildVM.ps1, nous vérifions que le fichier de configuration a une description de toutes les caractéristiques de la machine virtuelle: taille, env, sla, type, stockage, ram, réseau.
Assurez-vous de vérifier s'il existe une machine du même nom dans l'infrastructure (CheckVM.ps1).
Nous nous connectons via la
CLI VMWare PowerShell à notre vSphere:
$VmWareAdmin = Get-StoredCredential -Type Generic -Target ESXi Connect-VIServer -Server "vSphereSrv" -Credential $VmWareAdmin | Out-Null
Vérifier s'il existe une machine du même nom dans l'infrastructure
$VM=Get-VM $server -ErrorAction SilentlyContinue
Et désactivez:
Disconnect-VIServer * -Force -Confirm:$false
Assurez-vous que la machine n'est pas non plus disponible sur WinRM
$ping=Test-NetConnection -ComputerName $Configuration.Hostname -CommonTCPPort WINRM -InformationLevel Quiet -ErrorAction SilentlyContinue
Si $ VM et $ ping sont vides, vous pouvez créer une nouvelle machine. (Nous traitons les situations où une machine est déjà créée manuellement dans ESXi ou cette machine se trouve dans un autre centre de données.)
Quelques mots sur la voiture. Il s'agit d'une image de machine virtuelle préparée qui a été finalisée par sysprep et convertie en modèle dans notre vSphere. L'administrateur local avec le mot de passe connu est enregistré dans l'image, ce compte ne plante pas après sysprep, ce qui nous permettra d'accéder à chaque machine à partir de ce modèle, et plus tard nous pourrons remplacer ce mot de passe pour des raisons de sécurité.
Création d'une machine virtuelle
Trouvez le cluster SLR correspondant:
$Cluster=Get-Cluster -Name $Configuration.VM.SLA
Vérifiez que nous avons suffisamment d'espace sur le magasin de données:
$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 }
Et assez de mémoire:
$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 }
Nous prenons notre modèle
$VMTemplate = Get-Template -Name 'Win2016_Std_x64_Template'
Et créez une nouvelle machine virtuelle
New-VM -Name $Configuration.Hostname.ToUpper() -VMHost $VMHost -ResourcePool $ResourcePool -Datastore $Datastore -Template $VMTemplate -Location "AutoDeployed VMs"
Il est important de connecter l'interface réseau à un sous-réseau avec DHCP activé.
Nous démarrons la machine virtuelle
Start-VM $VM
Et enregistrez la description de la machine, afin de pouvoir ultérieurement déterminer la machine au niveau VMWare.
Set-Annotation -Entity $VM -CustomAttribute "Change request" -Value $Configuration.Request -Confirm:$false Set-VM $VM -Notes $Configuration.Description -Confirm:$false
La machine a démarré et maintenant nous pouvons trouver l'adresse MAC reçue:
$vMAC = (($VM | Get-NetworkAdapter | Select-Object -Property "MacAddress").MacAddress).Replace(':','')
Enregistrez cette valeur dans notre fichier JSON.
$Configuration.Network.MAC=$VMAC ConvertTo-Json -InputObject $Configuration -Depth 100 | Out-File "C:\Configs\TargetNodes\Build\$Hostname.json" -Force
Voici le temps de vous engager dans notre Git que la machine est créée et possède son propre MAC unique.
La machine commence l'initialisation (après sysprep), l'installation de l'équipement et la configuration initiale.
Attendons que notre machine WinRM soit disponible avec le script EstablishConnection.ps1.
Nous découvrons d'abord quelle IP la machine a reçue de DHCP:
# $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++ } } }
Et maintenant, nous attendrons, lorsque la machine sera disponible sur WinRM:
$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 } } }
La machine est prête à rouler.
Configuration de l'état souhaité
Pour configurer la configuration souhaitée, nous utilisons la partie PowerShell - DSC (Desired State Configuration). Il existe un serveur Pull DSC configuré sur le réseau: dscpull.testdomain.eu.
Vous trouverez ci-dessous la configuration de notre serveur DSC Pull.
Bon article sur la configuration de 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 } }
Il est disponible sur:
https://dscpull.testdomain.eu:8080Son point de terminaison:
https://dscpull.testdomain.eu:8080/PSDSCPullserver.svcTous les clients de serveur pull doivent avoir PowerShell 5.1 installé
Si PowerShell 5.1 n'est pas installé:
$PSVersionTable.PSVersion.Major –lt 5
installer 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"
Un serveur PKI est également déployé sur notre réseau. Il s'agit d'une condition pour le cryptage sécurisé des informations d'identification stockées dans les fichiers mof DSC (les fichiers mof sont la «langue» dans laquelle Pull Server et ses clients communiquent). Lorsqu'un client essaie de s'enregistrer sur le serveur Pull, il est nécessaire de spécifier un certificat d'empreinte numérique et plus tard, le serveur Pull utilisera ce certificat pour crypter les mots de passe. Ci-dessous, nous verrons comment cela fonctionne.
Importez Root CA sur notre nouvelle machine:
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
Pour poursuivre les travaux, nous avons besoin d'une paire de clés RSA.
Nous allons générer un certificat auto-signé et travailler temporairement avec lui.
Maintenant, nous pouvons nous inscrire sur le Pull Server:
$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
Envoyez la première configuration à notre machine
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
Le serveur renommera et redémarrera automatiquement. Nous pouvons maintenant exécuter Join Domain.
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
Voici à quoi ressemble notre fichier mof:
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"; };
de 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 ----- ----- FIN 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"; };
Informations d'identification chiffrées DSC à partir d'un compte de service avec des droits d'administrateur de domaine: testdomain.eu \\ service_DomainJoin_001 avec un certificat auto-signé. Le client DSC avec sa clé privée déchiffre les informations d'identification et applique tous les modules de configuration avec les informations d'identification de domaine spécifiées. Dans ce cas, il effectue la jointure de domaine dans l'unité d'organisation spécifiée.
GroupSet LocalAdmins { GroupName = @( 'Administrators') Ensure = 'Present' MembersToInclude = @( testdomain-eu\dscstaging' ) }
Ce module ajoute dscstaging aux administrateurs locaux pour une configuration supplémentaire.
Après le redémarrage, nous pourrons entrer dans la machine avec les informations d'identification du domaine.
Nous attendons que le serveur reçoive un certificat de notre PKI (nous avons configuré l'inscription automatique) et à l'avenir, nous travaillerons avec le certificat émis par notre PKI.
$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
Inscrivez-vous à nouveau à Pull Server avec l'empreinte numérique mise à jour.
Voilà, la machine jointe au domaine, et nous pouvons l'utiliser de la manière qui nous convient.
Installer SQL Server
Le fichier JSON décrit les exigences pour MS SQL Server, nous utilisons également DSC pour installer et configurer SQL Server. Voici à quoi ressemble la configuration:
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' }
Où $ MSSQL est défini:
$MSSQL=$Configuration.Applications | where {$_.Application -eq "Microsoft SQL Server 2012"}
$ MSSQL.InstanceName - tout cela est indiqué dans notre fichier Json. L'application de cette configuration installera MS SQL Server avec toutes les mises à jour du dossier Updates et redémarrera le serveur si nécessaire.
La voiture est prête.
Service maintenant
Plusieurs API sont disponibles dans Service-Now. Nous utilisons l'API Rest.
Pour obtenir une liste des machines avec le statut Alloué, une requête du formulaire est utilisée:
instance.service-now.com/cmdb_ci_server_list.do?sysparm_query=install_status=16 ^ u_subtype = ^ ORDERBYname
Dans PowerShell, cela ressemble à ceci:
$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
Le premier objet tableau est le nom d'hôte dont nous avons besoin.
Si la machine est prête, vous pouvez modifier l'état de la machine dans Service-Now, pour cela le script 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)
Pour obtenir la table et les enregistrements, les requêtes REST API GET sont utilisées, pour modifier les requêtes d'enregistrement PUT / POST, dans le corps desquelles les champs que vous devez modifier.
Nous avons créé un outil pratique avec un outil graphique comme Azure Portal, qui nous permet de gérer une infrastructure sur site aussi pratique que possible pour nous et nos clients.PS 12.24.2018. Tout semble-t-il obsolète? Il est temps d'utiliser Azure DevOps. Dans le prochain article, je vais vous montrer comment faire tout cela avec le pipeline Azure DevOps.