Crear una nueva máquina virtual es una rutina que consume mucho tiempo. Y mientras más infraestructura y organización, más procedimientos asociados con este proceso. Automatizamos este proceso usando PowerShell.
Bienvenido a Kat si estás interesado.

A los programadores no les gusta hacer doble trabajo, los administradores del sistema también.
A continuación se muestra un ejemplo de automatización de uno de nuestros clientes.
Queríamos asegurarnos de que cualquier ingeniero o gerente de proyecto pudiera crear una nueva máquina virtual con un mínimo esfuerzo y por un período mínimo. Nuestro cliente tiene un sistema ITSM, en este ejemplo es ServiceNow, creamos el formulario web correspondiente en el catálogo de servicios. Para "ordenar" una nueva máquina, el gerente necesita completar los campos y confirmar el "pedido", después de que se inicia la cadena de proceso, y en la salida tenemos la máquina lista para usar.
Entonces, echemos un vistazo a lo que un gerente necesita definir para crear una nueva máquina virtual:
Descripción de VM: descripción de máquina virtual
Aquí se necesitan algunas aclaraciones. En nuestra solución, PowerShell 5.1 se usa activamente, por lo que, aunque solo sea Windows, en el futuro intentaremos agregar soporte para máquinas Unix y cambiar a PowerShell Core.
OS , sistema operativo. No existen barreras particulares para usar Windows 2008 (R2), pero estamos usando 2012R2 o 2016.
Tamaño de máquina virtual, tamaño de máquina virtual. Para cada uno, esto se puede determinar a su manera, en este ejemplo Pequeño 1CPU-4Gb Ram, Medio 2CPU-8Gb, Grande 4-16.
VM Storage , Disk 0 (C: \) tiene un tamaño fijo que no puede cambiar, solo está disponible el selector de almacenamiento Fast / Slow. "Rápido" puede ser Nivel de almacenamiento con SSD, y "Lento" puede ser almacenamiento en HDD "normales" (por supuesto, SAN). Disk1 (Disk2 en adelante) también tiene un selector para seleccionar Almacenamiento, así como campos para ingresar el tamaño deseado en gigabytes, Letra para la partición y tamaño del clúster (que es importante para SQL Server).
Confianza , determinamos que la máquina debe estar unida al dominio o no, con acceso desde la red pública o no.
Tipo , tipo de máquina. Casi todas las máquinas se pueden definir como aplicaciones front-end o back-end u otras en todos los demás casos. Según el tipo seleccionado, podemos determinar la subred más adecuada para la máquina.
Medio ambiente , en la infraestructura del cliente hay dos centros de datos: Primario (Producción) y Secundario (Dev / prueba), DC están conectados por un canal de comunicación rápido y proporcionan tolerancia a fallas. Por acuerdo, todas las máquinas virtuales en DC primario tienen una dirección IP que comienza en 10.230 y en DC secundario en 10.231.
(SLA) Acuerdo de nivel de servicio , este parámetro afecta la calidad de servicio de esta máquina.
Aplicaciones Hemos agregado la capacidad de instalar y configurar SQL Server. Debe seleccionar la edición, el nombre de la instancia y la clasificación. También es posible configurar la función del servidor web y mucho más.
Ahora necesitamos determinar cómo almacenar los valores seleccionados. Decidimos que el formato más conveniente es un archivo JSON. Como dije anteriormente, el entorno del cliente utiliza ITSM ServiceNow; el administrador, después de seleccionar todos los valores necesarios, hace clic en el botón "ordenar" y luego ServiceNow pasa todos los parámetros a nuestro script de PowerShell (al ServiceNow de fondo), que creará el archivo JSON. Se parece a esto:
.\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
En el cuerpo del script CreateConfiguration .ps1:
# PowerShell- $config = [ordered]@{} # . $config.SecurityZone=$SecurityZone
Al final, exporte nuestro objeto a un archivo JSON:
$ServerConfig = New-Object –TypeName PSObject $config ConvertTo-Json -InputObject $ServerConfig -Depth 100 | Out-File "C:\Configs\TargetNodes\Build\$($Hostname.ToLower()).json" -Force
Configuración de muestra:
{ "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 }
Es posible que haya notado que el formulario web no tenía un nombre de máquina virtual y una dirección IP. Obtenemos estos valores automáticamente de la siguiente manera:
El nombre de la máquina , ITSM ServiceNow tiene una sección especial: CMDB (Base de datos de administración de configuración), esta base de datos almacena todos los registros sobre máquinas virtuales existentes, su estado, equipo de soporte, etc. Hemos creado alrededor de 200 registros de respaldo con estado asignado. Para obtener un nombre para la máquina virtual, realizamos una solicitud REST a CMDB y obtenemos el primer registro "gratuito" y cambiamos su estado de Asignado a Pendiente de instalación.
Dirección IP y VLAN , implementamos IPAM en nuestra red: esta es una característica incorporada en Windows Server 2016 que le permite administrar las direcciones IP en su red. No es necesario usar todas las funciones de IPAM (DHCP, DNS, AD), sino usarlo solo como una base de datos de direcciones IP con una posible extensión de funcionalidad. El script que crea el archivo JSON realiza una solicitud a IPAM para la primera dirección IP libre en la subred. Y la subred VLAN (subred x / 24) se determina en función de los valores de SLA, Entorno, Confianza y Tipo seleccionados.
El archivo de configuración está listo, todos los campos están en su lugar, puede crear una máquina. La pregunta es, "¿cómo almacenar credenciales para todos nuestros scripts?". Usamos el paquete
CredentialManager . Este paquete funciona con la API integrada de Windows Credential Manager para almacenar contraseñas. Ejemplo de creación de una contraseña:
New-StoredCredential -Target "ESXi" -UserName "testdomain.eu\vmwareadm" -Password "veryultraP@ssw00rd." -Type Generic -Persist LocalMachine
La contraseña estará disponible para leer dentro de esta máquina y cuenta.
$ESXiAdmin = Get-StoredCredential -Type Generic -Target ESXi
Tenemos un servidor en el que se almacenan todas las configuraciones con GIT, ahora podemos realizar un seguimiento confiable de todos los cambios en las configuraciones: quién, qué, dónde y cuándo.
La tarea programada se configura en este servidor: verifique la carpeta con las configuraciones y escriba en el Registro de eventos de Windows acerca de todos los cambios.
Después de 15 minutos, la tarea programada escribirá en Windows EventLog que se ha detectado un nuevo archivo de configuración.
Es hora de verificar esta configuración. En primer lugar, debemos asegurarnos de que el archivo tenga el formato correcto:
$Configuration=(Get-Content -Raw $File | Out-String | ConvertFrom-Json)
Si todo está bien, es hora de comenzar a crear la máquina y ejecutar el script BuildVM.ps1.
En BuildVM.ps1, verificamos que el archivo de configuración tiene una descripción de todas las características de la máquina virtual: tamaño, env, sla, tipo, almacenamiento, ram, red.
Asegúrese de verificar si hay una máquina con el mismo nombre en la infraestructura (CheckVM.ps1).
Nos conectamos a través de
VMWare PowerShell CLI a nuestro vSphere:
$VmWareAdmin = Get-StoredCredential -Type Generic -Target ESXi Connect-VIServer -Server "vSphereSrv" -Credential $VmWareAdmin | Out-Null
Verifique si hay una máquina con el mismo nombre en la infraestructura
$VM=Get-VM $server -ErrorAction SilentlyContinue
Y apaga:
Disconnect-VIServer * -Force -Confirm:$false
Asegúrese de que la máquina tampoco esté disponible en WinRM
$ping=Test-NetConnection -ComputerName $Configuration.Hostname -CommonTCPPort WINRM -InformationLevel Quiet -ErrorAction SilentlyContinue
Si $ VM y $ ping están vacíos, puede crear una nueva máquina. (Manejamos situaciones en las que una máquina ya está creada manualmente en ESXi o esta máquina está en otro centro de datos).
Algunas palabras sobre el auto. Esta es una imagen de máquina virtual preparada que fue finalizada por sysprep y convertida a plantilla en nuestro vSphere. El administrador local con la contraseña conocida se guarda en la imagen, esta cuenta no se bloquea después de sysprep, lo que nos permitirá acceder a cada máquina desde esta plantilla, y luego podemos reemplazar esta contraseña por motivos de seguridad.
Crear una máquina virtual
Encuentra el grupo SLR correspondiente:
$Cluster=Get-Cluster -Name $Configuration.VM.SLA
Comprueba que tenemos suficiente espacio en el almacén de datos:
$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 }
Y suficiente memoria:
$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 }
Tomamos nuestra plantilla
$VMTemplate = Get-Template -Name 'Win2016_Std_x64_Template'
Y crea una nueva máquina virtual
New-VM -Name $Configuration.Hostname.ToUpper() -VMHost $VMHost -ResourcePool $ResourcePool -Datastore $Datastore -Template $VMTemplate -Location "AutoDeployed VMs"
Es importante conectar la interfaz de red a una subred con DHCP habilitado.
Iniciamos la maquina virtual
Start-VM $VM
Y guarde la descripción de la máquina, para que luego pueda determinar la máquina en el nivel VMWare.
Set-Annotation -Entity $VM -CustomAttribute "Change request" -Value $Configuration.Request -Confirm:$false Set-VM $VM -Notes $Configuration.Description -Confirm:$false
La máquina se inició y ahora podemos encontrar la dirección MAC recibida:
$vMAC = (($VM | Get-NetworkAdapter | Select-Object -Property "MacAddress").MacAddress).Replace(':','')
Guarde este valor en nuestro archivo JSON.
$Configuration.Network.MAC=$VMAC ConvertTo-Json -InputObject $Configuration -Depth 100 | Out-File "C:\Configs\TargetNodes\Build\$Hostname.json" -Force
Este es el momento de comprometerse con nuestro Git para que la máquina se cree y tenga su propio MAC único.
La máquina comienza a inicializarse (después de sysprep), configurando el equipo y la configuración inicial.
Esperemos hasta que nuestra máquina WinRM esté disponible con el script EstablishConnection.ps1.
Primero descubrimos qué IP recibió la máquina 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++ } } }
Y ahora esperaremos, cuando la máquina estará disponible en 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 máquina está lista para conducir.
Configuración de estado deseada
Para configurar la configuración deseada, utilizamos la parte de PowerShell: DSC (Configuración de estado deseada). Hay un DSC Pull Server configurado en la red: dscpull.testdomain.eu.
A continuación se muestra la configuración de nuestro servidor DSC Pull.
Buen artículo sobre la configuración 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 } }
Está disponible en:
https://dscpull.testdomain.eu:8080Su punto final:
https://dscpull.testdomain.eu:8080/PSDSCPullserver.svcTodos los clientes del servidor de extracción deben tener instalado PowerShell 5.1
Si no está instalado PowerShell 5.1:
$PSVersionTable.PSVersion.Major –lt 5
instalar 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"
También se implementa un servidor PKI en nuestra red. Esta es una condición para el cifrado seguro de credenciales almacenadas en archivos DSC mof (los archivos Mof son el "idioma" en el que se comunican Pull Server y sus clientes). Cuando un cliente intenta registrarse en el servidor Pull, es necesario especificar un certificado de huella digital y luego el servidor Pull utilizará este certificado para cifrar las contraseñas. A continuación veremos cómo funciona.
Importe Root CA a nuestra nueva máquina:
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
Para más trabajo, necesitamos un par de claves RSA.
Generaremos un certificado autofirmado y trabajaremos temporalmente con él.
Ahora podemos registrarnos en el servidor Pull:
$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
Enviar la primera configuración a nuestra máquina
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
El servidor cambiará de nombre y se reiniciará automáticamente. Ahora podemos ejecutar 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
Así es como se ve nuestro archivo 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"; };
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"; };
Credenciales cifradas DSC de una cuenta de servicio con derechos de administrador de dominio: testdomain.eu \\ service_DomainJoin_001 con un certificado autofirmado. DSC Client con su clave privada descifra las credenciales y aplica todos los módulos de configuración con las credenciales de dominio especificadas. En este caso, realiza la unión de dominio en la unidad organizativa especificada.
GroupSet LocalAdmins { GroupName = @( 'Administrators') Ensure = 'Present' MembersToInclude = @( testdomain-eu\dscstaging' ) }
Este módulo agrega dscstaging a los administradores locales para una mayor configuración.
Después de reiniciar, podremos ingresar a la máquina con credenciales de dominio.
Estamos esperando que el servidor reciba un certificado de nuestra PKI (tenemos configurada la inscripción automática) y en el futuro trabajaremos con el certificado emitido por nuestra 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
Ahora regístrate en Pull Server nuevamente con la huella digital actualizada.
Eso es todo, la máquina unida al dominio, y podemos usarla de la manera que más nos convenga.
Instalar SQL Server
El archivo JSON describe los requisitos para MS SQL Server, también usamos DSC para instalar y configurar SQL Server. Así es como se ve la configuración:
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' }
Donde se define $ MSSQL:
$MSSQL=$Configuration.Applications | where {$_.Application -eq "Microsoft SQL Server 2012"}
$ MSSQL.InstanceName: todo esto se indica en nuestro archivo Json. La aplicación de esta configuración instalará MS SQL Server con todas las actualizaciones en la carpeta Actualizaciones y reiniciará el servidor si es necesario.
El auto está listo.
Servicio ahora
Hay
varias API disponibles en Service-Now. Usamos la API Rest.
Para obtener una lista de máquinas con estado Asignado, se utiliza una consulta del formulario:
instance.service-now.com/cmdb_ci_server_list.do?sysparm_query=install_status=16 ^ u_subtype = ^ ORDERBYname
En PowerShell, se ve así:
$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
El primer objeto de matriz es el nombre de host que necesitamos.
Si la máquina está lista, puede cambiar el estado de la máquina en Service-Now, para esto el 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)
Para obtener la tabla y los registros, se utilizan las solicitudes REST API GET, para cambiar las solicitudes PUT / POST del registro, en cuyo cuerpo se deben cambiar los campos.
Hemos creado una herramienta conveniente con una herramienta gráfica como Azure Portal, que nos permite administrar la infraestructura local de la manera más conveniente para nosotros y nuestros clientes.PS 12.24.2018. ¿Todo parece estar desactualizado? Es hora de usar Azure DevOps. En el siguiente artículo, le mostraré cómo hacer todo esto con la canalización de Azure DevOps.