Automatizando toda a instalação de uma nova Hyper-V VM (W2012-SQL2012).Core usando PowerShell3.0/Workflows


Constantemente em meu lab e meus testes eu tenho que criar novas Vms, deletar outras, instalar features..etc e isso me toma um temo razoável. Começa a ficar bem cansativo ter que fazer este processo manual e repetidamente.

Por causa disso que eu criei este script pra automatizar toda o processo. Desde a criaão da VM, instalação do SO, instalação do SQl Server e colocação da maquina no dominio. Vamos dar uma olhadinha nele, não é complicado.

Antes de começar  eu tive que configurar meu ambiente para dar suporte a esta automatização. My host não esta no dominio (somente as Vms)  e eu também estou workflows no codigo e eu preciso habilitar o PowerShell Remoting na nova mauqina (workflows implicitamente trabalham com PowerShell Remoting)

O nome da nova maquina, inicialmente durante o processo de instalaçao , será sempre Server2012. Desta maneira, para possibilitar a comunicação entre meu host e ela eu tive que adicionar ela no TRUSTEDHOST do WSMAN :

Set-Item WSMan:\localhost\Client\TrustedHosts –Value Server2012 -Force

 

O próximo passo foi salvar as credenciais locais da nova maquina de administrador e também o administrador do dominio. Como as duas tem a mesma senha, eu somente criei um arquivo. Se as suas são diferentes, crie outro arquivo.

Set-Item WSMan:\localhost\Client\TrustedHosts –Value Server2012 -Force

 

Agora eu preciso criar a unattended installation do Windows Server 2012 Core e pra isso eu recomendo você ler este excelente post do Derek Seaman :

Windows Server 2012 Unattended Installation

O ponto é que eu tive que customizar o answer file, para saber quando finalizou toda a instalação do Windows, desabilitar o firewall e habilitar o PowerShell remoting na maquina.

Para saber quando finalizou eu testei varias tecnicas. Com test-connection o ping estava respondendo antes de acabar toda a instalação e com o snippet criado pelo Ben Armstrong ((PowerShell snippet: “start a virtual machine and wait for a bit”), o heartbeat também ja estava respondendo antes de finalizar tudo.  Eu realmente não sei se é a melhor tecnica, mas esta funcionando.

Minha idéia foi , e você pode ver pelo answer file customizado, criar um novo comando (ultimo a ser executado) no autologon e no primeiro logon gerando uma nova key no registry chamada UnattendedInstallation. Então eu criei um simples while que fica testando via WMI quando achar esta key. Achou, indica que acabou toda a instalação.

fig1

cmd.exe /c powershell.exe "New-Item -Path HKLM:\software -Name UnattendedInstallation -Force"

também criei um comando para desabilitar o firewall e habilitar o powershell remoting :

Fig2

cmd.exe /c powershell.exe "enable-psremoting -force ; Get-NetFirewallProfile | Set-NetFirewallProfile -enabled false"

Download images here 

Download my answer files in here

 

Então foi usar o ULTRA ISO para gerar a ISO com o answer file . Abra a ISO do Windows Server 2012 e adicione o answer file na root (autounattended.xml) e salve a ISO (de preferencia com outro nome)

Vamos dar uma olhadinha no script passo a passo :

O primeiro set de comandos são para criar a VM e adicionar os NICS . Eu tenho 2 NICS – Um externo para Internet chamado Internet e um interno para o dominio chamado LAN :

#getting the password and storing in a variable
$LocalAdmCredential = New-Object System.Management.Automation.PSCredential “administrator”,(Get-Content C:\scripts\LocalAdmPassword.txt | ConvertTo-SecureString )
$DomainAdmCredential = New-Object System.Management.Automation.PSCredential “starwars\administrator”,(Get-Content C:\scripts\LocalAdmPassword.txt | ConvertTo-SecureString )

Write-Verbose -Message "Create and setup the Virtual Machine"
#Change to your network switch name
New-VM -Name $VmName -Path $VmPath -MemoryStartupBytes 512MB -NewVHDPath "$($VmPath)\$($VmName)\$($VmName).vhdx" -NewVHDSizeBytes 127GB -SwitchName Internet
Add-VMNetworkAdapter -VMName $VmName -SwitchName LAN
#Enabling vlan access and id
Get-VMNetworkAdapter -VMName $VmName | Where-Object {$_.switchname -like 'LAN'} | Set-VMNetworkAdapterVlan -Access -VlanId 2
#Change to your iso path
Set-VMDvdDrive -VMName $VmName -Path "c:\Softwares\Unattended_WindowsServer2012STDCore.iso"
#Set-VMDvdDrive -VMName $VmName -Path "c:\Softwares\Unnatended_WindowsServer2012STDFULL.iso"

Write-Verbose -message "Starting VM"
Start-VM -Name $VmName

 

O segundo bloco é para testar quando a instalação acabou. eu não quero rodar como job porque quero ver todo o processo :

do {
Start-Sleep -milliseconds 100
#trying to get the value from the registry that is created by the installation indicating when all is ok and finished the setup.
#Then I keep doing the setup. It is the last command ran, after disable the firewall
try {
#It is not the name of the VM, but the nsame of the server informed in the unnatended file.In my case Server2012. Below I will rename it and put
#in a domain
$FinishedInstallation = ([Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine", "Server2012")).OpenSubKey("software\unattendedinstallation")
if ($FinishedInstallation) {
Break
}
} catch{}
} while ($true)

Após finalizar eu rodo o workflow :

New-Server2012SetupWorkflow -NewIp $newIP ' -domain $domain -LocalAdmCredential $LocalAdmCredential  -DomainAdmCredential $DomainAdmCredential -pscomputername Server2012  -ComputerName $ComputerName -verbose

Os parametros :

  • –NewIp –  Novo IP do NIC LAN (no meu caso)
  • -Domain – Meu dominio (das vms) 
  • -LocalAdmCredential – credenciais Local administrator.
  • -DomainAdmCredential – credenciais Domain administrator
  • -pscomputername – Workflow parameter com o nome do computador que vai rodar (no meu caso Server 2012)
  • -ComputerName –Novo nome do computador, não da VM

A primeira squencia eu estou instalando a feature Net-Framework-Core

Sequence {

InLineScript {

Install-WindowsFeature -Name 'Net-Framework-Core' -IncludeAllSubFeature
}
}

 

Como eu disse antes, em workflows o PowerShell remoting é implicito. Isso quer dizer que todos os comandos rodarão nos computadores (ou computador) passado no commom parameter –pscomputername.

Tenha em mente que workflows são traduzidos para WWF (Windows Workflow Foundation) e executados por ele. Desta maneira somente comandos que o WWF entende podem ser usados dentro de workflows.  Como workflows são supostamente para rodar  tasks longas você pode querer  criar uma sequencia ordenada para estas tasks. O bloco Sequence faz isso para você. Tudo dentro de um bloco sequence ira rodar sequencialmente e somente passara para frente após terminar

Mas provavelmente você vai querer rodar cmdlets do PowerShell no workflow e não somente os que o WWF pode traduzir. Para isso temos o bloco InlineScript. Ele também é util para rodar comandos externos ao powershell e classes .net . Você somente tem que ter atenção nas chamadas das variáveis criadas fora do workflow, elas tem que ser usadas com o $using ($using:suavariavel)

Bom eu sugiro você ler mais sobre workflows e pra isso temos otimos conteudos no The Scripting Guys (com ujma serie muito legal – PowerShell Workflow for Mere Mortals) e na  PowerShell Magazine)

Também recomendo você comprar o livro do Guru Jeff Hicks (PowerShell in Depth) . É MUITO BOM !!!

 

Nesta sequencia eu estou criando um novo PSdrive para fazer a copia doas arquivos do SQL Server do meu host. Copy-Item não funciona com o parametro credential (mesmo tendo ele), então eu criei um novo PSdrive apontando para o folder no host, passei as credenciais e assim posso usar o copy item. Meu folder chama-se SQLServer2012Core

use Copy-Item. I have a folder called SQLServer2012core with all the files on that.

Write-Verbose -Message "Copying SQL Server Core folder from Host"

Sequence {

InLineScript {

New-PSDrive -Name H -Root \\vader\c$\Softwares -Persist -PSProvider FileSystem -Credential $using:LocalAdmCredential
Set-Location -Path c:\
Copy-Item -Path h:\SQLServer2012Core -Force -Recurse
}

 

Na proxima sequencia eu instalo o SQL Server 2012 core por linha de comando. Não use a opção QS e sim somente Q.

Sequence {

InLineScript {

Set-Location -Path c:\SQLServer2012Core
.\Setup.exe /Q /ACTION=Install /FEATURES=SQLEngine,AS,IS,Conn /INSTANCENAME="MSSQLSERVER" /SQLSVCACCOUNT="NT Service\MSSQLServer" /SQLSYSADMINACCOUNTS=".\Administrators" /AGTSVCACCOUNT="NT AUTHORITY\Network Service" /ASSVCACCOUNT="NT Service\MSSQLServerOLAPService" /ASSYSADMINACCOUNTS=".\administrators" /ASSERVERMODE="TABULAR" /TCPENABLED=1 /IACCEPTSQLSERVERLICENSETERMS /UpdateEnabled=False
}
}

 

Agora eu preciso renomear meus nics, atribuir um IP estatico para o NIC LAN,setar o DNS e renomear a maquina de Server2012 para o parametro computername (que passamos na chamada)

Meu range é 11.1.1.X e meu DC/DNS é 11.1.1.1

Como eu estou adicionando os NICS , eu sei que o LAN é o segundo e o description dele é Microsoft Hyper-V Network Adapter #2. Eu vou usar esta descrição para setar o ip fixo.

Sequence {

InLineSCript {

Write-Verbose -Message "Renaming NICS.."
Rename-NetAdapter -Name Ethernet -NewName Internet
Rename-NetAdapter -Name 'Ethernet 2' -NewName LAN

Write-Verbose -Message "Assigning Static IP to LAN NIC and setting DNS"
$NIc = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "Description='Microsoft Hyper-V Network Adapter #2'"
$NIC.SetDynamicDNSRegistration("TRUE")
$NIC.EnableStatic($using:newIp,"255.0.0.0")
$Nic.SetDNSServerSearchOrder("11.1.1.1")

Write-Verbose -Message "Joing domain $($using:domain)"
Add-Computer -Credential $using 😀 omainAdmCredential -DomainName $using:domain -Force -PassThru
Rename-Computer -NewName $using:ComputerName -Force -DomainCredential $using 😀 omainAdmCredential -Restart -PassThru

}
}

 

Pronto. Finalizado o workflow, agora fora dele eu coloco a nova maquina no dominio.

Add-Computer -ComputerName $ComputerName -Credential $DomainAdmCredential -DomainName $domain -Force -PassThru  -Restart
Write-Verbose -Message "Finished Installation"

Eu estou usando o W2012 e SQL2012 core nos exemplos , mas você pdoe usar qualquer versão, mesmo a GUI, que aceite linha de comando na instalação do SQL e unattended no Windows. Somente crie a ISO e o answer file com a versão que deseja usar.

Agora a grande questão.Porque usar Workflow ? Na verdade eu ACHO poderia ter usado os cmdlets de PowerShell Remoting e WMI para fazer este processo , ja que não preciso parar e startar o workflow ou rodar algo em paralelo. Claro que o trabalho seria dobrado, mas eu acho que daria. Quem sabe um novo teste 🙂

 

O codigo Completo :

#Requires -version 3.0

Param(

[String]$VmName,
[string]$VmPath,
[String]$NewIp,
[String]$domain,
[string]$ComputerName

)


workflow New-Server2012SetupWorkflow {

Param(

[String]$ComputerName,
[String]$NewIp,
[String]$domain,
[system.Management.Automation.PSCredential]$LocalAdmCredential,
[system.Management.Automation.PSCredential]$DomainAdmCredential

)


Write-Verbose -Message "Starting $($WorkFlowCommandname)"
Write-Verbose -Message "Need to make all installation needed with PowerShell Remoting first, because the name of the server. It is allowed in wsman only server2012 name"

#Removing Key. I dont need it anymore and I dont want crash in my registry

Remove-Item -Path 'HKLM:\software\UnattendedInstallation' -Force

Write-Verbose -Message "Enabling Feature Net-Framework-Core."



Sequence {

InLineScript {

Install-WindowsFeature -Name 'Net-Framework-Core' -IncludeAllSubFeature
}
}

Restart-Computer -Force -Wait


Write-Verbose -Message "Copying SQL Server Core folder from Host"

Sequence {

InLineScript {

#creating the drive to my host to copy the files
New-PSDrive -Name H -Root \\vader\c$\Softwares -Persist -PSProvider FileSystem -Credential $using:LocalAdmCredential
Set-Location -Path c:\
Copy-Item -Path h:\SQLServer2012Core -Force -Recurse #-Credential $using:LocalAdmCredential
}
}

Write-Verbose -Message "Installing SQL Server"


Sequence {

InLineScript {

Set-Location -Path c:\SQLServer2012Core
.\Setup.exe /Q /ACTION=Install /FEATURES=SQLEngine,AS,IS,Conn /INSTANCENAME="MSSQLSERVER" /SQLSVCACCOUNT="NT Service\MSSQLServer" /SQLSYSADMINACCOUNTS=".\Administrators" /AGTSVCACCOUNT="NT AUTHORITY\Network Service" /ASSVCACCOUNT="NT Service\MSSQLServerOLAPService" /ASSYSADMINACCOUNTS=".\administrators" /ASSERVERMODE="TABULAR" /TCPENABLED=1 /IACCEPTSQLSERVERLICENSETERMS /UpdateEnabled=False

}
}

Write-Verbose -Message "Changing de Name of the NICS and the IP Addrees from LAN (to put in the domain)"

Sequence {

InLineSCript {

Write-Verbose -Message "Renaming NICS.."
Rename-NetAdapter -Name Ethernet -NewName Internet
Rename-NetAdapter -Name 'Ethernet 2' -NewName LAN

Write-Verbose -Message "Assigning Static IP to LAN NIC and setting DNS"
$NIc = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "Description='Microsoft Hyper-V Network Adapter #2'"
$NIC.SetDynamicDNSRegistration("TRUE")
$NIC.EnableStatic($using:newIp,"255.0.0.0")
$Nic.SetDNSServerSearchOrder("11.1.1.1")

Write-Verbose -Message "Renaming the machine"
Rename-Computer -NewName $using:ComputerName -Force -PassThru

}
}
Restart-Computer -Wait -Force

}

$verbosepreference = "Continue"

#getting the local adm credentials (will be in the new vm)
$LocalAdmCredential = New-Object System.Management.Automation.PSCredential "administrator",(Get-Content C:\scripts\LocalAdmPassword.txt | ConvertTo-SecureString )
$DomainAdmCredential = New-Object System.Management.Automation.PSCredential "starwars\administrator",(Get-Content C:\scripts\LocalAdmPassword.txt | ConvertTo-SecureString )

Write-Verbose -Message "Create and setup the Virtual Machine"
#Change to your network switch name
New-VM -Name $VmName -Path $VmPath -MemoryStartupBytes 512MB -NewVHDPath "$($VmPath)\$($VmName)\$($VmName).vhdx" -NewVHDSizeBytes 127GB -SwitchName Internet
Add-VMNetworkAdapter -VMName $VmName -SwitchName LAN
Get-VMNetworkAdapter -VMName $VmName | Where-Object {$_.switchname -like 'LAN'} | Set-VMNetworkAdapterVlan -Access -VlanId 2
#Change to your iso path
Set-VMDvdDrive -VMName $VmName -Path "c:\Softwares\Unattended_WindowsServer2012STDCore.iso"
#Set-VMDvdDrive -VMName $VmName -Path "c:\Softwares\Unnatended_WindowsServer2012STDFULL.iso"

Write-Verbose -message "Starting VM"
Start-VM -Name $VmName

Write-Verbose -Message "Installing OS - This may take several minutes...."
$ErrorActionPreference = 'stop'

do {
Start-Sleep -milliseconds 100
#trying to get the value from the registry that is created by the installation indicating when all is ok and finished the setup.
#Then I keep doing the setup. It is the last command ran, after disable the firewall
try {
#It is not the name of the VM, but the nsame of the server informed in the unnatended file.In my case Server2012. Below I will rename it and join
#in a domain
$FinishedInstallation = ([Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine", "Server2012")).OpenSubKey("software\unattendedinstallation")
if ($FinishedInstallation) {
Break
}
} catch{}
} while ($true)

Write-Verbose -Message "Starting Workflow.."

New-Server2012SetupWorkflow -NewIp $newIP -domain $domain -LocalAdmCredential $LocalAdmCredential -DomainAdmCredential $DomainAdmCredential -pscomputername Server2012 -ComputerName $ComputerName -verbose

Add-Computer -ComputerName $ComputerName -Credential $DomainAdmCredential -DomainName $domain -Force -PassThru -Restart
Write-Verbose -Message "Finished Installation"

A chamada, que no meu caso o script chama New-Server2012Setup

New-Server2012Setup -VmName DarthTyranus-VmPath "c:\VirtualMachinesHyperV" -NewIP 11.1.1.78  -domain starwars 

 

É pessoal..automatização é uma Linda dama  e o PowerShell é o anél. Falta somente casar com ela 🙂

About Laerte Junior

Laerte Junior Laerte Junior is a SQL Server specialist and an active member of WW SQL Server and the Windows PowerShell community. He also is a huge Star Wars fan (yes, he has the Darth Vader´s Helmet with the voice changer). He has a passion for DC comics and living the simple life. "May The Force be with all of us"
This entry was posted in Powershell, SQL SERVER EM GERAL, Virtual Pass BR. Bookmark the permalink.

1 Response to Automatizando toda a instalação de uma nova Hyper-V VM (W2012-SQL2012).Core usando PowerShell3.0/Workflows

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s