Skip to content

Commit

Permalink
Fix issue when VHD file mounted #209
Browse files Browse the repository at this point in the history
  • Loading branch information
nyanhp committed Apr 18, 2023
1 parent 6c0e583 commit cbf9bd6
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 16 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ For older change log history see the [historic changelog](HISTORIC_CHANGELOG.md)
- BREAKING CHANGE: Rename embedded instance class #203
- Fix multiple DNS IP adresses does not work #190
- NetworkSetting parameter is now optional and no default actions are taken if not specified
- VhdFile
- Fix issue with Get- and Test-DscConfiguration if disk is mounted #207.

## [3.18.0] - 2022-06-04

Expand Down
8 changes: 4 additions & 4 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ stages:
- job: Package_Module
displayName: 'Package Module'
pool:
vmImage: 'ubuntu-latest'
vmImage: 'windows-latest'
steps:
- pwsh: |
dotnet tool install --global GitVersion.Tool
Expand Down Expand Up @@ -154,7 +154,7 @@ stages:
displayName: 'Publish Code Coverage'
dependsOn: Test_Unit
pool:
vmImage: 'ubuntu-latest'
vmImage: 'windows-latest'
timeoutInMinutes: 0
steps:
- task: DownloadPipelineArtifact@2
Expand All @@ -177,7 +177,7 @@ stages:
summaryFileLocation: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml'
pathToSources: '$(Build.SourcesDirectory)/$(sourceFolderName)/'
- script: |
bash <(curl -s https://codecov.io/bash) -f "./$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml"
bash <(curl -s https://codecov.io/bash) -f "$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml"
displayName: 'Publish Code Coverage to Codecov.io'
condition: succeededOrFailed()
Expand All @@ -196,7 +196,7 @@ stages:
- job: Deploy_Module
displayName: 'Deploy Module'
pool:
vmImage: 'ubuntu-latest'
vmImage: 'windows-latest'
steps:
- task: DownloadPipelineArtifact@2
displayName: 'Download Pipeline Artifact'
Expand Down
115 changes: 103 additions & 12 deletions source/DSCResources/DSC_VhdFileDirectory/DSC_VhdFileDirectory.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ function Get-TargetResource
# Mount VHD.
$mountVHD = EnsureVHDState -Mounted -vhdPath $vhdPath

if ($null -eq $mountVHD)
{
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.NoDiskMounted -f $vhdPath)
))
return @{
VhdPath = $VhdPath
FileDirectory = @()
}
}

$itemsFound = foreach ($Item in $FileDirectory)
{
$item = GetItemToCopy -item $item
Expand All @@ -64,7 +76,10 @@ function Get-TargetResource

$finalPath = Join-Path $letterDrive $item.DestinationPath

Write-Verbose "Getting the current value at $finalPath ..."
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.GetCurrentValue -f $finalPath)
))

if (Test-Path $finalPath)
{
Expand Down Expand Up @@ -207,14 +222,26 @@ function Test-TargetResource
# mount the vhd.
$mountedVHD = EnsureVHDState -Mounted -vhdPath $VhdPath

if ($mountedVHD.Attached -and $null -eq $mountedVHD.DiskNumber)
{
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.SkippingAttachedDisk -f $VhdPath)
))
return $true
}

try
{
# Show the drive letters after mount
Get-PSDrive | Write-Verbose

$mountedDrive = $mountedVHD | Get-Disk | Get-Partition | Where-Object -FilterScript { $_.Type -ne 'Recovery' } | Get-Volume
$letterDrive = (-join $mountedDrive.DriveLetter) + ':\'
Write-Verbose $letterDrive
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.GetDriveLetter -f $letterDrive)
))

# return test result equal to true unless one of the tests in the loop below fails.
$result = $true
Expand All @@ -223,7 +250,10 @@ function Test-TargetResource
{
$itemToCopy = GetItemToCopy -item $item
$destination = $itemToCopy.DestinationPath
Write-Verbose ("Testing the file with relative VHD destination $destination")
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.TestRelativePath -f $letterDrive)
))
$destination = $itemToCopy.DestinationPath
$finalDestinationPath = $letterDrive
$finalDestinationPath = Join-Path $letterDrive $destination
Expand All @@ -244,11 +274,17 @@ function Test-TargetResource
{
# Verify if the file exist inside the folder
$fileName = Split-Path $itemToCopy.SourcePath -Leaf
Write-Verbose "Checking if $fileName exist under $finalDestinationPath"
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.TestFileExists -f $fileName,$finalDestinationPath)
))
$fileExistInDestination = Test-Path (Join-Path $finalDestinationPath $fileName)

# Report if the file exist on the destination folder.
Write-Verbose "File exist on the destination under $finalDestinationPath :- $fileExistInDestination"
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.TestFileExistsInDestination -f $finalDestinationPath,$fileExistInDestination)
))
$result = $fileExistInDestination
$result = $result -and -not(ItemHasChanged -sourcePath $itemToCopy.SourcePath -destinationPath (Join-Path $finalDestinationPath $fileName) -CheckSum $CheckSum)
}
Expand Down Expand Up @@ -287,8 +323,10 @@ function Test-TargetResource
EnsureVHDState -Dismounted -vhdPath $VhdPath
}


Write-Verbose "Test returned $result"
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.TestResult -f $result)
))
return $result
}

Expand All @@ -308,20 +346,64 @@ function EnsureVHDState
$vhdPath
)

Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.EnsureEntry -f $vhdPath, $PSCmdlet.ParameterSetName)
))

if (-not (Get-Module -ListAvailable 'Hyper-V'))
{
throw 'Hyper-v-Powershell Windows Feature is required to run this resource. Please install Hyper-v feature and try again'
}

$vhd = Get-VHD -Path $vhdPath
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.DiskDetails -f $vhdPath, $vhd.Attached, $vhd.DiskNumber)
))
if ($PSCmdlet.ParameterSetName -eq 'Mounted' -and $vhd.Attached -and $null -eq $vhd.DiskNumber)
{
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.UnableToMount -f $vhdPath)
))
return $vhd
}

if ($PSCmdlet.ParameterSetName -eq 'Mounted' -and $vhd.DiskNumber -ge 0)
{
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.AlreadyMounted -f $vhdPath)
))
return $vhd
}

if ($PSCmdlet.ParameterSetName -eq 'Dismounted' -and $null -eq $vhd.DiskNumber)
{
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.AlreadyDismounted -f $vhdPath)
))
return
}

if ($PSCmdlet.ParameterSetName -eq 'Mounted')
{
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.Mounting -f $vhdPath)
))
# Try mounting the VHD.
$mountedVHD = Mount-VHD -Path $vhdPath -Passthru -ErrorAction SilentlyContinue -ErrorVariable var

# If mounting the VHD failed. Dismount the VHD and mount it again.
if ($var)
{
Write-Verbose 'Mounting Failed. Attempting to dismount and mount it back'
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.FailedToMount -f $vhdPath)
))
Dismount-VHD $vhdPath
$mountedVHD = Mount-VHD -Path $vhdPath -Passthru -ErrorAction SilentlyContinue

Expand All @@ -334,8 +416,11 @@ function EnsureVHDState
}
else
{
Dismount-VHD $vhdPath -ea SilentlyContinue

Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.Dismounting -f $vhdPath)
))
Dismount-VHD -Path $vhdPath -ErrorAction SilentlyContinue
}
}

Expand Down Expand Up @@ -447,7 +532,10 @@ function SetVHDFile
$ensure
)

Write-Verbose "Setting the VHD file $($PSCmdlet.ParameterSetName)"
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.SetVhdFileContent -f $PSCmdlet.ParameterSetName))
)
if ($PSCmdlet.ParameterSetName -eq 'Copy')
{
New-Item -Path (Split-Path $destinationPath) -ItemType Directory -ErrorAction SilentlyContinue
Expand All @@ -468,7 +556,10 @@ function SetVHDFile
}
elseif ($PSCmdlet.ParameterSetName -eq 'Set')
{
Write-Verbose "Attempting to change the attribute of the file $destinationPath to value $attribute"
Write-Verbose -Message ( -join @(
"$($MyInvocation.MyCommand): "
$($script:localizedData.SetFileAttributes -f $destinationPath, $attribute))
)
Set-ItemProperty -Path $destinationPath -Name Attributes -Value $attribute
}
elseif (!($ensure))
Expand Down
3 changes: 3 additions & 0 deletions source/DSCResources/DSC_VhdFileDirectory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ file attribute to 'ReadOnly' or 'Hidden').
This resource is particularly useful when bootstrapping DSC Configurations
into a VM.

If the VHD is going to be attached to a VM, the resource will silently
skip mounting and dismounting the VHD and thus not copy any content.

## Requirements

* The Hyper-V Role has to be installed on the machine.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,20 @@
ConvertFrom-StringData @'
GetCurrentValue = Getting the current value at {0}...
GetDriveLetter = Drive letter of mounted drive is {0}.
NoDiskMounted = {0} was not mounted. Ensure it is not attached to a running VM.
TestRelativePath = Testing the file with relative VHD destination {0}.
TestFileExists = Checking if {0} exist under {1}.
TestFileExistsInDestination = File exist on the destination under {0} :- {1}.
TestResult = Test returned {0}.
EnsureEntry = Ensuring that {0} is {1}.
DiskDetails = Disk {0}, attached: {1}, disk number {2}.
SkippingAttachedDisk = Disk {0} is attached to a running VM. Skipping file operations as they would fail.
UnableToMount = Unable to mount {0} when it is attached to a running VM.
Mounting = Attempting to mount {0}.
Dismounting = Attempting to dismount {0}.
AlreadyMounted = {0} is already mounted.
AlreadyDismounted = {0} is already dismounted.
FailedToMount = Mounting {0} failed. Attempting to dismount and mount it back.
SetVhdFileContent = Setting the VHD file {0}.
SetFileAttributes = Attempting to change the attribute of the file {0} to value {1}.
'@
80 changes: 80 additions & 0 deletions tests/Unit/DSC_VhdFileDirectory.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ Mock -CommandName Mount-Vhd { [PSCustomObject] @{ Path = 'TestDrive:\VhdExists.v

Mock -CommandName Dismount-Vhd { }

Mock -CommandName Get-Vhd { [PSCustomObject] @{
Path = 'TestDrive:\VhdExists.vhdx'
DiskNumber = 1
}}

Mock -CommandName Get-Disk {
New-CimInstance -ClassName MSFT_Disk -Namespace root/Microsoft/Windows/Storage -ClientOnly
}
Expand Down Expand Up @@ -253,6 +258,40 @@ Describe 'DSC_VhdFileDirectory\Get-TargetResource' -Tag 'Get' {
}
}
)

# Mount: Get-Vhd -> Skip mount and Get-Vhd -> Mount
$testCases_MountDismount = @(
@{
TestName = 'Disk is attached to running VM'
VhdPath = 'TestDrive:\VhdExists.vhdx'
Type = 'Mount'
DiskNumber = $null
Attached = $true
GetVhdCount = 1
MountVhdCount = 0
DismountVhdCount = 0
}
@{
TestName = 'Disk is mounted'
VhdPath = 'TestDrive:\VhdExists.vhdx'
Type = 'Mount'
DiskNumber = 1
Attached = $false
GetVhdCount = 1
MountVhdCount = 0
DismountVhdCount = 0
}
@{
TestName = 'Disk is already dismounted'
VhdPath = 'TestDrive:\VhdExists.vhdx'
Type = 'Dismount'
DiskNumber = $null
Attached = $false
GetVhdCount = 1
MountVhdCount = 0
DismountVhdCount = 0
}
)
#endRegion

It 'Should correctly return state when: <TestName>' -TestCases $testCases_Get {
Expand All @@ -270,6 +309,47 @@ Describe 'DSC_VhdFileDirectory\Get-TargetResource' -Tag 'Get' {
$result['VhdPath'] |
Should -Be $ExpectedResult['VhdPath']
}

It 'Should mount and/or dismount disk: <TestName>' -TestCases $testCases_MountDismount {
param(
$VhdPath,
[nullable[int]]$DiskNumber,
$Attached,
$Type,
$GetVhdCount,
$MountVhdCount,
$DismountVhdCount
)

[pscustomobject] $mockedobject = if ($null -eq $DiskNumber)
{
@{
Path = $VhdPath
Attached = $Attached
}
}
else
{
@{
Path = $VhdPath
DiskNumber = $DiskNumber
Attached = $Attached
}
}
Mock -CommandName Get-Vhd { $mockedobject }

$null = if ($Type -eq 'Mount')
{
EnsureVHDState -VhdPath $VhdPath -Mounted
}
else
{
EnsureVHDState -VhdPath $VhdPath -Dismounted
}
Assert-MockCalled -CommandName Get-VHD -Exactly -Times $GetVhdCount -Scope It
Assert-MockCalled -CommandName Mount-VHD -Exactly -Times $MountVhdCount -Scope It
Assert-MockCalled -CommandName Dismount-VHD -Exactly -Times $DismountVhdCount -Scope It
}
}

Describe "DSC_VhdFileDirectory\Test-TargetResource" -Tag 'Test' {
Expand Down

0 comments on commit cbf9bd6

Please sign in to comment.