diff --git a/lib/vagrant/util/platform.rb b/lib/vagrant/util/platform.rb index c8658e18502..b3a3a9cc0ca 100644 --- a/lib/vagrant/util/platform.rb +++ b/lib/vagrant/util/platform.rb @@ -71,14 +71,14 @@ def windows? # privileges. # # From: https://support.microsoft.com/en-us/kb/243330 - # SID: S-1-5-19 + # SID: S-1-5-32-544 + # Name: Administrators # # @return [Boolean] def windows_admin? return @_windows_admin if defined?(@_windows_admin) - @_windows_admin = -> { - ps_cmd = '(new-object System.Security.Principal.WindowsPrincipal([System.Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)' + ps_cmd = '$x = (Get-LocalGroupMember -SID "S-1-5-32-544" | where Name -eq $(Get-WMIObject -class Win32_ComputerSystem | select username).username); if ($x){ Write-Output "True"}' output = Vagrant::Util::PowerShell.execute_cmd(ps_cmd) return output == 'True' }.call @@ -105,6 +105,13 @@ def windows_hyperv_admin? return @_windows_hyperv_admin = true end + ps_cmd = "$x = (Get-LocalGroupMember -SID 'S-1-5-32-578' | where Name -eq $(Get-WMIObject -class Win32_ComputerSystem | select username).username); if ($x){ Write-Output 'true'}" + output = Vagrant::Util::PowerShell.execute_cmd(ps_cmd) + return @_windows_hyperv_admin = true if output == "true" + + # This won't work in constrained language mode, but there is no easy way to replicate. + # Leaving it as is because the most likley two cases (user is in administrators or hyperv administrators group) + # are already checked, so this script is unlikley to be required. ps_cmd = "Write-Output ([System.Security.Principal.WindowsIdentity]::GetCurrent().Groups | " \ "Select-Object Value | ConvertTo-JSON)" output = Vagrant::Util::PowerShell.execute_cmd(ps_cmd) @@ -115,8 +122,7 @@ def windows_hyperv_admin? [] end admin_group = groups.detect do |g| - g["Value"].to_s == "S-1-5-32-578" || - (g["Value"].start_with?("S-1-5-21") && g["Value"].to_s.end_with?("-512")) + g["Value"].start_with?("S-1-5-21") && g["Value"].to_s.end_with?("-512") end if admin_group @@ -124,7 +130,7 @@ def windows_hyperv_admin? end end - ps_cmd = "$x = (Get-VMHost).Name; if($x -eq [System.Net.Dns]::GetHostName()){ Write-Output 'true'}" + ps_cmd = "if ((Get-VMHost).Name.StartsWith($env:computername)){ Write-Output 'true'}" output = Vagrant::Util::PowerShell.execute_cmd(ps_cmd) result = output == "true" diff --git a/plugins/providers/hyperv/scripts/get_vm_status.ps1 b/plugins/providers/hyperv/scripts/get_vm_status.ps1 index 5c7630d6436..ab92267fea0 100644 --- a/plugins/providers/hyperv/scripts/get_vm_status.ps1 +++ b/plugins/providers/hyperv/scripts/get_vm_status.ps1 @@ -5,32 +5,14 @@ param( [string]$VmId ) -# Make sure the exception type is loaded -try -{ - # Microsoft.HyperV.PowerShell is present on all versions of Windows with HyperV - [void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.HyperV.PowerShell, Culture=neutral, PublicKeyToken=31bf3856ad364e35') - # Microsoft.HyperV.PowerShell.Objects is only present on Windows >= 10.0, so this will fail, and we ignore it since the needed exception - # type was loaded in Microsoft.HyperV.PowerShell - [void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.HyperV.PowerShell.Objects, Culture=neutral, PublicKeyToken=31bf3856ad364e35') -} catch { - # Empty catch ok, since if we didn't load the types, we will fail in the next block -} - -$VmmsPath = if ([environment]::Is64BitProcess) { "$($env:SystemRoot)\System32\vmms.exe" } else { "$($env:SystemRoot)\Sysnative\vmms.exe" } -$HyperVVersion = [version](Get-Item $VmmsPath).VersionInfo.ProductVersion - -if($HyperVVersion -lt ([version]'10.0')) { - $ExceptionType = [Microsoft.HyperV.PowerShell.VirtualizationOperationFailedException] -} else { - $ExceptionType = [Microsoft.HyperV.PowerShell.VirtualizationException] -} try { $VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop" $State = $VM.state $Status = $VM.status } catch [Exception] { - if($_.Exception.GetType() -eq $ExceptionType) + # "ObjectNotFound" when Hyper-V >= 10 (Microsoft.HyperV.PowerShell.VirtualizationException) + # "NotSpecified" when Hyper-V < 10 (Microsoft.HyperV.PowerShell.VirtualizationOperationFailedException) + if(("ObjectNotFound", "NotSpecified") -Contains $_.Exception.ErrorCategory) { $State = "not_created" $Status = $State diff --git a/plugins/providers/hyperv/scripts/utils/VagrantVM/VagrantVM.psm1 b/plugins/providers/hyperv/scripts/utils/VagrantVM/VagrantVM.psm1 index e2333f3f7d9..62c7334ca1f 100644 --- a/plugins/providers/hyperv/scripts/utils/VagrantVM/VagrantVM.psm1 +++ b/plugins/providers/hyperv/scripts/utils/VagrantVM/VagrantVM.psm1 @@ -1,6 +1,5 @@ # Always stop when errors are encountered unless instructed not to $ErrorActionPreference = "Stop" - # Vagrant VM creation functions function New-VagrantVM { @@ -18,7 +17,7 @@ function New-VagrantVM { [parameter(Mandatory=$false)] [string] $VMName ) - if([IO.Path]::GetExtension($VMConfigFile).ToLower() -eq ".xml") { + if($VMConfigFile.EndsWith(".xml", "CurrentCultureIgnoreCase")) { return New-VagrantVMXML @PSBoundParameters } else { return New-VagrantVMVMCX @PSBoundParameters @@ -85,6 +84,7 @@ function New-VagrantVMVMCX { VhdDestinationPath = Join-Path $DataPath "Virtual Hard Disks"; VirtualMachinePath = $DataPath; } + $VMConfig = (Hyper-V\Compare-VM -Copy -GenerateNewID @NewVMConfig -ErrorAction SilentlyContinue) # If the config is empty it means the import failed. Attempt to provide @@ -122,7 +122,7 @@ function New-VagrantVMVMCX { } foreach($Controller in $Controllers) { foreach($Drive in $Controller.Drives) { - if([System.IO.Path]::GetFileName($Drive.Path) -eq [System.IO.Path]::GetFileName($SourcePath)) { + if((Split-Path -Leaf $Drive.Path) -eq (Split-Path -Leaf $SourcePath)) { $Path = $Drive.Path Hyper-V\Remove-VMHardDiskDrive $Drive Hyper-V\New-VHD -Path $DestinationPath -ParentPath $SourcePath -Differencing @@ -729,8 +729,10 @@ function Check-VagrantHyperVAccess { [string] $Path ) $acl = Get-ACL -Path $Path + $systemAccount = [wmi]"Win32_SID.SID='S-1-5-18'" + $localisedAccount = $systemAccount.ReferencedDomainName + "\" + $systemAccount.AccountName $systemACL = $acl.Access | where { - try { return $_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value -eq "S-1-5-18" } catch { return $false } -and + $_.IdentityReference.Value -eq $localisedAccount -and $_.FileSystemRights -eq "FullControl" -and $_.AccessControlType -eq "Allow" -and $_.IsInherited -eq $true} diff --git a/test/unit/vagrant/util/platform_test.rb b/test/unit/vagrant/util/platform_test.rb index e0dc29c84c2..6bf07b7b20a 100644 --- a/test/unit/vagrant/util/platform_test.rb +++ b/test/unit/vagrant/util/platform_test.rb @@ -230,20 +230,22 @@ context "when user is in the Hyper-V administators group" do it "should return true" do - expect(Vagrant::Util::PowerShell).to receive(:execute_cmd).and_return(["Value" => "S-1-5-32-578"].to_json) + expect(Vagrant::Util::PowerShell).to receive(:execute_cmd).with(/S-1-5-32-578/).and_return("true") expect(Vagrant::Util::Platform.windows_hyperv_admin?).to be_truthy end end context "when user is in the Domain Admins group" do it "should return true" do - expect(Vagrant::Util::PowerShell).to receive(:execute_cmd).and_return(["Value" => "S-1-5-21-000-000-000-512"].to_json) + expect(Vagrant::Util::PowerShell).to receive(:execute_cmd).with(/S-1-5-32-578/).and_return(nil) + expect(Vagrant::Util::PowerShell).to receive(:execute_cmd).with(/GetCurrent/).and_return(["Value" => "S-1-5-21-000-000-000-512"].to_json) expect(Vagrant::Util::Platform.windows_hyperv_admin?).to be_truthy end end context "when user has access to Hyper-V" do it "should return true" do + expect(Vagrant::Util::PowerShell).to receive(:execute_cmd).with(/S-1-5-32-578/).and_return(nil) expect(Vagrant::Util::PowerShell).to receive(:execute_cmd).with(/GetCurrent/).and_return(nil) expect(Vagrant::Util::PowerShell).to receive(:execute_cmd).with(/Get-VMHost/).and_return("true") expect(Vagrant::Util::Platform.windows_hyperv_admin?).to be_truthy