Create Hyper-V Virtual Machine from a .vhd file - powershell

I'm trying to programmatically create a VM from a .vhd file - my code is below. I'm getting a weirdo file not found error, but I've TRIPLE checked all my paths.
ADD-VMHardDiskDrive : 'MyVM' failed to add device 'Virtual Hard Disk'. (Virtual machine ID C4A0BAFF-35EF-4B4C-9CA1-68D2ECF77BA1)
'MyVM': Attachment 'C:\temp\VMs\TheVhd.vhd' not found. Error: 'The system cannot find the file specified.' (0x80070002). (Virtual machine ID C4A0BAFF-35EF-4B4C-9CA1-68D2ECF77BA1)
My PowerShell code is:
$n = "MyVM";
$directory = "C:\temp\VMs"
$vhdxPath = "$directory\$n.vhdx";
$vhdPath = "$directory\Flash.480x800.vhd";
$vm = Get-VM $n;
if($vm -ne $null) { Remove-VM $n -Force; }
New-VM -Name $n -MemoryStartupBytes (Invoke-Expression "1000MB") -Path $directory;
Set-VMMemory -VMName $n -DynamicMemoryEnabled $true -MinimumBytes 512MB -MaximumBytes 4096MB -StartupBytes 2048MB -Buffer 20;
ADD-VMHardDiskDrive -VMName $n -Path $vhdPath;
Start-VM $n;
Any help is very much appreciated!!! Thanks!

I figured it out. The VM I was trying to attach was a "difference disk". I was missing the "master" disk that the reference disk was dependent on - hence the file not found error.
Thanks all.

In you code snippet, I guess you are assuming that ADD-VMHardDiskDrive would also "create" a VHD file for you. If that is the case, then we have root-caused the error. ADD-VMHardDiskDrive just adds a Virtual Hard Disk at indicated path to the VM. If the VHD does not already exist, that error is thrown.
If you want to create a new VHD or VHDX, use NEW-VHD commandlet before adding to the VM.
Alternatively, you can add an existing VHD to the VM or create a new VM as a part of New-VM commandlet itself. Take a look at the examples on technet:
http://technet.microsoft.com/en-us/library/hh848537.aspx

Related

Setting the VirtualHardDiskPath when adding a virtual machine through powershell to Hyper-V

I'm adding a vm through powershell to Hyper-V. The add is working but it's setting the config/xml files on the same drive as the vhdx file.
I am setting the $config and then running my new-vm.
$config= Get-VMHost | Select-Object VirtualMachinePath
I end up with this:
#{VirtualMachinePath=F:\vmconfigs}
This is how I'm adding the vm:
New-VM -Name $name -MemoryStartupBytes 8192MB -VirtualHardDiskPath $config -Path $driv\vm -Generation 2 -SwitchName (Get-VMSwitch).Name
If I run it without the -VirtualHardDiskPath, it places the configs in a folder on the same drive as the vhdx file. Of course, it will not run with the way it's set with the path added since it is not formatted correctly.
You can see here that my default is f:\vmconfigs but it's not using that folder when I manually add it.
So, I have two questions. First, how do I get the VirtualMachinePath correctly. Second, why isn't it putting the configs in the default folder (f:\vmconfigs) if I do not set it with powershell at the command line? If I add it through the interface, it is correct.
Thanks!
EDIT
This is what happens:
Even though the virtual machine path is f:\vmconfigs
My current command:
New-VM -Name $name -MemoryStartupBytes 8192MB -Path $driv\vm -Generation 2 -SwitchName (Get-VMSwitch).Name
I wasn't using -path correctly. I ended up with this:
# ---------------- vhdx File Locations -----------------
# the main virtual machine to be added
$sysprep= "C:\SysPrep\sysprep2019.vhdx"
# the 'd: drive' that is to be added
$sysprep2= "C:\SysPrep\sysprep2019_2Drive.vhdx"
# ---------------- Hardware Settings -----------------
# number of processors to allocate
$numprocs= 6
# startup memory, defaults to 8gb
$startupmem= 8192MB
Write-Output "Creating the new virtual machine $name."
New-VM -Name $name -MemoryStartupBytes $startupmem -Generation 2 -SwitchName (Get-VMSwitch).Name
Write-Output "Adding $sysprep to the new virtual machine."
Add-VMHardDiskDrive -VMName $name -Path $newfile
if($secdrive -match 'y')
{
Write-Output "Adding second drive to guest operating system."
Add-VMHardDiskDrive -VMName $name -Path $newfile2
Write-Output "$sysprep2 has been attached."
}
# set number of processors
Write-Output "Setting the number of processors to $numprocs"
Set-VMProcessor $name -Count $numprocs
Granted, this is only part of my script. So, I create the VM first in Hyper-V then add the drives to it.

Argument -MemoryStartupBytes via variable

Good evening everybody,
I need help with a simple variable in Powershell. I'm trying to auto the creation of VM in Hyper-V trough Powershell.
I'm trying to prompt via Read-Host the amount of RAM GB but I received the same error all time.
$ram = Read-Host -Prompt "Ram to use"
New-VM -Name $vm -MemoryStartupBytes $ram -BootDevice VHD -NewVHDPath C:\HyperV\Virtualmachines\$vm.vhdx -Path C:\HyperV\Virtualmachines -NewVHDSizeBytes $disc1 -Generation 2 -Switch Data
New-VHD -Path C:\HyperV\virtualmachines\"$vm"_2.vhdx -SizeBytes 40GB -Dynamic
This is the error I received.
Two of them, one from the bytes and the other the correct format
New-VHD : Cannot bind parameter 'SizeBytes'. Cannot convert value
"[60GB]" to type "System.UInt64". Error: "Input string was not in a
correct format."
The minimum amount of memory you can assign to this virtual machine is
'32' MB.
I dont understand why is not working with GB if a put myself the amount in the properly powershell work fine but if I write in the variable doesnt work
A user can type in anything in Read-Host..
To remove the fluff, do something like
$ram = Read-Host -Prompt "Ram to use (GB)"
$ram = [uint64]($ram -replace '\D') * 1GB # now you have the value as UInt64

Accessing USB stick without drive letter

I am using powershell 2.0 in windows 7.
I would like to copy a file from my USB stick to a directory on my main hard drive using cmd or powershell. However, I need this to function on any PC without any input of the USB's current drive letter. In case that didn't make sense, let me rephrase it. I need a powershell or cmd command/ batch script to copy a file from my USB stick to my hard drive without any input.
Ideal command would assign the variable mydrive to the drive letter and allow me to run something like this in cmd
copy myvar:/path/fileToCopy.txt/ C:/path/of/target/directory/
I would really appreciate if I could use just my USB sticks name ('DD') to copy like this:
copy DD:/path/fileToCopy.txt/ C:/path/of/target/directory/
I've done well over an hours worth of research trying to find a way to pull this off and can't. Any help is greatly appreciated. Especially if it is clear how to use it. I am very new to powershell and cmd commands and don't understand the syntax. So stuff like [put drive name here] to show me how to use it would be amazing and is where a lot of forums are missing out.
You can do this like below:
$destination = 'C:\path\of\target\directory'
$sourceFile = 'path\fileToCopy.txt' # the path to the file without drive letter
# get (an array of) USB disk drives currently connected to the pc
$wmiQuery1 = 'ASSOCIATORS OF {{Win32_DiskDrive.DeviceID="{0}"}} WHERE AssocClass = Win32_DiskDriveToDiskPartition'
$wmiQuery2 = 'ASSOCIATORS OF {{Win32_DiskPartition.DeviceID="{0}"}} WHERE AssocClass = Win32_LogicalDiskToPartition'
$usb = Get-WmiObject Win32_Diskdrive | Where-Object { $_.InterfaceType -eq 'USB' } |
ForEach-Object {
Get-WmiObject -Query ($wmiQuery1 -f $_.DeviceID.Replace('\','\\')) #'# double-up the backslash(es)
} |
ForEach-Object {
Get-WmiObject -Query ($wmiQuery2 -f $_.DeviceID)
}
# loop through these disk(s) and test if the file to copy is on it
$usb | ForEach-Object {
# join the DeviceID (like 'H:') with the file path you need to copy
$file = Join-Path -Path $_.DeviceID -ChildPath $sourceFile
if (Test-Path -Path $file -PathType Leaf) {
Copy-Item -Path $file -Destination $destination
break # exit the loop because you're done
}
}
Hope that helps
If you upgrade your version of PowerShell, you can replace the Get-WmiObject with Get-CimInstance for better performance. See this and that

Moving VM to a folder within DataCenter

There are two datastores within the vcenter I'm working on. In both datastores, I want to have a folder named simply "Test".
I figured that if I ran Move-VM -VM VmName -Destination Test, the VM would be moved to the "Test" folder found within the datastore it resides on. This is proving not to be the case as I'm getting an error for having multiple values for my Destination.
Is there a way to accomplish this without having to move VMs to another datastore or having the two folders in question named differently?
Thanks for your help
The following line worked for what I wanted to do:
Move-VM -VM $vmObject -Destination (($vmObject | Get-Datacenter) | Get-Folder -Name "Test" )
If there are duplicate folder in Datacenter. You can use below Syntax
$folder = Get-Datacenter -Name "Prod" | Get-Folder -Name "MST" | Get-Folder -Name "Application" | Get-Folder -Name Linux

PowerShell Test-Path returns False when testing a network share

The user has appropriate permissions to read the share, and the share maps properly.
This issue seems to happen only in PowerShell v2.0.
If I remove all mapped drives, remap one drive, and run Test-Path:
net use /delete * /y
net use G: \\somefileserver\share
Test-Path G:\
Test-Path returns False, even though the drive is clearly mapped, and I can access it through Windows Explorer.
If I close the PowerShell session, and open a new session, Test-Path returns True, as it should. Any ideas on, (A): what could be causing this, and how do I make it work properly? or (B): Is there another way to test for the existence of a network drive besides Test-Path? I am attempting to write a logon script to map user's network drives and this behavior is driving me insane.
I do think I found the answer with this:
Test-Path $('filesystem::\\Server\share$\install.wim')
If the Test-Path cmdlet is truly buggy, then I'd suggest using the Exists() method on the System.IO.Directory .NET class.
[System.IO.Directory]::Exists('G:\'); # This returns $true for me
To validate a UNC, use:
[bool]([System.Uri]$path).IsUnc
I had the exact same problem trying two network paths like this:
$verif_X = Test-Path "X:\"
$verif_S = Test-Path "S:\"
if($verif_X -and $verif_S){
Write-Host "X: and S: network paths found"
}
else{
Write-Host "Not all network paths found"
}
... At first, I always got > "Not all network paths found" which seemed really strange to me...
Then I tried to put those paths in simple quotes (') and searched for an existing folder inside those both network paths like this:
$verif_X = Test-Path 'X:\ISOs'
$verif_S = Test-Path 'S:\Scripts'
And then
if($verif_X -and $verif_S){[..]}
condition is accepted..
I think the Test-path cmdlet is a bit buggy as we enter only the first letter of a network path... but like this I do not get errors any more...
I found also this answer which can be found here:
It says that one can use the .NET framework to do so:
$path = "C:\text.txt"
if([System.IO.File]::Exists($path)){
# file with path $path exists
}
Works fine if its a file. To find a folder, use:
$path = x:\
if([System.IO.Directory]::Exists($path)){
# file with path $path exists
}
For me the only way to fix it was to use the full UNC, rather than the share name.
So instead of \\server\share I used \\server\c$\sharedir.
Another issue I ran into was when using Import-Module sqlps, I had to make sure to CD back into the file system and then the PowerShell commands worked normally.
SHORT ANSWER
if(-Not(Test-Path "filesystem::\\$server\c$")) {Write-Error "Server not found: $server"; continue}
If Test-Path fails unexpectedly then make sure SMB2=1 (or other SMB setting) is set on both client and target server.
MORE INFO
IMPORTANT SMB NOTE: Both current system and target system must have at least on common SMB protocol enabled for Test-Path to succeed. (SMB2 or later strongly recommended.) For example, if target has SMB1 enabled + SMB2 disabled and client has only SMB2 enabled then logic above will return "Server not found...". This threw me off track until I finally checked my target server (Win7) and found it had SMB2=0 (disabled) and no entry for SMB1 (enabled by default). I fixed by setting SMB2=1 per article below.
SMB OS-specific and scripting details: https://support.microsoft.com/en-us/help/2696547/detect-enable-disable-smbv1-smbv2-smbv3-in-windows-and-windows-server
Excerpt: Win8/Win20012
Detect: Get-SmbServerConfiguration | Select EnableSMB1Protocol
Disable: Set-SmbServerConfiguration -EnableSMB1Protocol $false
Enable: Set-SmbServerConfiguration -EnableSMB1Protocol $true
Excerpt: Win7/Win2008R2Server
Detect:
Get-Item HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters | ForEach-Object {Get-ItemProperty $_.pspath}
Default configuration = Enabled (No registry key is created), so no SMB1 value will be returned
Disable:
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" SMB1 -Type DWORD -Value 0 –Force
Enable:
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" SMB1 -Type DWORD -Value 1 –Force
Code sample: Copy-Item a folder (recursive) only if target server exists
$scriptRootPath="."
$scriptToolsPath="$scriptRootPath\Tools"
$targetServerList="$scriptToolsPath\DeployServerList-INT-POC.txt" #DeployServerList-INT.txt, DeployServerList-QA.txt, DeployServerList-PROD.txt
$stageTargetDrive="c"
$stageFolderPath="$stageTargetDrive$\staging"
$VerbosePreference="Continue" #"SilentlyContinue" (default), "Continue", "Stop", "Inquire"
$InformationPreference="Continue"
Write-Host "Getting list of servers from $targetServerList to stage deployment files to..."
$serverList = (get-content "$targetServerList")
Write-Verbose "scriptToolsPath=$scriptToolsPath"
Write-Verbose "serverlist=$serverList"
Write-Verbose "stageFolderPath=$StageFolderPath"
Write-Host -Separator "-"
Read-Host -Prompt "READY TO STAGE FILES: Check info above, then press Enter to continue (or Ctrl+C to exit)."
Write-Host "-------------------------------------------------"
Write-Host "Staging files to $stageFolderPath on each target server..."
foreach ($server in $serverlist) {
# Input validation
if([string]::IsNullOrWhiteSpace($server)) {continue}
if($server.StartsWith("#")) {Write-Verbose "Comment skipped: $server"; continue} # Skip line if line begins with hashtag comment char
Write-Verbose "Testing filesystem access to $server..."
if(-Not(Test-Path "filesystem::\\$server\$stageTargetDrive$")) {Write-Error "Server not found: $server"; continue}
# TIP: If Test-Path returns false unexpectedly then check if SMB2 is enabled on target server, check SMB1 disabled for both src and target servers.
Write-Verbose "Staging files to $server..."
Copy-Item ".\" -Destination "\\$server\$stageFolderPath" -Recurse -Force -ErrorAction Continue
Write-Information "Files staged on $server."
}