Criar uma nova máquina virtual é uma rotina demorada. E quanto mais infraestrutura e organização, mais procedimentos associados a esse processo. Automatizamos esse processo usando o PowerShell.
Bem-vindo ao kat, se você estiver interessado.

Os programadores não gostam de trabalhar duas vezes, os administradores de sistema também.
Abaixo está um exemplo de automação de um de nossos clientes.
Queríamos garantir que qualquer engenheiro ou gerente de projeto pudesse criar uma nova máquina virtual com o mínimo de esforço e por um período mínimo. Nosso cliente possui um sistema ITSM; neste exemplo, é ServiceNow, criamos o formulário da Web correspondente no catálogo de serviços. Para "pedir" uma nova máquina, o gerente precisa preencher os campos e confirmar a "ordem", depois que a cadeia do processo for lançada e, na saída, deixaremos a máquina pronta para uso.
Então, vejamos o que um gerente precisa definir para criar uma nova máquina virtual:
Descrição da VM: descrição da máquina virtual
Alguns esclarecimentos são necessários aqui. Em nossa solução, o PowerShell 5.1 é usado ativamente; portanto, somente no Windows, tentaremos adicionar no futuro suporte para máquinas Unix e mudar para o PowerShell Core.
SO , sistema operacional. Não há barreiras específicas ao uso do Windows 2008 (R2), mas estamos usando o 2012R2 ou 2016.
Tamanho da VM , tamanho da máquina virtual. Para cada um, isso pode ser determinado à sua maneira, neste exemplo Ram pequeno de 1CPU-4Gb, Médio 2CPU-8Gb, Grande 4-16.
O Armazenamento 0 da
VM , Disco 0 (C: \) tem um tamanho fixo que você não pode alterar, apenas o seletor de armazenamento Rápido / Lento está disponível. “Rápido” pode ser de camada de armazenamento com SSD e “Lento” pode ser armazenado em HDDs “regulares” (é claro, SAN). O Disco1 (disco2 em diante) também possui um seletor para selecionar Armazenamento, além de campos para inserir o tamanho desejado em gigabytes, Carta para a partição e tamanho do cluster (o que é importante para o SQL Server).
Confie , determinamos que a máquina deve ser ingressada no domínio ou não, com acesso da rede pública ou não.
Tipo , tipo de máquina. Quase todas as máquinas podem ser definidas como um aplicativo front-end ou back-end ou outro em todos os outros casos. Com base no tipo selecionado, podemos determinar ainda a sub-rede mais adequada para a máquina.
Ambiente , na infraestrutura do cliente, existem dois datacenters: primário (produção) e secundário (desenvolvimento / teste), os controladores de domínio são conectados por um canal de comunicação rápido e fornecem tolerância a falhas. Por contrato, todas as máquinas virtuais no DC primário têm um endereço IP a partir de 10.230 e no DC secundário em 10.231.
(SLA) Service Level Agreement , esse parâmetro afeta a qualidade do serviço desta máquina.
Aplicações Adicionamos a capacidade de instalar e configurar o SQL Server. Você deve selecionar a edição, nome da instância e agrupamento. Também é possível configurar a função de servidor Web e muito mais.
Agora precisamos determinar como armazenar os valores selecionados. Decidimos que o formato mais conveniente é um arquivo JSON. Como eu disse anteriormente, o ambiente do cliente usa o ITSM ServiceNow; o gerente, depois de selecionar todos os valores necessários, clica no botão "ordem" e, em seguida, o ServiceNow passa todos os parâmetros para o script do PowerShell (para o ServiceNow de back-end), que criará o arquivo JSON. Parece algo como isto:
.\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
No corpo do script CreateConfiguration .ps1:
# PowerShell- $config = [ordered]@{} # . $config.SecurityZone=$SecurityZone
No final, exporte nosso objeto para um arquivo JSON:
$ServerConfig = New-Object –TypeName PSObject $config ConvertTo-Json -InputObject $ServerConfig -Depth 100 | Out-File "C:\Configs\TargetNodes\Build\$($Hostname.ToLower()).json" -Force
Exemplo de configuração:
{ "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 }
Você deve ter notado que o formulário da Web não tinha um nome de máquina virtual e endereço IP. Obtemos esses valores automaticamente da seguinte maneira:
O nome da máquina , ITSM ServiceNow, possui uma seção especial: CMDB (banco de dados de gerenciamento de configuração), esse banco de dados armazena todos os registros sobre máquinas virtuais existentes, status, equipe de suporte etc. Criamos cerca de 200 registros de backup com status Alocado. Para obter um nome para a máquina virtual, solicitamos REST ao CMDB, obtemos o primeiro registro "gratuito" e alteramos seu status de Instalação alocada para Instalação pendente.
Endereço IP e VLAN , implantamos o IPAM em nossa rede - esse é um recurso interno do Windows Server 2016 que permite gerenciar endereços IP em sua rede. Não é necessário usar todos os recursos do IPAM (DHCP, DNS, AD), mas usá-lo apenas como um banco de dados de endereços IP com uma extensão potencial de funcionalidade. O script que cria o arquivo JSON faz uma solicitação ao IPAM para o primeiro endereço IP gratuito na sub-rede. E a sub-rede VLAN (sub-rede x / 24) é determinada com base nos valores SLA, Ambiente, Confiança e Tipo selecionados.
O arquivo de configuração está pronto, todos os campos estão no lugar, você pode criar uma máquina. A pergunta é: "como armazenar credenciais para todos os nossos scripts?". Usamos o pacote
CredentialManager . Este pacote funciona com a API incorporada do Windows Credential Manager para armazenar senhas. Exemplo de criação de uma senha:
New-StoredCredential -Target "ESXi" -UserName "testdomain.eu\vmwareadm" -Password "veryultraP@ssw00rd." -Type Generic -Persist LocalMachine
A senha estará disponível para leitura nesta máquina e conta.
$ESXiAdmin = Get-StoredCredential -Type Generic -Target ESXi
Temos um servidor no qual todas as configurações com o GIT são armazenadas, agora podemos rastrear com segurança todas as alterações nas configurações: quem, o que, onde e quando.
A tarefa agendada está configurada neste servidor: verifique a pasta com as configurações e escreva no Log de Eventos do Windows todas as alterações.
Após 15 minutos, a tarefa agendada gravará no Windows EventLog que um novo arquivo de configuração foi detectado.
É hora de verificar essa configuração. Primeiro de tudo, precisamos garantir que o arquivo tenha a formatação correta:
$Configuration=(Get-Content -Raw $File | Out-String | ConvertFrom-Json)
Se tudo estiver bem, é hora de começar a criar a máquina e executar o script BuildVM.ps1.
No BuildVM.ps1, verificamos que o arquivo de configuração possui uma descrição de todas as características da máquina virtual: tamanho, env, sla, tipo, armazenamento, ram, rede.
Certifique-se de verificar se há uma máquina com o mesmo nome na infraestrutura (CheckVM.ps1).
Nós nos conectamos através do
VMWare PowerShell CLI ao nosso vSphere:
$VmWareAdmin = Get-StoredCredential -Type Generic -Target ESXi Connect-VIServer -Server "vSphereSrv" -Credential $VmWareAdmin | Out-Null
Verifique se há uma máquina com o mesmo nome na infraestrutura
$VM=Get-VM $server -ErrorAction SilentlyContinue
E desligue:
Disconnect-VIServer * -Force -Confirm:$false
Verifique se a máquina também não está disponível no WinRM
$ping=Test-NetConnection -ComputerName $Configuration.Hostname -CommonTCPPort WINRM -InformationLevel Quiet -ErrorAction SilentlyContinue
Se $ VM e $ ping estiverem vazios, você poderá criar uma nova máquina. (Lidamos com situações em que uma máquina já foi criada manualmente no ESXi ou está em outro datacenter.)
Algumas palavras sobre o carro. Essa é uma imagem de máquina virtual preparada que foi finalizada pelo sysprep e convertida em modelo no nosso vSphere. O administrador local com a senha que conhecemos é salva na imagem, essa conta não falha após o sysprep, o que nos permitirá acessar cada máquina desse modelo e, posteriormente, podemos substituí-la por motivos de segurança.
Criando uma máquina virtual
Encontre o cluster SLR correspondente:
$Cluster=Get-Cluster -Name $Configuration.VM.SLA
Verifique se temos espaço suficiente no armazenamento de dados:
$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 }
E memória suficiente:
$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 nosso modelo
$VMTemplate = Get-Template -Name 'Win2016_Std_x64_Template'
E crie uma nova máquina virtual
New-VM -Name $Configuration.Hostname.ToUpper() -VMHost $VMHost -ResourcePool $ResourcePool -Datastore $Datastore -Template $VMTemplate -Location "AutoDeployed VMs"
É importante conectar a interface de rede a uma sub-rede com o DHCP ativado.
Começamos a máquina virtual
Start-VM $VM
E salve a descrição da máquina, para que posteriormente você possa determinar a máquina no nível do VMWare.
Set-Annotation -Entity $VM -CustomAttribute "Change request" -Value $Configuration.Request -Confirm:$false Set-VM $VM -Notes $Configuration.Description -Confirm:$false
A máquina foi iniciada e agora podemos descobrir o endereço MAC recebido:
$vMAC = (($VM | Get-NetworkAdapter | Select-Object -Property "MacAddress").MacAddress).Replace(':','')
Salve esse valor em nosso arquivo JSON.
$Configuration.Network.MAC=$VMAC ConvertTo-Json -InputObject $Configuration -Depth 100 | Out-File "C:\Configs\TargetNodes\Build\$Hostname.json" -Force
Aqui é o momento de confirmar com o nosso Git que a máquina é criada e possui seu próprio MAC exclusivo.
A máquina inicia a inicialização (após sysprep), instalando o equipamento e a configuração inicial.
Vamos esperar até que a nossa máquina WinRM esteja disponível com o script EstablishConnection.ps1.
Primeiro, descobrimos qual IP a máquina recebeu do 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++ } } }
E agora vamos esperar, quando a máquina estará disponível no 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 } } }
A máquina está pronta para dirigir.
Configuração de estado desejada
Para definir a configuração desejada, usamos a parte do PowerShell - DSC (Configuração do estado desejado). Há um DSC Pull Server configurado na rede: dscpull.testdomain.eu.
Abaixo está a configuração do nosso DSC Pull Server.
Bom artigo sobre a configuração do 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á disponível em:
https://dscpull.testdomain.eu:8080Seu ponto final:
https://dscpull.testdomain.eu:8080/PSDSCPullserver.svcTodos os clientes do servidor pull devem ter o PowerShell 5.1 instalado
Caso contrário, o PowerShell 5.1 instalado:
$PSVersionTable.PSVersion.Major –lt 5
instale o 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"
Um servidor PKI também é implantado em nossa rede. Essa é uma condição para a criptografia segura de credenciais armazenadas em arquivos DSC mof (arquivos Mof são o "idioma" no qual o Pull Server e seus clientes se comunicam). Quando um cliente tenta se registrar no servidor Pull, é necessário especificar um certificado Thumbprint e, posteriormente, o servidor Pull usará esse certificado para criptografar senhas. Abaixo veremos como funciona.
Importe a CA raiz para nossa nova 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 trabalho adicional, precisamos de um par de chaves RSA.
Geraremos um certificado autoassinado e trabalharemos temporariamente com ele.
Agora podemos registrar no 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
Envie a primeira configuração para nossa 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
O servidor renomeia e reinicia automaticamente. Agora podemos executar o ingresso no domínio.
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
Aqui está a aparência do nosso arquivo 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 ----- 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"; };
Credenciais criptografadas DSC de uma conta de serviço com direitos de administrador de domínio: testdomain.eu \\ service_DomainJoin_001 com um certificado autoassinado. O DSC Client com sua chave privada descriptografa as credenciais e aplica todos os módulos de configuração com as credenciais de domínio especificadas. Nesse caso, ele realiza a junção de domínio na unidade organizacional especificada.
GroupSet LocalAdmins { GroupName = @( 'Administrators') Ensure = 'Present' MembersToInclude = @( testdomain-eu\dscstaging' ) }
Este módulo adiciona o dscstaging aos administradores locais para configuração adicional.
Após a reinicialização, poderemos entrar na máquina com credenciais de domínio.
Estamos aguardando o servidor receber um certificado da nossa PKI (temos a inscrição automática configurada) e, no futuro, trabalharemos com o certificado emitido pela nossa 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
Agora, inscreva-se no Pull Server novamente com a impressão digital atualizada.
É isso, a máquina ingressada no domínio, e podemos usá-la da maneira que mais nos convém.
Instale o SQL Server
O arquivo JSON descreve os requisitos para o MS SQL Server; também usamos o DSC para instalar e configurar o SQL Server. É assim que a configuração se parece:
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' }
Onde $ MSSQL está definido:
$MSSQL=$Configuration.Applications | where {$_.Application -eq "Microsoft SQL Server 2012"}
$ MSSQL.InstanceName - tudo isso é indicado em nosso arquivo Json. A aplicação dessa configuração instalará o MS SQL Server com todas as atualizações na pasta Atualizações e reiniciará o servidor, se necessário.
O carro está pronto.
Serviço agora
Existem
várias APIs disponíveis no Service-Now. Usamos a API Rest.
Para obter uma lista de máquinas com status Alocado, uma consulta do formulário é usada:
instance.service-now.com/cmdb_ci_server_list.do?sysparm_query=install_status=16 ^ u_subtype = ^ ORDERBYname
No PowerShell, fica assim:
$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
O primeiro objeto da matriz é o nome do host que precisamos.
Se a máquina estiver pronta, você poderá alterar o status da máquina no Service-Now, para isso o 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 obter a tabela e os registros, as solicitações GET API REST são usadas para alterar as solicitações PUT / POST do registro, no corpo do qual os campos que você precisa alterar.
Criamos uma ferramenta conveniente com uma ferramenta gráfica como o Portal do Azure, que nos permite gerenciar a infraestrutura local o mais conveniente possível para nós e nossos clientes.PS 12.24.2018. Tudo parece estar desatualizado? É hora de usar o Azure DevOps. No próximo artigo, mostrarei como fazer tudo isso com o pipeline do Azure DevOps.