Full automation of the installation of Hyper-V VM – (W2012 / SQL2012).Core using PowerShell 3.0/Workflows


Constantly in my labs and test I have to create new Hyper-V virtual machines, delete old ones,  install new features .. etc, and this used me a reasonable time. When you get to be too repetitive for example, installing windows and SQL server, it starts to get tiring when doing this process manually.

Precisely to that reason I wrote this script to help me in my day to day of testing. Come on, let’s see a little about it: The script is pretty simple.

Before start I had to setup my environment . My  host is not in a domain and as I am also using workflows to setup I need to enable PowerShell remoting to the new VM. (workflows implicitly use PowerShell Remoting)

The name of the physical computer will always be, initially – to setup first , SERVER2012, so I needed  to enable it in the TrustedHost in WSMAN :

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

Now  I can use PowerShell Remoting from my Host to my new VM.

The next step if store the credentials(password) encrypted  in a text file. I will  us it to copy the SQL Server files from my Host (Vader) to the VM and to add to domain. As my Password is the same for the local administrators and domain, I just need to store de password and change the user name (starwars\administrator and .\administrator). If your credentials are not the same, just create another file.

read-host -assecurestring | convertfrom-securestring | out-file C:\scripts\LocalAdmPassword.txt

My passwords is saved in the LocalAdmPassword .

I need now to create an unattended installation of Windows Server 2012 Core and for that I recommend you read  the excellent blog post from Derek Seaman :

Windows Server 2012 Unattended Installation

The point is I had to customize the answer file. Why ? Because I needed to know when the installation of the W2012 completely finished (to keep doing the next steps) , disable the firewall and enable PowerShell Remoting on it.

1 – How to know when the installation completely finished ?

I tried some techniques , but none of them worked fine.  Test-Connection was pinging before the setup finished and I tested also a snippet wrote by Ben Armstrong (PowerShell snippet: “start a virtual machine and wait for a bit”), but also the heartbeat property was OK before the setup finished . I don’t know if there is another way to do it, but it works.

My Idea was , and you can see at the answer file that you can add new commands in the autologon to the fist logon, create a new command and put it as the last cmd to execute creating a key in the registry called UnattendedInstallation. Then I wrote a simple while that keep testing to check if this Key exists. If it is, means that finished installation and I keep running with my script.

You can see the image of the answer file :

fig1

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

 

and also another cmd to disable the firewall and enable PowerShell remoting :

Fig2

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

Download images here

Then it is just use the UltraISO , open the ISO file from Windows Server 2012 ,add the autounattend.xml file to the root and save it (with other name). 

You can download my answer files in here

Lets check the script step by step :

The first set of cmdlets  are to create the VM and setup the NICS : I have 2 NICS – Internet is external (to access the internet) and LAN (internal . it is used for my domain in the VM’s)

#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 "Step 1 - 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

The Second set are the while to check if the installation finished. I don’t want to run as job because I want to check step by step the installation in another PowerShell host

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)

 

After finished the  installation, I run the Workflow at the Machine Server2012

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

 

The Parameter  :

  • –NewIp –  the new IP in the LAN NIC (in my case)
  • -Domnain – My Domain
  • -LocalAdmCredential – The Local administrator credential.
  • -DomainAdmCredential – The Domain administrator credential
  • -pscomputername – Workflow parameter with the computer that will be run
  • -ComputerName – New name of the computer, not the VM

 

The First sequence I am enabling and installing the feature Net-Framework-Core. I put in a sequence because maybe in other installations I need to install more features.

Sequence {

InLineScript {

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

As I said before, in workflows PowerShell remoting is implicitly. This means that these commands will run in the computers  used in the –pscomputername. This common parameter determine the computer to run the workflow.

Keep in mind that workflows are translated do WWF (Windows Workflow Foundation) and executed but it , because of that, only commands that WWF understand can be used within a workflow. As workflows are supposed to use when you need perform log-running tasks, sometimes you need to have an order of execution  of the tasks. The Sequence block ensures that for you. Everything inside a Sequence Script Block will run sequentially and must be finished before  keep running the script. (or run another sequence)

But probably you will also want to run PowerShell commands  in workflows  and  not only what WWF can translate. For that, there is InlineScript  block . Also it is helpful to run external PowerShell scripts and to use .NET classes . You just need to pay attention that variables created outside of the InlineScript block must be accessed by using $using ($using:MyVariable)

Well, I suggest to you read about workflows and there are some good content in the web. The Scripting Guys (with a very cool series – PowerShell Workflow for Mere Mortals) and  PowerShell Magazine have some good samples. Also, you may want to get the amazing book “PowerShell in Depth” from the PowerShell Guru Jeff Hicks.

In this sequence I am creating a new PSDrive to copy from my host. Copy-Item does not work with Credentials, so I need to use PSDRIVE and pass the credentials to connect and 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 #-Credential $using:LocalAdmCredential

}

 

The next sequence I am installing SQL Server by command line. Check that I am using only Q and not QS parameter. If you try QS, the setup  will fail. (it will take several minutes –a LOT more time than install W2012)

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
}
}

The next sequence I will rename the NICs name, assign a new static IP address to the LAN Nic ,renaming the Server2012 to the parameter $ComputerName. My range in the Domain is 11.1.1.X..and my DC/DNS is 11.1.1.1

As I am adding the NICS in the order first Internet and second LAN, I know that my LAN NIC always will  the be one with Description = Microsoft Hyper-V Network Adapter #2. I will use that to assign the Static IP to LAN NIC.

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:DomainAdmCredential -DomainName $using:domain -Force -PassThru
Rename-Computer -NewName $using:ComputerName -Force -DomainCredential $using:DomainAdmCredential -Restart -PassThru

}
}

 

The I finished the whole process adding the machine to the domain, outside the workflow.

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

Keep in mind that in the examples I am using W2012 core and SQL Server 2012 core, but you can do it the same with the GUI versions (both Windows and SQL – and old versions as well). Just need to create an ISO and the answer file with unattended installation of the GUI Version, copy the SQL Server 2012 GUI from the host and install by command line. It is the same command.

Now the big question. Why am I using workflows ? In fact for this specific process I *guess* that I could have done  (with more work) using PowerShell remoting cmlets and WMI, since I don’t need to stop and resume the process or run something in parallel.

Perhaps a new test to do it.

The full code :

#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"

To run the script, in  my case called New-Server2012Setup it is just

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

Ho boy..Automation is a beautiful lady and PowerShell is the ring..just marry her !!!!

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 Algo que Esqueci de Categorizar. Bookmark the permalink.

2 Responses to Full automation of the installation of Hyper-V VM – (W2012 / SQL2012).Core using PowerShell 3.0/Workflows

  1. Pingback: Post TechNet test lab: Part 1 | Bodgers MS Test Lab Blog

  2. Pingback: PowerShell V4 Desired State Configuration – My Precio… ops… Desired !!!!! | $hell Your Experience !!!

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