diff --git a/CHANGELOG.md b/CHANGELOG.md index 06a4028c..2ff237c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Azure Pipelines - Fixes [Issue #255](https://github.com/dsccommunity/StorageDsc/issues/255). - Updated build to use `Sampler.GitHubTasks` - Fixes [Issue #254](https://github.com/dsccommunity/StorageDsc/issues/254). - Updated pipeline tasks to latest pattern. +- Added serial number as disk identifier to Disk, DiskAccessPath and WaitForDisk resources - Fixes [Issue #259](https://github.com/dsccommunity/StorageDsc/issues/259). ### Fixed diff --git a/source/DSCResources/DSC_Disk/DSC_Disk.psm1 b/source/DSCResources/DSC_Disk/DSC_Disk.psm1 index fc1ec50e..c87838cd 100644 --- a/source/DSCResources/DSC_Disk/DSC_Disk.psm1 +++ b/source/DSCResources/DSC_Disk/DSC_Disk.psm1 @@ -67,7 +67,7 @@ function Get-TargetResource $DiskId, [Parameter()] - [ValidateSet('Number', 'UniqueId', 'Guid', 'Location')] + [ValidateSet('Number', 'UniqueId', 'Guid', 'Location', 'SerialNumber')] [System.String] $DiskIdType = 'Number', @@ -190,7 +190,7 @@ function Set-TargetResource $DiskId, [Parameter()] - [ValidateSet('Number', 'UniqueId', 'Guid', 'Location')] + [ValidateSet('Number', 'UniqueId', 'Guid', 'Location', 'SerialNumber')] [System.String] $DiskIdType = 'Number', @@ -687,7 +687,7 @@ function Test-TargetResource $DiskId, [Parameter()] - [ValidateSet('Number', 'UniqueId', 'Guid', 'Location')] + [ValidateSet('Number', 'UniqueId', 'Guid', 'Location', 'SerialNumber')] [System.String] $DiskIdType = 'Number', diff --git a/source/DSCResources/DSC_Disk/DSC_Disk.schema.mof b/source/DSCResources/DSC_Disk/DSC_Disk.schema.mof index 39bc8603..725ed1df 100644 --- a/source/DSCResources/DSC_Disk/DSC_Disk.schema.mof +++ b/source/DSCResources/DSC_Disk/DSC_Disk.schema.mof @@ -4,7 +4,7 @@ class DSC_Disk : OMI_BaseResource { [Key, Description("Specifies the identifier for which disk to modify.")] String DriveLetter; [Required, Description("Specifies the disk identifier for the disk to modify.")] String DiskId; - [Write, Description("Specifies the identifier type the DiskId contains. Defaults to Number."), ValueMap{"Number","UniqueId","Guid","Location"}, Values{"Number","UniqueId","Guid","Location"}] String DiskIdType; + [Write, Description("Specifies the identifier type the DiskId contains. Defaults to Number."), ValueMap{"Number","UniqueId","Guid","Location","SerialNumber"}, Values{"Number","UniqueId","Guid","Location","SerialNumber"}] String DiskIdType; [Write, Description("Specifies the partition style of the disk. Defaults to GPT."), ValueMap{"MBR","GPT"}, Values{"MBR","GPT"}] String PartitionStyle; [Write, Description("Specifies the size of new volume. Leave empty to use the remaining free space.")] Uint64 Size; [Write, Description("Define volume label if required.")] String FSLabel; diff --git a/source/DSCResources/DSC_Disk/README.md b/source/DSCResources/DSC_Disk/README.md index 882fc263..aa78495f 100644 --- a/source/DSCResources/DSC_Disk/README.md +++ b/source/DSCResources/DSC_Disk/README.md @@ -4,17 +4,17 @@ The resource is used to initialize, format and mount the partition/volume as a d letter. The disk to add the partition/volume to is selected by specifying the _DiskId_ and optionally _DiskIdType_. -The _DiskId_ value can be a _Disk Number_, _Unique Id_, _Guid_ or _Location_. +The _DiskId_ value can be a _Disk Number_, _Serial Number_, _Unique Id_, _Guid_ or _Location_. **Important: The _Disk Number_ is not a reliable method of selecting a disk because it has been shown to change between reboots in some environments. It is recommended to use the _Unique Id_ if possible.** -The _Disk Number_, _Unique Id_, _Guid_ and _Location_ can be identified for a +The _Disk Number_, _Serial Number_, _Unique Id_, _Guid_ and _Location_ can be identified for a disk by using the PowerShell command: ```powershell -Get-Disk | Select-Object -Property FriendlyName,DiskNumber,UniqueId,Guid,Location +Get-Disk | Select-Object -Property FriendlyName,DiskNumber,SerialNumber,UniqueId,Guid,Location ``` Note: The _Guid_ identifier method of specifying disks is only supported as an diff --git a/source/DSCResources/DSC_DiskAccessPath/DSC_DiskAccessPath.psm1 b/source/DSCResources/DSC_DiskAccessPath/DSC_DiskAccessPath.psm1 index 3d78b4a2..29cb225b 100644 --- a/source/DSCResources/DSC_DiskAccessPath/DSC_DiskAccessPath.psm1 +++ b/source/DSCResources/DSC_DiskAccessPath/DSC_DiskAccessPath.psm1 @@ -57,7 +57,7 @@ function Get-TargetResource $DiskId, [Parameter()] - [ValidateSet('Number', 'UniqueId', 'Guid', 'Location')] + [ValidateSet('Number', 'UniqueId', 'Guid', 'Location', 'SerialNumber')] [System.String] $DiskIdType = 'Number', @@ -173,7 +173,7 @@ function Set-TargetResource $DiskId, [Parameter()] - [ValidateSet('Number', 'UniqueId', 'Guid', 'Location')] + [ValidateSet('Number', 'UniqueId', 'Guid', 'Location', 'SerialNumber')] [System.String] $DiskIdType = 'Number', @@ -585,7 +585,7 @@ function Test-TargetResource $DiskId, [Parameter()] - [ValidateSet('Number', 'UniqueId', 'Guid', 'Location')] + [ValidateSet('Number', 'UniqueId', 'Guid', 'Location', 'SerialNumber')] [System.String] $DiskIdType = 'Number', diff --git a/source/DSCResources/DSC_DiskAccessPath/DSC_DiskAccessPath.schema.mof b/source/DSCResources/DSC_DiskAccessPath/DSC_DiskAccessPath.schema.mof index a4832e4f..ef46ebce 100644 --- a/source/DSCResources/DSC_DiskAccessPath/DSC_DiskAccessPath.schema.mof +++ b/source/DSCResources/DSC_DiskAccessPath/DSC_DiskAccessPath.schema.mof @@ -5,7 +5,7 @@ class DSC_DiskAccessPath : OMI_BaseResource [Key, Description("Specifies the access path folder to the assign the disk volume to.")] String AccessPath; [Write, Description("Specifies no automatic drive letter assignment to the partition: Defaults to True")] Boolean NoDefaultDriveLetter; [Required, Description("Specifies the disk identifier for the disk to modify.")] String DiskId; - [Write, Description("Specifies the identifier type the DiskId contains. Defaults to Number."), ValueMap{"Number","UniqueId","Guid","Location"}, Values{"Number","UniqueId","Guid","Location"}] String DiskIdType; + [Write, Description("Specifies the identifier type the DiskId contains. Defaults to Number."), ValueMap{"Number","UniqueId","Guid","Location","SerialNumber"}, Values{"Number","UniqueId","Guid","Location","SerialNumber"}] String DiskIdType; [Write, Description("Specifies the size of new volume.")] Uint64 Size; [Write, Description("Define volume label if required.")] String FSLabel; [Write, Description("Specifies the allocation unit size to use when formatting the volume.")] Uint32 AllocationUnitSize; diff --git a/source/DSCResources/DSC_DiskAccessPath/README.md b/source/DSCResources/DSC_DiskAccessPath/README.md index 72592f33..35e92e0c 100644 --- a/source/DSCResources/DSC_DiskAccessPath/README.md +++ b/source/DSCResources/DSC_DiskAccessPath/README.md @@ -4,17 +4,17 @@ The resource is used to initialize, format and mount the partition/volume to a f access path. The disk to add the partition/volume to is selected by specifying the _DiskId_ and optionally _DiskIdType_. -The _DiskId_ value can be a _Disk Number_, _Unique Id_, _Guid_ or _Location_. +The _DiskId_ value can be a _Disk Number_, _Serial Number_, _Unique Id_, _Guid_ or _Location_. **Important: The _Disk Number_ is not a reliable method of selecting a disk because it has been shown to change between reboots in some environments. It is recommended to use the _Unique Id_ if possible.** -The _Disk Number_, _Unique Id_, _Guid_ and _Location_ can be identified for a +The _Disk Number_, _Serial Number_, _Unique Id_, _Guid_ and _Location_ can be identified for a disk by using the PowerShell command: ```powershell -Get-Disk | Select-Object -Property FriendlyName,DiskNumber,UniqueId,Guid,Location +Get-Disk | Select-Object -Property FriendlyName,DiskNumber,SerialNumber,UniqueId,Guid,Location ``` Note: The _Guid_ for a disk is only assigned once the partition table for the disk diff --git a/source/DSCResources/DSC_WaitForDisk/DSC_WaitForDisk.psm1 b/source/DSCResources/DSC_WaitForDisk/DSC_WaitForDisk.psm1 index 6fd425c8..b04fda2b 100644 --- a/source/DSCResources/DSC_WaitForDisk/DSC_WaitForDisk.psm1 +++ b/source/DSCResources/DSC_WaitForDisk/DSC_WaitForDisk.psm1 @@ -37,7 +37,7 @@ function Get-TargetResource $DiskId, [Parameter()] - [ValidateSet('Number','UniqueId','Guid','Location')] + [ValidateSet('Number', 'UniqueId', 'Guid', 'Location', 'SerialNumber')] [System.String] $DiskIdType = 'Number', @@ -94,7 +94,7 @@ function Set-TargetResource $DiskId, [Parameter()] - [ValidateSet('Number','UniqueId','Guid','Location')] + [ValidateSet('Number', 'UniqueId', 'Guid', 'Location', 'SerialNumber')] [System.String] $DiskIdType = 'Number', @@ -176,7 +176,7 @@ function Test-TargetResource $DiskId, [Parameter()] - [ValidateSet('Number','UniqueId','Guid','Location')] + [ValidateSet('Number', 'UniqueId', 'Guid', 'Location', 'SerialNumber')] [System.String] $DiskIdType = 'Number', diff --git a/source/DSCResources/DSC_WaitForDisk/DSC_WaitForDisk.schema.mof b/source/DSCResources/DSC_WaitForDisk/DSC_WaitForDisk.schema.mof index 0ce2f8cf..6d15e6c5 100644 --- a/source/DSCResources/DSC_WaitForDisk/DSC_WaitForDisk.schema.mof +++ b/source/DSCResources/DSC_WaitForDisk/DSC_WaitForDisk.schema.mof @@ -3,7 +3,7 @@ class DSC_WaitForDisk : OMI_BaseResource { [Key, Description("Specifies the disk identifier for the disk to wait for.")] String DiskId; - [Write, Description("Specifies the identifier type the DiskId contains. Defaults to Number."), ValueMap{"Number","UniqueId","Guid","Location"}, Values{"Number","UniqueId","Guid","Location"}] String DiskIdType; + [Write, Description("Specifies the identifier type the DiskId contains. Defaults to Number."), ValueMap{"Number","UniqueId","Guid","Location","SerialNumber"}, Values{"Number","UniqueId","Guid","Location","SerialNumber"}] String DiskIdType; [Write, Description("Specifies the number of seconds to wait for the disk to become available.")] Uint32 RetryIntervalSec; [Write, Description("The number of times to loop the retry interval while waiting for the disk.")] Uint32 RetryCount; [Read, Description("Will indicate whether Disk is available.")] Boolean IsAvailable; diff --git a/source/DSCResources/DSC_WaitForDisk/README.md b/source/DSCResources/DSC_WaitForDisk/README.md index 242e0d90..889db8c8 100644 --- a/source/DSCResources/DSC_WaitForDisk/README.md +++ b/source/DSCResources/DSC_WaitForDisk/README.md @@ -3,17 +3,17 @@ This resource is used to wait for a disk to become available. The disk to wait for is selected by specifying the _DiskId_ and optionally _DiskIdType_. -The _DiskId_ value can be a _Disk Number_, _Unique Id_, _Guid_ or _Location_. +The _DiskId_ value can be a _Disk Number_, _Serial Number_, _Unique Id_, _Guid_ or _Location_. **Important: The _Disk Number_ is not a reliable method of selecting a disk because it has been shown to change between reboots in some environments. It is recommended to use the _Unique Id_ if possible.** -The _Disk Number_, _Unique Id_, _Guid_ and _Location_ can be identified for a +The _Disk Number_, _Serial Number_, _Unique Id_, _Guid_ and _Location_ can be identified for a disk by using the PowerShell command: ```powershell -Get-Disk | Select-Object -Property FriendlyName,DiskNumber,UniqueId,Guid,Location +Get-Disk | Select-Object -Property FriendlyName,DiskNumber,SerialNumber,UniqueId,Guid,Location ``` Note: The _Guid_ for a disk is only assigned once the partition table for the disk diff --git a/source/Modules/StorageDsc.Common/StorageDsc.Common.psm1 b/source/Modules/StorageDsc.Common/StorageDsc.Common.psm1 index d04dc934..323b4d7d 100644 --- a/source/Modules/StorageDsc.Common/StorageDsc.Common.psm1 +++ b/source/Modules/StorageDsc.Common/StorageDsc.Common.psm1 @@ -160,14 +160,14 @@ function Get-DiskByIdentifier $DiskId, [Parameter()] - [ValidateSet('Number','UniqueId','Guid','Location')] + [ValidateSet('Number','UniqueId','Guid','Location','SerialNumber')] [System.String] $DiskIdType = 'Number' ) switch -regex ($DiskIdType) { - 'Number|UniqueId' # for filters supported by the Get-Disk CmdLet + 'Number|UniqueId|SerialNumber' # for filters supported by the Get-Disk CmdLet { $diskIdParameter = @{ $DiskIdType = $DiskId diff --git a/tests/Integration/DSC_Disk.Integration.Tests.ps1 b/tests/Integration/DSC_Disk.Integration.Tests.ps1 index 011f6eee..512f15a9 100644 --- a/tests/Integration/DSC_Disk.Integration.Tests.ps1 +++ b/tests/Integration/DSC_Disk.Integration.Tests.ps1 @@ -170,7 +170,7 @@ try } } - Context 'When partitioniong and formatting a newly provisioned disk using Disk Number with one volume and assigning Drive Letters then resizing' { + Context 'When partitioning and formatting a newly provisioned disk using Disk Number with one volume and assigning Drive Letters then resizing' { BeforeAll { # Create a VHD and attach it to the computer $VHDPath = Join-Path -Path $TestDrive ` @@ -449,6 +449,212 @@ try } } + Context 'When partitioning and formatting a newly provisioned disk using Serial number with two volumes and assigning Drive Letters' { + BeforeAll { + # Create a VHD and attach it to the computer + $VHDPath = Join-Path -Path $TestDrive ` + -ChildPath 'TestDisk.vhd' + $null = New-VDisk -Path $VHDPath -SizeInMB 1024 + $null = Mount-DiskImage -ImagePath $VHDPath -StorageType VHD -NoDriveLetter + $diskImage = Get-DiskImage -ImagePath $VHDPath + $disk = Get-Disk -Number $diskImage.Number + $FSLabelA = 'TestDiskA' + $FSLabelB = 'TestDiskB' + + # Get a spare drive letter + $lastDrive = ((Get-Volume).DriveLetter | Sort-Object | Select-Object -Last 1) + $driveLetterA = [char](([int][char]$lastDrive) + 1) + $driveLetterB = [char](([int][char]$lastDrive) + 2) + } + + Context "When creating the first volume on Disk Serial number $($disk.SerialNumber)" { + It 'Should compile and apply the MOF without throwing' { + { + # This is to pass to the Config + $configData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + DriveLetter = $driveLetterA + DiskId = $disk.SerialNumber + DiskIdType = 'SerialNumber' + PartitionStyle = 'GPT' + FSLabel = $FSLabelA + Size = 100MB + } + ) + } + + & "$($script:dscResourceName)_Config" ` + -OutputPath $TestDrive ` + -ConfigurationData $configData + + Start-DscConfiguration ` + -Path $TestDrive ` + -ComputerName localhost ` + -Wait ` + -Verbose ` + -Force ` + -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $current = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq "$($script:dscResourceName)_Config" + } + $current.DiskId | Should -Be $disk.SerialNumber + $current.DriveLetter | Should -Be $driveLetterA + $current.PartitionStyle | Should -Be 'GPT' + $current.FSLabel | Should -Be $FSLabelA + $current.Size | Should -Be 100MB + } + } + + Context "When resizing the first volume on Disk Serial number $($disk.SerialNumber) and allowing the disk to be cleared" { + <# + There is an issue with Format-Volume that occurs when formatting a volume + with ReFS in Windows Server 2019 (build 17763 and above). Therefore on + Windows Server 2019 the integration tests will use NTFS only. + + See Issue #227: https://github.com/dsccommunity/StorageDsc/issues/227 + #> + if ((Get-CimInstance -ClassName WIN32_OperatingSystem).BuildNumber -ge 17763) + { + $FSFormat = 'NTFS' + } + else + { + $FSFormat = 'ReFS' + } + + It 'should compile and apply the MOF without throwing' { + { + # This is to pass to the Config + $configData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + DriveLetter = $driveLetterA + DiskId = $disk.SerialNumber + DiskIdType = 'SerialNumber' + PartitionStyle = 'GPT' + FSLabel = $FSLabelA + Size = 900MB + FSFormat = $FSFormat + } + ) + } + + & "$($script:dscResourceName)_ConfigClearDisk" ` + -OutputPath $TestDrive ` + -ConfigurationData $configData + + Start-DscConfiguration ` + -Path $TestDrive ` + -ComputerName localhost ` + -Wait ` + -Verbose ` + -Force ` + -ErrorAction Stop + } | Should -Not -Throw + } + + It 'should be able to call Get-DscConfiguration without throwing' { + { $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop } | Should -Not -Throw + } + + It 'should have set the resource and all the parameters should match' { + $current = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq "$($script:dscResourceName)_ConfigClearDisk" + } + $current.DiskId | Should -Be $disk.SerialNumber + $current.DriveLetter | Should -Be $driveLetterA + $current.PartitionStyle | Should -Be 'GPT' + $current.FSLabel | Should -Be $FSLabelA + $current.Size | Should -Be 900MB + $current.FSFormat | Should -Be $FSFormat + } + } + + Context "When creating second volume on Disk Serial number $($disk.SerialNumber)" { + It 'Should compile and apply the MOF without throwing' { + { + # This is to pass to the Config + $configData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + DriveLetter = $driveLetterB + DiskId = $disk.SerialNumber + DiskIdType = 'SerialNumber' + PartitionStyle = 'GPT' + FSLabel = $FSLabelB + } + ) + } + + & "$($script:dscResourceName)_Config" ` + -OutputPath $TestDrive ` + -ConfigurationData $configData + + Start-DscConfiguration ` + -Path $TestDrive ` + -ComputerName localhost ` + -Wait ` + -Verbose ` + -Force ` + -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $current = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq "$($script:dscResourceName)_Config" + } + $current.DiskId | Should -Be $disk.SerialNumber + $current.PartitionStyle | Should -Be 'GPT' + $current.DriveLetter | Should -Be $driveLetterB + $current.FSLabel | Should -Be $FSLabelB + <# + The size of the volume differs depending on OS. + - Windows Server 2016: 96337920 + - Windows Server 2019: 113180672 + + The reason for this difference is not known, but Get-PartitionSupportedSize + does return correct and expected values for each OS. + #> + $current.Size | Should -BeIn @(96337920,113180672) + } + } + + # A system partition will have been added to the disk as well as the 2 test partitions + It 'Should have 3 partitions on disk' { + ($disk | Get-Partition).Count | Should -Be 3 + } + + It "Should have attached drive $driveLetterA" { + Get-PSDrive -Name $driveLetterA -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + } + + It "Should have attached drive $driveLetterB" { + Get-PSDrive -Name $driveLetterB -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + } + + AfterAll { + $null = Dismount-DiskImage -ImagePath $VHDPath -StorageType VHD + $null = Remove-Item -Path $VHDPath -Force + } + } + Context 'When partitioning and formatting a newly provisioned disk using Unique Id with two volumes and assigning Drive Letters' { BeforeAll { # Create a VHD and attach it to the computer diff --git a/tests/Unit/DSC_Disk.Tests.ps1 b/tests/Unit/DSC_Disk.Tests.ps1 index 2595da07..8eb79540 100644 --- a/tests/Unit/DSC_Disk.Tests.ps1 +++ b/tests/Unit/DSC_Disk.Tests.ps1 @@ -32,13 +32,15 @@ Invoke-TestSetup try { InModuleScope $script:dscResourceName { - $script:testDriveLetter = 'G' - $script:testDiskNumber = 1 - $script:testDiskUniqueId = 'TESTDISKUNIQUEID' - $script:testDiskGptGuid = [guid]::NewGuid() + $script:testDriveLetter = 'G' + $script:testDiskNumber = 1 + $script:testDiskSerialNumber = 'TESTDISKSERIALNUMBER' + $script:testDiskUniqueId = 'TESTDISKUNIQUEID' + $script:testDiskGptGuid = [guid]::NewGuid() $script:mockedDisk0Gpt = [pscustomobject] @{ Number = $script:testDiskNumber + SerialNumber = $script:testDiskSerialNumber UniqueId = $script:testDiskUniqueId Guid = $script:testDiskGptGuid IsOffline = $false @@ -48,6 +50,7 @@ try $script:mockedDisk0Mbr = [pscustomobject] @{ Number = $script:testDiskNumber + SerialNumber = $script:testDiskSerialNumber UniqueId = $script:testDiskUniqueId Guid = '' IsOffline = $false @@ -57,6 +60,7 @@ try $script:mockedDisk0Raw = [pscustomobject] @{ Number = $script:testDiskNumber + SerialNumber = $script:testDiskSerialNumber UniqueId = $script:testDiskUniqueId Guid = '' IsOffline = $false @@ -66,6 +70,7 @@ try $script:mockedDisk0GptOffline = [pscustomobject] @{ Number = $script:testDiskNumber + SerialNumber = $script:testDiskSerialNumber UniqueId = $script:testDiskUniqueId Guid = $script:testDiskGptGuid IsOffline = $true @@ -75,6 +80,7 @@ try $script:mockedDisk0RawOffline = [pscustomobject] @{ Number = $script:testDiskNumber + SerialNumber = $script:testDiskSerialNumber UniqueId = $script:testDiskUniqueId Guid = '' IsOffline = $true @@ -84,6 +90,7 @@ try $script:mockedDisk0GptReadonly = [pscustomobject] @{ Number = $script:testDiskNumber + SerialNumber = $script:testDiskSerialNumber UniqueId = $script:testDiskUniqueId Guid = $script:testDiskGptGuid IsOffline = $false @@ -484,6 +491,73 @@ try } } + Context 'When online GPT disk with a partition/volume and correct Drive Letter assigned using Disk Serial number' { + # verifiable (should be called) mocks + Mock ` + -CommandName Get-CimInstance ` + -MockWith { $script:mockedCim } ` + -Verifiable + + Mock ` + -CommandName Get-DiskByIdentifier ` + -ParameterFilter { $DiskId -eq $script:mockedDisk0Gpt.SerialNumber -and $DiskIdType -eq 'SerialNumber' } ` + -MockWith { $script:mockedDisk0Gpt } ` + -Verifiable + + Mock ` + -CommandName Get-Partition ` + -MockWith { $script:mockedPartition } ` + -Verifiable + + Mock ` + -CommandName Get-Volume ` + -MockWith { $script:mockedVolume } ` + -Verifiable + + $resource = Get-TargetResource ` + -DiskId $script:mockedDisk0Gpt.SerialNumber ` + -DiskIdType 'SerialNumber' ` + -DriveLetter $script:testDriveLetter ` + -Verbose + + It "Should return DiskId $($script:mockedDisk0Gpt.SerialNumber)" { + $resource.DiskId | Should -Be $script:mockedDisk0Gpt.SerialNumber + } + + It "Should return PartitionStyle $($script:mockedDisk0Gpt.PartitionStyle)" { + $resource.PartitionStyle | Should -Be $script:mockedDisk0Gpt.PartitionStyle + } + + It "Should return DriveLetter $($script:testDriveLetter)" { + $resource.DriveLetter | Should -Be $script:testDriveLetter + } + + It "Should return size $($script:mockedPartition.Size)" { + $resource.Size | Should -Be $script:mockedPartition.Size + } + + It "Should return FSLabel $($script:mockedVolume.FileSystemLabel)" { + $resource.FSLabel | Should -Be $script:mockedVolume.FileSystemLabel + } + + It "Should return AllocationUnitSize $($script:mockedCim.BlockSize)" { + $resource.AllocationUnitSize | Should -Be $script:mockedCim.BlockSize + } + + It "Should return FSFormat $($script:mockedVolume.FileSystem)" { + $resource.FSFormat | Should -Be $script:mockedVolume.FileSystem + } + + It 'Should call the correct mocks' { + Assert-VerifiableMock + Assert-MockCalled -CommandName Get-CimInstance -Exactly 1 + Assert-MockCalled -CommandName Get-DiskByIdentifier -Exactly 1 ` + -ParameterFilter { $DiskId -eq $script:mockedDisk0Gpt.SerialNumber -and $DiskIdType -eq 'SerialNumber' } + Assert-MockCalled -CommandName Get-Partition -Exactly 1 + Assert-MockCalled -CommandName Get-Volume -Exactly 1 + } + } + Context 'When online GPT disk with a partition/volume and correct Drive Letter assigned using Disk Unique Id' { # verifiable (should be called) mocks Mock ` @@ -938,6 +1012,73 @@ try } } + Context 'When offline GPT disk using Disk Serial number' { + # verifiable (should be called) mocks + Mock ` + -CommandName Get-DiskByIdentifier ` + -ParameterFilter { $DiskId -eq $script:mockedDisk0GptOffline.SerialNumber -and $DiskIdType -eq 'SerialNumber' } ` + -MockWith { $script:mockedDisk0GptOffline } ` + -Verifiable + + Mock ` + -CommandName Set-Disk ` + -Verifiable + + Mock ` + -CommandName Get-Partition ` + -Verifiable + + Mock ` + -CommandName New-Partition ` + -ParameterFilter { + $DriveLetter -eq $script:testDriveLetter + } ` + -MockWith { $script:mockedPartitionNoDriveLetter } ` + -Verifiable + + Mock ` + -CommandName Get-Volume ` + -MockWith { $script:mockedVolumeUnformatted } ` + -Verifiable + + Mock ` + -CommandName Format-Volume ` + -Verifiable + + Mock ` + -CommandName Set-Partition ` + -Verifiable + + # mocks that should not be called + Mock -CommandName Initialize-Disk + + It 'Should not throw an exception' { + { + Set-TargetResource ` + -DiskId $script:mockedDisk0GptOffline.SerialNumber ` + -DiskIdType 'SerialNumber' ` + -Driveletter $script:testDriveLetter ` + -Verbose + } | Should -Not -Throw + } + + It 'Should call the correct mocks' { + Assert-VerifiableMock + Assert-MockCalled -CommandName Get-DiskByIdentifier -Exactly -Times 1 ` + -ParameterFilter { $DiskId -eq $script:mockedDisk0GptOffline.SerialNumber -and $DiskIdType -eq 'SerialNumber' } + Assert-MockCalled -CommandName Set-Disk -Exactly -Times 1 + Assert-MockCalled -CommandName Initialize-Disk -Exactly -Times 0 + Assert-MockCalled -CommandName Get-Partition -Exactly -Times 4 + Assert-MockCalled -CommandName Get-Volume -Exactly -Times 1 + Assert-MockCalled -CommandName New-Partition -Exactly -Times 1 ` + -ParameterFilter { + $DriveLetter -eq $script:testDriveLetter + } + Assert-MockCalled -CommandName Format-Volume -Exactly -Times 1 + Assert-MockCalled -CommandName Set-Partition -Exactly -Times 1 + } + } + Context 'When offline GPT disk using Disk Unique Id' { # verifiable (should be called) mocks Mock ` @@ -2350,7 +2491,7 @@ try } } - Context 'When testing disk offline using Disk Unique Id' { + Context 'When testing disk offline using Disk Numbe' { # verifiable (should be called) mocks Mock ` -CommandName Get-DiskByIdentifier ` @@ -2389,6 +2530,46 @@ try } } + Context 'When testing disk offline using Serial number' { + # verifiable (should be called) mocks + Mock ` + -CommandName Get-DiskByIdentifier ` + -ParameterFilter { $DiskId -eq $script:mockedDisk0Gpt.SerialNumber -and $DiskIdType -eq 'SerialNumber' } ` + -MockWith { $script:mockedDisk0GptOffline } ` + -Verifiable + + # mocks that should not be called + Mock -CommandName Get-Volume + Mock -CommandName Get-Partition + Mock -CommandName Get-CimInstance + + $script:result = $null + + It 'Should not throw an exception' { + { + $script:result = Test-TargetResource ` + -DiskId $script:mockedDisk0GptOffline.SerialNumber ` + -DiskIdType 'SerialNumber' ` + -DriveLetter $script:testDriveLetter ` + -AllocationUnitSize 4096 ` + -Verbose + } | Should -Not -Throw + } + + It 'Should be false' { + $script:result | Should -Be $false + } + + It 'Should call the correct mocks' { + Assert-VerifiableMock + Assert-MockCalled -CommandName Get-DiskByIdentifier -Exactly -Times 1 ` + -ParameterFilter { $DiskId -eq $script:mockedDisk0GptOffline.SerialNumber -and $DiskIdType -eq 'SerialNumber' } + Assert-MockCalled -CommandName Get-Partition -Exactly -Times 0 + Assert-MockCalled -CommandName Get-Volume -Exactly -Times 0 + Assert-MockCalled -CommandName Get-CimInstance -Exactly -Times 0 + } + } + Context 'When testing disk offline using Unique Id' { # verifiable (should be called) mocks Mock `