#################################################################### # Recover-VM.ps1 # # Syntax: Recover-VM.ps1 "" # (Use quotes in the argument) # # Example: Recover-VM.ps1 "v:\VirtualMachine1\Virtual Machines\7660AA46-BA41-4171-8820-CDD7C71050A0.xml" # # Purpose: Creates Symbolic Links and sets the required permissions # to make a Hyper-V Virtual Machine available in Hyper-V # Management Console. This script is intended for use in # recovery scenario where a Virtual Guests files have been # manually moved to another Virtual Server, but the volume # drive mappings are the same, only. # The methodology that this script employs is as discussed # in the below blog. # It should be noted that once this script has been used # the Virtual Guest is in an unsupported state and should # be exported and re-imported in Hyper-V Manager to # correctly set all the Virtual Machines permissions. # # Params: The drive, folder and filename of the Virtual Machine # xml configuration file. # # Req: Windows 2008 or above, Powershell V2, mklink, icacls. # run "set-executionpolicy remotesigned" in Powershell # # Ref: http://blogs.msdn.com/b/robertvi/archive/2008/12/19/howto-manually-add-a-vm-configuration-to-hyper-v.aspx # http://blogs.technet.com/b/carlh # # Author: CarlH # # Myself or my employer do not warantee this script in any way and use # of it is entirely at the users own risk. The script is intended to # Recover Virtual Machines only in a recovery scenario, and such VM's # should be exported and imported onto the Hyper-V server to ensure # a supported state. # # Version: 1.0 - First cut # #################################################################### Param($VMConfigFile) Write-Host "" Write-Host "---------------Recover-VM Script Start" $SystemDrive = (Get-item env:systemdrive).Value $VMFolder = (Get-Item $VMConfigFile).directoryname $VMFolder = (Get-Item $VMFolder).parent $VMFolderName = $VMFolder.fullname Write-Host "" #Read from the VM Configuration file. VM GUID for Service account. VM Name. Write-Host "---------------Reading VM Configuration File" [xml]$VMXML = Get-Content $VMConfigFile $VMSvcAccount = $VMXML.configuration.properties.global_id."#text" $VMName = $VMXML.configuration.properties.name."#text" Write-Host "---------------Completed reading VM Configuration File" Write-Host "" #This function takes a file name including the full path as a ThisItem string parameter and gets the current ACL. #It adds the VM Service account to the current ACL and then Sets the new ACL function SetACL { Param($ThisItem) Write-Host "" Write-Host "---------------Setting Permissions for $ThisItem" Write-Host "" $Acl = Get-Acl $ThisItem $AccessRule = New-Object system.security.accesscontrol.filesystemaccessrule("NT VIRTUAL MACHINE\$VMSvcAccount","FullControl","Allow") $Acl.SetAccessRule($AccessRule) Set-Acl $ThisItem $Acl Write-Host "" Write-Host "---------------Finished Permissions for $ThisItem" } #This Function uses the cmd line utility mklink to create a symbolic link of the physical file passed to it in the HardFile parameter as a string #The LinkLocation string parameter defines what folder the link will be created in under systemdrive\programdata\Microsoft\Windows\Hyper-V\ function MkSymLink { Param($HardFile, $LinkLocation) Write-Host "" Write-Host "---------------Creating Symbolic Link for $HardFile" Write-Host "" $LinkFile = (get-item $HardFile).name #Write-Host "%systemdrive%\programdata\Microsoft\Windows\Hyper-V\$LinkLocation\$LinkFile" $HardFile cmd /c mklink "%systemdrive%\programdata\Microsoft\Windows\Hyper-V\$LinkLocation\$LinkFile" $HardFile Write-Host "" Write-Host "---------------Finished Symbolic Link for $HardFile" } #Get the VM config xml Fully Qualified Name and pass it to the MKSymLink function with the location for the for storing the Symbolic Link $VMFileString = (get-item $VMConfigFile).name MkSymLink -HardFile $VMConfigFile -LinkLocation "Virtual Machines" #Call the SetACL function and pass it the FQN of the Symbolic Link to which we need to set the ACL $ConfigFileSymLink = "$SystemDrive\programdata\Microsoft\Windows\Hyper-V\Virtual Machines\$VMFileString" SetACL -ThisItem $ConfigFileSymLink #Get the FQN of the boot VHD from the VM Config xml file passed into the script. Then pass this to the SetACL function $VMVHDBootFile = $VMXML.configuration.'_83f8638b-8dca-4152-9eda-2ca8b33039b4_'.controller0.drive0.pathname."#text" SetACL -ThisItem $VMVHDBootFile #Build a string that defines the VM Service Account and use it with the cmd line utility icacls to set the permissions for all folders and files of the VM Write-Host "" Write-Host "---------------Setting Permissions for Folder $VMFolderName" Write-Host "" $VMSvcAccountFQN = 'NT VIRTUAL MACHINE\'+$VMSvcAccount cmd /c icacls ""$VMFolderName"" /T /grant ""$VMSvcAccountFQN""':(F)' Write-Host "" Write-Host "---------------Finished Permissions for Folder $VMFolderName" #Discover if there are any Snapshots. If so, for each one, create a Symbolic link for each by passing the FQN of the snapshot and snapsot link location #Also, call SetACL to set permissions for the VM Services account on the Symbolic Link foreach ($Thing in (Get-ChildItem $VMFolderName)) { If ($Thing.name -eq "Snapshots") { $SnapshotsFolder = $Thing.Fullname foreach ($Thing2 in (Get-ChildItem $SnapshotsFolder)) { If ($Thing2.extension -eq ".xml") { $VMSnapshotFileString = $Thing2.name MkSymLink -HardFile $Thing2.fullname -LinkLocation "Snapshots" $SnapshotFileSymLink = "$SystemDrive\programdata\Microsoft\Windows\Hyper-V\Snapshots\$VMSnapshotFileString" SetACL -ThisItem $SnapshotFileSymLink } } } } Write-Host "" Write-Host "----------------End Script Recover-VM.ps1"