With PowerShell, is there a way to make an external hard disk attached to host available to VM? - powershell

I have Hyper-V module installed but don't know what cmdlet to use. I've seen blogs showing how to do this using Hyper-V Manager. I have added a VHDx disk image with following cmdlet:
Add-VMHardDiskDrive -VMName MyWin7PC -ControllerType IDE -ControllerNumber 0 `
-ControllerLocation 0 -Path "C:\Virtual Hard Disks\VDisk.MyWin7PC.Vhdx"
What I now need is a way for VM to have another drive E: which will show files and folders the host has on its G: drive (which is physically connected to a USB hard disk). I need this temporarily to install applications from the USB hard disk. You can do this using the Hyper-V Manager GUI.

I don't use Win8, so this is entirely untested. Judging from the description of Add-VMHardDiskDrive something like this might work, though:
$usbdisk = gwmi Win32_DiskDrive | ? { $_.PNPDeviceID -like 'USBSTOR\*' }
Add-VMHardDiskDrive -VMName MyWin7PC -ControllerType IDE -ControllerNumber 0 `
-ControllerLocation 1 -DiskNumber $usbdisk.Index

You have to make this disk Offliine. Try this:
"select disk 1","offline disk" | diskpart
Where 1 is your USB HD id. And then use Add-VMHardDiskDrive. If you want to do this when VM is online you must use SCSI Controller in VM.

Related

PowerCLI - Get VM Disk Partition Type

I'm looking to conduct an audit on our virtual environment to get the disk partition types (MBR, GPT) of our VMs. I haven't found any documentation in PowerCLI to get the partition type. Any ideas how I can go about this? Thanks!
That sort of information is normally not known at the VM object level and instead known at the Guest-OS level. If the VMs you're working with have VMware Tools (or Open VM Tools), you can still use PowerCLI to run scripts against them to pull that information with Invoke-VMScript (docs), but you'll still need to write your own code to pass to the guest OS to pull partition type.
If they're windows systems, you may be able to do something as simple as:
Invoke-VMScript -ScriptText {Get-Partition | select DriveLetter, Type} -VM VMName -GuestCredential $guestCredential
Thanks #Kyle Ruddy!
This was what I did:
$vmName = "VM NAME"
$output = Invoke-VMScript -ScriptText {Get-Disk | select Number, #{name='Size (GB)';expr={[int]($_.Size/1GB)}}, PartitionStyle} -VM $vmName -GuestUser $Username -GuestPassword $Password
$output.ScriptOutput | FT -AutoSize

PowerShell Hyper-V VM creation and boot

I am attempting to use PowerShell to create and start a VM:
$vmName = "vm" + (Get-Date -Format "yyyy-MM-dd-HH-mm")
New-VM -Name $vmName -NewVHDPath "$vmName.vhdx" -NewVHDSizeBytes 64GB -MemoryStartupBytes 8GB -Path $vmName -Generation 2
# Attach the Windows 10 ISO as a DVD drive to the VM
Add-VMDvdDrive -VMName $vmName -Path win.iso
# Set correct boot order (DVD drive first)
$dvd = Get-VMDVDDrive -VMName $vmName
Set-VMFirmware -VMName $vmName -FirstBootDevice $dvd
# Start the VM and connect to it
Start-VM -Name $vmName
vmconnect $env:COMPUTERNAME $vmName
This works well, creates the VM, attaches the Windows 10 ISO, sets the correct boot order, starts the VM and connects to it.
However, right after starting up, the VM fails to boot. First, a black screen prompting me to press any key to boot from the DVD pops up, however, it is only for a brief second and before I manage to do it, I get this screen:
The boot order is correct in the VM settings: DVD first, then network, then the VHD. I want to boot from the DVD, but I don't have a chance to press a key to do so before the white screen appears.
Morever, I want to make the script so that it automatically enters the DVD/ISO boot without me having to press a key to enter that boot option. How can I do that? My ultimate goal is a completely unattended installation which starts by invoking the scripts and ends by the installed Windows 10 guest executing a PowerShell script shared from the host. That means I can't ask the user to press a key at a certain time to help the boot sequence along.
Edit: Bounty of 50 points for someone who can demonstrate a PowerShell script which creates, starts, connects and boots a Hyper-V VM with a Windows 10 ISO so that at the end there is a running VM on the first screen of the Windows 10 installer.
I think your issue is not a powershell issue. Your issue is that you are using a regular boot iso, but in your case you need to create a custom WIM (Windows Imaging File). If you create one you can start directly the installation process without human interaction.
I don't want to duplicate the text written already on superuser.com; it is long and takes many steps. I recommend using the second approach - Creating custom ISO from Windows 10 as it makes sense and takes you step-by-step over the creating custom WIM.
Try this:
# Start the VM and connect to it
vmconnect $env:COMPUTERNAME $vmName
Start-VM -Name $vmName
Start-Sleep -Seconds 1
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
You might have to adjust the sleep time, so a custom wim is probably the better option.

Attaching a locally uploaded VHD to a Classic Azure VM

I have a VHD I uploaded to Azure using the
Set-AzureStorageBlobContent
and attempted using the -
Add-AzureVhd
When I utilize the
Add-AzureDataDisk
in the console, the VHD appears to be attached to LUN 0 on the VM, when I utilize
Get-AzureVM I utilized the appropriate URI for the MediaLocation argument, but when I check the Classic Portal (Web Interface) or go into the VM itself, the VHD is still not attached.
If I do the process manually, the VHD attaches all fine and dandy. Under
VMs->Instances->Disks
I can see the VHD thats been uploaded there if I do the process manually, but utilizing cmdlets, I cannot seem to get the VHD to appear in "existing disks" via VM instances.
Now I have triple checked everything, my storage account is in the same region as my VM instance. My locally uploaded VHD is fixed and labeled correctly in blobs as "someVHD.vhd", when I attempt to use Add-AzureDataDisk, in the console it returns that the disk is attached, the weird behavior is that if I labelled the existing disk during the attach cmdlet arguments, the disk still does not attach utilizing cmdlets.
This is my exact script -
$createVHD = New-VHD -Path $($vhdInstallFullPath) -Fixed -SizeBytes 256MB -ComputerName $hostName
Copy files to VHD and prepare them for upload to azure utilizing either Set-AzureStorageBlobContent or Add-AzureVHD, in this case I utilized Set-AzureStorageBlobContent, because the VHD is really small.
$migrateVHD = Set-AzureStorageBlobContent -File $vhdInstallFullPath -Blob $VHDInstallName -Container $StorageContainerName -Context $($newAzureContext.Context) -BlobType Page -Confirm:$False
$addAzureDataDisk = Add-AzureDataDisk -VM $azureVMInfo -ImportFrom -MediaLocation $azureInstallBlobURI -DiskLabel "InstallPackage" -LUN $azureDataDiskLUN
Now I have a lot of variables and I'm doing a lot of other things to get the storage context, the Azure VM object, and copying files to the vhd before I uplaod, but that script block should give everyone my gist.
Could my issue possibly be between utilizing page blobs over block blobs for the VHD? From documentation I understood that a VHD with multiple files would want to be a page blob.
Maybe you could try to use the following cmdlet.
Get-AzureVM "stlcs01" -Name "shuitest1" | Add-AzureDataDisk -ImportFrom -MediaLocation "https://t5portalvhdsx2463gvmvrz7.blob.core.windows.net/vhds/shui-shui-2017-02-02.vhd" -DiskLabel "InstallPackage" -LUN 1
I find a good article about your problem, maybe you could check this article:Add, Import Data Disk to Azure Virtual Machine using Powershell.
I test in my lab.
Add-AzureVhd -Destination “https://t5portalvhdsx2463gvmvrz7.blob.core.windows.net/vhds/shui.vhd” -LocalFilePath “D:\shui.vhd” -NumberOfUploaderThreads 32
Get-AzureVM -name shuitest -ServiceName shuitest | Add-AzureDataDisk -ImportFrom -MediaLocation “https://t5portalvhdsx2463gvmvrz7.blob.core.windows.net/vhds/shui.vhd” -DiskLabel “test” -LUN 1
Get-AzureVM -ServiceName shuitest -Name shuitest|Get-AzureDataDisk

Net view - get Just ' Share Name'

I need to get all of the shares name in some storage's.
Im using Net view $StorageName and it's show The result in a Table format :
Share name Type Used as Comment
----------------------------------------------------------------------------
Backups Disk
CallRecordings Disk
Download Disk System default share
home Disk Home
homes Disk System default share
Installs Disk
Justin Disk Copy of files from Justin laptop
michael Disk
Multimedia Disk System default share
Network Recycle Bin 1 Disk [RAID5 Disk Volume: Drive 1 2 3 4]
Public Disk System default share
Qsync Disk Qsync
Recordings Disk System default share
Sales Disk Sales Documents
SalesMechanix Disk
Server2012 Disk Windows Server 2012 Install Media
Usb Disk System default share
VMWareTemplates Disk
Web Disk System default share
The command completed successfully.
Thats good, but I need Just the Share Name.
I need Help please.
Thanke You!
Here is one way you could do it with the net view output:
(net view $StorageName | Where-Object { $_ -match '\sDisk\s' }) -replace '\s\s+', ',' | ForEach-Object{ ($_ -split ',')[0] }
Basically that is saying find the lines that have Disk surrounded by whitespace just in case something else might have Disk in the name. Then replace multiple spaces with a comma. Then, for each of those lines, split again by the comma and take the first value which would be the share name.
If you are on a Windows 8/2012 or newer system (and attempting to enumerate shares on other Windows systems), you could use a CIM session along with Get-SmbShare instead of net view which would return the results as objects and allow you to select the fields you want in the native PowerShell way.
For example:
$cimSession = New-CimSession $StorageName
Get-SmbShare -CimSession $cimSession | Select Name
Name
----
Admin
ADMIN$
C$
IPC$
J$
print$
Public
Software
Another option for parsing the net view output that relies on positioning rather than regular expression matching. personally I feel it's a bit easier to read and just as reliable.
function get-shares {
param($server)
$rawShares = net view \\$server
$shares = $rawShares[7..($s.Count - 3)]
$shares | . {process{$_.substring(0,($_.indexof(" ")))}}
}

powershell and diskpart

In short I have a volume that I need to assign a drive letter to (using diskpart). The problem now comes in that the volume does not remain the same. You enter disk part a do a "list volume" and the specific volume would be volume 0, then "exit". Enter again and the do a "list volume" again and this time it is volume 4. And so it continues.
Now if this was done by a person it would not be an issue, however this is an automated task, that will "disconnect" the volume on windows 2003 and used on other servers and mounted again on the windows 2003 server.
I'm trying to write a script in powershell that will be able to identify the volume based on a few unique field(s). The problem comes in that I'm getting stuck on interpreting the output of diskpart's "list volume" command with powershell.
The following command provides the output that I need to work with but there after I'm lost.
cls
$dp = "list volume" | diskpart | ? { $_ -match "^ [^-]" }
$dp | format-table -auto
and this is the output it provides and the volume that I'm looking for is Volume 1.
Volume ### Ltr Label Fs Type Size Status Info
Volume 0 F DVD-ROM 0 B Healthy
*Volume 1 Partition 100 GB Healthy*
Volume 2 E DATA NTFS Partition 547 GB Healthy
Volume 3 C OS NTFS Partition 39 GB Healthy System
Volume 4 D APPS NTFS Partition 98 GB Healthy
Can anybody help me in the right direction here please. I'm at my tether's end.
Yes I got it!!
Here is the answer. Using VB Script I managed to create a script that did what I was looking for, this I then translated to Powershell and below is the script.
$drive = gwmi Win32_Volume | where {$_.DeviceID -like "*b0f012f6-82b1-11df-a41c-001f29e8f0be*"}
$drive.AddMountPoint("T:\")
$drive.DriveLetter = "T:"
$drive.Put_
$drive.Mount()
The Device ID I obtained by running the following script:
# get volumes on this system
$volumes = get-wmiobject Win32_Volume
# display volume info
# There are {0} volumes on this system, as follows: " -f ($volumes.length)
# Iterate through volumes and display information
foreach ($vol in $volumes) {
"Volume: {0}" -f ++$i
"============================="
$vol | Format-List Access,Automount,Availability,BlockSize,BootVolume,Capacity,Caption,Compressed,ConfigManagerErrorCode,ConfigManagerUserConfig,CreationClassName,Description,DeviceID,DirtyBitSet,DriveLetter,DriveType,ErrorCleared,ErrorDescription,ErrorMethodology,FileSystem,FreeSpace,IndexingEnabled,InstallDate,Label,LastErrorCode,MaximumFileNameLength,Name,NumberOfBlocks,PageFilePresent,PNPDeviceID,PowerManagementCapabilities,PowerManagementSupported,Purpose,QuotasEnabled,QuotasIncomplete,QuotasRebuilding,SerialNumber,Status,StatusInfo,SupportsDiskQuotas,SupportsFileBasedCompression,SystemCreationClassName,SystemName,SystemVolume
}
from a post on msdn on the class Win32_Volume.
I hope this might help somebody else Thank you to everybody that help!
You can just use Powershell and WMI to set the drive letter. Shoudn't need diskpart unless you are doing something else (I'm unfamiliar with that tool)
So (assuming you are trying to set the drive letter of the one volume that doesn't have a letter) this should work:
$drive = gwmi Win32_Volume | where {$_.DriveLetter -eq ""}
$drive.DriveLetter = "X:"
$drive.Put()
If you aren't sure about the drive, just query it first and make sure you are only getting the one you want:
gwmi Win32_Volume | where {$_.DriveLetter -eq ""}
Yep. This is a "feature" of diskpart.
Suggestions from MS (not very useful in your case)
Keep the Disk Management console
(Diskmgmt.msc) running while you
process scripts. Or, keep an instance
of the Diskpart.exe utility running
in the background while you process
scripts. When you do this, the volume
numbers should not change between
instances of the Diskpart.exe
utility. Use the volume Label
information instead of the volume
number to track particular volumes.
See bug report here.