Running hyperV inside a windows docker container - powershell

I have a process which runs primarily with powershell and relies heavily on the hyperV pwsh module to build (at script runtime) and launch a hyper-v instance. This is to programmatically build a windows machine with specific features, updates, and applications, then capture an image of that machine for deployment to physical devices later.
We want to containerize this process so it can be run more dynamically than on a physical box like it is today.
Critical in this, is we need to be able to build and turn on a hyper-v instance inside the container. Currently experimenting on win 1803 with the dockerFile below.
# note it doesn't necessarily need to be this image, I just picked it because it was easy
FROM microsoft/powershell:nanoserver-1803 AS powershell
COPY ./mainContainer/ c:/app/
and then need pwsh like the lines below to work (primary issue is the lack of the hyperV pwsh module):
New-VHD -SizeBytes 100GB -Path $vhdPath
New-VM -Name $VmName -Generation 2 -Path "$TempDirectory\$VmName" -VHDPath $vhdPath -Switch $switchName
Set-VMMemory -VMName $VmName -StartupBytes 4096MB -DynamicMemoryEnabled $false
Add-VMScsiController -VMName $VmName
Add-VMDvdDrive -VMName $VmName -ControllerNumber 1 -ControllerLocation 0 -Path $WindowsIsoPath
Set-VMFirmware -VMName $VmName -FirstBootDevice $dvdDrive
Start-VM -VMName $VmName
$vm = Get-Vm $VMName
# then there's little loop waiting for the machine to turn off before continuing
I have tried Install-WindowsFeature and similar, but always get an error that:
"The term 'Install-WindowsFeature' is not recognized as the name of a cmdlet, function, script file, or operable program."
I have tried import-module servermanager, but that also gives "...not loaded because no valid module file was..."
It seems perhaps the main (current) hurdle may be to get a new module imported in pwsh (within the container) so I can enable the windows feature?
any advice?
update: I found part of the problem was it appears the nanoserver doesn't allow things like dism, so I've updated the dockerFile to:
FROM microsoft/powershell:windowsservercore-1803 AS powershell
COPY ./mainContainer/ c:/app/
RUN DISM /Online /Enable-Feature /All /FeatureName:Microsoft-Hyper-V
but now I get an error:
The source files could not be found.
Use the "Source" option to specify the location of the files that are required to restore the feature. For more information on specifying a source location, see http://go.microsoft.com/fwlink/?LinkId=243077.
seems I need a different base image, not sure if there is one that can enable hyper-V

Related

How to easily fix virtualbox removable drives being used as physical drive vmdk's when they get unplugged/replugged?

I am using removable thunderbolt (NVMe) drives that are partitioned/configured for bare metal boot drives in Virtualbox. The idea is that I either boot this drive bare metal or in Virtualbox.
When un-plugging/re-plugging removable drives in Windows, my Virtualbox rawvmdk files consistently break. The reason I found is that there is no guarantee how Windows may path your physical drive each time or where you plug it in. And you can't just create rawvmdk's for all the likely paths because Virtualbox machines can't have a broken vmdk file. Each time it breaks, I have to get just right the particulars of how to correctly re-create the rawvmdk file, and the right order of configuration of the virtual machine. It takes a bunch time/frustration to figure this out each time. I need an easy and consistent way to start the machine.
The following powershell script I worked on looks up the windows path for a hardcoded drive serial number, and does everything necessary to replace the broken rawvmdk file with a working one. The script shows the serials for all the host machine's physical drives each time it runs:
echo "*** This powershell script finds a raw drive by its serial number, and then attaches it to a desired vbox virtual machine***"
echo "*** When VBOX loses its track on the drive, between unplug/replug, this will fix it for that serial."
echo "*** Must be run as administrator ***
"
echo "Here are all the drives we can find:"
Get-Disk |Format-Table -Autosize -Property DeviceID, Model, Path, Partitions, SerialNumber
#This is the Serial number we're looking for:
$SERIAL = "6479_110F."
echo "Attempting to attach Drive Serial Number: $SERIAL
"
#Get the Drive Path for that Serial
$DrivePath = Get-Disk | Where-Object -Property SerialNumber -eq $SERIAL | Select -ExpandProperty "Path"
if ( $null -eq $DrivePath )
{
echo "Could not find your hardcoded serial number"
} else
{
# Where we want to put the rawVMDK file
$VMDKStorage = "C:\VirtualBox VMs\LaCie_Drive\"
cd "$VMDKStorage"
# The Target Vbox VM to attach the rawvmdk
$MachineName = "Ubuntu"
# Arbitrary name for controller.
$ControllerName = "NVMe"
#The Filename to call the rawvmdk
$FileName = "Firecuda.vmdk"
#Clean up the Target Machine of the non-working image.
echo "Attempting to remove specified vmdk hdd from target machine"
Start-Process -FilePath "C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" -ArgumentList "storageattach $MachineName --storagectl $ControllerName --port 0 --device 0 --medium none" -NoNewWindow -Wait
# Remove the Virtualbox traces of the File
Start-Process -FilePath "C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" -ArgumentList "closemedium $FileName --delete" -NoNewWindow -Wait
echo "Creating the new RawVMDK File"
Start-Process -FilePath "C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" -ArgumentList "internalcommands createrawvmdk -filename $FileName -rawdisk $DrivePath" -NoNewWindow -Wait
echo "Adding the now supposedly working RawVMDK to Target Machine"
Start-Process -FilePath "C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" -ArgumentList "storageattach $MachineName --storagectl=NVMe --port 0 --device 0 --type hdd --medium=$FileName" -NoNewWindow -Wait
echo "You now can start the Machine up normally from VirtualBox."
# I found I don't need to start VBox with admin privledges for my VM to work.
}
I did find that when plugging my drive in one particular port on my machine that windows wanted to call my drive an entirely different serial number. After changing this code's serial number to correct for that, I found that I also had other problems. Namely that the kernel did not recognize any of the LVM configuration that contained my drive root (/). What it appears is that on this computer port, linux cannot identify these partitions as nvme partitions, and that this mucks up the boot configuration pretty badly (I was stuck in initramfs). I believe this a USB-C port and that the drive has some alternative hardware to make the drive "work". I really want the drive to operate at NVMe speeds, so I'm just going to avoid this port.
Hope this helps someone.

How to execute an installer executable on a remote machine with Powershell?

I'm trying to automate the upgrading of Spotfire BI system on our Windows servers.
The instructions show a command that can be used to silently install which states the location of the executable file followed by all the required options/parameters as shown with this example:
install.exe INSTALLDIR="<node manager installation dir>" NODEMANAGER_REGISTRATION_PORT=9080 NODEMANAGER_COMMUNICATION_PORT=9443 SERVER_NAME=SpotfireServerName SERVER_BACKEND_REGISTRATION_PORT=9080 SERVER_BACKEND_COMMUNICATION_PORT=9443 NODEMANAGER_HOST_NAMES=NodeManagerHostNames NODEMANAGER_HOST=NodeManagerHost -silent -log "C:\Users\user\Log file.log"
This does work, and as long as it preceded by the call operator (&), it runs well in PowerShell. However, I can't get it to work when running this on a remote server:
function nodeManagerUpgrade {
Param([String]$ServiceName1,
[String]$InstallDirectory,
[String]$HostNames1
)
Stop-Service $ServiceName1
# this is the line that fails
& "X:/downloads/spotfire/install.exe" INSTALLDIR="$InstallDirectory" NODEMANAGER_REGISTRATION_PORT=17080 NODEMANAGER_COMMUNICATION_PORT=17443 SERVER_NAME=localhost SERVER_BACKEND_REGISTRATION_PORT=19080 SERVER_BACKEND_COMMUNICATION_PORT1=9443 NODEMANAGER_HOST_NAMES=$HostNames1 -silent -log "install.log"
}
Invoke-Command -ComputerName $NodeIP -ScriptBlock ${function:nodeManagerUpgrade} -argumentlist ($ServiceName,$InstallDirectory,$HostNames) -credential $credentials
I can run the code contained in the function directly on the remote server and it works correctly. However, when I try and run it from the central server through WinRM/Invoke-Command within a function, it fails giving me this error:
The term 'X:/downloads/spotfire/install.exe' is not recognized as the name of a cmdlet
Is it possible to run an executable using PowerShell on a remote server?
Your executable path is based on a drive letter, X:.
However, in remote sessions mapped drives (drives connected to network shares) aren't available by default, so you have two options:
Establish a (temporary) drive mapping with New-PSDrive before calling the executable (e.g., $null = New-PSDrive X FileSystem \\foo\bar)
More simply, use the full UNC path of the target executable (e.g. & \\foo\bar\downloads\spotfire\install.exe ...)
If the target executable isn't actually accessible from the remote session, you'd have to copy it there first, which requires establishing a remote session explicitly and using Copy-Item -ToSession - see the docs for an example.

Installing Windows Updates via PSWindowsUpdate

I am trying to remotely make a domain-computer install its windows updates. This sounds like it should be quite easy, but I've been working on this for over 7 hours now and can't get it to work. I know you can do this via a GPO, but that doesn't give me enough control over the interval. I want our servers to install them and reboot monthly - a GPO can only be used to install and reboot weekly. Since our production works 24/7 I absolutely don't want the servers to reboot outside of the few hours downtime per month I am allowed for maintenance!
I have found several tutorials like this that use the Module PSWindowsUpdate, but these tutorials use an older version of that Module. They use a Function called Invoke-WUInstall which doesn't exist in the newest version. I have tried downgrading the module, but the packagesource doesn't provide versions older than 2.0.0.0
Also the project page doesn't provide a documentation - no examples - neither does it have a discussion or bugtracker. There is a discussion on the page of the original author, but he stopped working on it 2 years ago when it was still the old version.
I tried using Invoke-Command instead of Invoke-WUInstall, but Windows doesn't seem to allow remote update installation like that. PSWindowsUpdate apparently circumvents this problem by running the command as a scheduled task on the target machine, so looking at the output of Get-Command -Module PSWindowsUpdate, I thought I might need to use Invoke-WUJob instead and wrote this code:
Import-Module -Name PSWindowsUpdate
Enable-PSRemoting -Force
ForEach ($hostname in $args) {
Write-Output "Processing $hostname"
Set-Item WSMan:\localhost\Client\TrustedHosts -Value $hostname -Concatenate -Force
# Install PSWindowsUpdate on target machine
Invoke-Command -computername $hostname -ScriptBlock {
PackageManagement\Get-PackageProvider -Name NuGet -Force
inmo PSWindowsUpdate -Force
}
# Install the Updates
Invoke-WUJob -ComputerName $hostname -Script {
ipmo PSWindowsUpdate;
Install-WindowsUpdate -install -AcceptAll -IgnoreReboot
} -Confirm:$false -RunNow
}
I run this as a user who has administrative rights on the target machine and the output looks fine, but it didn't do anything.
Does anyone have experience with that module? how do you do this properly in versions >= 2?
Well, i already did something like this and also faced this same problem.
That was my solution:
Create a powershell file to execute your commands. Place all your commands to install the updates there.
Copy this file to the remote server.
You can do something like this:
copy myfile.ps1 \\myserver\c$\temp\myfile.ps1;
Run a remote script to create a registry inside the RunOnce, and the set value with a command to run your script:
Set-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce" -Name '!InstallUpdates' -Value "c:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe -File c:\temp\myfile.ps1"
Run another command remotelly to restart your server.
The server will reboot and then will execute your script locally.

PowerShell script runs Get-CDDrive | Set-CDDrive but won't continue after that line

In one of my scripts, PowerShell gets to this point, it actually does set the cd drive but then it doesn't progress from there. i have other Write-Host commands above the code and it looks like everything progresses and sets the cd drive, but then nothing below it will happen. Any ideas? Anyone seen this kind of issue before?
The Get/Set-CDDrive commands are coming from the VMware PowerCLI snap-in for PowerShell.
Get-CDDrive -VM $vmname | Set-CDDrive -IsoPath $isopath -StartConnected:$true -Confirm:$false
Write-host "ISO attached successfully"
Write-host "[INFO] VM $($vmname) deployed"

Exiting VMware vSphere PowerCLI command prompt?

Is there a script to input to exit VMware vSphere PowerCLI command prompt after executing a set of script for example created of VM.
MY last line of my .ps1 script is shown below, but the exit does not work, after executing my script, the command prompt is still there, unlike windows command prompt as the exit command works but not in powercli.
New-VM -name $vm -DiskMB 10000 -memoryMB 4000
New-CDDrive -VM $vm -ISOPath $win7 -StartConnected:$true -Confirm:$false
$scsiController = Get-HardDisk -VM $vm | Select -First 1 | Get-ScsiController
Set-ScsiController -ScsiController $scsiController -Type VirtualLsiLogicSAS -Confirm:$false
Start-VM -vm $vm
Exit
My hunch is that this has more to do with powershell and little to do with PowerCLI. I'm also guessing that the window closes if you focus it and press 'Enter'. I ran into this when executing some powershell scripts long ago, but never came across a decent fix until this evening. It seems powershell waits on a Readline() call after executing the script.
The solution: include the flag -InputFormat None in your call to powershell.exe. In your case, I'd include it in the call to the PowerCLI executable, it ought to be passed through.
Resources:
This looks like a known issue from Microsoft's tracker.
These two questions reference the same fix:
Powershell and WiX
Powershell and MSDeploy
Please let me know if this works correctly, I'm not on a system with PowerCLI installed currently.
Good luck!