Skip to content

Instantly share code, notes, and snippets.

@primeinc
Last active December 27, 2016 00:15
Show Gist options
  • Save primeinc/486795211d9c64e56d262d27e4f282db to your computer and use it in GitHub Desktop.
Save primeinc/486795211d9c64e56d262d27e4f282db to your computer and use it in GitHub Desktop.
Home Assistant install on Windows 2016 NanoServer

deployHA

Wanted to deploy Home Assistant to a Windows 2016 Nano Server. For fun I figured I'd create a script to deploy it effortlessly. This script would probably work for any newer version of Windows although I haven't tested that.

You should have the x64 version of Python 3.5. I tested this version specifically. If you changed the install path from the default you should specify the install path as the second argument.

.\deployHA.ps1 servername-or-ip path-2-Python-3.5(optional) "Time Zone"

# Example Usages:

.\deployHA.ps1 ha C:\Python-Custom-Path\Python35 "Pacific Standard Time"

.\deployHA.ps1 ha C:\Python-Custom-Path\Python35

.\deployHA.ps1 10.30.50.25

.\deployHA.ps1 
.\restartHA.ps1 servername-or-ip

# Example Usages:

.\restartHA.ps1 ha

.\restartHA.ps1 10.30.50.25

.\restartHA.ps1 

If you haven't deployed a Nano Server here's a generic jist

Domain version

Need to run from domain joined computer in administrator powershell prompt Assumes D:\ is a mounted Server 2016 ISO

$VMName = "ComputerName"
$InstallDisk = "D:\"
$Domain = "corp.contoso.com"

Djoin.exe /provision /domain $Domain /machine $VMName /savefile "$env:temp\blobNano-$VMName"

Import-Module "$InstallDisk\NanoServer\NanoServerImageGenerator\NanoServerImageGenerator" -Verbose

New-NanoServerImage -Edition Standard -DeploymentType Guest -MediaPath $InstallDisk -TargetPath "$($(Get-VMHost).VirtualHardDiskPath)$VMName.vhdx" -DomainBlobPath "$env:temp\blobNano-$VMName"

New-VM -VHDPath "$($(Get-VMHost).VirtualHardDiskPath)$VMName.vhdx" -BootDevice VHD -Generation 2 -MemoryStartupBytes 1073741824 -Name $VMName -Path "$($(Get-VMHost).VirtualMachinePath)$VMName" -SwitchName (Get-VMSwitch).Name[0]

Start-VM $VMName

Workgroup version

Assumes D:\ is a mounted Server 2016 ISO

Import-Module D:\NanoServer\NanoServerImageGenerator\NanoServerImageGenerator -Verbose

New-NanoServerImage -Edition Standard -DeploymentType Guest -MediaPath
 D:\ -TargetPath "C:\Hyper-V\Virtual Hard Disks\ha.vhdx" -ComputerName ha-test

Then create a new Generation 2 VM in Hyper-V using the disk you just created. Start it up and run the powershell script.

param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$targetComputer = $(throw "targetComputer is mandatory, please provide a value."),
[string]$PythonInstallPath = “$env:LOCALAPPDATA\Programs\Python\Python35”,
[string]$tz = "Eastern Standard Time"
)
Write-Progress -Activity "Deploying Home Assistant" -Status 'Connecting to remote computer..';
$s = New-PSSession -ComputerName $targetComputer
if(-not($s))
{
Write-Warning "$targetComputer inaccessible!"
$trustedHosts = Get-Item wsman:\localhost\Client\TrustedHosts
$trustedHosts = $trustedHosts.Value -split ','
if($trustedHosts -notcontains $targetComputer) {
$user = [Security.Principal.WindowsIdentity]::GetCurrent();
$isAdmin = (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
if($isAdmin){
Set-Item WSMan:\LocalHost\Client\TrustedHosts $targetComputer -Concatenate
} else {
Write-Warning "Could not add ${targetComputer} to TrustedHosts`nRun this script as administrator to auto-configure TrustedHosts"
Write-Warning "Alternatively run the following in an administrator powershell shell: Set-Item WSMan:\LocalHost\Client\TrustedHosts $targetComputer -Concatenate"
write-host -nonewline "Continue anyway? (Y/N) "
$response = read-host
if ( $response -ne "Y" ) { exit }
}
}
$s = New-PSSession -ComputerName $targetComputer -Credential ~\Administrator
if(-not($s)) {
Write-Warning "$targetComputer still inaccessible!"
Write-Warning "Is winrm configured? Run: winrm quickconfig"
exit
}
}
#Copy-Item -ToSession $s -Path $PythonInstallPath -Destination “C:\” -Force -Recurse # This takes a while
$tmp = New-TemporaryFile | Rename-Item -NewName { $_ -replace 'tmp$', 'zip' } –PassThru; Compress-Archive -Path $PythonInstallPath\* -CompressionLevel Fastest -Destination $tmp.FullName -Force
Copy-Item -ToSession $s -Path $tmp.FullName -Destination “C:\Windows\Temp\pythonInstall.zip” -Force # This takes a while
Invoke-Command -Session $s -Scriptblock {Expand-Archive -Path C:\Windows\Temp\pythonInstall.zip -DestinationPath C:\Python\Python35 -Force}
Invoke-Command -Session $s -Scriptblock {Remove-Item C:\Windows\Temp\pythonInstall.zip}
Write-Progress -Activity "Deploying Home Assistant" -CurrentOperation 'Configuring...' -Status 'Configure Python Install';
# Add the Python folders to the path environment variable and persist:
Invoke-Command -Session $s -Scriptblock {$env:Path += “;C:\Python\Python35;C:\Python\Python35\Scripts”; setx PATH $env:Path /M}
$pTest = Invoke-Command -Session $s -Scriptblock {python --version}
if($pTest -notlike "Python 3.5.*") {Write-Warning "Something went wrong, Python not found"; exit}
# Upgrade pip
Invoke-Command -Session $s -Scriptblock {python -m pip install --upgrade pip}
# Install HA
Write-Progress -Activity "Deploying Home Assistant" -CurrentOperation 'Installing..' -Status 'Installing Home Assistant';
Invoke-Command -Session $s -Scriptblock {pip3 install homeassistant}
# Allow HA through firewall
Write-Progress -Activity "Deploying Home Assistant" -CurrentOperation 'Configurating..' -Status 'Configure Firewall';
Invoke-Command -Session $s -Scriptblock {netsh advfirewall firewall add rule name=”HA Web” dir=in action=allow protocol=TCP localport=8123}
# Allow access to Files & Remote Managment & Time Zone
Invoke-Command -Session $s -Scriptblock {Import-Module NetSecurity}
Invoke-Command -Session $s -Scriptblock {Set-NetFirewallRule -DisplayGroup “File And Printer Sharing” -Enabled True}
Invoke-Command -Session $s -Scriptblock {Set-NetFirewallRule -DisplayGroup “Remote Service Management” -Enabled True}
Invoke-Command -Session $s -Scriptblock {Set-NetFirewallRule -DisplayGroup “Remote Event Log Management” -Enabled True}
Invoke-Command -Session $s -Scriptblock {tzutil /s $tz} -ArgumentList $tz
# Create config directory (HA won't do it for you)
Invoke-Command -Session $s -Scriptblock {New-Item -ItemType Directory -Force -Path C:\ProgramData\.homeassistant | Out-Null}
Write-Progress -Activity "Deploying HA" -CurrentOperation 'Starting..' -Status 'Start Home Assistant';
# Start HA via Scheduled Task
function Invoke-PrepareScheduledTask
{
$taskName = "HomeAssistant"
$task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($task -ne $null)
{
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
}
# Argument could be '-m homeassistant --config C:\ProgramData\.homeassistant' but it prevents Stop-ScheduledTask from working
$action = New-ScheduledTaskAction -Execute 'C:\Python\Python35\python.exe' -Argument 'C:\Python\Python35\lib\site-packages\homeassistant\__main__.py --config C:\ProgramData\.homeassistant --runner'
$trigger = New-ScheduledTaskTrigger -AtStartup
$settings = New-ScheduledTaskSettingsSet -Compatibility Win8 -ExecutionTimeLimit 0
$settings.CimInstanceProperties['MultipleInstances'].Value=3
$principal = New-ScheduledTaskPrincipal -UserId "LOCALSERVICE" -LogonType ServiceAccount -RunLevel Limited
$definition = New-ScheduledTask -Action $action -Principal $principal -Trigger $trigger -Settings $settings -Description "Run $($taskName) at startup"
Register-ScheduledTask -TaskName $taskName -InputObject $definition
$task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($task -ne $null)
{
Write-Output "Created scheduled task: '$($task.ToString())'."
}
else
{
Write-Output "Created scheduled task: FAILED."
exit
}
}
# Create Scheduled Task
Invoke-Command -Session $s -Scriptblock ${function:Invoke-PrepareScheduledTask}
Invoke-Command -Session $s -Scriptblock {Start-ScheduledTask HomeAssistant}
Write-Host "`n`nHome Assistant deployed!` Point your broswer to:"
Write-Host "http://${targetComputer}:8123" -foregroundcolor "green"
Write-Host "`nConfiguration files are located:"
Write-Host "\\${targetComputer}\C$\ProgramData\.homeassistant" -foregroundcolor "green"
Write-Host "`n First run make take a few minutes to respond" -foregroundcolor "Yellow"
Remove-PSSession -Session $s
param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$targetComputer = $(throw "targetComputer is mandatory, please provide a value.")
)
$s = New-PSSession -ComputerName $targetComputer
if(-not($s))
{
Write-Warning "$targetComputer inaccessible!"
$trustedHosts = Get-Item wsman:\localhost\Client\TrustedHosts
$trustedHosts = $trustedHosts.Value -split ','
if($trustedHosts -notcontains $targetComputer) {
$user = [Security.Principal.WindowsIdentity]::GetCurrent();
$isAdmin = (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
if($isAdmin){
Set-Item WSMan:\LocalHost\Client\TrustedHosts $targetComputer -Concatenate
} else {
Write-Warning "Could not add ${targetComputer} to TrustedHosts`nRun this script as administrator to auto-configure TrustedHosts"
Write-Warning "Alternatively run the following in an administrator powershell shell: Set-Item WSMan:\LocalHost\Client\TrustedHosts $targetComputer -Concatenate"
write-host -nonewline "Continue anyway? (Y/N) "
$response = read-host
if ( $response -ne "Y" ) { exit }
}
}
$s = New-PSSession -ComputerName $targetComputer -Credential ~\Administrator
if(-not($s)) {
Write-Warning "$targetComputer still inaccessible!"
Write-Warning "Is winrm configured? Run: winrm quickconfig"
exit
}
}
Write-Progress -Activity "Restarting Home Assistant" -Status "Please wait..."
$oldPid = Invoke-Command -Session $s -Scriptblock {Get-Process python | select Id; $oldPid = Get-Process python | select Id}
Invoke-Command -Session $s -Scriptblock {Start-ScheduledTask HomeAssistant}
Invoke-Command -Session $s -Scriptblock {Do {
$ProcessesFound = Get-Process | ? {$oldPid.Id -contains $_.Id} | Select-Object -ExpandProperty Id
If ($ProcessesFound) {
"Still running: $($ProcessesFound -join ', ')" | Write-Output
Start-Sleep -Seconds 1
}
} Until (!$ProcessesFound)}
$newPid = Invoke-Command -Session $s -Scriptblock {Get-Process python | select Id}
if ($oldPid.Id -ne $newPid.Id) {
Write-Host "Home Assistant Restarted! `nPrevious PID: " $oldPid.Id "`nNew PID:" $newPid.Id -foregroundcolor "green"
} else {
Write-Host "Failed to restart Home Assistant `nPrevious PID: " $oldPid.Id "`nNew PID:" $newPid.Id -foregroundcolor "red"
}
Remove-PSSession -Session $s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment