diff --git a/.MetaTestOptIn.json b/.MetaTestOptIn.json deleted file mode 100644 index 3c16740..0000000 --- a/.MetaTestOptIn.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - "Common Tests - Validate Markdown Files", - "Common Tests - Validate Example Files", - "Common Tests - Validate Module Files", - "Common Tests - Validate Script Files", - "Common Tests - Flagged Script Analyzer Rules", - "Common Tests - New Error-Level Script Analyzer Rules", - "Common Tests - Custom Script Analyzer Rules", - "Common Tests - Validate Example Files To Be Published", - "Common Tests - Validate Markdown Links", - "Common Tests - Relative Path Length" -] diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index 411198f..0000000 --- a/.codecov.yml +++ /dev/null @@ -1,26 +0,0 @@ -codecov: - notify: - require_ci_to_pass: no - # dev should be the baseline for reporting - branch: dev - -comment: - layout: "reach, diff" - behavior: default - -coverage: - range: 50..80 - round: down - precision: 0 - - status: - project: - default: - # Set the overall project code coverage requirement to 70% - target: 70 - patch: - default: - # Set the pull request requirement to not regress overall coverage by more than 5% - # and let codecov.io set the goal for the code changed in the patch. - target: auto - threshold: 5 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9dbbd55 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text eol=crlf + +# Ensure any exe files are treated as binary +*.exe binary +*.jpg binary +*.xl* binary +*.pfx binary diff --git a/.gitignore b/.gitignore index b03606a..67f54dc 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,10 @@ -DSCResource.Tests +*.suo +*.user +*.coverage +.vs +.vscode +.psproj +.sln +markdownissues.txt +output/ + diff --git a/.vscode/analyzersettings.psd1 b/.vscode/analyzersettings.psd1 index be415e4..78312d2 100644 --- a/.vscode/analyzersettings.psd1 +++ b/.vscode/analyzersettings.psd1 @@ -1,11 +1,6 @@ @{ - <# - For the custom rules to work, the DscResource.Tests repo must be - cloned. It is automatically clone as soon as any unit or - integration tests are run. - #> - CustomRulePath = '.\DSCResource.Tests\DscResource.AnalyzerRules' - + CustomRulePath = '.\output\RequiredModules\DscResource.AnalyzerRules' + includeDefaultRules = $true IncludeRules = @( # DSC Resource Kit style guideline rules. 'PSAvoidDefaultValueForMandatoryParameter', @@ -43,11 +38,7 @@ 'PSUseDeclaredVarsMoreThanAssignments', 'PSUsePSCredentialType', - <# - This is to test all the DSC Resource Kit custom rules. - The name of the function-blocks of each custom rule start - with 'Measure*'. - #> 'Measure-*' ) + } diff --git a/.vscode/settings.json b/.vscode/settings.json index 43f01cf..a060627 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,15 +1,38 @@ -// Place your settings in this file to overwrite default and user settings. { "powershell.codeFormatting.openBraceOnSameLine": false, - "powershell.codeFormatting.newLineAfterOpenBrace": false, + "powershell.codeFormatting.newLineAfterOpenBrace": true, "powershell.codeFormatting.newLineAfterCloseBrace": true, "powershell.codeFormatting.whitespaceBeforeOpenBrace": true, "powershell.codeFormatting.whitespaceBeforeOpenParen": true, "powershell.codeFormatting.whitespaceAroundOperator": true, "powershell.codeFormatting.whitespaceAfterSeparator": true, "powershell.codeFormatting.ignoreOneLineBlock": false, + "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationAfterEveryPipeline", "powershell.codeFormatting.preset": "Custom", + "powershell.codeFormatting.alignPropertyValuePairs": true, "files.trimTrailingWhitespace": true, "files.insertFinalNewline": true, - "powershell.scriptAnalysis.settingsPath": ".vscode\\analyzersettings.psd1" + "powershell.scriptAnalysis.settingsPath": ".vscode\\analyzersettings.psd1", + "powershell.scriptAnalysis.enable": true, + "files.associations": { + "*.ps1xml": "xml" + }, + "cSpell.words": [ + "COMPANYNAME", + "ICONURI", + "LICENSEURI", + "PROJECTURI", + "RELEASENOTES", + "buildhelpers", + "endregion", + "gitversion", + "icontains", + "keepachangelog", + "notin", + "pscmdlet", + "steppable" + ], + "[markdown]": { + "files.encoding": "utf8" + } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 2de4ef3..24979e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,26 @@ -# Versions +# Change log for ComputerManagementDsc -## 1.5.1.0 +The format is based on and uses the types of changes according to [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Changed + +- Transferred ownership to DSCCommunity.org - Fixes [Issue #50](https://github.com/dsccommunity/iSCSIDsc/issues/50). +- Fix hash table style guideline violations. +- Added .gitattributes file to fix bug publishing examples - Fixes [Issue #40](https://github.com/PlagueHO/iSCSIDsc/issues/40). +- Opted into Common Tests 'Common Tests - Validate Localization' - Fixes [Issue #44](https://github.com/PlagueHO/iSCSIDsc/issues/44). +- Renamed `iSCSIDsc.ResourceHelpers` module to `iSCSIDsc.Common` + to align to other modules. +- Renamed all localization strings so that they are detected by + 'Common Tests - Validate Localization'. +- Fixed issues with mismatched localization strings. +- Replaced `iSCSIDsc.Common` module with the latest version from + [DSCResource.Template](https://github.com/PowerShell/DSCResource.Template). +- Fix minor style issues in statement case. +- Fix minor style issues in hashtable layout. +- Correct other minor style issues. - Enabled PSSA rule violations to fail build - Fixes [Issue #27](https://github.com/PlagueHO/iSCSIDsc/issues/27). - Updated tests to meet Pester v4 standard. - Added Open Code of Conduct. @@ -16,28 +35,45 @@ - Common Tests - Relative Path Length - Update to new format LICENSE. - Added .VSCode settings for applying DSC PSSA rules - fixes [Issue #37](https://github.com/PlagueHO/iSCSIDsc/issues/37). +- BREAKING CHANGE: Changed resource prefix from MSFT to DSC. +- Updated to use continuous delivery pattern using Azure DevOps - fixes + [Issue #52](https://github.com/dsccommunity/iSCSIDsc/issues/52). +- Update build badges in `README.md` with correct build IDs. +- Removed unused test helper function `New-VDisk`. +- Added test helper function `Assert-CanRunIntegrationTest` to validate if + integration tests can be successfully run on a machine. -## 1.5.0.0 +## [1.5.0.41] - 2017-09-02 + +### Changed - Converted resource module to be compliant with HQRM. - Changed AppVeyor.yml to use AppVeyor module. - Converted to Autodocumentation module. -## 1.2.1.0 +## [1.2.1.95] - 2016-05-04 + +### Changed - iSCSIInitiator: Fix bug when converting connected target to IsPersistent. -## 1.2.0.0 +## [1.2.0.0] - 2016-01-01 + +### Changed - iSCSIInitiator: - Fix bug when TargetPortalAddress is set to a Hostname instead of an IP address. - InitiatorPortalAddress property made optional. - Unit and Integration test headers updated to v1.1.0 -## 1.1.0.0 +## [1.1.0.0] - 2016-01-01 + +### Changed - Added iSNS Server support. -## 1.0.0.0 +## [1.0.0.0] - 2016-01-01 + +### Changed - Initial release. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 2b2b442..d7589dd 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,133 +1,3 @@ -# Open Source Code of Conduct +# Code of Conduct -This code of conduct outlines our expectations for participants within this -DSC Resource Module community, as well as steps to reporting unacceptable behavior. -We are committed to providing a welcoming and inspiring community for all and expect -our code of conduct to be honored. Anyone who violates this code of conduct may be -banned from the community. - -Our open source communities strive to: - -- **Be friendly and patient.** -- **Be welcoming:** We strive to be a community that welcomes and supports people - of all backgrounds and identities. This includes, but is not limited to members - of any race, ethnicity, culture, national origin, colour, immigration status, - social and economic class, educational level, sex, sexual orientation, gender - identity and expression, age, size, family status, political belief, religion, - and mental and physical ability. -- **Be considerate:** Your work will be used by other people, and you in turn - will depend on the work of others. Any decision you take will affect users and - colleagues, and you should take those consequences into account when making - decisions. Remember that we're a world-wide community, so you might not be - communicating in someone else's primary language. -- **Be respectful:** Not all of us will agree all the time, but disagreement is - no excuse for poor behavior and poor manners. We might all experience some - frustration now and then, but we cannot allow that frustration to turn into a - personal attack. It's important to remember that a community where people feel - uncomfortable or threatened is not a productive one. -- **Be careful in the words that we choose:** we are a community of professionals, - and we conduct ourselves professionally. Be kind to others. Do not insult or - put down other participants. Harassment and other exclusionary behavior aren't - acceptable. -- **Try to understand why we disagree:** Disagreements, both social and technical, - happen all the time. It is important that we resolve disagreements and differing - views constructively. Remember that we're different. The strength of our community - comes from its diversity, people from a wide range of backgrounds. Different people - have different perspectives on issues. Being unable to understand why someone holds - a viewpoint doesn't mean that they're wrong. Don't forget that it is human to err - and blaming each other doesn't get us anywhere. Instead, focus on helping to resolve - issues and learning from mistakes. - -## Definitions - -Harassment includes, but is not limited to: - -- Offensive comments related to gender, gender identity and expression, sexual - orientation, disability, mental illness, neuro(a)typicality, physical appearance, - body size, race, age, regional discrimination, political or religious affiliation -- Unwelcome comments regarding a person's lifestyle choices and practices, - including those related to food, health, parenting, drugs, and employment -- Deliberate misgendering. This includes deadnaming or persistently using a - pronoun that does not correctly reflect a person's gender identity. You must - address people by the name they give you when not addressing them by their username - or handle -- Physical contact and simulated physical contact (eg, textual descriptions like - "hug" or "backrub") without consent or after a request to stop -- Threats of violence, both physical and psychological -- Incitement of violence towards any individual, including encouraging a person - to commit suicide or to engage in self-harm -- Deliberate intimidation -- Stalking or following -- Harassing photography or recording, including logging online activity for - harassment purposes -- Sustained disruption of discussion -- Unwelcome sexual attention, including gratuitous or off-topic sexual images or - behaviour -- Pattern of inappropriate social contact, such as requesting/assuming inappropriate - levels of intimacy with others -- Continued one-on-one communication after requests to cease -- Deliberate "outing" of any aspect of a person's identity without their consent - except as necessary to protect others from intentional abuse -- Publication of non-harassing private communication - -Our open source community prioritizes marginalized people's safety over privileged -people's comfort. We will not act on complaints regarding: - -- 'Reverse' -isms, including 'reverse racism,' 'reverse sexism,' and 'cisphobia' -- Reasonable communication of boundaries, such as "leave me alone," "go away," or - "I'm not discussing this with you" -- Refusal to explain or debate social justice concepts -- Communicating in a 'tone' you don't find congenial -- Criticizing racist, sexist, cissexist, or otherwise oppressive behavior or assumptions - -## Diversity Statement - -We encourage everyone to participate and are committed to building a community -for all. Although we will fail at times, we seek to treat everyone both as fairly -and equally as possible. Whenever a participant has made a mistake, we expect them -to take responsibility for it. If someone has been harmed or offended, it is our -responsibility to listen carefully and respectfully, and do our best to right the -wrong. - -Although this list cannot be exhaustive, we explicitly honor diversity in age, -gender, gender identity or expression, culture, ethnicity, language, national -origin, political beliefs, profession, race, religion, sexual orientation, socioeconomic -status, and technical ability. We will not tolerate discrimination based on any -of the protected characteristics above, including participants with disabilities. - -## Reporting Code of Conduct Issues - -If you experience or witness unacceptable behavior—or have any other concerns, -please report it by contacting us via [dscottraynsford@outlook.com](mailto:dscottraynsford@outlook.com). -All reports will be handled with discretion. - -In your report please include: - -- Your contact information. -- Names (real, usernames or pseudonyms) of any individuals involved. If there - are additional witnesses, please include them as well. -- Your account of what occurred, and if you believe the incident is ongoing. -- If there is a publicly available record (e.g. a mailing list archive or a - public chat log), please include a link or attachment. -- Any additional information that may be helpful. - -After filing a report, a representative will contact you personally, review the -incident, follow up with any additional questions, and make a decision as to how -to respond. If the person who is harassing you is part of the response team, they -will recuse themselves from handling your incident. If the complaint originates -from a member of the response team, it will be handled by a different member of -the response team. We will respect confidentiality requests for the purpose of -protecting victims of abuse. - -## Attribution & Acknowledgements - -We all stand on the shoulders of giants across many open source communities. -We’d like to thank the communities and projects that established code of conducts -and diversity statements as our inspiration: - -- [Django](https://www.djangoproject.com/conduct/reporting/) -- [Python](https://www.python.org/community/diversity/) -- [Ubuntu](http://www.ubuntu.com/about/about-ubuntu/conduct) -- [Contributor Covenant](http://contributor-covenant.org/) -- [Geek Feminism](http://geekfeminism.org/about/code-of-conduct/) -- [Citizen Code of Conduct](http://citizencodeofconduct.org/) +This project has adopted the [DSC Community Code of Conduct](https://dsccommunity.org/code_of_conduct). diff --git a/GitVersion.yml b/GitVersion.yml new file mode 100644 index 0000000..8493c01 --- /dev/null +++ b/GitVersion.yml @@ -0,0 +1,26 @@ +mode: ContinuousDelivery +next-version: 1.5.0 +major-version-bump-message: '\s?(breaking|major|breaking\schange)' +minor-version-bump-message: '\s?(add|feature|minor)' +patch-version-bump-message: '\s?(fix|patch)' +no-bump-message: '\+semver:\s?(none|skip)' +assembly-informational-format: '{NuGetVersionV2}+Sha.{Sha}.Date.{CommitDate}' +branches: + master: + tag: preview + pull-request: + tag: PR + feature: + tag: useBranchName + increment: Minor + regex: f(eature(s)?)?[\/-] + source-branches: ['master'] + hotfix: + tag: fix + increment: Patch + regex: (hot)?fix(es)?[\/-] + source-branches: ['master'] + +ignore: + sha: [] +merge-message-formats: {} diff --git a/LICENSE b/LICENSE index d03e93a..be601b5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) Daniel Scott-Raynsford. All rights reserved. +Copyright the DSC Community contributors. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Modules/iSCSIDsc.ResourceHelper/iSCSIDsc.ResourceHelper.psm1 b/Modules/iSCSIDsc.ResourceHelper/iSCSIDsc.ResourceHelper.psm1 deleted file mode 100644 index 0216696..0000000 --- a/Modules/iSCSIDsc.ResourceHelper/iSCSIDsc.ResourceHelper.psm1 +++ /dev/null @@ -1,178 +0,0 @@ -<# - .SYNOPSIS - Tests if the current machine is a Nano server. -#> -function Test-IsNanoServer -{ - if (Test-Command -Name Get-ComputerInfo) - { - $computerInfo = Get-ComputerInfo - - if ("Server" -eq $computerInfo.OsProductType ` - -and "NanoServer" -eq $computerInfo.OsServerLevel) - { - return $true - } - } - - return $false -} - -<# - .SYNOPSIS - Tests if the the specified command is found. -#> -function Test-Command -{ - param - ( - [Parameter()] - [String] - $Name - ) - - return ($null -ne (Get-Command -Name $Name -ErrorAction Continue 2> $null)) -} - -<# - .SYNOPSIS - Creates and throws an invalid argument exception - - .PARAMETER Message - The message explaining why this error is being thrown - - .PARAMETER ArgumentName - The name of the invalid argument that is causing this error to be thrown -#> -function New-InvalidArgumentException -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [String] - $Message, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [String] - $ArgumentName - ) - - $argumentException = New-Object -TypeName 'ArgumentException' -ArgumentList @( $Message, - $ArgumentName ) - $newObjectParams = @{ - TypeName = 'System.Management.Automation.ErrorRecord' - ArgumentList = @( $argumentException, $ArgumentName, 'InvalidArgument', $null ) - } - $errorRecord = New-Object @newObjectParams - - throw $errorRecord -} - -<# - .SYNOPSIS - Creates and throws an invalid operation exception - - .PARAMETER Message - The message explaining why this error is being thrown - - .PARAMETER ErrorRecord - The error record containing the exception that is causing this terminating error -#> -function New-InvalidOperationException -{ - [CmdletBinding()] - param - ( - [Parameter()] - [ValidateNotNullOrEmpty()] - [String] - $Message, - - [Parameter()] - [ValidateNotNull()] - [System.Management.Automation.ErrorRecord] - $ErrorRecord - ) - - if ($null -eq $Message) - { - $invalidOperationException = New-Object -TypeName 'InvalidOperationException' - } - elseif ($null -eq $ErrorRecord) - { - $invalidOperationException = - New-Object -TypeName 'InvalidOperationException' -ArgumentList @( $Message ) - } - else - { - $invalidOperationException = - New-Object -TypeName 'InvalidOperationException' -ArgumentList @( $Message, - $ErrorRecord.Exception ) - } - - $newObjectParams = @{ - TypeName = 'System.Management.Automation.ErrorRecord' - ArgumentList = @( $invalidOperationException.ToString(), 'MachineStateIncorrect', - 'InvalidOperation', $null ) - } - $errorRecordToThrow = New-Object @newObjectParams - throw $errorRecordToThrow -} - -<# - .SYNOPSIS - Retrieves the localized string data based on the machine's culture. - Falls back to en-US strings if the machine's culture is not supported. - - .PARAMETER ResourceName - The name of the resource as it appears before '.strings.psd1' of the localized string file. - - For example: - For WindowsOptionalFeature: DSR_xWindowsOptionalFeature - For Service: DSR_xServiceResource - For Registry: DSR_xRegistryResource - - .PARAMETER ResourcePath - The path the resource file is located in. -#> -function Get-LocalizedData -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [String] - $ResourceName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [String] - $ResourcePath - ) - - $localizedStringFileLocation = Join-Path -Path $ResourcePath -ChildPath $PSUICulture - - if (-not (Test-Path -Path $localizedStringFileLocation)) - { - # Fallback to en-US - $localizedStringFileLocation = Join-Path -Path $ResourcePath -ChildPath 'en-US' - } - - Import-LocalizedData ` - -BindingVariable 'localizedData' ` - -FileName "$ResourceName.strings.psd1" ` - -BaseDirectory $localizedStringFileLocation - - return $localizedData -} - -Export-ModuleMember -Function @( - 'Test-IsNanoServer', - 'New-InvalidArgumentException', - 'New-InvalidOperationException', - 'Get-LocalizedData' -) diff --git a/PSScriptAnalyzerSettings.psd1 b/PSScriptAnalyzerSettings.psd1 deleted file mode 100644 index ba07a45..0000000 --- a/PSScriptAnalyzerSettings.psd1 +++ /dev/null @@ -1,10 +0,0 @@ -@{ - ExcludeRules=@('PSDSCDscExamplesPresent', - 'PSDSCDscTestsPresent', - 'PSUseBOMForUnicodeEncodedFile', - 'PSUseShouldProcessForStateChangingFunctions', - 'PSUseSingularNouns', - 'PSUseToExportFieldsInManifest', - 'PSUseUTF8EncodingForHelpFile', - 'PSUseDeclaredVarsMoreThanAssignments') -} diff --git a/README.md b/README.md index 66b80f2..0fefa7f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,32 @@ # iSCSIDsc +[![Build Status](https://dev.azure.com/dsccommunity/iSCSIDsc/_apis/build/status/dsccommunity.iSCSIDsc?branchName=master)](https://dev.azure.com/dsccommunity/iSCSIDsc/_build/latest?definitionId=36&branchName=master) +![Code Coverage](https://img.shields.io/azure-devops/coverage/dsccommunity/iSCSIDsc/36/master) +[![Azure DevOps tests](https://img.shields.io/azure-devops/tests/dsccommunity/iSCSIDsc/36/master)](https://dsccommunity.visualstudio.com/iSCSIDsc/_test/analytics?definitionId=36&contextType=build) +[![PowerShell Gallery (with prereleases)](https://img.shields.io/powershellgallery/vpre/iSCSIDsc?label=iSCSIDsc%20Preview)](https://www.powershellgallery.com/packages/iSCSIDsc/) +[![PowerShell Gallery](https://img.shields.io/powershellgallery/v/iSCSIDsc?label=iSCSIDsc)](https://www.powershellgallery.com/packages/iSCSIDsc/) + +## Code of Conduct + +This project has adopted [this code of conduct](CODE_OF_CONDUCT.md). + +## Releases + +For each merge to the branch `master` a preview release will be +deployed to [PowerShell Gallery](https://www.powershellgallery.com/). +Periodically a release version tag will be pushed which will deploy a +full release to [PowerShell Gallery](https://www.powershellgallery.com/). + +## Contributing + +Please check out common DSC Community [contributing guidelines](https://dsccommunity.org/guidelines/contributing). + +## Change log + +A full list of changes in each version can be found in the [change log](CHANGELOG.md). + +## Resources + The **iSCSIDsc** module contains DSC resources for configuring Windows iSCSI Targets and Initiators. @@ -10,40 +37,12 @@ Targets and Initiators. - **iSCSIVirtualDisk**: This resource is used to create or remove Virtual Disks for use by iSCSI Targets. -**This project is not maintained or supported by Microsoft.** - This project has adopted this [Open Source Code of Conduct](CODE_OF_CONDUCT.md). -This module should meet the [PowerShell DSC Resource Kit High Quality Resource -Module Guidelines](https://github.com/PowerShell/DscResources/blob/master/HighQualityModuleGuidelines.md). - ## Documentation and Examples For a full list of resources in iSCSIDsc and examples on their use, check out -the [iSCSIDsc wiki](https://github.com/PlagueHO/iSCSIDsc/wiki). - -## Branches - -### master - -[![Build status](https://ci.appveyor.com/api/projects/status/xnuik3kpyag237mv/branch/master?svg=true)](https://ci.appveyor.com/project/PlagueHO/iSCSIDsc/branch/master) -[![codecov](https://codecov.io/gh/PlagueHO/iSCSIDsc/branch/master/graph/badge.svg)](https://codecov.io/gh/PlagueHO/iSCSIDsc/branch/master) - -This is the branch containing the latest release - no contributions should be made -directly to this branch. - -### dev - -[![Build status](https://ci.appveyor.com/api/projects/status/xnuik3kpyag237mv/branch/dev?svg=true)](https://ci.appveyor.com/project/PlagueHO/iSCSIDsc/branch/dev) -[![codecov](https://codecov.io/gh/PlagueHO/iSCSIDsc/branch/dev/graph/badge.svg)](https://codecov.io/gh/PlagueHO/iSCSIDsc/branch/dev) - -This is the development branch to which contributions should be proposed by contributors -as pull requests. This development branch will periodically be merged to the master -branch, and be released to [PowerShell Gallery](https://www.powershellgallery.com/). - -## Contributing - -Please check out common DSC Resources [contributing guidelines](https://github.com/PowerShell/DscResource.Kit/blob/master/CONTRIBUTING.md). +the [iSCSIDsc wiki](https://github.com/dsccommunity/iSCSIDsc/wiki). ## Requirements diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 new file mode 100644 index 0000000..bf0130b --- /dev/null +++ b/RequiredModules.psd1 @@ -0,0 +1,22 @@ +@{ + PSDependOptions = @{ + AddToPath = $true + Target = 'output\RequiredModules' + Parameters = @{ + Repository = '' + } + } + + InvokeBuild = 'latest' + PSScriptAnalyzer = 'latest' + Pester = 'latest' + Plaster = 'latest' + ModuleBuilder = 'latest' + ChangelogManagement = 'latest' + Sampler = 'latest' + MarkdownLinkCheck = 'latest' + 'DscResource.Test' = 'latest' + 'DscResource.AnalyzerRules' = 'latest' + 'DscResource.DocGenerator' = 'latest' + xDscResourceDesigner = 'latest' +} diff --git a/Resolve-Dependency.ps1 b/Resolve-Dependency.ps1 new file mode 100644 index 0000000..ec909b8 --- /dev/null +++ b/Resolve-Dependency.ps1 @@ -0,0 +1,288 @@ +[CmdletBinding()] +param +( + [Parameter()] + [String] + $DependencyFile = 'RequiredModules.psd1', + + [Parameter()] + [String] + # Path for PSDepend to be bootstrapped and save other dependencies. + # Can also be CurrentUser or AllUsers if you wish to install the modules in such scope + # Default to $PWD.Path/output/modules + $PSDependTarget = (Join-Path $PSScriptRoot './output/RequiredModules'), + + [Parameter()] + [uri] + # URI to use for Proxy when attempting to Bootstrap PackageProvider & PowerShellGet + $Proxy, + + [Parameter()] + # Credential to contact the Proxy when provided + [PSCredential]$ProxyCredential, + + [Parameter()] + [ValidateSet('CurrentUser', 'AllUsers')] + [String] + # Scope to bootstrap the PackageProvider and PSGet if not available + $Scope = 'CurrentUser', + + [Parameter()] + [String] + # Gallery to use when bootstrapping PackageProvider, PSGet and when calling PSDepend (can be overridden in Dependency files) + $Gallery = 'PSGallery', + + [Parameter()] + [PSCredential] + # Credentials to use with the Gallery specified above + $GalleryCredential, + + + [Parameter()] + [switch] + # Allow you to use a locally installed version of PowerShellGet older than 1.6.0 (not recommended, default to $false) + $AllowOldPowerShellGetModule, + + [Parameter()] + [String] + # Allow you to specify a minimum version fo PSDepend, if you're after specific features. + $MinimumPSDependVersion, + + [Parameter()] + [Switch] + $AllowPrerelease, + + [Parameter()] + [Switch] + $WithYAML +) + +# Load Defaults for parameters values from Resolve-Dependency.psd1 if not provided as parameter +try +{ + Write-Verbose -Message "Importing Bootstrap default parameters from '$PSScriptRoot/Resolve-Dependency.psd1'." + $ResolveDependencyDefaults = Import-PowerShellDataFile -Path (Join-Path $PSScriptRoot '.\Resolve-Dependency.psd1' -Resolve -ErrorAction Stop) + $ParameterToDefault = $MyInvocation.MyCommand.ParameterSets.Where{ $_.Name -eq $PSCmdlet.ParameterSetName }.Parameters.Keys + if ($ParameterToDefault.Count -eq 0) + { + $ParameterToDefault = $MyInvocation.MyCommand.Parameters.Keys + } + # Set the parameters available in the Parameter Set, or it's not possible to choose yet, so all parameters are an option + foreach ($ParamName in $ParameterToDefault) + { + if (-Not $PSBoundParameters.Keys.Contains($ParamName) -and $ResolveDependencyDefaults.ContainsKey($ParamName)) + { + Write-Verbose -Message "Setting $ParamName with $($ResolveDependencyDefaults[$ParamName])" + try + { + $variableValue = $ResolveDependencyDefaults[$ParamName] + if ($variableValue -is [string]) + { + $variableValue = $ExecutionContext.InvokeCommand.ExpandString($variableValue) + } + $PSBoundParameters.Add($ParamName, $variableValue) + Set-Variable -Name $ParamName -value $variableValue -Force -ErrorAction SilentlyContinue + } + catch + { + Write-Verbose -Message "Error adding default for $ParamName : $($_.Exception.Message)" + } + } + } +} +catch +{ + Write-Warning -Message "Error attempting to import Bootstrap's default parameters from $(Join-Path $PSScriptRoot '.\Resolve-Dependency.psd1'): $($_.Exception.Message)." +} + +Write-Progress -Activity "Bootstrap:" -PercentComplete 0 -CurrentOperation "NuGet Bootstrap" + +if (!(Get-PackageProvider -Name NuGet -ForceBootstrap -ErrorAction SilentlyContinue)) +{ + $providerBootstrapParams = @{ + Name = 'nuget' + force = $true + ForceBootstrap = $true + ErrorAction = 'Stop' + } + + switch ($PSBoundParameters.Keys) + { + 'Proxy' + { + $providerBootstrapParams.Add('Proxy', $Proxy) + } + 'ProxyCredential' + { + $providerBootstrapParams.Add('ProxyCredential', $ProxyCredential) + } + 'Scope' + { + $providerBootstrapParams.Add('Scope', $Scope) + } + } + + if ($AllowPrerelease) + { + $providerBootstrapParams.Add('AllowPrerelease', $true) + } + + Write-Information "Bootstrap: Installing NuGet Package Provider from the web (Make sure Microsoft addresses/ranges are allowed)" + $null = Install-PackageProvider @providerBootstrapParams + $latestNuGetVersion = (Get-PackageProvider -Name NuGet -ListAvailable | Select-Object -First 1).Version.ToString() + Write-Information "Bootstrap: Importing NuGet Package Provider version $latestNuGetVersion to current session." + $Null = Import-PackageProvider -Name NuGet -RequiredVersion $latestNuGetVersion -Force +} + +Write-Progress -Activity "Bootstrap:" -PercentComplete 10 -CurrentOperation "Ensuring Gallery $Gallery is trusted" + +# Fail if the given PSGallery is not Registered +$Policy = (Get-PSRepository $Gallery -ErrorAction Stop).InstallationPolicy +Set-PSRepository -Name $Gallery -InstallationPolicy Trusted -ErrorAction Ignore +try +{ + Write-Progress -Activity "Bootstrap:" -PercentComplete 25 -CurrentOperation "Checking PowerShellGet" + # Ensure the module is loaded and retrieve the version you have + $PowerShellGetVersion = (Import-Module PowerShellGet -PassThru -ErrorAction SilentlyContinue).Version + + Write-Verbose "Bootstrap: The PowerShellGet version is $PowerShellGetVersion" + # Versions below 1.6.0 are considered old, unreliable & not recommended + if (!$PowerShellGetVersion -or ($PowerShellGetVersion -lt [System.version]'1.6.0' -and !$AllowOldPowerShellGetModule)) + { + Write-Progress -Activity "Bootstrap:" -PercentComplete 40 -CurrentOperation "Installing newer version of PowerShellGet" + $InstallPSGetParam = @{ + Name = 'PowerShellGet' + Force = $true + SkipPublisherCheck = $true + AllowClobber = $true + Scope = $Scope + Repository = $Gallery + } + + switch ($PSBoundParameters.Keys) + { + 'Proxy' + { + $InstallPSGetParam.Add('Proxy', $Proxy) + } + 'ProxyCredential' + { + $InstallPSGetParam.Add('ProxyCredential', $ProxyCredential) + } + 'GalleryCredential' + { + $InstallPSGetParam.Add('Credential', $GalleryCredential) + } + } + + Install-Module @InstallPSGetParam + Remove-Module PowerShellGet -force -ErrorAction SilentlyContinue + Import-Module PowerShellGet -Force + $NewLoadedVersion = (Get-Module PowerShellGet).Version.ToString() + Write-Information "Bootstrap: PowerShellGet version loaded is $NewLoadedVersion" + Write-Progress -Activity "Bootstrap:" -PercentComplete 60 -CurrentOperation "Installing newer version of PowerShellGet" + } + + # Try to import the PSDepend module from the available modules + try + { + $ImportPSDependParam = @{ + Name = 'PSDepend' + ErrorAction = 'Stop' + Force = $true + } + + if ($MinimumPSDependVersion) + { + $ImportPSDependParam.add('MinimumVersion', $MinimumPSDependVersion) + } + $null = Import-Module @ImportPSDependParam + } + catch + { + # PSDepend module not found, installing or saving it + if ($PSDependTarget -in 'CurrentUser', 'AllUsers') + { + Write-Debug "PSDepend module not found. Attempting to install from Gallery $Gallery" + Write-Warning "Installing PSDepend in $PSDependTarget Scope" + $InstallPSDependParam = @{ + Name = 'PSDepend' + Repository = $Gallery + Force = $true + Scope = $PSDependTarget + SkipPublisherCheck = $true + AllowClobber = $true + } + + if ($MinimumPSDependVersion) + { + $InstallPSDependParam.add('MinimumVersion', $MinimumPSDependVersion) + } + + Write-Progress -Activity "Bootstrap:" -PercentComplete 75 -CurrentOperation "Installing PSDepend from $Gallery" + Install-Module @InstallPSDependParam + } + else + { + Write-Debug "PSDepend module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" + $SaveModuleParam = @{ + Name = 'PSDepend' + Repository = $Gallery + Path = $PSDependTarget + } + + if ($MinimumPSDependVersion) + { + $SaveModuleParam.add('MinimumVersion', $MinimumPSDependVersion) + } + + Write-Progress -Activity "Bootstrap:" -PercentComplete 75 -CurrentOperation "Saving & Importing PSDepend from $Gallery to $Scope" + Save-Module @SaveModuleParam + } + } + finally + { + Write-Progress -Activity "Bootstrap:" -PercentComplete 100 -CurrentOperation "Loading PSDepend" + # We should have successfully bootstrapped PSDepend. Fail if not available + Import-Module PSDepend -ErrorAction Stop + } + + if ($WithYAML) + { + if (-Not (Get-Module -ListAvailable -Name 'PowerShell-Yaml')) + { + Write-Verbose "PowerShell-Yaml module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" + $SaveModuleParam = @{ + Name = 'PowerShell-Yaml' + Repository = $Gallery + Path = $PSDependTarget + } + + Save-Module @SaveModuleParam + Import-Module "PowerShell-Yaml" -ErrorAction Stop + } + else + { + Write-Verbose "PowerShell-Yaml is already available" + } + } + + Write-Progress -Activity "PSDepend:" -PercentComplete 0 -CurrentOperation "Restoring Build Dependencies" + if (Test-Path $DependencyFile) + { + $PSDependParams = @{ + Force = $true + Path = $DependencyFile + } + + # TODO: Handle when the Dependency file is in YAML, and -WithYAML is specified + Invoke-PSDepend @PSDependParams + } + Write-Progress -Activity "PSDepend:" -PercentComplete 100 -CurrentOperation "Dependencies restored" -Completed +} +finally +{ + # Reverting the Installation Policy for the given gallery + Set-PSRepository -Name $Gallery -InstallationPolicy $Policy + Write-Verbose "Project Bootstrapped, returning to Invoke-Build" +} diff --git a/Resolve-Dependency.psd1 b/Resolve-Dependency.psd1 new file mode 100644 index 0000000..2ae8c0d --- /dev/null +++ b/Resolve-Dependency.psd1 @@ -0,0 +1,5 @@ +@{ + Gallery = 'PSGallery' + AllowPrerelease = $false + WithYAML = $true +} diff --git a/Tests/Integration/DSR_iSCSIInitiator.Integration.Tests.ps1 b/Tests/Integration/DSR_iSCSIInitiator.Integration.Tests.ps1 deleted file mode 100644 index c1df67b..0000000 --- a/Tests/Integration/DSR_iSCSIInitiator.Integration.Tests.ps1 +++ /dev/null @@ -1,159 +0,0 @@ -$script:DSCModuleName = 'iSCSIDsc' -$script:DSCResourceName = 'DSR_iSCSIInitiator' - -# These tests are disabled because they require iSCSI Loopback -# iSCSI Loopback is supposed to work in Windows Server 2012 R2 -# However, as of 2016-01-03 I have not been able to get it to -# work and there is no documentation available on how to do so. -# See http://blogs.technet.com/b/filecab/archive/2012/05/21/introduction-of-iscsi-target-in-windows-server-2012.aspx -# This has been left here in case someone is able to figure out -# how to get it going. -return - -#region HEADER -# Integration Test Template Version: 1.1.1 -[System.String] $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) - -if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` - (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) -{ - & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) -} - -Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force -Import-Module (Join-Path -Path $script:moduleRoot -ChildPath "$($script:DSCModuleName).psd1") -Force -$TestEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:DSCModuleName ` - -DSCResourceName $script:DSCResourceName ` - -TestType Integration -#endregion - -# Using try/finally to always cleanup even if something awful happens. -try -{ - # Ensure that the tests can be performed on this computer - $ProductType = (Get-CimInstance Win32_OperatingSystem).ProductType - Describe 'Environment' { - Context 'Operating System' { - It 'Should be a Server OS' { - $ProductType | Should -Be 3 - } - } - } - if ($ProductType -ne 3) - { - Break - } - - $Installed = (Get-WindowsFeature -Name FS-iSCSITarget-Server).Installed - Describe 'Environment' { - Context 'Windows Features' { - It 'Should have the iSCSI Target Feature Installed' { - $Installed | Should -Be $true - } - } - } - if ($Installed -eq $false) - { - Break - } - - #region Integration Tests - $ConfigFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:DSCResourceName).config.ps1" - . $ConfigFile - - Describe "$($script:DSCResourceName)_Integration" { - BeforeAll { - # Create a Server Target on this computer to test with - $VirtualDiskPath = Join-Path -Path $ENV:Temp -ChildPath ([System.IO.Path]::ChangeExtension([System.IO.Path]::GetRandomFileName(),'vhdx')) - New-iSCSIVirtualDisk ` - -ComputerName LOCALHOST ` - -Path $VirtualDiskPath ` - -SizeBytes 500MB - New-iSCSIServerTarget ` - -TargetName $TargetName ` - -InitiatorIds "Iqn:iqn.1991-05.com.microsoft:$($Initiator.InitiatorPortalAddress)" ` - -ComputerName LOCALHOST - Add-IscsiVirtualDiskTargetMapping ` - -ComputerName LOCALHOST ` - -TargetName $TargetName ` - -Path $VirtualDiskPath - } # BeforeAll - - #region DEFAULT TESTS - It 'Should compile and apply the MOF without throwing' { - { - & "$($script:DSCResourceName)_Config" -OutputPath $TestDrive - Start-DscConfiguration -Path $TestDrive -ComputerName localhost -Wait -Verbose -Force - } | Should -Not -Throw - } - - It 'should be able to call Get-DscConfiguration without throwing' { - { Get-DscConfiguration -Verbose -ErrorAction Stop } | Should -Not -throw - } - #endregion - - It 'Should have set the resource and all the parameters should match' { - # Get the Target Portal details - $TargetPortalNew = Get-iSCSITargetPortal ` - -TargetPortalAddress $TargetPortal.TargetPortalAddress ` - -InitiatorPortalAddress $TargetPortal.InitiatorPortalAddress - $Initiator.TargetPortalAddress | Should -Be $TargetPortalNew.TargetPortalAddress - $Initiator.TargetPortalPortNumber | Should -Be $TargetPortalNew.TargetPortalPortNumber - $Initiator.InitiatorInstanceName | Should -Be $TargetPortalNew.InitiatorInstanceName - $Initiator.InitiatorPortalAddress | Should -Be $TargetPortalNew.InitiatorPortalAddress - $Initiator.IsDataDigest | Should -Be $TargetPortalNew.IsDataDigest - $Initiator.IsHeaderDigest | Should -Be $TargetPortalNew.IsHeaderDigest - $TargetNew = Get-iSCSITarget ` - -NodeAddress $Target.NodeAddress - $Initiator.IsConnected | Should -Be $True - $Initiator.NodeAddress | Should -Be $TargetNew.NodeAddress - $SessionNew = Get-iSCSISession ` - -IscsiTarget $TargetNew - $Initiator.TargetPortalAddress | Should -Be $SessionNew.TargetAddress - $Initiator.InitiatorPortalAddress | Should -Be $SessionNew.InitiatorAddress - $Initiator.TargetPortalPortNumber | Should -Be $SessionNew.TargetPortNumber - $Initiator.ConnectionIdentifier | Should -Be $SessionNew.ConnectionIdentifier - $ConnectionNew = Get-iSCSIConnection ` - -NodeAddress $Target.NodeAddress - $Initiator.AuthenticationType | Should -Be $ConnectionNew.AuthenticationType - $Initiator.InitiatorInstanceName | Should -Be $ConnectionNew.InitiatorInstanceName - $Initiator.InitiatorPortalAddress | Should -Be $ConnectionNew.InitiatorPortalAddress - $Initiator.IsConnected | Should -Be $ConnectionNew.IsConnected - $Initiator.IsDataDigest | Should -Be $ConnectionNew.IsDataDigest - $Initiator.IsDiscovered | Should -Be $ConnectionNew.IsDiscovered - $Initiator.IsHeaderDigest | Should -Be $ConnectionNew.IsHeaderDigest - $Initiator.IsPersistent | Should -Be $ConnectionNew.IsPersistent - $iSNSServerNew = Get-WmiObject -Class MSiSCSIInitiator_iSNSServerClass -Namespace root\wmi - # The iSNS Server is not usually accessible so won't be able to be set - # $Initiator.iSNSServer | Should Be $iSNSServerNew.iSNSServerAddress - } - - AfterAll { - # Clean up - Disconnect-IscsiTarget ` - -NodeAddress $Initiator.NodeAddress ` - -Confirm:$False - Remove-IscsiTargetPortal ` - -TargetPortalAddress $Initiator.TargetPortalAddress ` - -InitiatorPortalAddress $Initiator.InitiatorPortalAddress ` - -Confirm:$False - Remove-iSCSIServerTarget ` - -ComputerName LOCALHOST ` - -TargetName $TargetName - Remove-iSCSIVirtualDisk ` - -ComputerName LOCALHOST ` - -Path $VirtualDiskPath - Remove-Item ` - -Path $VirtualDiskPath ` - -Force - } # AfterAll - } - #endregion -} -finally -{ - #region FOOTER - Restore-TestEnvironment -TestEnvironment $TestEnvironment - #endregion -} diff --git a/Tests/Integration/DSR_iSCSIInitiator.config.ps1 b/Tests/Integration/DSR_iSCSIInitiator.config.ps1 deleted file mode 100644 index 5febe0c..0000000 --- a/Tests/Integration/DSR_iSCSIInitiator.config.ps1 +++ /dev/null @@ -1,42 +0,0 @@ -$IPAddress = (Get-NetIPAddress -InterfaceIndex (Get-NetConnectionProfile -IPv4Connectivity Internet).InterfaceIndex -AddressFamily IPv4).IPAddress -$TargetName = 'TestServerTarget' -$Initiator = @{ - NodeAddress = "iqn.1991-05.com.microsoft:$($ENV:ComputerName)-$TargetName-target-target" - TargetPortalAddress = $ENV:ComputerName - InitiatorPortalAddress = $ENV:ComputerName - Ensure = 'Present' - TargePortalPortNumber = 3260 - InitiatorInstanceName = 'ROOT\ISCSIPRT\0000_0' - AuthenticationType = 'OneWayCHAP' - ChapUsername = 'MyUsername' - ChapSecret = 'MySecret' - IsDataDigest = $false - IsHeaderDigest = $false - IsMultipathEnabled = $false - IsPersistent = $true - ReportToPnP = $true - iSNSServer = "isns.contoso.com" -} - -Configuration DSR_iSCSIInitiator_Config { - Import-DscResource -ModuleName iSCSIDsc - node localhost { - iSCSIInitiator Integration_Test { - NodeAddress = $Initiator.NodeAddress - TargetPortalAddress = $Initiator.TargetPortalAddress - InitiatorPortalAddress = $Initiator.InitiatorPortalAddress - Ensure = $Initiator.Ensure - TargetPortalPortNumber = $Initiator.TargetPortalPortNumber - InitiatorInstanceName = $Initiator.InitiatorInstanceName - AuthenticationType = $Initiator.AuthenticationType - ChapUsername = $Initiator.ChapUsername - ChapSecret = $Initiator.ChapSecret - IsDataDigest = $Initiator.IsDataDigest - IsHeaderDigest = $Initiator.IsHeaderDigest - IsMultipathEnabled = $Initiator.IsMultipathEnabled - IsPersistent = $Initiator.IsPersistent - ReportToPnP = $Initiator.ReportToPnP - iSNSServer = $Initiator.iSNSServer - } - } -} diff --git a/Tests/Integration/DSR_iSCSIServerTarget.Integration.Tests.ps1 b/Tests/Integration/DSR_iSCSIServerTarget.Integration.Tests.ps1 deleted file mode 100644 index 6e33427..0000000 --- a/Tests/Integration/DSR_iSCSIServerTarget.Integration.Tests.ps1 +++ /dev/null @@ -1,108 +0,0 @@ -$script:DSCModuleName = 'iSCSIDsc' -$script:DSCResourceName = 'DSR_iSCSIServerTarget' - -#region HEADER -# Integration Test Template Version: 1.1.1 -[System.String] $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) - -if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` - (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) -{ - & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) -} - -Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force -Import-Module (Join-Path -Path $script:moduleRoot -ChildPath "$($script:DSCModuleName).psd1") -Force -$TestEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:DSCModuleName ` - -DSCResourceName $script:DSCResourceName ` - -TestType Integration -#endregion - -# Using try/finally to always cleanup even if something awful happens. -try -{ - # Ensure that the tests can be performed on this computer - $ProductType = (Get-CimInstance Win32_OperatingSystem).ProductType - Describe 'Environment' { - Context 'Operating System' { - It 'Should be a Server OS' { - $ProductType | Should -Be 3 - } - } - } - if ($ProductType -ne 3) - { - Break - } - - $Installed = (Get-WindowsFeature -Name FS-iSCSITarget-Server).Installed - Describe 'Environment' { - Context 'Windows Features' { - It 'Should have the iSCSI Target Feature Installed' { - $Installed | Should -Be $true - } - } - } - if ($Installed -eq $false) - { - Break - } - - #region Integration Tests - $ConfigFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:DSCResourceName).config.ps1" - . $ConfigFile - - Describe "$($script:DSCResourceName)_Integration" { - BeforeAll { - New-iSCSIVirtualDisk ` - -Path $VirtualDisk.Path ` - -Size 10GB - } # BeforeAll - - #region DEFAULT TESTS - It 'Should compile and apply the MOF without throwing' { - { - & "$($script:DSCResourceName)_Config" -OutputPath $TestDrive - Start-DscConfiguration -Path $TestDrive -ComputerName localhost -Wait -Verbose -Force - } | Should -Not -Throw - } - - It 'should be able to call Get-DscConfiguration without throwing' { - { Get-DscConfiguration -Verbose -ErrorAction Stop } | Should -Not -throw - } - #endregion - - It 'Should have set the resource and all the parameters should match' { - # Get the Rule details - $ServerTargetNew = Get-iSCSIServerTarget -TargetName $ServerTarget.TargetName - $ServerTargetNew.TargetName | Should -Be $ServerTarget.TargetName - $ServerTargetNew.InitiatorIds | Should -Be $ServerTarget.InitiatorIds - $ServerTargetNew.LunMappings.Path | Should -Be $ServerTarget.Paths - $iSNSServerNew = Get-WmiObject -Class WT_iSNSServer -Namespace root\wmi - # The iSNS Server is not usually accessible so won't be able to be set - # $iSNSServerNew.ServerName | Should Be $ServerTarget.iSNSServer - } - - AfterAll { - # Clean up - Get-WmiObject ` - -Class WT_iSNSServer ` - -Namespace root\wmi | Remove-WmiObject - Remove-iSCSIServerTarget ` - -TargetName $ServerTarget.TargetName - Remove-iSCSIVirtualDisk ` - -Path $VirtualDisk.Path - Remove-Item ` - -Path $VirtualDisk.Path ` - -Force - } # AfterAll - } - #endregion -} -finally -{ - #region FOOTER - Restore-TestEnvironment -TestEnvironment $TestEnvironment - #endregion -} diff --git a/Tests/Integration/DSR_iSCSIServerTarget.config.ps1 b/Tests/Integration/DSR_iSCSIServerTarget.config.ps1 deleted file mode 100644 index 848e30a..0000000 --- a/Tests/Integration/DSR_iSCSIServerTarget.config.ps1 +++ /dev/null @@ -1,23 +0,0 @@ -$VirtualDisk = @{ - Path = Join-Path -Path $ENV:Temp -ChildPath 'TestiSCSIServerTarget.vhdx' -} -$ServerTarget = @{ - TargetName = 'testtarget' - Ensure = 'Present' - InitiatorIds = @( 'Iqn:iqn.1991-05.com.microsoft:fs1.contoso.com','Iqn:iqn.1991-05.com.microsoft:fs2.contoso.com' ) - Paths = @( $VirtualDisk.Path ) - iSNSServer = 'isns.contoso.com' -} - -Configuration DSR_iSCSIServerTarget_Config { - Import-DscResource -ModuleName iSCSIDsc - node localhost { - iSCSIServerTarget Integration_Test { - TargetName = $ServerTarget.TargetName - Ensure = $ServerTarget.Ensure - InitiatorIds = $ServerTarget.InitiatorIds - Paths = $ServerTarget.Paths - iSNSServer = $ServerTarget.iSNSServer - } - } -} diff --git a/Tests/Integration/DSR_iSCSIVirtualDisk.Integration.Tests.ps1 b/Tests/Integration/DSR_iSCSIVirtualDisk.Integration.Tests.ps1 deleted file mode 100644 index 848affb..0000000 --- a/Tests/Integration/DSR_iSCSIVirtualDisk.Integration.Tests.ps1 +++ /dev/null @@ -1,95 +0,0 @@ -$script:DSCModuleName = 'iSCSIDsc' -$script:DSCResourceName = 'DSR_iSCSIVirtualDisk' - -#region HEADER -# Integration Test Template Version: 1.1.1 -[System.String] $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) - -if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` - (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) -{ - & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) -} - -Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force -Import-Module (Join-Path -Path $script:moduleRoot -ChildPath "$($script:DSCModuleName).psd1") -Force -$TestEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:DSCModuleName ` - -DSCResourceName $script:DSCResourceName ` - -TestType Integration -#endregion - -# Using try/finally to always cleanup even if something awful happens. -try -{ - # Ensure that the tests can be performed on this computer - $ProductType = (Get-CimInstance Win32_OperatingSystem).ProductType - Describe 'Environment' { - Context 'Operating System' { - It 'Should be a Server OS' { - $ProductType | Should -Be 3 - } - } - } - if ($ProductType -ne 3) - { - Break - } - - $Installed = (Get-WindowsFeature -Name FS-iSCSITarget-Server).Installed - Describe 'Environment' { - Context 'Windows Features' { - It 'Should have the iSCSI Target Feature Installed' { - $Installed | Should -Be $true - } - } - } - if ($Installed -eq $false) - { - Break - } - - #region Integration Tests - $ConfigFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:DSCResourceName).config.ps1" - . $ConfigFile - - Describe "$($script:DSCResourceName)_Integration" { - #region DEFAULT TESTS - It 'Should compile and apply the MOF without throwing' { - { - & "$($script:DSCResourceName)_Config" -OutputPath $TestDrive - Start-DscConfiguration -Path $TestDrive -ComputerName localhost -Wait -Verbose -Force - } | Should -Not -Throw - } - - It 'should be able to call Get-DscConfiguration without throwing' { - { Get-DscConfiguration -Verbose -ErrorAction Stop } | Should -Not -throw - } - #endregion - - It 'Should have set the resource and all the parameters should match' { - # Get the Rule details - $virtualDiskNew = Get-iSCSIVirtualDisk -Path $VirtualDisk.Path - $VirtualDisk.Path | Should -Be $virtualDiskNew.Path - $VirtualDisk.DiskType | Should -Be $virtualDiskNew.DiskType - $VirtualDisk.Size | Should -Be $virtualDiskNew.Size - $VirtualDisk.Description | Should -Be $virtualDiskNew.Description - } - - AfterAll { - # Clean up - Remove-iSCSIVirtualDisk ` - -Path $VirtualDisk.Path - Remove-Item ` - -Path $VirtualDisk.Path ` - -Force - } # AfterAll - } - #endregion -} -finally -{ - #region FOOTER - Restore-TestEnvironment -TestEnvironment $TestEnvironment - #endregion -} diff --git a/Tests/Integration/DSR_iSCSIVirtualDisk.config.ps1 b/Tests/Integration/DSR_iSCSIVirtualDisk.config.ps1 deleted file mode 100644 index 63bbbd4..0000000 --- a/Tests/Integration/DSR_iSCSIVirtualDisk.config.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -$VirtualDisk = @{ - Path = Join-Path -Path $ENV:Temp -ChildPath 'TestiSCSIVirtualDisk.vhdx' - Ensure = 'Present' - DiskType = 'Dynamic' - Size = 100MB - Description = 'Integration Test iSCSI Virtual Disk' -} - -Configuration DSR_iSCSIVirtualDisk_Config { - Import-DscResource -ModuleName iSCSIDsc - node localhost { - iSCSIVirtualDisk Integration_Test { - Path = $VirtualDisk.Path - Ensure = $VirtualDisk.Ensure - DiskType = $VirtualDisk.DiskType - SizeBytes = $VirtualDisk.Size - Description = $VirtualDisk.Description - } - } -} diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 8ff7a59..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,43 +0,0 @@ -#---------------------------------# -# environment configuration # -#---------------------------------# -version: 1.5.1.{build} -install: - - git clone https://github.com/PowerShell/DscResource.Tests - - - ps: | - Install-WindowsFeature FS-iSCSITarget-Server,ISNS - Start-Service msiscsi - $moduleName = 'iSCSIDsc' - $mainModuleFolder = "Modules\$moduleName" - Import-Module "$env:APPVEYOR_BUILD_FOLDER\DscResource.Tests\AppVeyor.psm1" - Invoke-AppveyorInstallTask - -#---------------------------------# -# build configuration # -#---------------------------------# - -build: false - -#---------------------------------# -# test configuration # -#---------------------------------# - -test_script: - - ps: | - Invoke-AppveyorTestScriptTask ` - -CodeCoverage ` - -CodeCovIo - -#---------------------------------# -# deployment configuration # -#---------------------------------# - -# scripts to run before deployment -deploy_script: - - ps: | - Invoke-AppveyorAfterTestTask ` - -Type 'Wiki' ` - -ResourceModuleName $moduleName - - Invoke-AppVeyorDeployTask diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..9b9c02c --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,329 @@ +trigger: + branches: + include: + - master + paths: + include: + - source/* + tags: + include: + - "v*" + exclude: + - "*-*" + +stages: + - stage: Build + jobs: + - job: Package_Module + displayName: 'Package Module' + pool: + vmImage: 'ubuntu 16.04' + steps: + - task: GitVersion@5 + name: gitversion + displayName: 'Evaluate Next Version' + inputs: + runtime: 'core' + configFilePath: 'GitVersion.yml' + + - task: PowerShell@2 + name: package + displayName: 'Build & Package Module' + inputs: + filePath: './build.ps1' + arguments: '-Tasks pack -ResolveDependency' + pwsh: true + env: + ModuleVersion: $(gitVersion.Informationalversion) + + - task: PublishBuildArtifacts@1 + displayName: 'Publish Build Artifact' + inputs: + PathtoPublish: 'output/' + ArtifactName: 'output' + publishLocation: 'Container' + + - stage: Test + dependsOn: Build + jobs: + - job: Test_HQRM + displayName: 'HQRM' + pool: + vmImage: 'windows-2019' + timeoutInMinutes: 0 + steps: + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + downloadType: 'single' + artifactName: 'output' + downloadPath: '$(Build.SourcesDirectory)' + + - task: PowerShell@2 + name: test + displayName: 'Run HQRM Test' + inputs: + filePath: './build.ps1' + arguments: '-Tasks hqrmtest' + pwsh: false + + - task: PublishTestResults@2 + displayName: 'Publish Test Results' + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: 'output/testResults/NUnit*.xml' + testRunTitle: 'HQRM' + condition: succeededOrFailed() + + - job: Test_Unit_2016 + displayName: 'Unit (Windows Server 2016)' + pool: + vmImage: 'vs2017-win2016' + timeoutInMinutes: 0 + steps: + - powershell: | + $repositoryOwner,$repositoryName = $env:BUILD_REPOSITORY_NAME -split '/' + echo "##vso[task.setvariable variable=RepositoryOwner;isOutput=true]$repositoryOwner" + echo "##vso[task.setvariable variable=RepositoryName;isOutput=true]$repositoryName" + name: dscBuildVariable + displayName: 'Set Environment Variables' + + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + downloadType: 'single' + artifactName: 'output' + downloadPath: '$(Build.SourcesDirectory)' + + - task: PowerShell@2 + name: installDFSFeatures + displayName: 'Install iSCSI Features' + inputs: + targetType: 'inline' + script: | + Install-WindowsFeature -Name FS-iSCSITarget-Server, ISNS + Start-Service -Name msiscsi + pwsh: false + + - task: PowerShell@2 + name: test + displayName: 'Run Unit Test' + inputs: + filePath: './build.ps1' + arguments: "-Tasks test -PesterScript 'tests/Unit'" + pwsh: false + + - task: PublishTestResults@2 + displayName: 'Publish Test Results' + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: 'output/testResults/NUnit*.xml' + testRunTitle: 'Unit (Windows Server 2016)' + condition: succeededOrFailed() + + - task: PublishCodeCoverageResults@1 + displayName: 'Publish Code Coverage' + condition: succeededOrFailed() + inputs: + codeCoverageTool: 'JaCoCo' + summaryFileLocation: 'output/testResults/CodeCov*.xml' + pathToSources: '$(Build.SourcesDirectory)/output/$(DscBuildVariable.RepositoryName)' + + - job: Test_Integration_2016 + displayName: 'Integration (Windows Server 2016)' + pool: + vmImage: 'vs2017-win2016' + timeoutInMinutes: 0 + steps: + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + downloadType: 'single' + artifactName: 'output' + downloadPath: '$(Build.SourcesDirectory)' + + - task: PowerShell@2 + name: configureWinRM + displayName: 'Configure WinRM' + inputs: + targetType: 'inline' + script: 'winrm quickconfig -quiet' + pwsh: false + + - task: PowerShell@2 + name: installDFSFeatures + displayName: 'Install iSCSI Features' + inputs: + targetType: 'inline' + script: | + Install-WindowsFeature -Name FS-iSCSITarget-Server, ISNS + Start-Service -Name msiscsi + pwsh: false + + - task: PowerShell@2 + name: test + displayName: 'Run Integration Test' + inputs: + filePath: './build.ps1' + arguments: "-Tasks test -PesterScript 'tests/Integration' -CodeCoverageThreshold 0" + pwsh: false + + - task: PublishTestResults@2 + displayName: 'Publish Test Results' + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: 'output/testResults/NUnit*.xml' + testRunTitle: 'Integration (Windows Server 2016)' + condition: succeededOrFailed() + + - job: Test_Unit_2019 + displayName: 'Unit (Windows Server 2019)' + pool: + vmImage: 'windows-2019' + timeoutInMinutes: 0 + steps: + - powershell: | + $repositoryOwner,$repositoryName = $env:BUILD_REPOSITORY_NAME -split '/' + echo "##vso[task.setvariable variable=RepositoryOwner;isOutput=true]$repositoryOwner" + echo "##vso[task.setvariable variable=RepositoryName;isOutput=true]$repositoryName" + name: dscBuildVariable + displayName: 'Set Environment Variables' + + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + downloadType: 'single' + artifactName: 'output' + downloadPath: '$(Build.SourcesDirectory)' + + - task: PowerShell@2 + name: installDFSFeatures + displayName: 'Install iSCSI Features' + inputs: + targetType: 'inline' + script: | + Install-WindowsFeature -Name FS-iSCSITarget-Server, ISNS + Start-Service -Name msiscsi + pwsh: false + + - task: PowerShell@2 + name: test + displayName: 'Run Unit Test' + inputs: + filePath: './build.ps1' + arguments: "-Tasks test -PesterScript 'tests/Unit'" + pwsh: false + + - task: PublishTestResults@2 + displayName: 'Publish Test Results' + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: 'output/testResults/NUnit*.xml' + testRunTitle: 'Unit (Windows Server 2019)' + condition: succeededOrFailed() + + - task: PublishCodeCoverageResults@1 + displayName: 'Publish Code Coverage' + condition: succeededOrFailed() + inputs: + codeCoverageTool: 'JaCoCo' + summaryFileLocation: 'output/testResults/CodeCov*.xml' + pathToSources: '$(Build.SourcesDirectory)/output/$(dscBuildVariable.RepositoryName)' + + - job: Test_Integration_2019 + displayName: 'Integration (Windows Server 2019)' + pool: + vmImage: 'windows-2019' + timeoutInMinutes: 0 + steps: + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + downloadType: 'single' + artifactName: 'output' + downloadPath: '$(Build.SourcesDirectory)' + + - task: PowerShell@2 + name: configureWinRM + displayName: 'Configure WinRM' + inputs: + targetType: 'inline' + script: 'winrm quickconfig -quiet' + pwsh: false + + - task: PowerShell@2 + name: installDFSFeatures + displayName: 'Install iSCSI Features' + inputs: + targetType: 'inline' + script: | + Install-WindowsFeature -Name FS-iSCSITarget-Server, ISNS + Start-Service -Name msiscsi + pwsh: false + + - task: PowerShell@2 + name: test + displayName: 'Run Integration Test' + inputs: + filePath: './build.ps1' + arguments: "-Tasks test -PesterScript 'tests/Integration' -CodeCoverageThreshold 0" + pwsh: false + + - task: PublishTestResults@2 + displayName: 'Publish Test Results' + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: 'output/testResults/NUnit*.xml' + testRunTitle: 'Integration (Windows Server 2019)' + condition: succeededOrFailed() + + - stage: Deploy + dependsOn: Test + condition: | + and( + succeeded(), + or( + eq(variables['Build.SourceBranch'], 'refs/heads/master'), + startsWith(variables['Build.SourceBranch'], 'refs/tags/') + ), + contains(variables['System.TeamFoundationCollectionUri'], 'dsccommunity') + ) + jobs: + - job: Deploy_Module + displayName: 'Deploy Module' + pool: + vmImage: 'ubuntu 16.04' + steps: + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + downloadType: 'single' + artifactName: 'output' + downloadPath: '$(Build.SourcesDirectory)' + + - task: PowerShell@2 + name: publishRelease + displayName: 'Publish Release' + inputs: + filePath: './build.ps1' + arguments: '-tasks publish' + pwsh: true + env: + GitHubToken: $(GitHubToken) + GalleryApiToken: $(GalleryApiToken) + + - task: PowerShell@2 + name: sendChangelogPR + displayName: 'Send Changelog PR' + inputs: + filePath: './build.ps1' + arguments: '-tasks Create_ChangeLog_GitHub_PR' + pwsh: true + env: + GitHubToken: $(GitHubToken) diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..4630cb8 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,381 @@ +<# + +.DESCRIPTION + Bootstrap and build script for PowerShell module pipeline + +#> +[CmdletBinding()] +param +( + [Parameter(Position = 0)] + [string[]]$Tasks = '.', + + [Parameter()] + [String] + $CodeCoverageThreshold = '', + + [Parameter()] + [validateScript( + { Test-Path -Path $_ } + )] + $BuildConfig, + + [Parameter()] + # A Specific folder to build the artefact into. + $OutputDirectory = 'output', + + [Parameter()] + # Subdirectory name to build the module (under $OutputDirectory) + $BuiltModuleSubdirectory = '', + + # Can be a path (relative to $PSScriptRoot or absolute) to tell Resolve-Dependency & PSDepend where to save the required modules, + # or use CurrentUser, AllUsers to target where to install missing dependencies + # You can override the value for PSDepend in the Build.psd1 build manifest + # This defaults to $OutputDirectory/modules (by default: ./output/modules) + [Parameter()] + $RequiredModulesDirectory = $(Join-Path 'output' 'RequiredModules'), + + [Parameter()] + [object[]] + $PesterScript, + + # Filter which tags to run when invoking Pester tests + # This is used in the Invoke-Pester.pester.build.ps1 tasks + [Parameter()] + [string[]] + $PesterTag, + + # Filter which tags to exclude when invoking Pester tests + # This is used in the Invoke-Pester.pester.build.ps1 tasks + [Parameter()] + [string[]] + $PesterExcludeTag, + + # Filter which tags to run when invoking DSC Resource tests + # This is used in the DscResource.Test.build.ps1 tasks + [Parameter()] + [string[]] + $DscTestTag, + + # Filter which tags to exclude when invoking DSC Resource tests + # This is used in the DscResource.Test.build.ps1 tasks + [Parameter()] + [string[]] + $DscTestExcludeTag, + + [Parameter()] + [Alias('bootstrap')] + [switch]$ResolveDependency, + + [Parameter(DontShow)] + [AllowNull()] + $BuildInfo, + + [Parameter()] + [switch] + $AutoRestore +) + +# The BEGIN block (at the end of this file) handles the Bootstrap of the Environment before Invoke-Build can run the tasks +# if the -ResolveDependency (aka Bootstrap) is specified, the modules are already available, and can be auto loaded + +process +{ + + if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + { + # Only run the process block through InvokeBuild (Look at the Begin block at the bottom of this script) + return + } + + # Execute the Build Process from the .build.ps1 path. + Push-Location -Path $PSScriptRoot -StackName BeforeBuild + + try + { + Write-Host -ForeGroundColor magenta "[build] Parsing defined tasks" + + # Load Default BuildInfo if not provided as parameter + if (!$PSBoundParameters.ContainsKey('BuildInfo')) + { + try + { + if (Test-Path $BuildConfig) + { + $ConfigFile = (Get-Item -Path $BuildConfig) + Write-Host "[build] Loading Configuration from $ConfigFile" + $BuildInfo = switch -Regex ($ConfigFile.Extension) + { + # Native Support for PSD1 + '\.psd1' + { + Import-PowerShellDataFile -Path $BuildConfig + } + # Support for yaml when module PowerShell-Yaml is available + '\.[yaml|yml]' + { + Import-Module -ErrorAction Stop -Name 'powershell-yaml' + ConvertFrom-Yaml -Yaml (Get-Content -Raw $ConfigFile) + } + # Native Support for JSON and JSONC (by Removing comments) + '\.[json|jsonc]' + { + $JSONC = (Get-Content -Raw -Path $ConfigFile) + $JSON = $JSONC -replace '(?m)\s*//.*?$' -replace '(?ms)/\*.*?\*/' + # This should probably be converted to hashtable for splatting + $JSON | ConvertFrom-Json + } + default + { + Write-Error "Extension '$_' not supported. using @{}" + @{ } + } + } + } + else + { + Write-Host -Object "Configuration file $BuildConfig not found" -ForegroundColor Red + $BuildInfo = @{ } + } + } + catch + { + Write-Host -Object "Error loading Config $ConfigFile.`r`n Are you missing dependencies?" -ForegroundColor Yellow + Write-Host -Object "Make sure you run './build.ps1 -ResolveDependency -tasks noop' to restore the Required modules the first time" -ForegroundColor Yellow + $BuildInfo = @{ } + Write-Error $_.Exception.Message + } + } + + # If the Invoke-Build Task Header is specified in the Build Info, set it + if ($BuildInfo.TaskHeader) + { + Set-BuildHeader ([scriptblock]::Create($BuildInfo.TaskHeader)) + } + + # Import Tasks from modules via their exported aliases when defined in BUild Manifest + # https://github.com/nightroman/Invoke-Build/tree/master/Tasks/Import#example-2-import-from-a-module-with-tasks + if ($BuildInfo.containsKey('ModuleBuildTasks')) + { + foreach ($Module in $BuildInfo['ModuleBuildTasks'].Keys) + { + try + { + Write-Host -ForegroundColor DarkGray -Verbose "Importing tasks from module $Module" + $LoadedModule = Import-Module $Module -PassThru -ErrorAction Stop + foreach ($TaskToExport in $BuildInfo['ModuleBuildTasks'].($Module)) + { + $LoadedModule.ExportedAliases.GetEnumerator().Where{ + # using -like to support wildcard + Write-Host -ForegroundColor DarkGray "`t Loading $($_.Key)..." + $_.Key -like $TaskToExport + }.ForEach{ + # Dot sourcing the Tasks via their exported aliases + . (Get-Alias $_.Key) + } + } + } + catch + { + Write-Host -ForegroundColor Red -Object "Could not load tasks for module $Module." + Write-Error $_ + } + } + } + + # Loading Build Tasks defined in the .build/ folder (will override the ones imported above if same task name) + Get-ChildItem -Path ".build/" -Recurse -Include *.ps1 -ErrorAction Ignore | ForEach-Object { + "Importing file $($_.BaseName)" | Write-Verbose + . $_.FullName + } + + # Synopsis: Empty task, useful to test the bootstrap process + task noop { } + + # Define default task sequence ("."), can be overridden in the $BuildInfo + task . { + Write-Build Yellow "No sequence currently defined for the default task" + } + + # Load Invoke-Build task sequences/workflows from $BuildInfo + Write-Host -ForegroundColor DarkGray "Adding Workflow from configuration:" + foreach ($Workflow in $BuildInfo.BuildWorkflow.keys) + { + Write-Verbose "Creating Build Workflow '$Workflow' with tasks $($BuildInfo.BuildWorkflow.($Workflow) -join ', ')" + $WorkflowItem = $BuildInfo.BuildWorkflow.($Workflow) + if ($WorkflowItem.Trim() -match '^\{(?[\w\W]*)\}$') + { + $WorkflowItem = [ScriptBlock]::Create($Matches['sb']) + } + Write-Host -ForegroundColor DarkGray " +-> $Workflow" + task $Workflow $WorkflowItem + } + + Write-Host -ForeGroundColor magenta "[build] Executing requested workflow: $($Tasks -join ', ')" + + } + finally + { + Pop-Location -StackName BeforeBuild + } +} + +Begin +{ + # Find build config if not specified + if (-not $BuildConfig) { + $config = Get-ChildItem -Path "$PSScriptRoot\*" -Include 'build.y*ml', 'build.psd1', 'build.json*' -ErrorAction:Ignore + if (-not $config -or ($config -is [array] -and $config.Length -le 0)) { + throw "No build configuration found. Specify path via -BuildConfig" + } + elseif ($config -is [array]) { + if ($config.Length -gt 1) { + throw "More than one build configuration found. Specify which one to use via -BuildConfig" + } + $BuildConfig = $config[0] + } + else { + $BuildConfig = $config + } + } + # Bootstrapping the environment before using Invoke-Build as task runner + + if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + { + Write-Host -foregroundColor Green "[pre-build] Starting Build Init" + Push-Location $PSScriptRoot -StackName BuildModule + } + + if ($RequiredModulesDirectory -in @('CurrentUser', 'AllUsers')) + { + # Installing modules instead of saving them + Write-Host -foregroundColor Green "[pre-build] Required Modules will be installed for $RequiredModulesDirectory, not saved." + # Tell Resolve-Dependency to use provided scope as the -PSDependTarget if not overridden in Build.psd1 + $PSDependTarget = $RequiredModulesDirectory + } + else + { + if (-Not (Split-Path -IsAbsolute -Path $OutputDirectory)) + { + $OutputDirectory = Join-Path -Path $PSScriptRoot -ChildPath $OutputDirectory + } + + # Resolving the absolute path to save the required modules to + if (-Not (Split-Path -IsAbsolute -Path $RequiredModulesDirectory)) + { + $RequiredModulesDirectory = Join-Path -Path $PSScriptRoot -ChildPath $RequiredModulesDirectory + } + + # Create the output/modules folder if not exists, or resolve the Absolute path otherwise + if (Resolve-Path $RequiredModulesDirectory -ErrorAction SilentlyContinue) + { + Write-Debug "[pre-build] Required Modules path already exist at $RequiredModulesDirectory" + $RequiredModulesPath = Convert-Path $RequiredModulesDirectory + } + else + { + Write-Host -foregroundColor Green "[pre-build] Creating required modules directory $RequiredModulesDirectory." + $RequiredModulesPath = (New-Item -ItemType Directory -Force -Path $RequiredModulesDirectory).FullName + } + + # Prepending $RequiredModulesPath folder to PSModulePath to resolve from this folder FIRST + if ($RequiredModulesDirectory -notIn @('CurrentUser', 'AllUsers') -and + (($Env:PSModulePath -split [io.path]::PathSeparator) -notContains $RequiredModulesDirectory)) + { + Write-Host -foregroundColor Green "[pre-build] Prepending '$RequiredModulesDirectory' folder to PSModulePath" + $Env:PSModulePath = $RequiredModulesDirectory + [io.path]::PathSeparator + $Env:PSModulePath + } + + # Checking if the user should -ResolveDependency + if ((!(Get-Module -ListAvailable powershell-yaml) -or !(Get-Module -ListAvailable InvokeBuild) -or !(Get-Module -ListAvailable PSDepend)) -and !$ResolveDependency) + { + if ($AutoRestore -or !$PSBoundParameters.ContainsKey('Tasks') -or $Tasks -contains 'build') + { + Write-Host -ForegroundColor Yellow "[pre-build] Dependency missing, running './build.ps1 -ResolveDependency -Tasks noop' for you `r`n" + $ResolveDependency = $true + } + else + { + Write-Warning "Some required Modules are missing, make sure you first run with the '-ResolveDependency' parameter." + Write-Warning "Running 'build.ps1 -ResolveDependency -Tasks noop' will pull required modules without running the build task." + } + } + + if ($BuiltModuleSubdirectory) + { + if (-Not (Split-Path -IsAbsolute $BuiltModuleSubdirectory)) + { + $BuildModuleOutput = Join-Path $OutputDirectory $BuiltModuleSubdirectory + } + else + { + $BuildModuleOutput = $BuiltModuleSubdirectory + } + } + else + { + $BuildModuleOutput = $OutputDirectory + } + + # Prepending $BuildModuleOutput folder to PSModulePath to resolve built module from this folder + if (($Env:PSModulePath -split [io.path]::PathSeparator) -notContains $BuildModuleOutput) + { + Write-Host -foregroundColor Green "[pre-build] Prepending '$BuildModuleOutput' folder to PSModulePath" + $Env:PSModulePath = $BuildModuleOutput + [io.path]::PathSeparator + $Env:PSModulePath + } + + # Tell Resolve-Dependency to use $RequiredModulesPath as -PSDependTarget if not overridden in Build.psd1 + $PSDependTarget = $RequiredModulesPath + } + + if ($ResolveDependency) + { + Write-Host -Object "[pre-build] Resolving dependencies." -foregroundColor Green + $ResolveDependencyParams = @{ } + + # If BuildConfig is a Yaml file, bootstrap powershell-yaml via ResolveDependency + if ($BuildConfig -match '\.[yaml|yml]$') + { + $ResolveDependencyParams.add('WithYaml', $True) + } + + $ResolveDependencyAvailableParams = (Get-Command -Name '.\Resolve-Dependency.ps1').parameters.keys + foreach ($CmdParameter in $ResolveDependencyAvailableParams) + { + + # The parameter has been explicitly used for calling the .build.ps1 + if ($MyInvocation.BoundParameters.ContainsKey($CmdParameter)) + { + $ParamValue = $MyInvocation.BoundParameters.ContainsKey($CmdParameter) + Write-Debug " adding $CmdParameter :: $ParamValue [from user-provided parameters to Build.ps1]" + $ResolveDependencyParams.Add($CmdParameter, $ParamValue) + } + # Use defaults parameter value from Build.ps1, if any + else + { + if ($ParamValue = Get-Variable -Name $CmdParameter -ValueOnly -ErrorAction Ignore) + { + Write-Debug " adding $CmdParameter :: $ParamValue [from default Build.ps1 variable]" + $ResolveDependencyParams.add($CmdParameter, $ParamValue) + } + } + } + + Write-Host -foregroundColor Green "[pre-build] Starting bootstrap process." + .\Resolve-Dependency.ps1 @ResolveDependencyParams + } + + if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + { + Write-Verbose "Bootstrap completed. Handing back to InvokeBuild." + if ($PSBoundParameters.ContainsKey('ResolveDependency')) + { + Write-Verbose "Dependency already resolved. Removing task" + $null = $PSBoundParameters.Remove('ResolveDependency') + } + Write-Host -foregroundColor Green "[build] Starting build with InvokeBuild." + Invoke-Build @PSBoundParameters -Task $Tasks -File $MyInvocation.MyCommand.Path + Pop-Location -StackName BuildModule + return + } +} diff --git a/build.yaml b/build.yaml new file mode 100644 index 0000000..7974fea --- /dev/null +++ b/build.yaml @@ -0,0 +1,90 @@ +--- +#################################################### +# ModuleBuilder Configuration # +#################################################### +CopyDirectories: + - en-US + - DSCResources + - Modules +Encoding: UTF8 +VersionedOutputDirectory: true + +#################################################### +# Pipeline Configuration # +#################################################### +BuildWorkflow: + '.': + - build + - test + + build: + - Clean + - Build_Module_ModuleBuilder + - Build_NestedModules_ModuleBuilder + - Create_changelog_release_output + - Generate_Conceptual_Help + - Generate_Wiki_Content + + pack: + - build + - package_module_nupkg + + hqrmtest: + - DscResource_Tests_Stop_On_Fail + + test: + - Pester_Tests_Stop_On_Fail + - Pester_if_Code_Coverage_Under_Threshold + + publish: + - Publish_release_to_GitHub + - publish_module_to_gallery + +#################################################### +# PESTER Configuration # +#################################################### + +Pester: + OutputFormat: NUnitXML + ExcludeFromCodeCoverage: + Script: + - tests/Unit + ExcludeTag: + Tag: + CodeCoverageThreshold: 70 + +DscTest: + OutputFormat: NUnitXML + ExcludeTag: + - 'Common Tests - New Error-Level Script Analyzer Rules' + ExcludeSourceFile: + - output + +Resolve-Dependency: + Gallery: 'PSGallery' + AllowPrerelease: false + Verbose: false + +ModuleBuildTasks: + Sampler: + - '*.build.Sampler.ib.tasks' + DscResource.DocGenerator: + - 'Task.*' + +TaskHeader: | + param($Path) + "" + "=" * 79 + Write-Build Cyan "`t`t`t$($Task.Name.replace("_"," ").ToUpper())" + Write-Build DarkGray "$(Get-BuildSynopsis $Task)" + "-" * 79 + Write-Build DarkGray " $Path" + Write-Build DarkGray " $($Task.InvocationInfo.ScriptName):$($Task.InvocationInfo.ScriptLineNumber)" + "" + +GitHubConfig: + GitHubFilesToAdd: + - 'CHANGELOG.md' + GitHubConfigUserName: dscbot + GitHubConfigUserEmail: dsccommunity@outlook.com + UpdateChangelogOnPrerelease: false diff --git a/iSCSIDsc.psd1 b/iSCSIDsc.psd1 deleted file mode 100644 index 732353d..0000000 --- a/iSCSIDsc.psd1 +++ /dev/null @@ -1,115 +0,0 @@ -@{ - -# Script module or binary module file associated with this manifest. -# RootModule = '' - -# Version number of this module. -ModuleVersion = '1.5.0.0' - -# ID used to uniquely identify this module -GUID = 'f2793754-6dc7-439a-a779-b1529b5e704c' - -# Author of this module -Author = 'Daniel Scott-Raynsford' - -# Company or vendor of this module -CompanyName = '' - -# Copyright statement for this module -Copyright = '(c) 2018 Daniel Scott-Raynsford. All rights reserved.' - -# Description of the functionality provided by this module -Description = 'DSC resources for configuring Windows iSCSI Targets and Initiators.' - -# Minimum version of the Windows PowerShell engine required by this module -PowerShellVersion = '4.0' - -# Name of the Windows PowerShell host required by this module -# PowerShellHostName = '' - -# Minimum version of the Windows PowerShell host required by this module -# PowerShellHostVersion = '' - -# Minimum version of Microsoft .NET Framework required by this module -# DotNetFrameworkVersion = '' - -# Minimum version of the common language runtime (CLR) required by this module -CLRVersion = '4.0' - -# Processor architecture (None, X86, Amd64) required by this module -ProcessorArchitecture = 'None' - -# Modules that must be imported into the global environment prior to importing this module -# RequiredModules = @() - -# Assemblies that must be loaded prior to importing this module -RequiredAssemblies = @() - -# Script files (.ps1) that are run in the caller's environment prior to importing this module. -ScriptsToProcess = @() - -# Type files (.ps1xml) to be loaded when importing this module -TypesToProcess = @() - -# Format files (.ps1xml) to be loaded when importing this module -FormatsToProcess = @() - -# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess -# NestedModules = @() - -# Functions to export from this module -FunctionsToExport = @() - -# Cmdlets to export from this module -CmdletsToExport = @() - -# Variables to export from this module -VariablesToExport = @() - -# Aliases to export from this module -AliasesToExport = @() - -# DSC resources to export from this module -DscResourcesToExport = @('iSCSIInitiator','iSCSIServerTarget','iSCSIVirtualDisk') - -# List of all modules packaged with this module -# ModuleList = @() - -# List of all files packaged with this module -FileList = @() - -# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. -PrivateData = @{ - - PSData = @{ - - # Tags applied to this module. These help with module discovery in online galleries. - Tags = 'DSC','DesiredStateConfiguration','iSCSI','Target','Initiator' - - # A URL to the license for this module. - LicenseUri = 'https://github.com/PlagueHO/iSCSIDsc/blob/dev/LICENSE' - - # A URL to the main website for this project. - ProjectUri = 'https://github.com/PlagueHO/iSCSIDsc' - - # A URL to an icon representing this module. - # IconUri = '' - - # ReleaseNotes of this module - # ReleaseNotes = '' - - # External dependent modules of this module - # ExternalModuleDependencies = '' - - } # End of PSData hashtable - - } # End of PrivateData hashtable - -# HelpInfo URI of this module -# HelpInfoURI = '' - -# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. -# DefaultCommandPrefix = '' - -} - diff --git a/source/Build.psd1 b/source/Build.psd1 new file mode 100644 index 0000000..494cffc --- /dev/null +++ b/source/Build.psd1 @@ -0,0 +1,5 @@ +@{ + Path = 'iSCSIDsc.psd1' +} +# Waiting for ModuleBuilder to do away with this file +# when all parameters are provided to the function diff --git a/DSCResources/DSR_iSCSIInitiator/DSR_iSCSIInitiator.psm1 b/source/DSCResources/DSC_iSCSIInitiator/DSC_iSCSIInitiator.psm1 similarity index 88% rename from DSCResources/DSR_iSCSIInitiator/DSR_iSCSIInitiator.psm1 rename to source/DSCResources/DSC_iSCSIInitiator/DSC_iSCSIInitiator.psm1 index 6c54ec2..7cc7601 100644 --- a/DSCResources/DSR_iSCSIInitiator/DSR_iSCSIInitiator.psm1 +++ b/source/DSCResources/DSC_iSCSIInitiator/DSC_iSCSIInitiator.psm1 @@ -1,14 +1,12 @@ $modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules' -# Import the Networking Resource Helper Module +# Import the iSCSI Common Module Import-Module -Name (Join-Path -Path $modulePath ` - -ChildPath (Join-Path -Path 'iSCSIDsc.ResourceHelper' ` - -ChildPath 'iSCSIDsc.ResourceHelper.psm1')) + -ChildPath (Join-Path -Path 'iSCSIDsc.Common' ` + -ChildPath 'iSCSIDsc.Common.psm1')) # Import Localization Strings -$LocalizedData = Get-LocalizedData ` - -ResourceName 'DSR_iSCSIInitiator' ` - -ResourcePath (Split-Path -Parent $Script:MyInvocation.MyCommand.Path) +$script:localizedData = Get-LocalizedData -ResourceName 'DSC_iSCSIInitiator' <# .SYNOPSIS @@ -54,7 +52,7 @@ function Get-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.GettingiSCSIInitiatorMessage) ` + $($script:localizedData.GettingiSCSIInitiatorMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress ) -join '' ) @@ -81,7 +79,7 @@ function Get-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetPortalExistsMessage) ` + $($script:localizedData.iSCSITargetPortalExistsMessage) ` -f $TargetPortalAddress, $InitiatorPortalAddress ) -join '' ) } @@ -89,7 +87,7 @@ function Get-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetPortalDoesNotExistMessage) ` + $($script:localizedData.iSCSITargetPortalDoesNotExistMessage) ` -f $TargetPortalAddress, $InitiatorPortalAddress ) -join '' ) } # if @@ -105,7 +103,7 @@ function Get-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetExistsMessage) ` + $($script:localizedData.iSCSITargetExistsMessage) ` -f $NodeAddress ) -join '' ) } @@ -113,7 +111,7 @@ function Get-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetDoesNotExistMessage) ` + $($script:localizedData.iSCSITargetDoesNotExistMessage) ` -f $NodeAddress ) -join '' ) } # if @@ -136,7 +134,7 @@ function Get-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIConnectionExistsMessage) ` + $($script:localizedData.iSCSIConnectionExistsMessage) ` -f $NodeAddress ) -join '' ) } @@ -144,7 +142,7 @@ function Get-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIConnectionDoesNotExistMessage) ` + $($script:localizedData.iSCSIConnectionDoesNotExistMessage) ` -f $NodeAddress ) -join '' ) } # if @@ -167,7 +165,7 @@ function Get-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSISessionExistsMessage) ` + $($script:localizedData.iSCSISessionExistsMessage) ` -f $NodeAddress ) -join '' ) } @@ -175,7 +173,7 @@ function Get-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSISessionDoesNotExistMessage) ` + $($script:localizedData.iSCSISessionDoesNotExistMessage) ` -f $NodeAddress ) -join '' ) } # if @@ -192,7 +190,7 @@ function Get-TargetResource } } - $returnValue + return $returnValue } # Get-TargetResource <# @@ -321,17 +319,22 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.SettingiSCSIInitiatorMessage) ` + $($script:localizedData.SettingiSCSIInitiatorMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress ) -join '' ) # Remove any parameters that can't be splatted. $null = $PSBoundParameters.Remove('Ensure') - $targetSplat = @{ TargetPortalAddress = $TargetPortalAddress } + $targetSplat = @{ + TargetPortalAddress = $TargetPortalAddress + } + if ($PSBoundParameters.ContainsKey('InitiatorPortalAddress')) { - $targetSplat += @{ InitiatorPortalAddress = $InitiatorPortalAddress } + $targetSplat += @{ + InitiatorPortalAddress = $InitiatorPortalAddress + } } # Lookup the existing iSCSI Target Portal @@ -350,7 +353,7 @@ function Set-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.EnsureiSCSITargetPortalExistsMessage) ` + $($script:localizedData.EnsureiSCSITargetPortalExistsMessage) ` -f $TargetPortalAddress, $InitiatorPortalAddress ) -join '' ) @@ -388,7 +391,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetPortalRemovedForRecreateMessage) ` + $($script:localizedData.iSCSITargetPortalRemovedForRecreateMessage) ` -f $TargetPortalAddress, $InitiatorPortalAddress ) -join '' ) } # if @@ -413,7 +416,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetPortalCreatedMessage) ` + $($script:localizedData.iSCSITargetPortalCreatedMessage) ` -f $TargetPortalAddress, $InitiatorPortalAddress ) -join '' ) } @@ -427,7 +430,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.EnsureiSCSITargetIsConnectedMessage) ` + $($script:localizedData.EnsureiSCSITargetIsConnectedMessage) ` -f $NodeAddress ) -join '' ) @@ -465,35 +468,42 @@ function Set-TargetResource { $connect = $true } # if + if ($connection.InitiatorAddress -ne $InitiatorPortalAddress) { $connect = $true } # if + if (($TargetPortalPortNumber) ` -and ($connection.TargetPortNumber -ne $TargetPortalPortNumber)) { $connect = $true } # if + if (($AuthenticationType) ` -and ($session.AuthenticationType -ne $AuthenticationType)) { $connect = $true } # if + if (($InitiatorInstanceName) ` -and ($session.InitiatorInstanceName -ne $InitiatorInstanceName)) { $connect = $true } # if + if ($PSBoundParameters.ContainsKey('InitiatorPortalAddress') ` -and ($session.InitiatorPortalAddress -ne $InitiatorPortalAddress)) { $connect = $true } # if + if (($null -ne $IsDataDigest) ` -and ($session.IsDataDigest -ne $IsDataDigest)) { $connect = $true } # if + if (($null -ne $IsHeaderDigest) ` -and ($session.IsHeaderDigest -ne $IsHeaderDigest)) { @@ -511,7 +521,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetDisconnectedMessage) ` + $($script:localizedData.iSCSITargetDisconnectedMessage) ` -f $NodeAddress ) -join '' ) @@ -541,7 +551,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetConnectedMessage) ` + $($script:localizedData.iSCSITargetConnectedMessage) ` -f $NodeAddress ) -join '' ) } # if @@ -558,7 +568,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSISessionSetPersistentMessage) ` + $($script:localizedData.iSCSISessionSetPersistentMessage) ` -f $NodeAddress ) -join '' ) } @@ -570,7 +580,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSISessionRemovedPersistentMessage) ` + $($script:localizedData.iSCSISessionRemovedPersistentMessage) ` -f $NodeAddress ) -join '' ) } @@ -590,7 +600,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSNSServerRemovedMessage) + $($script:localizedData.iSNSServerRemovedMessage) ) -join '' ) } # if } @@ -601,12 +611,14 @@ function Set-TargetResource Set-WmiInstance ` -Namespace root\wmi ` -Class WT_iSNSServer ` - -Arguments @{ServerName = $iSNSServer} ` + -Arguments @{ + ServerName = $iSNSServer + } ` -ErrorAction Stop Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSNSServerUpdatedMessage) ` + $($script:localizedData.iSNSServerUpdatedMessage) ` -f $iSNSServer ) -join '' ) } @@ -614,7 +626,7 @@ function Set-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSNSServerUpdateErrorMessage) ` + $($script:localizedData.iSNSServerUpdateErrorMessage) ` -f $iSNSServer ) -join '' ) } @@ -625,7 +637,7 @@ function Set-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.EnsureiSCSITargetIsDisconnectedMessage) ` + $($script:localizedData.EnsureiSCSITargetIsDisconnectedMessage) ` -f $NodeAddress ) -join '' ) @@ -644,7 +656,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetDisconnectedMessage) ` + $($script:localizedData.iSCSITargetDisconnectedMessage) ` -f $NodeAddress ) -join '' ) } @@ -652,7 +664,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.EnsureiSCSITargetPortalDoesNotExistMessage) ` + $($script:localizedData.EnsureiSCSITargetPortalDoesNotExistMessage) ` -f $TargetPortalAddress, $InitiatorPortalAddress ) -join '' ) @@ -666,7 +678,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetPortalRemovedMessage) ` + $($script:localizedData.iSCSITargetPortalRemovedMessage) ` -f $TargetPortalAddress, $InitiatorPortalAddress ) -join '' ) } # if @@ -680,7 +692,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIServerTargetiSNSRemovedMessage) ` + $($script:localizedData.iSNSServerRemovedMessage) ` -f $TargetName ) -join '' ) } # if @@ -812,18 +824,23 @@ function Test-TargetResource ) # Flag to signal whether settings are correct - [Boolean] $desiredConfigurationMatch = $true + $desiredConfigurationMatch = $true Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.TestingiSCSIInitiatorMessage) ` + $($script:localizedData.TestingiSCSIInitiatorMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress ) -join '' ) - $targetSplat = @{ TargetPortalAddress = $TargetPortalAddress } + $targetSplat = @{ + TargetPortalAddress = $TargetPortalAddress + } + if ($PSBoundParameters.ContainsKey('InitiatorPortalAddress')) { - $targetSplat += @{ InitiatorPortalAddress = $InitiatorPortalAddress } + $targetSplat += @{ + InitiatorPortalAddress = $InitiatorPortalAddress + } } # Lookup the existing iSCSI Target Portal @@ -849,7 +866,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress, 'TargetPortal', 'TargetPortalPortNumber' ) -join '' ) $desiredConfigurationMatch = $false @@ -860,7 +877,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress, 'TargetPortal', 'InitiatorInstanceName' ) -join '' ) $desiredConfigurationMatch = $false @@ -871,7 +888,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress, 'TargetPortal', 'IsDataDigest' ) -join '' ) $desiredConfigurationMatch = $false @@ -882,7 +899,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress, 'TargetPortal', 'IsHeaderDigest' ) -join '' ) $desiredConfigurationMatch = $false @@ -897,7 +914,7 @@ function Test-TargetResource # Ths iSCSI Target doesn't exist but should Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetDoesNotExistButShouldMessage) ` + $($script:localizedData.iSCSITargetDoesNotExistButShouldMessage) ` -f $NodeAddress ) -join '' ) $desiredConfigurationMatch = $false @@ -909,7 +926,7 @@ function Test-TargetResource # Ths iSCSI Target exists but is not connected Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetNotConnectedMessage) ` + $($script:localizedData.iSCSITargetNotConnectedMessage) ` -f $NodeAddress ) -join '' ) $desiredConfigurationMatch = $false @@ -924,7 +941,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIConnectionDoesNotExistButShouldMessage) ` + $($script:localizedData.iSCSIConnectionDoesNotExistButShouldMessage) ` -f $NodeAddress ) -join '' ) $desiredConfigurationMatch = $false @@ -954,7 +971,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress, 'Connection', 'TargetAddress' ) -join '' ) $desiredConfigurationMatch = $false @@ -965,7 +982,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress, 'Connection', 'InitiatorAddress' ) -join '' ) $desiredConfigurationMatch = $false @@ -976,7 +993,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress, 'Connection', 'TargetPortNumber' ) -join '' ) $desiredConfigurationMatch = $false @@ -990,7 +1007,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSISessionDoesNotExistButShouldMessage) ` + $($script:localizedData.iSCSISessionDoesNotExistButShouldMessage) ` -f $NodeAddress ) -join '' ) $desiredConfigurationMatch = $false @@ -1003,7 +1020,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress, 'Session', 'AuthenticationType' ) -join '' ) $desiredConfigurationMatch = $false @@ -1014,7 +1031,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress, 'Session', 'InitiatorInstanceName' ) -join '' ) $desiredConfigurationMatch = $false @@ -1025,7 +1042,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress, 'Session', 'InitiatorAddress' ) -join '' ) $desiredConfigurationMatch = $false @@ -1036,7 +1053,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress, 'Session', 'IsDataDigest' ) -join '' ) $desiredConfigurationMatch = $false @@ -1047,7 +1064,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress, 'Session', 'IsHeaderDigest' ) -join '' ) $desiredConfigurationMatch = $false @@ -1058,7 +1075,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIInitiatorParameterNeedsUpdateMessage) ` -f $NodeAddress, $TargetPortalAddress, $InitiatorPortalAddress, 'Session', 'IsPersistent' ) -join '' ) $desiredConfigurationMatch = $false @@ -1069,7 +1086,7 @@ function Test-TargetResource # Ths iSCSI Target Portal doesn't exist but should Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetPortalDoesNotExistButShouldMessage) ` + $($script:localizedData.iSCSITargetPortalDoesNotExistButShouldMessage) ` -f $TargetPortalAddress, $InitiatorPortalAddress ) -join '' ) $desiredConfigurationMatch = $false @@ -1082,7 +1099,7 @@ function Test-TargetResource # The iSNS Server is different so needs update Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSNSServerNeedsUpdateMessage) ` + $($script:localizedData.iSNSServerNeedsUpdateMessage) ` -f $iSNSServerCurrent.iSNSServerAddress, $iSNSServer ) -join '' ) $desiredConfigurationMatch = $false @@ -1099,7 +1116,7 @@ function Test-TargetResource # The iSCSI Target exists and is connected Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetExistsButShouldNotMessage) ` + $($script:localizedData.iSCSITargetExistsButShouldNotMessage) ` -f $NodeAddress ) -join '' ) $desiredConfigurationMatch = $false @@ -1111,7 +1128,7 @@ function Test-TargetResource # The iSCSI Target Portal exists but should not Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetPortalExistsButShouldNotMessage) ` + $($script:localizedData.iSCSITargetPortalExistsButShouldNotMessage) ` -f $TargetPortalAddress, $InitiatorPortalAddress ) -join '' ) $desiredConfigurationMatch = $false @@ -1121,7 +1138,7 @@ function Test-TargetResource # The iSCSI Target Portal does not exist and should not Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSITargetPortalDoesNotExistAndShouldNotMessage) ` + $($script:localizedData.iSCSITargetPortalDoesNotExistAndShouldNotMessage) ` -f $TargetPortalAddress, $InitiatorPortalAddress ) -join '' ) } # if @@ -1132,11 +1149,12 @@ function Test-TargetResource # The iSNS Server is set but should not be Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSNSServerIsSetButShouldNotBeMessage) + $($script:localizedData.iSNSServerIsSetButShouldBeNotMessage) ) -join '' ) $desiredConfigurationMatch = $false } # if } # if + return $desiredConfigurationMatch } # Test-TargetResource @@ -1175,7 +1193,7 @@ function Get-TargetPortal } catch { - Throw $_ + throw $_ } return $targetPortal } # Get-TargetPortal @@ -1207,7 +1225,7 @@ function Get-Target } catch { - Throw $_ + throw $_ } return $target } # Get-Target @@ -1239,7 +1257,7 @@ function Get-Session } catch { - Throw $_ + throw $_ } return $session } # Get-Session @@ -1271,7 +1289,7 @@ function Get-Connection } catch { - Throw $_ + throw $_ } return $Connection } # Get-Connection diff --git a/DSCResources/DSR_iSCSIInitiator/DSR_iSCSIInitiator.schema.mof b/source/DSCResources/DSC_iSCSIInitiator/DSC_iSCSIInitiator.schema.mof similarity index 98% rename from DSCResources/DSR_iSCSIInitiator/DSR_iSCSIInitiator.schema.mof rename to source/DSCResources/DSC_iSCSIInitiator/DSC_iSCSIInitiator.schema.mof index a7951c4..7a25bdd 100644 --- a/DSCResources/DSR_iSCSIInitiator/DSR_iSCSIInitiator.schema.mof +++ b/source/DSCResources/DSC_iSCSIInitiator/DSC_iSCSIInitiator.schema.mof @@ -1,5 +1,5 @@ [ClassVersion("1.0.0.0"), FriendlyName("iSCSIInitiator")] -class DSR_iSCSIInitiator : OMI_BaseResource +class DSC_iSCSIInitiator : OMI_BaseResource { [Key, Description("Represents the IQN of the discovered target.")] String NodeAddress; [Required, Description("Specifies the IP address or DNS name of the target portal.")] String TargetPortalAddress; diff --git a/DSCResources/DSR_iSCSIInitiator/README.md b/source/DSCResources/DSC_iSCSIInitiator/README.md similarity index 100% rename from DSCResources/DSR_iSCSIInitiator/README.md rename to source/DSCResources/DSC_iSCSIInitiator/README.md diff --git a/DSCResources/DSR_iSCSIInitiator/en-us/DSR_iSCSIInitiator.strings.psd1 b/source/DSCResources/DSC_iSCSIInitiator/en-US/DSC_iSCSIInitiator.strings.psd1 similarity index 100% rename from DSCResources/DSR_iSCSIInitiator/en-us/DSR_iSCSIInitiator.strings.psd1 rename to source/DSCResources/DSC_iSCSIInitiator/en-US/DSC_iSCSIInitiator.strings.psd1 diff --git a/DSCResources/DSR_iSCSIServerTarget/DSR_iSCSIServerTarget.psm1 b/source/DSCResources/DSC_iSCSIServerTarget/DSC_iSCSIServerTarget.psm1 similarity index 86% rename from DSCResources/DSR_iSCSIServerTarget/DSR_iSCSIServerTarget.psm1 rename to source/DSCResources/DSC_iSCSIServerTarget/DSC_iSCSIServerTarget.psm1 index 1a70543..d978b79 100644 --- a/DSCResources/DSR_iSCSIServerTarget/DSR_iSCSIServerTarget.psm1 +++ b/source/DSCResources/DSC_iSCSIServerTarget/DSC_iSCSIServerTarget.psm1 @@ -1,14 +1,12 @@ $modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules' -# Import the Networking Resource Helper Module +# Import the iSCSI Common Module Import-Module -Name (Join-Path -Path $modulePath ` - -ChildPath (Join-Path -Path 'iSCSIDsc.ResourceHelper' ` - -ChildPath 'iSCSIDsc.ResourceHelper.psm1')) + -ChildPath (Join-Path -Path 'iSCSIDsc.Common' ` + -ChildPath 'iSCSIDsc.Common.psm1')) # Import Localization Strings -$LocalizedData = Get-LocalizedData ` - -ResourceName 'DSR_iSCSIServerTarget' ` - -ResourcePath (Split-Path -Parent $Script:MyInvocation.MyCommand.Path) +$script:localizedData = Get-LocalizedData -ResourceName 'DSC_iSCSIServerTarget' <# .SYNOPSIS @@ -46,7 +44,7 @@ function Get-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.GettingiSCSIServerTargetMessage) ` + $($script:localizedData.GettingiSCSIServerTargetMessage) ` -f $TargetName ) -join '' ) @@ -55,11 +53,12 @@ function Get-TargetResource $returnValue = @{ TargetName = $TargetName } + if ($serverTarget) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIServerTargetExistsMessage) ` + $($script:localizedData.iSCSIServerTargetExistsMessage) ` -f $TargetName ) -join '' ) @@ -73,7 +72,7 @@ function Get-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIServerTargetDoesNotExistMessage) ` + $($script:localizedData.iSCSIServerTargetDoesNotExistMessage) ` -f $TargetName ) -join '' ) @@ -86,6 +85,7 @@ function Get-TargetResource $iSNSServerCurrent = Get-WmiObject ` -Class WT_iSNSServer ` -Namespace root\wmi + if ($iSNSServerCurrent) { $returnValue += @{ @@ -93,7 +93,7 @@ function Get-TargetResource } } # if - $returnValue + return $returnValue } # Get-TargetResource <# @@ -146,7 +146,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.SettingiSCSIServerTargetMessage) ` + $($script:localizedData.SettingiSCSIServerTargetMessage) ` -f $TargetName ) -join '' ) @@ -162,7 +162,7 @@ function Set-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.EnsureiSCSIServerTargetExistsMessage) ` + $($script:localizedData.EnsureiSCSIServerTargetExistsMessage) ` -f $TargetName ) -join '' ) @@ -182,7 +182,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIServerTargetUpdatedMessage) ` + $($script:localizedData.iSCSIServerTargetUpdatedMessage) ` -f $TargetName ) -join '' ) } # if @@ -198,7 +198,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIServerTargetCreatedMessage) ` + $($script:localizedData.iSCSIServerTargetCreatedMessage) ` -f $TargetName ) -join '' ) } # if @@ -216,11 +216,12 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIServerTargetDiskAddedMessage) ` + $($script:localizedData.iSCSIServerTargetDiskAddedMessage) ` -f $TargetName,$Path ) -join '' ) } # if } # foreach + foreach ($Path in $serverTarget.LunMappings.Path) { if ($Path -notin $Paths) @@ -233,7 +234,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIServerTargetDiskRemovedMessage) ` + $($script:localizedData.iSCSIServerTargetDiskRemovedMessage) ` -f $TargetName,$Path ) -join '' ) } # if @@ -253,7 +254,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSNSServerRemovedMessage) + $($script:localizedData.iSNSServerRemovedMessage) ) -join '' ) } # if } @@ -264,12 +265,14 @@ function Set-TargetResource Set-WmiInstance ` -Namespace root\wmi ` -Class WT_iSNSServer ` - -Arguments @{ServerName=$iSNSServer} ` + -Arguments @{ + ServerName = $iSNSServer + } ` -ErrorAction Stop Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSNSServerUpdatedMessage) ` + $($script:localizedData.iSNSServerUpdatedMessage) ` -f $iSNSServer ) -join '' ) } @@ -277,7 +280,7 @@ function Set-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSNSServerUpdateErrorMessage) ` + $($script:localizedData.iSNSServerUpdateErrorMessage) ` -f $iSNSServer ) -join '' ) } @@ -288,7 +291,7 @@ function Set-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.EnsureiSCSIServerTargetDoesNotExistMessage) ` + $($script:localizedData.EnsureiSCSIServerTargetDoesNotExistMessage) ` -f $TargetName ) -join '' ) @@ -302,7 +305,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIServerTargetRemovedMessage) ` + $($script:localizedData.iSCSIServerTargetRemovedMessage) ` -f $TargetName ) -join '' ) } # if @@ -316,7 +319,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSNSServerRemovedMessage) + $($script:localizedData.iSNSServerRemovedMessage) ) -join '' ) } # if } # if @@ -372,11 +375,11 @@ function Test-TargetResource ) # Flag to signal whether settings are correct - [Boolean] $desiredConfigurationMatch = $true + $desiredConfigurationMatch = $true Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.TestingiSCSIServerTargetMessage) ` + $($script:localizedData.TestingiSCSIServerTargetMessage) ` -f $TargetName ) -join '' ) @@ -401,20 +404,21 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIServerTargetParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIServerTargetParameterNeedsUpdateMessage) ` -f $TargetName,'InitiatorIds' ) -join '' ) $desiredConfigurationMatch = $false } # if [String[]] $ExistingPaths = @($serverTarget.LunMappings.Path) + if (($Paths) -and (Compare-Object ` -ReferenceObject $Paths ` -DifferenceObject $ExistingPaths).Count -ne 0) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIServerTargetParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIServerTargetParameterNeedsUpdateMessage) ` -f $TargetName,'Paths' ) -join '' ) $desiredConfigurationMatch = $false @@ -425,7 +429,7 @@ function Test-TargetResource # Ths iSCSI Server Target doesn't exist but should Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIServerTargetDoesNotExistButShouldMessage) ` + $($script:localizedData.iSCSIServerTargetDoesNotExistButShouldMessage) ` -f $TargetName ) -join '' ) $desiredConfigurationMatch = $false @@ -438,7 +442,7 @@ function Test-TargetResource # The iSNS Server is different so needs update Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSNSServerNeedsUpdateMessage) ` + $($script:localizedData.iSNSServerNeedsUpdateMessage) ` -f $iSNSServerCurrent.ServerName,$iSNSServer ) -join '' ) $desiredConfigurationMatch = $false @@ -452,7 +456,7 @@ function Test-TargetResource # The iSCSI Server Target exists but should not Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIServerTargetExistsButShouldNotMessage) ` + $($script:localizedData.iSCSIServerTargetExistsButShouldNotMessage) ` -f $TargetName ) -join '' ) $desiredConfigurationMatch = $false @@ -462,7 +466,7 @@ function Test-TargetResource # The iSCSI Server Target does not exist and should not Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIServerTargetDoesNotExistAndShouldNotMessage) ` + $($script:localizedData.iSCSIServerTargetDoesNotExistAndShouldNotMessage) ` -f $TargetName ) -join '' ) } # if @@ -473,7 +477,7 @@ function Test-TargetResource # The iSNS Server is set but should not be Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSNSServerIsSetButShouldBeNotMessage) + $($script:localizedData.iSNSServerIsSetButShouldBeNotMessage) ) -join '' ) $desiredConfigurationMatch = $false } # if @@ -499,6 +503,7 @@ function Get-ServerTarget [System.String] $TargetName ) + try { $serverTarget = Get-iSCSIServerTarget ` @@ -512,9 +517,10 @@ function Get-ServerTarget } catch { - Throw $_ + throw $_ } - Return $serverTarget + + return $serverTarget } Export-ModuleMember -function *-TargetResource diff --git a/DSCResources/DSR_iSCSIServerTarget/DSR_iSCSIServerTarget.schema.mof b/source/DSCResources/DSC_iSCSIServerTarget/DSC_iSCSIServerTarget.schema.mof similarity index 93% rename from DSCResources/DSR_iSCSIServerTarget/DSR_iSCSIServerTarget.schema.mof rename to source/DSCResources/DSC_iSCSIServerTarget/DSC_iSCSIServerTarget.schema.mof index ede9ab3..ec2da37 100644 --- a/DSCResources/DSR_iSCSIServerTarget/DSR_iSCSIServerTarget.schema.mof +++ b/source/DSCResources/DSC_iSCSIServerTarget/DSC_iSCSIServerTarget.schema.mof @@ -1,5 +1,5 @@ [ClassVersion("1.0.0.0"), FriendlyName("iSCSIServerTarget")] -class DSR_iSCSIServerTarget : OMI_BaseResource +class DSC_iSCSIServerTarget : OMI_BaseResource { [Key, Description("Specifies the name of the iSCSI target.")] String TargetName; [Write, Description("Ensures that Server Target is either Absent or Present."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; diff --git a/DSCResources/DSR_iSCSIServerTarget/README.md b/source/DSCResources/DSC_iSCSIServerTarget/README.md similarity index 100% rename from DSCResources/DSR_iSCSIServerTarget/README.md rename to source/DSCResources/DSC_iSCSIServerTarget/README.md diff --git a/DSCResources/DSR_iSCSIServerTarget/en-us/DSR_iSCSIServerTarget.strings.psd1 b/source/DSCResources/DSC_iSCSIServerTarget/en-US/DSC_iSCSIServerTarget.strings.psd1 similarity index 100% rename from DSCResources/DSR_iSCSIServerTarget/en-us/DSR_iSCSIServerTarget.strings.psd1 rename to source/DSCResources/DSC_iSCSIServerTarget/en-US/DSC_iSCSIServerTarget.strings.psd1 diff --git a/DSCResources/DSR_iSCSIVirtualDisk/DSR_iSCSIVirtualDisk.psm1 b/source/DSCResources/DSC_iSCSIVirtualDisk/DSC_iSCSIVirtualDisk.psm1 similarity index 87% rename from DSCResources/DSR_iSCSIVirtualDisk/DSR_iSCSIVirtualDisk.psm1 rename to source/DSCResources/DSC_iSCSIVirtualDisk/DSC_iSCSIVirtualDisk.psm1 index 45736f5..d0db3de 100644 --- a/DSCResources/DSR_iSCSIVirtualDisk/DSR_iSCSIVirtualDisk.psm1 +++ b/source/DSCResources/DSC_iSCSIVirtualDisk/DSC_iSCSIVirtualDisk.psm1 @@ -1,14 +1,12 @@ $modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules' -# Import the Networking Resource Helper Module +# Import the iSCSI Common Module Import-Module -Name (Join-Path -Path $modulePath ` - -ChildPath (Join-Path -Path 'iSCSIDsc.ResourceHelper' ` - -ChildPath 'iSCSIDsc.ResourceHelper.psm1')) + -ChildPath (Join-Path -Path 'iSCSIDsc.Common' ` + -ChildPath 'iSCSIDsc.Common.psm1')) # Import Localization Strings -$LocalizedData = Get-LocalizedData ` - -ResourceName 'DSR_iSCSIVirtualDisk' ` - -ResourcePath (Split-Path -Parent $Script:MyInvocation.MyCommand.Path) +$script:localizedData = Get-LocalizedData -ResourceName 'DSC_iSCSIVirtualDisk' <# .SYNOPSIS @@ -31,7 +29,7 @@ function Get-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.GettingiSCSIVirtualDiskMessage) ` + $($script:localizedData.GettingiSCSIVirtualDiskMessage) ` -f $Path ) -join '' ) @@ -44,7 +42,7 @@ function Get-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIVirtualDiskExistsMessage) ` + $($script:localizedData.iSCSIVirtualDiskExistsMessage) ` -f $Path ) -join '' ) @@ -60,7 +58,7 @@ function Get-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIVirtualDiskDoesNotExistMessage) ` + $($script:localizedData.iSCSIVirtualDiskDoesNotExistMessage) ` -f $Path ) -join '' ) @@ -155,7 +153,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.SettingiSCSIVirtualDiskMessage) ` + $($script:localizedData.SettingiSCSIVirtualDiskMessage) ` -f $Path ) -join '' ) @@ -170,14 +168,14 @@ function Set-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.EnsureiSCSIVirtualDiskExistsMessage) ` + $($script:localizedData.EnsureiSCSIVirtualDiskExistsMessage) ` -f $Path ) -join '' ) if ($virtualDisk) { # The iSCSI Virtual Disk exists - [Boolean] $recreate = $false + $recreate = $false if (($DiskType) ` -and ($virtualDisk.DiskType -ne $DiskType)) @@ -208,7 +206,7 @@ function Set-TargetResource { $errorId = 'iSCSIVirtualDiskRequiresRecreateError' $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument - $errorMessage = $($LocalizedData.iSCSIVirtualDiskRequiresRecreateError) -f $Path + $errorMessage = $($script:localizedData.iSCSIVirtualDiskRequiresRecreateError) -f $Path $exception = New-Object -TypeName System.InvalidOperationException ` -ArgumentList $errorMessage $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord ` @@ -225,7 +223,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIVirtualDiskUpdatedMessage) ` + $($script:localizedData.iSCSIVirtualDiskUpdatedMessage) ` -f $Path ) -join '' ) } @@ -240,6 +238,7 @@ function Set-TargetResource { $null = $PSBoundParameters.Remove('LogicalSectorSizeBytes') } + New-iSCSIVirtualDisk ` @PSBoundParameters ` -ComputerName LOCALHOST ` @@ -247,7 +246,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIVirtualDiskCreatedMessage) ` + $($script:localizedData.iSCSIVirtualDiskCreatedMessage) ` -f $Path ) -join '' ) } @@ -256,7 +255,7 @@ function Set-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.EnsureiSCSIVirtualDiskDoesNotExistMessage) ` + $($script:localizedData.EnsureiSCSIVirtualDiskDoesNotExistMessage) ` -f $Path ) -join '' ) @@ -270,7 +269,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIVirtualDiskRemovedMessage) ` + $($script:localizedData.iSCSIVirtualDiskRemovedMessage) ` -f $Path ) -join '' ) } # if @@ -360,11 +359,11 @@ function Test-TargetResource ) # Flag to signal whether settings are correct - [Boolean] $desiredConfigurationMatch = $true + $desiredConfigurationMatch = $true Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.TestingiSCSIVirtualDiskMessage) ` + $($script:localizedData.TestingiSCSIVirtualDiskMessage) ` -f $Path ) -join '' ) @@ -377,14 +376,14 @@ function Test-TargetResource if ($virtualDisk) { # The iSCSI Virtual Disk exists already - check the parameters - [Boolean] $recreate = $false + $recreate = $false if (($Description) ` -and ($virtualDisk.Description -ne $Description)) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIVirtualDiskParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIVirtualDiskParameterNeedsUpdateMessage) ` -f $Path, 'Description' ) -join '' ) $desiredConfigurationMatch = $false @@ -395,7 +394,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIVirtualDiskParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIVirtualDiskParameterNeedsUpdateMessage) ` -f $Path, 'SizeBytes' ) -join '' ) $recreate = $true @@ -406,7 +405,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIVirtualDiskParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIVirtualDiskParameterNeedsUpdateMessage) ` -f $Path, 'SizeBytes' ) -join '' ) $recreate = $true @@ -417,7 +416,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIVirtualDiskParameterNeedsUpdateMessage) ` + $($script:localizedData.iSCSIVirtualDiskParameterNeedsUpdateMessage) ` -f $Path, 'ParentPath' ) -join '' ) $recreate = $true @@ -434,7 +433,7 @@ function Test-TargetResource { $errorId = 'iSCSIVirtualDiskRequiresRecreateError' $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument - $errorMessage = $($LocalizedData.iSCSIVirtualDiskRequiresRecreateError) -f $Path + $errorMessage = $($script:localizedData.iSCSIVirtualDiskRequiresRecreateError) -f $Path $exception = New-Object -TypeName System.InvalidOperationException ` -ArgumentList $errorMessage $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord ` @@ -448,7 +447,7 @@ function Test-TargetResource # Ths iSCSI Virtual Disk doesn't exist but should Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIVirtualDiskDoesNotExistButShouldMessage) ` + $($script:localizedData.iSCSIVirtualDiskDoesNotExistButShouldMessage) ` -f $Path ) -join '' ) $desiredConfigurationMatch = $false @@ -462,7 +461,7 @@ function Test-TargetResource # The iSCSI Virtual Disk exists but should not Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIVirtualDiskExistsButShouldNotMessage) ` + $($script:localizedData.iSCSIVirtualDiskExistsButShouldNotMessage) ` -f $Path ) -join '' ) $desiredConfigurationMatch = $false @@ -472,11 +471,12 @@ function Test-TargetResource # The iSCSI Virtual Disk does not exist and should not Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($LocalizedData.iSCSIVirtualDiskDoesNotExistAndShouldNotMessage) ` + $($script:localizedData.iSCSIVirtualDiskDoesNotExistAndShouldNotMessage) ` -f $Path ) -join '' ) } } # if + return $desiredConfigurationMatch } # Test-TargetResource @@ -499,8 +499,10 @@ function Get-VirtualDisk ) try { - # Specify Localhost as computer because - # it speeds cmdlet up significantly + <# + Specify Localhost as computer because + it speeds cmdlet up significantly. + #> $virtualDisk = Get-iSCSIVirtualDisk ` -ComputerName LOCALHOST ` -Path $Path ` @@ -512,9 +514,9 @@ function Get-VirtualDisk } catch { - Throw $_ + throw $_ } - Return $virtualDisk + return $virtualDisk } Export-ModuleMember -function *-TargetResource diff --git a/DSCResources/DSR_iSCSIVirtualDisk/DSR_iSCSIVirtualDisk.schema.mof b/source/DSCResources/DSC_iSCSIVirtualDisk/DSC_iSCSIVirtualDisk.schema.mof similarity index 96% rename from DSCResources/DSR_iSCSIVirtualDisk/DSR_iSCSIVirtualDisk.schema.mof rename to source/DSCResources/DSC_iSCSIVirtualDisk/DSC_iSCSIVirtualDisk.schema.mof index 38d4947..0cbb51b 100644 --- a/DSCResources/DSR_iSCSIVirtualDisk/DSR_iSCSIVirtualDisk.schema.mof +++ b/source/DSCResources/DSC_iSCSIVirtualDisk/DSC_iSCSIVirtualDisk.schema.mof @@ -1,5 +1,5 @@ [ClassVersion("1.0.0.0"), FriendlyName("iSCSIVirtualDisk")] -class DSR_iSCSIVirtualDisk : OMI_BaseResource +class DSC_iSCSIVirtualDisk : OMI_BaseResource { [Key, Description("Specifies the path of the VHDX file that is associated with the iSCSI virtual disk.")] String Path; [Write, Description("Ensures that Virtual Disk is either Absent or Present."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; diff --git a/DSCResources/DSR_iSCSIVirtualDisk/README.md b/source/DSCResources/DSC_iSCSIVirtualDisk/README.md similarity index 100% rename from DSCResources/DSR_iSCSIVirtualDisk/README.md rename to source/DSCResources/DSC_iSCSIVirtualDisk/README.md diff --git a/DSCResources/DSR_iSCSIVirtualDisk/en-us/DSR_iSCSIVirtualDisk.strings.psd1 b/source/DSCResources/DSC_iSCSIVirtualDisk/en-US/DSC_iSCSIVirtualDisk.strings.psd1 similarity index 100% rename from DSCResources/DSR_iSCSIVirtualDisk/en-us/DSR_iSCSIVirtualDisk.strings.psd1 rename to source/DSCResources/DSC_iSCSIVirtualDisk/en-US/DSC_iSCSIVirtualDisk.strings.psd1 diff --git a/Examples/Resources/iSCSIInitiator/1-iSCSIInitiator_ConfigureTargetPortal_Config.ps1 b/source/Examples/Resources/iSCSIInitiator/1-iSCSIInitiator_ConfigureTargetPortal_Config.ps1 similarity index 83% rename from Examples/Resources/iSCSIInitiator/1-iSCSIInitiator_ConfigureTargetPortal_Config.ps1 rename to source/Examples/Resources/iSCSIInitiator/1-iSCSIInitiator_ConfigureTargetPortal_Config.ps1 index ea4ec41..08d8763 100644 --- a/Examples/Resources/iSCSIInitiator/1-iSCSIInitiator_ConfigureTargetPortal_Config.ps1 +++ b/source/Examples/Resources/iSCSIInitiator/1-iSCSIInitiator_ConfigureTargetPortal_Config.ps1 @@ -1,12 +1,12 @@ <#PSScriptInfo .VERSION 1.0.0 .GUID b5db6465-b609-4e35-b7aa-ddc62efc8553 -.AUTHOR Daniel Scott-Raynsford -.COMPANYNAME -.COPYRIGHT (c) 2018 Daniel Scott-Raynsford. All rights reserved. +.AUTHOR DSC Community +.COMPANYNAME DSC Community +.COPYRIGHT Copyright the DSC Community contributors. All rights reserved. .TAGS DSCConfiguration -.LICENSEURI https://github.com/PlagueHO/iSCSIDsc/blob/master/LICENSE -.PROJECTURI https://github.com/PlagueHO/iSCSIDsc +.LICENSEURI https://github.com/dsccommunity/iSCSIDsc/blob/master/LICENSE +.PROJECTURI https://github.com/dsccommunity/iSCSIDsc .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS diff --git a/Examples/Resources/iSCSIServerTarget/1-iSCSIServerTarget_ConfigureServerTarget_Config.ps1 b/source/Examples/Resources/iSCSIServerTarget/1-iSCSIServerTarget_ConfigureServerTarget_Config.ps1 similarity index 90% rename from Examples/Resources/iSCSIServerTarget/1-iSCSIServerTarget_ConfigureServerTarget_Config.ps1 rename to source/Examples/Resources/iSCSIServerTarget/1-iSCSIServerTarget_ConfigureServerTarget_Config.ps1 index 86dd9c8..723b540 100644 --- a/Examples/Resources/iSCSIServerTarget/1-iSCSIServerTarget_ConfigureServerTarget_Config.ps1 +++ b/source/Examples/Resources/iSCSIServerTarget/1-iSCSIServerTarget_ConfigureServerTarget_Config.ps1 @@ -1,12 +1,12 @@ <#PSScriptInfo .VERSION 1.0.0 .GUID 23579591-4d15-492d-88fe-1f8365d8a0da -.AUTHOR Daniel Scott-Raynsford -.COMPANYNAME -.COPYRIGHT (c) 2018 Daniel Scott-Raynsford. All rights reserved. +.AUTHOR DSC Community +.COMPANYNAME DSC Community +.COPYRIGHT Copyright the DSC Community contributors. All rights reserved. .TAGS DSCConfiguration -.LICENSEURI https://github.com/PlagueHO/iSCSIDsc/blob/master/LICENSE -.PROJECTURI https://github.com/PlagueHO/iSCSIDsc +.LICENSEURI https://github.com/dsccommunity/iSCSIDsc/blob/master/LICENSE +.PROJECTURI https://github.com/dsccommunity/iSCSIDsc .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS diff --git a/Examples/Resources/iSCSIVirtualDisk/1-iSCSIServerTarget_ConfigureVirtualDisk_Config.ps1 b/source/Examples/Resources/iSCSIVirtualDisk/1-iSCSIServerTarget_ConfigureVirtualDisk_Config.ps1 similarity index 90% rename from Examples/Resources/iSCSIVirtualDisk/1-iSCSIServerTarget_ConfigureVirtualDisk_Config.ps1 rename to source/Examples/Resources/iSCSIVirtualDisk/1-iSCSIServerTarget_ConfigureVirtualDisk_Config.ps1 index d831166..8771698 100644 --- a/Examples/Resources/iSCSIVirtualDisk/1-iSCSIServerTarget_ConfigureVirtualDisk_Config.ps1 +++ b/source/Examples/Resources/iSCSIVirtualDisk/1-iSCSIServerTarget_ConfigureVirtualDisk_Config.ps1 @@ -1,12 +1,12 @@ <#PSScriptInfo .VERSION 1.0.0 .GUID d65ed9a0-dc79-4743-a9f6-d9b2200cc457 -.AUTHOR Daniel Scott-Raynsford -.COMPANYNAME -.COPYRIGHT (c) 2018 Daniel Scott-Raynsford. All rights reserved. +.AUTHOR DSC Community +.COMPANYNAME DSC Community +.COPYRIGHT Copyright the DSC Community contributors. All rights reserved. .TAGS DSCConfiguration -.LICENSEURI https://github.com/PlagueHO/iSCSIDsc/blob/master/LICENSE -.PROJECTURI https://github.com/PlagueHO/iSCSIDsc +.LICENSEURI https://github.com/dsccommunity/iSCSIDsc/blob/master/LICENSE +.PROJECTURI https://github.com/dsccommunity/iSCSIDsc .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS diff --git a/source/Modules/iSCSIDsc.Common/en-US/iSCSIDsc.Common.strings.psd1 b/source/Modules/iSCSIDsc.Common/en-US/iSCSIDsc.Common.strings.psd1 new file mode 100644 index 0000000..4739bcf --- /dev/null +++ b/source/Modules/iSCSIDsc.Common/en-US/iSCSIDsc.Common.strings.psd1 @@ -0,0 +1,11 @@ +# Localized resources for helper module DscResource.Common. + +ConvertFrom-StringData @' + PropertyTypeInvalidForDesiredValues = Property 'DesiredValues' must be either a [System.Collections.Hashtable], [CimInstance] or [PSBoundParametersDictionary]. The type detected was {0}. + PropertyTypeInvalidForValuesToCheck = If 'DesiredValues' is a CimInstance, then property 'ValuesToCheck' must contain a value. + PropertyValidationError = Expected to find an array value for property {0} in the current values, but it was either not present or was null. This has caused the test method to return false. + PropertiesDoesNotMatch = Found an array for property {0} in the current values, but this array does not match the desired state. Details of the changes are below. + PropertyThatDoesNotMatch = {0} - {1} + ValueOfTypeDoesNotMatch = {0} value for property {1} does not match. Current state is '{2}' and desired state is '{3}'. + UnableToCompareProperty = Unable to compare property {0} as the type {1} is not handled by the Test-DscParameterState cmdlet. +'@ diff --git a/source/Modules/iSCSIDsc.Common/iSCSIDsc.Common.psm1 b/source/Modules/iSCSIDsc.Common/iSCSIDsc.Common.psm1 new file mode 100644 index 0000000..6a0ec49 --- /dev/null +++ b/source/Modules/iSCSIDsc.Common/iSCSIDsc.Common.psm1 @@ -0,0 +1,493 @@ +$script:modulesFolderPath = Split-Path -Path $PSScriptRoot -Parent + +<# + .SYNOPSIS + This method is used to compare current and desired values for any DSC resource. + + .PARAMETER CurrentValues + This is hash table of the current values that are applied to the resource. + + .PARAMETER DesiredValues + This is a PSBoundParametersDictionary of the desired values for the resource. + + .PARAMETER ValuesToCheck + This is a list of which properties in the desired values list should be checked. + If this is empty then all values in DesiredValues are checked. +#> +function Test-DscParameterState +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.Collections.Hashtable] + $CurrentValues, + + [Parameter(Mandatory = $true)] + [System.Object] + $DesiredValues, + + [Parameter()] + [System.Array] + $ValuesToCheck + ) + + $returnValue = $true + + if (($DesiredValues.GetType().Name -ne 'HashTable') ` + -and ($DesiredValues.GetType().Name -ne 'CimInstance') ` + -and ($DesiredValues.GetType().Name -ne 'PSBoundParametersDictionary')) + { + $errorMessage = $script:localizedData.PropertyTypeInvalidForDesiredValues -f $($DesiredValues.GetType().Name) + New-InvalidArgumentException -ArgumentName 'DesiredValues' -Message $errorMessage + } + + if (($DesiredValues.GetType().Name -eq 'CimInstance') -and ($null -eq $ValuesToCheck)) + { + $errorMessage = $script:localizedData.PropertyTypeInvalidForValuesToCheck + New-InvalidArgumentException -ArgumentName 'ValuesToCheck' -Message $errorMessage + } + + if (($null -eq $ValuesToCheck) -or ($ValuesToCheck.Count -lt 1)) + { + $keyList = $DesiredValues.Keys + } + else + { + $keyList = $ValuesToCheck + } + + $keyList | ForEach-Object -Process { + if (($_ -ne 'Verbose')) + { + if (($CurrentValues.ContainsKey($_) -eq $false) ` + -or ($CurrentValues.$_ -ne $DesiredValues.$_) ` + -or (($DesiredValues.GetType().Name -ne 'CimInstance' -and $DesiredValues.ContainsKey($_) -eq $true) -and ($null -ne $DesiredValues.$_ -and $DesiredValues.$_.GetType().IsArray))) + { + if ($DesiredValues.GetType().Name -eq 'HashTable' -or ` + $DesiredValues.GetType().Name -eq 'PSBoundParametersDictionary') + { + $checkDesiredValue = $DesiredValues.ContainsKey($_) + } + else + { + # If DesiredValue is a CimInstance. + $checkDesiredValue = $false + if (([System.Boolean]($DesiredValues.PSObject.Properties.Name -contains $_)) -eq $true) + { + if ($null -ne $DesiredValues.$_) + { + $checkDesiredValue = $true + } + } + } + + if ($checkDesiredValue) + { + $desiredType = $DesiredValues.$_.GetType() + $fieldName = $_ + if ($desiredType.IsArray -eq $true) + { + if (($CurrentValues.ContainsKey($fieldName) -eq $false) ` + -or ($null -eq $CurrentValues.$fieldName)) + { + Write-Verbose -Message ($script:localizedData.PropertyValidationError -f $fieldName) -Verbose + + $returnValue = $false + } + else + { + $arrayCompare = Compare-Object -ReferenceObject $CurrentValues.$fieldName ` + -DifferenceObject $DesiredValues.$fieldName + if ($null -ne $arrayCompare) + { + Write-Verbose -Message ($script:localizedData.PropertiesDoesNotMatch -f $fieldName) -Verbose + + $arrayCompare | ForEach-Object -Process { + Write-Verbose -Message ($script:localizedData.PropertyThatDoesNotMatch -f $_.InputObject, $_.SideIndicator) -Verbose + } + + $returnValue = $false + } + } + } + else + { + switch ($desiredType.Name) + { + 'String' + { + if (-not [System.String]::IsNullOrEmpty($CurrentValues.$fieldName) -or ` + -not [System.String]::IsNullOrEmpty($DesiredValues.$fieldName)) + { + Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` + -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose + + $returnValue = $false + } + } + + 'Int32' + { + if (-not ($DesiredValues.$fieldName -eq 0) -or ` + -not ($null -eq $CurrentValues.$fieldName)) + { + Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` + -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose + + $returnValue = $false + } + } + + { $_ -eq 'Int16' -or $_ -eq 'UInt16' -or $_ -eq 'Single' } + { + if (-not ($DesiredValues.$fieldName -eq 0) -or ` + -not ($null -eq $CurrentValues.$fieldName)) + { + Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` + -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose + + $returnValue = $false + } + } + + 'Boolean' + { + if ($CurrentValues.$fieldName -ne $DesiredValues.$fieldName) + { + Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` + -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose + + $returnValue = $false + } + } + + default + { + Write-Warning -Message ($script:localizedData.UnableToCompareProperty ` + -f $fieldName, $desiredType.Name) + + $returnValue = $false + } + } + } + } + } + } + } + + return $returnValue +} + +<# + .SYNOPSIS + Retrieves the localized string data based on the machine's culture. + Falls back to en-US strings if the machine's culture is not supported. + + .PARAMETER ResourceName + The name of the resource as it appears before '.strings.psd1' of the localized string file. + For example: + For WindowsOptionalFeature: MSFT_WindowsOptionalFeature + For Service: MSFT_ServiceResource + For Registry: MSFT_RegistryResource + For Helper: SqlServerDscHelper + + .PARAMETER ScriptRoot + Optional. The root path where to expect to find the culture folder. This is only needed + for localization in helper modules. This should not normally be used for resources. + + .NOTES + To be able to use localization in the helper function, this function must + be first in the file, before Get-LocalizedData is used by itself to load + localized data for this helper module (see directly after this function). +#> +function Get-LocalizedData +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ResourceName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $ScriptRoot + ) + + if (-not $ScriptRoot) + { + $dscResourcesFolder = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'DSCResources' + $resourceDirectory = Join-Path -Path $dscResourcesFolder -ChildPath $ResourceName + } + else + { + $resourceDirectory = $ScriptRoot + } + + $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath $PSUICulture + + if (-not (Test-Path -Path $localizedStringFileLocation)) + { + # Fallback to en-US + $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath 'en-US' + } + + Import-LocalizedData ` + -BindingVariable 'localizedData' ` + -FileName "$ResourceName.strings.psd1" ` + -BaseDirectory $localizedStringFileLocation + + return $localizedData +} + +<# + .SYNOPSIS + Creates and throws an invalid argument exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ArgumentName + The name of the invalid argument that is causing this error to be thrown. +#> +function New-InvalidArgumentException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ArgumentName + ) + + $argumentException = New-Object -TypeName 'ArgumentException' ` + -ArgumentList @($Message, $ArgumentName) + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @($argumentException, $ArgumentName, 'InvalidArgument', $null) + } + + $errorRecord = New-Object @newObjectParameters + + throw $errorRecord +} + +<# + .SYNOPSIS + Creates and throws an invalid operation exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. +#> +function New-InvalidOperationException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` + -ArgumentList @($Message) + } + else + { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $invalidOperationException.ToString(), + 'MachineStateIncorrect', + 'InvalidOperation', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} + +<# + .SYNOPSIS + Creates and throws an object not found exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. +#> +function New-ObjectNotFoundException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message) + } + else + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $exception.ToString(), + 'MachineStateIncorrect', + 'ObjectNotFound', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} + +<# + .SYNOPSIS + Creates and throws an invalid result exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. +#> +function New-InvalidResultException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message) + } + else + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $exception.ToString(), + 'MachineStateIncorrect', + 'InvalidResult', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} + +function New-NotImplementedException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $invalidOperationException = New-Object -TypeName 'NotImplementedException' ` + -ArgumentList @($Message) + } + else + { + $invalidOperationException = New-Object -TypeName 'NotImplementedException' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $invalidOperationException.ToString(), + 'MachineStateIncorrect', + 'NotImplemented', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} + +$script:localizedData = Get-LocalizedData -ResourceName 'iSCSIDsc.Common' -ScriptRoot $PSScriptRoot + +Export-ModuleMember -Function @( + 'Test-DscParameterState', + 'New-InvalidArgumentException', + 'New-InvalidOperationException', + 'New-ObjectNotFoundException', + 'New-InvalidResultException', + 'New-NotImplementedException' + 'Get-LocalizedData' +) diff --git a/source/en-US/about_iSCSIDsc.help.txt b/source/en-US/about_iSCSIDsc.help.txt new file mode 100644 index 0000000..46e22e5 --- /dev/null +++ b/source/en-US/about_iSCSIDsc.help.txt @@ -0,0 +1,26 @@ +TOPIC + about_iSCSIDsc + +SHORT DESCRIPTION + DSC resources for configuring Windows iSCSI Targets and Initiators. + +LONG DESCRIPTION + This module contains DSC resources for configuring Windows iSCSI Targets + and Initiators. + +EXAMPLES + PS C:\> Get-DscResource -Module iSCSIDsc + +NOTE: + Thank you to the DSC Community contributors who contributed to this module by + writing code, sharing opinions, and provided feedback. + +TROUBLESHOOTING NOTE: + Go to the Github repository for read about issues, submit a new issue, and read + about new releases. https://github.com/dsccommunity/iSCSIDsc + +SEE ALSO + - https://github.com/dsccommunity/iSCSIDsc + +KEYWORDS + DesiredStateConfiguration, DSC, DSCResource, iSCSI, Target, Initiator diff --git a/source/iSCSIDsc.psd1 b/source/iSCSIDsc.psd1 new file mode 100644 index 0000000..2a6e6b2 --- /dev/null +++ b/source/iSCSIDsc.psd1 @@ -0,0 +1,67 @@ +@{ + # Version number of this module. + ModuleVersion = '0.0.1' + + # ID used to uniquely identify this module + GUID = 'f2793754-6dc7-439a-a779-b1529b5e704c' + + # Author of this module + Author = 'DSC Community' + + # Company or vendor of this module + CompanyName = 'DSC Community' + + # Copyright statement for this module + Copyright = 'Copyright the DSC Community contributors. All rights reserved.' + + # Description of the functionality provided by this module + Description = 'DSC resources for configuring Windows iSCSI Targets and Initiators.' + + # Minimum version of the Windows PowerShell engine required by this module + PowerShellVersion = '4.0' + + # Minimum version of the common language runtime (CLR) required by this module + CLRVersion = '4.0' + + # Processor architecture (None, X86, Amd64) required by this module + ProcessorArchitecture = 'None' + + # Functions to export from this module + FunctionsToExport = @() + + # Cmdlets to export from this module + CmdletsToExport = @() + + # Variables to export from this module + VariablesToExport = @() + + # Aliases to export from this module + AliasesToExport = @() + + # DSC resources to export from this module + DscResourcesToExport = @('iSCSIInitiator', 'iSCSIServerTarget', 'iSCSIVirtualDisk') + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ + + PSData = @{ + # Set to a prerelease string value if the release should be a prerelease. + Prerelease = '' + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = @('DesiredStateConfiguration', 'DSC', 'DSCResource', 'iSCSI', 'Target', 'Initiator') + + # A URL to the license for this module. + LicenseUri = 'https://github.com/dsccommunity/iSCSIDsc/blob/dev/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/dsccommunity/iSCSIDsc' + + # A URL to an icon representing this module. + IconUri = 'https://dsccommunity.org/images/DSC_Logo_300p.png' + + # ReleaseNotes of this module + ReleaseNotes = '' + } # End of PSData hashtable + } # End of PrivateData hashtable +} diff --git a/tests/Integration/DSC_iSCSIInitiator.Integration.Tests.ps1 b/tests/Integration/DSC_iSCSIInitiator.Integration.Tests.ps1 new file mode 100644 index 0000000..e987ae8 --- /dev/null +++ b/tests/Integration/DSC_iSCSIInitiator.Integration.Tests.ps1 @@ -0,0 +1,185 @@ +<# + These tests are disabled because they require iSCSI Loopback + iSCSI Loopback is supposed to work in Windows Server 2012 R2 + However, as of 2016-01-03 I have not been able to get it to + work and there is no documentation available on how to do so. + See http://blogs.technet.com/b/filecab/archive/2012/05/21/introduction-of-iscsi-target-in-windows-server-2012.aspx + This has been left here in case someone is able to figure out + how to get it going. +#> +return + +$script:dscModuleName = 'iSCSIDsc' +$script:dscResourceName = 'DSC_iSCSIInitiator' + +try +{ + Import-Module -Name DscResource.Test -Force -ErrorAction 'Stop' +} +catch [System.IO.FileNotFoundException] +{ + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' +} + +$script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Integration' + +Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\TestHelpers\CommonTestHelper.psm1') + +try +{ + $configFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dscResourceName).Config.ps1" + . $configFile + + Describe "$($script:DSCResourceName)_Integration" { + Context 'When creating an iSCSI Initiator' { + BeforeAll { + $script:ipAddress = (Get-NetIPAddress -InterfaceIndex (Get-NetConnectionProfile -IPv4Connectivity Internet).InterfaceIndex -AddressFamily IPv4).IPAddress + $script:targetName = 'TestServerTarget' + $script:Initiator = @{ + NodeAddress = "iqn.1991-05.com.microsoft:$($ENV:ComputerName)-$script:targetName-target-target" + TargetPortalAddress = $ENV:ComputerName + InitiatorPortalAddress = $ENV:ComputerName + Ensure = 'Present' + TargePortalPortNumber = 3260 + InitiatorInstanceName = 'ROOT\ISCSIPRT\0000_0' + AuthenticationType = 'OneWayCHAP' + ChapUsername = 'MyUsername' + ChapSecret = 'MySecret' + IsDataDigest = $false + IsHeaderDigest = $false + IsMultipathEnabled = $false + IsPersistent = $true + ReportToPnP = $true + iSNSServer = "isns.contoso.com" + } + + # Create a Server Target on this computer to test with + $script:virtualDiskPath = Join-Path ` + -Path $TestDrive ` + -ChildPath ([System.IO.Path]::ChangeExtension([System.IO.Path]::GetRandomFileName(),'vhdx')) + + New-iSCSIVirtualDisk ` + -ComputerName LOCALHOST ` + -Path $script:virtualDiskPath ` + -SizeBytes 500MB + New-iSCSIServerTarget ` + -TargetName $script:targetName ` + -InitiatorIds "Iqn:iqn.1991-05.com.microsoft:$($script:Initiator.InitiatorPortalAddress)" ` + -ComputerName LOCALHOST + Add-IscsiVirtualDiskTargetMapping ` + -ComputerName LOCALHOST ` + -TargetName $script:targetName ` + -Path $script:virtualDiskPath + } # BeforeAll + + It 'Should compile and apply the MOF without throwing' { + { + $configData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + NodeAddress = $script:initiator.NodeAddress + TargetPortalAddress = $script:initiator.TargetPortalAddress + InitiatorPortalAddress = $script:initiator.InitiatorPortalAddress + Ensure = $script:initiator.Ensure + TargetPortalPortNumber = $script:initiator.TargetPortalPortNumber + InitiatorInstanceName = $script:initiator.InitiatorInstanceName + AuthenticationType = $script:initiator.AuthenticationType + ChapUsername = $script:initiator.ChapUsername + ChapSecret = $script:initiator.ChapSecret + IsDataDigest = $script:initiator.IsDataDigest + IsHeaderDigest = $script:initiator.IsHeaderDigest + IsMultipathEnabled = $script:initiator.IsMultipathEnabled + IsPersistent = $script:initiator.IsPersistent + ReportToPnP = $script:initiator.ReportToPnP + iSNSServer = $script:initiator.iSNSServer + } + ) + } + + & "$($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' { + { + Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -throw + } + + It 'Should have set the resource and all the parameters should match' { + # Get the Target Portal details + $targetPortalNew = Get-iSCSITargetPortal ` + -TargetPortalAddress $TargetPortal.TargetPortalAddress ` + -InitiatorPortalAddress $TargetPortal.InitiatorPortalAddress + $script:Initiator.TargetPortalAddress | Should -Be $targetPortalNew.TargetPortalAddress + $script:Initiator.TargetPortalPortNumber | Should -Be $targetPortalNew.TargetPortalPortNumber + $script:Initiator.InitiatorInstanceName | Should -Be $targetPortalNew.InitiatorInstanceName + $script:Initiator.InitiatorPortalAddress | Should -Be $targetPortalNew.InitiatorPortalAddress + $script:Initiator.IsDataDigest | Should -Be $targetPortalNew.IsDataDigest + $script:Initiator.IsHeaderDigest | Should -Be $targetPortalNew.IsHeaderDigest + $targetNew = Get-iSCSITarget ` + -NodeAddress $script:Initiator.NodeAddress + $script:Initiator.IsConnected | Should -Be $True + $script:Initiator.NodeAddress | Should -Be $targetNew.NodeAddress + $sessionNew = Get-iSCSISession ` + -IscsiTarget $targetNew + $script:Initiator.TargetPortalAddress | Should -Be $sessionNew.TargetAddress + $script:Initiator.InitiatorPortalAddress | Should -Be $sessionNew.InitiatorAddress + $script:Initiator.TargetPortalPortNumber | Should -Be $sessionNew.TargetPortNumber + $script:Initiator.ConnectionIdentifier | Should -Be $sessionNew.ConnectionIdentifier + $connectionNew = Get-iSCSIConnection ` + -NodeAddress $Target.NodeAddress + $script:Initiator.AuthenticationType | Should -Be $connectionNew.AuthenticationType + $script:Initiator.InitiatorInstanceName | Should -Be $connectionNew.InitiatorInstanceName + $script:Initiator.InitiatorPortalAddress | Should -Be $connectionNew.InitiatorPortalAddress + $script:Initiator.IsConnected | Should -Be $connectionNew.IsConnected + $script:Initiator.IsDataDigest | Should -Be $connectionNew.IsDataDigest + $script:Initiator.IsDiscovered | Should -Be $connectionNew.IsDiscovered + $script:Initiator.IsHeaderDigest | Should -Be $connectionNew.IsHeaderDigest + $script:Initiator.IsPersistent | Should -Be $connectionNew.IsPersistent + $iSNSServerNew = Get-WmiObject -Class MSiSCSIInitiator_iSNSServerClass -Namespace root\wmi + # The iSNS Server is not usually accessible so won't be able to be set + # $script:Initiator.iSNSServer | Should Be $iSNSServerNew.iSNSServerAddress + } + + AfterAll { + # Clean up + Disconnect-IscsiTarget ` + -NodeAddress $script:Initiator.NodeAddress ` + -Confirm:$False + Remove-IscsiTargetPortal ` + -TargetPortalAddress $script:Initiator.TargetPortalAddress ` + -InitiatorPortalAddress $script:Initiator.InitiatorPortalAddress ` + -Confirm:$False + Remove-iSCSIServerTarget ` + -ComputerName LOCALHOST ` + -TargetName $script:targetName + Remove-iSCSIVirtualDisk ` + -ComputerName LOCALHOST ` + -Path $script:virtualDiskPath + Remove-Item ` + -Path $script:virtualDiskPath ` + -Force + } # AfterAll + } + } +} +finally +{ + Restore-TestEnvironment -TestEnvironment $script:testEnvironment +} diff --git a/tests/Integration/DSC_iSCSIInitiator.config.ps1 b/tests/Integration/DSC_iSCSIInitiator.config.ps1 new file mode 100644 index 0000000..a5d4802 --- /dev/null +++ b/tests/Integration/DSC_iSCSIInitiator.config.ps1 @@ -0,0 +1,23 @@ +Configuration DSC_iSCSIInitiator_Config { + Import-DscResource -ModuleName iSCSIDsc + + Node localhost { + iSCSIInitiator Integration_Test { + NodeAddress = $Node.NodeAddress + TargetPortalAddress = $Node.TargetPortalAddress + InitiatorPortalAddress = $Node.InitiatorPortalAddress + Ensure = $Node.Ensure + TargetPortalPortNumber = $Node.TargetPortalPortNumber + InitiatorInstanceName = $Node.InitiatorInstanceName + AuthenticationType = $Node.AuthenticationType + ChapUsername = $Node.ChapUsername + ChapSecret = $Node.ChapSecret + IsDataDigest = $Node.IsDataDigest + IsHeaderDigest = $Node.IsHeaderDigest + IsMultipathEnabled = $Node.IsMultipathEnabled + IsPersistent = $Node.IsPersistent + ReportToPnP = $Node.ReportToPnP + iSNSServer = $Node.iSNSServer + } + } +} diff --git a/tests/Integration/DSC_iSCSIServerTarget.Integration.Tests.ps1 b/tests/Integration/DSC_iSCSIServerTarget.Integration.Tests.ps1 new file mode 100644 index 0000000..6bfa96e --- /dev/null +++ b/tests/Integration/DSC_iSCSIServerTarget.Integration.Tests.ps1 @@ -0,0 +1,121 @@ +$script:dscModuleName = 'iSCSIDsc' +$script:dscResourceName = 'DSC_iSCSIServerTarget' + +try +{ + Import-Module -Name DscResource.Test -Force -ErrorAction 'Stop' +} +catch [System.IO.FileNotFoundException] +{ + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' +} + +$script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Integration' + +Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\TestHelpers\CommonTestHelper.psm1') + +try +{ + Assert-CanRunIntegrationTest -Verbose +} +catch +{ + Write-Warning -Message $_ + return +} + +try +{ + $configFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dscResourceName).Config.ps1" + . $configFile + + Describe "$($script:DSCResourceName)_Integration" { + Context 'When creating an iSCSI Server Target' { + BeforeAll { + $script:virtualDisk = @{ + Path = Join-Path -Path $TestDrive -ChildPath 'TestiSCSIServerTarget.vhdx' + } + $script:serverTarget = @{ + TargetName = 'testtarget' + Ensure = 'Present' + InitiatorIds = @( 'Iqn:iqn.1991-05.com.microsoft:fs1.contoso.com','Iqn:iqn.1991-05.com.microsoft:fs2.contoso.com' ) + Paths = @( $script:virtualDisk.Path ) + iSNSServer = 'isns.contoso.com' + } + + New-iSCSIVirtualDisk ` + -Path $script:virtualDisk.Path ` + -Size 104857600 + } # BeforeAll + + + It 'Should compile and apply the MOF without throwing' { + { + $configData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + TargetName = $script:serverTarget.TargetName + Ensure = $script:serverTarget.Ensure + InitiatorIds = $script:serverTarget.InitiatorIds + Paths = $script:serverTarget.Paths + iSNSServer = $script:serverTarget.iSNSServer + } + ) + } + + & "$($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' { + { + Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -throw + } + + It 'Should have set the resource and all the parameters should match' { + # Get the Rule details + $ServerTargetNew = Get-iSCSIServerTarget -TargetName $script:serverTarget.TargetName + $ServerTargetNew.TargetName | Should -Be $script:serverTarget.TargetName + $ServerTargetNew.InitiatorIds | Should -Be $script:serverTarget.InitiatorIds + $ServerTargetNew.LunMappings.Path | Should -Be $script:serverTarget.Paths + $iSNSServerNew = Get-WmiObject -Class WT_iSNSServer -Namespace root\wmi + # The iSNS Server is not usually accessible so won't be able to be set + # $iSNSServerNew.ServerName | Should Be $ServerTarget.iSNSServer + } + + AfterAll { + # Clean up + Get-WmiObject ` + -Class WT_iSNSServer ` + -Namespace root\wmi | Remove-WmiObject + Remove-iSCSIServerTarget ` + -TargetName $script:serverTarget.TargetName + Remove-iSCSIVirtualDisk ` + -Path $script:virtualDisk.Path + Remove-Item ` + -Path $script:virtualDisk.Path ` + -Force + } # AfterAll + } + } +} +finally +{ + Restore-TestEnvironment -TestEnvironment $script:testEnvironment +} diff --git a/tests/Integration/DSC_iSCSIServerTarget.config.ps1 b/tests/Integration/DSC_iSCSIServerTarget.config.ps1 new file mode 100644 index 0000000..3656c57 --- /dev/null +++ b/tests/Integration/DSC_iSCSIServerTarget.config.ps1 @@ -0,0 +1,13 @@ +Configuration DSC_iSCSIServerTarget_Config { + Import-DscResource -ModuleName iSCSIDsc + + Node localhost { + iSCSIServerTarget Integration_Test { + TargetName = $Node.TargetName + Ensure = $Node.Ensure + InitiatorIds = $Node.InitiatorIds + Paths = $Node.Paths + iSNSServer = $Node.iSNSServer + } + } +} diff --git a/tests/Integration/DSC_iSCSIVirtualDisk.Integration.Tests.ps1 b/tests/Integration/DSC_iSCSIVirtualDisk.Integration.Tests.ps1 new file mode 100644 index 0000000..01149f6 --- /dev/null +++ b/tests/Integration/DSC_iSCSIVirtualDisk.Integration.Tests.ps1 @@ -0,0 +1,106 @@ +$script:dscModuleName = 'iSCSIDsc' +$script:dscResourceName = 'DSC_iSCSIVirtualDisk' + +try +{ + Import-Module -Name DscResource.Test -Force -ErrorAction 'Stop' +} +catch [System.IO.FileNotFoundException] +{ + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' +} + +$script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Integration' + +Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\TestHelpers\CommonTestHelper.psm1') + +try +{ + Assert-CanRunIntegrationTest -Verbose +} +catch +{ + Write-Warning -Message $_ + return +} + +try +{ + $configFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dscResourceName).Config.ps1" + . $configFile + + Describe "$($script:DSCResourceName)_Integration" { + Context 'When creating a iSCSI Virtual Disk' { + BeforeAll { + $script:testVirtualDisk = @{ + Path = Join-Path -Path $TestDrive -ChildPath 'TestiSCSIVirtualDisk.vhdx' + Ensure = 'Present' + DiskType = 'Dynamic' + SizeBytes = 104857600 + Description = 'Integration Test iSCSI Virtual Disk' + } + } + + It 'Should compile and apply the MOF without throwing' { + { + $configData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + Path = $script:testVirtualDisk.Path + Ensure = $script:testVirtualDisk.Ensure + DiskType = $script:testVirtualDisk.DiskType + SizeBytes = $script:testVirtualDisk.SizeBytes + Description = $script:testVirtualDisk.Description + } + ) + } + + & "$($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' { + { + Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -throw + } + + It 'Should have set the resource and all the parameters should match' { + # Get the Rule details + $virtualDiskNew = Get-iSCSIVirtualDisk -Path $script:testVirtualDisk.Path + $virtualDiskNew.Path | Should -Be $script:testVirtualDisk.Path + $virtualDiskNew.DiskType | Should -Be $script:testVirtualDisk.DiskType + $virtualDiskNew.Size | Should -Be $script:testVirtualDisk.SizeBytes + $virtualDiskNew.Description | Should -Be $script:testVirtualDisk.Description + } + + AfterAll { + # Clean up + Remove-iSCSIVirtualDisk ` + -Path $script:testVirtualDisk.Path + Remove-Item ` + -Path $script:testVirtualDisk.Path ` + -Force + } # AfterAll + } + } +} +finally +{ + Restore-TestEnvironment -TestEnvironment $script:testEnvironment +} diff --git a/tests/Integration/DSC_iSCSIVirtualDisk.config.ps1 b/tests/Integration/DSC_iSCSIVirtualDisk.config.ps1 new file mode 100644 index 0000000..88a179e --- /dev/null +++ b/tests/Integration/DSC_iSCSIVirtualDisk.config.ps1 @@ -0,0 +1,13 @@ +Configuration DSC_iSCSIVirtualDisk_Config { + Import-DscResource -ModuleName iSCSIDsc + + Node localhost { + iSCSIVirtualDisk Integration_Test { + Path = $Node.Path + Ensure = $Node.Ensure + DiskType = $Node.DiskType + SizeBytes = $Node.SizeBytes + Description = $Node.Description + } + } +} diff --git a/Tests/TestHelpers/CommonTestHelper.psm1 b/tests/TestHelpers/CommonTestHelper.psm1 similarity index 55% rename from Tests/TestHelpers/CommonTestHelper.psm1 rename to tests/TestHelpers/CommonTestHelper.psm1 index 4afd62a..6b1e3ef 100644 --- a/Tests/TestHelpers/CommonTestHelper.psm1 +++ b/tests/TestHelpers/CommonTestHelper.psm1 @@ -83,63 +83,63 @@ function Get-InvalidOperationRecord <# .SYNOPSIS - Creates a new VHD using DISKPART. - DISKPART is used because New-VHD is only available if Hyper-V is installed. + Asserts if the system is able to run integration tests. - .PARAMETER Path - The path to the VHD file to create. + .NOTES + When running integrations tests in Azure DevOps agents an exception is thrown + when creating a virtual iSCSI disk on the system: + UnauthorizedAccessException: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)) - .PARAMETER SizeInMB - The size of the VHD disk to create. - - .PARAMETER Initialize - Should the disk be initialized? This is for testing matching disks by GUID. + This previously did not occur and a solution to this has not been found. + Therefore suppress execution of these tests if the Virtual Disk can not + be created. #> -function New-VDisk +function Assert-CanRunIntegrationTest { [CmdletBinding()] - param - ( - [Parameter(Mandatory = $True)] - [String] - $Path, + param () - [Parameter()] - [Uint32] - $SizeInMB, + # Ensure that the tests can be performed on this computer + Write-Verbose -Message 'Assert operating system is Windows Server' + $productType = (Get-CimInstance Win32_OperatingSystem).ProductType - [Parameter()] - [Switch] - $Initialize - ) - - $tempScriptPath = Join-Path -Path $ENV:Temp -ChildPath 'DiskPartVdiskScript.txt' - Write-Verbose -Message ('Creating DISKPART script {0}' -f $tempScriptPath) + if ($productType -ne 3) + { + throw 'Integration tests can only be run on Windows Server operating systems' + } - $diskPartScript = "CREATE VDISK FILE=`"$Path`" TYPE=EXPANDABLE MAXIMUM=$SizeInMB" + Write-Verbose -Message 'Assert FS-iSCSITarget-Server feature is installed' + $installed = (Get-WindowsFeature -Name FS-iSCSITarget-Server).Installed - if ($Initialize) + if ($installed -eq $false) { - # The disk will be initialized with GPT (first blank line required because we're adding to existing string) - $diskPartScript += @" - -SELECT VDISK FILE=`"$Path`" -ATTACH VDISK -CONVERT GPT -DETACH VDISK -"@ + throw 'Integration tests require FS-iSCSITarget-Server windows feature to be installed' } - Set-Content ` - -Path $tempScriptPath ` - -Value $diskPartScript ` - -Encoding Ascii - $result = & DISKPART @('/s',$tempScriptPath) - Write-Verbose -Message ($Result | Out-String) - $null = Remove-Item -Path $tempScriptPath -Force -} # end function New-VDisk + $virtualDiskPath = Join-Path -Path $ENV:Temp -ChildPath 'AssertCreateIscsiVirtualDisk.vhdx' + + try + { + Write-Verbose -Message 'Assert iSCSI Virtual Disk can be created' + New-iSCSIVirtualDisk ` + -Path $virtualDiskPath ` + -Size 10MB + } + catch + { + Remove-iSCSIVirtualDisk ` + -Path $virtualDiskPath ` + -ErrorAction SilentlyContinue + Remove-Item ` + -Path $virtualDiskPath ` + -Force ` + -ErrorAction SilentlyContinue + + throw ('Integration tests can only be run if an iSCSI Virtual Disk can be created. Failed with {0}' -f $_) + } +} Export-ModuleMember -Function ` - New-VDisk, ` Get-InvalidArgumentRecord, ` - Get-InvalidOperationRecord + Get-InvalidOperationRecord, ` + Assert-CanRunIntegrationTest diff --git a/Tests/Unit/DSR_iSCSIInitiator.Tests.ps1 b/tests/Unit/DSC_iSCSIInitiator.Tests.ps1 similarity index 98% rename from Tests/Unit/DSR_iSCSIInitiator.Tests.ps1 rename to tests/Unit/DSC_iSCSIInitiator.Tests.ps1 index ec89c00..bd7acc5 100644 --- a/Tests/Unit/DSR_iSCSIInitiator.Tests.ps1 +++ b/tests/Unit/DSC_iSCSIInitiator.Tests.ps1 @@ -1,30 +1,37 @@ -$script:DSCModuleName = 'iSCSIDsc' -$script:DSCResourceName = 'DSR_iSCSIInitiator' +$script:dscModuleName = 'iSCSIDsc' +$script:dscResourceName = 'DSC_iSCSIInitiator' -Import-Module -Name (Join-Path -Path (Join-Path -Path (Split-Path $PSScriptRoot -Parent) -ChildPath 'TestHelpers') -ChildPath 'CommonTestHelper.psm1') -Global +function Invoke-TestSetup +{ + try + { + Import-Module -Name DscResource.Test -Force -ErrorAction 'Stop' + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' + } + + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' -#region HEADER -# Unit Test Template Version: 1.1.0 -[System.String] $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) -if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` - (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) + Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\TestHelpers\CommonTestHelper.psm1') +} + +function Invoke-TestCleanup { - & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) + Restore-TestEnvironment -TestEnvironment $script:testEnvironment } -Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force -$TestEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:DSCModuleName ` - -DSCResourceName $script:DSCResourceName ` - -TestType Unit -#endregion HEADER +Invoke-TestSetup # Begin Testing try { - #region Pester Tests - InModuleScope $script:DSCResourceName { - + InModuleScope $script:dscResourceName { # Create the Mock Objects that will be used for running tests $TestInitiator = [PSObject]@{ NodeAddress = 'iqn.1991-05.com.microsoft:fileserver-cluster-target-target' @@ -182,7 +189,7 @@ try ) } - Describe "DSR_iSCSIInitiator\Get-TargetResource" { + Describe "DSC_iSCSIInitiator\Get-TargetResource" { Context 'Target Portal and Target do not exist' { Mock Get-TargetPortal Mock Get-Target @@ -394,7 +401,7 @@ try } } - Describe "DSR_iSCSIInitiator\Set-TargetResource" { + Describe "DSC_iSCSIInitiator\Set-TargetResource" { Context 'Target Portal does not exist but should' { Mock Get-TargetPortal Mock New-IscsiTargetPortal @@ -1193,7 +1200,7 @@ try } } - Describe "DSR_iSCSIInitiator\Test-TargetResource" { + Describe "DSC_iSCSIInitiator\Test-TargetResource" { Context 'Target Portal does not exist but should' { Mock Get-TargetPortal Mock Get-Target @@ -1620,7 +1627,7 @@ try } } - Describe "DSR_iSCSIInitiator\Get-TargetPortal" { + Describe "DSC_iSCSIInitiator\Get-TargetPortal" { Context 'Target Portal does not exist' { Mock Get-iSCSITargetPortal It 'should return null' { @@ -1655,7 +1662,7 @@ try } } - Describe "DSR_iSCSIInitiator\Get-Target" { + Describe "DSC_iSCSIInitiator\Get-Target" { Context 'Target does not exist' { Mock Get-iSCSITarget It 'should return null' { @@ -1684,11 +1691,8 @@ try } } } - #endregion } finally { - #region FOOTER - Restore-TestEnvironment -TestEnvironment $TestEnvironment - #endregion + Invoke-TestCleanup } diff --git a/Tests/Unit/DSR_iSCSIServerTarget.Tests.ps1 b/tests/Unit/DSC_iSCSIServerTarget.Tests.ps1 similarity index 95% rename from Tests/Unit/DSR_iSCSIServerTarget.Tests.ps1 rename to tests/Unit/DSC_iSCSIServerTarget.Tests.ps1 index f778a26..7269513 100644 --- a/Tests/Unit/DSR_iSCSIServerTarget.Tests.ps1 +++ b/tests/Unit/DSC_iSCSIServerTarget.Tests.ps1 @@ -1,30 +1,37 @@ -$script:DSCModuleName = 'iSCSIDsc' -$script:DSCResourceName = 'DSR_iSCSIServerTarget' +$script:dscModuleName = 'iSCSIDsc' +$script:dscResourceName = 'DSC_iSCSIServerTarget' -Import-Module -Name (Join-Path -Path (Join-Path -Path (Split-Path $PSScriptRoot -Parent) -ChildPath 'TestHelpers') -ChildPath 'CommonTestHelper.psm1') -Global +function Invoke-TestSetup +{ + try + { + Import-Module -Name DscResource.Test -Force -ErrorAction 'Stop' + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' + } + + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' -#region HEADER -# Unit Test Template Version: 1.1.0 -[System.String] $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) -if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` - (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) + Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\TestHelpers\CommonTestHelper.psm1') +} + +function Invoke-TestCleanup { - & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) + Restore-TestEnvironment -TestEnvironment $script:testEnvironment } -Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force -$TestEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:DSCModuleName ` - -DSCResourceName $script:DSCResourceName ` - -TestType Unit -#endregion HEADER +Invoke-TestSetup # Begin Testing try { - #region Pester Tests - InModuleScope $script:DSCResourceName { - + InModuleScope $script:dscResourceName { # Create the Mock Objects that will be used for running tests $TestVirtualDisk = [PSObject]@{ Path = Join-Path -Path $ENV:Temp -ChildPath 'TestiSCSIVirtualDisk.vhdx' @@ -92,7 +99,7 @@ try Break } - Describe "DSR_iSCSIServerTarget\Get-TargetResource" { + Describe "DSC_iSCSIServerTarget\Get-TargetResource" { Context 'Server Target does not exist' { @@ -156,7 +163,7 @@ try } } - Describe "DSR_iSCSIServerTarget\Set-TargetResource" { + Describe "DSC_iSCSIServerTarget\Set-TargetResource" { Context 'Server Target does not exist but should' { @@ -477,7 +484,7 @@ try } } - Describe "DSR_iSCSIServerTarget\Test-TargetResource" { + Describe "DSC_iSCSIServerTarget\Test-TargetResource" { Context 'Server Target does not exist but should' { @@ -637,7 +644,7 @@ try } } - Describe "DSR_iSCSIServerTarget\Get-ServerTarget" { + Describe "DSC_iSCSIServerTarget\Get-ServerTarget" { Context 'Server Target does not exist' { @@ -669,11 +676,8 @@ try } } } - #endregion } finally { - #region FOOTER - Restore-TestEnvironment -TestEnvironment $TestEnvironment - #endregion + Invoke-TestCleanup } diff --git a/Tests/Unit/DSR_iSCSIVirtualDisk.Tests.ps1 b/tests/Unit/DSC_iSCSIVirtualDisk.Tests.ps1 similarity index 93% rename from Tests/Unit/DSR_iSCSIVirtualDisk.Tests.ps1 rename to tests/Unit/DSC_iSCSIVirtualDisk.Tests.ps1 index e3a5bd4..7a41de5 100644 --- a/Tests/Unit/DSR_iSCSIVirtualDisk.Tests.ps1 +++ b/tests/Unit/DSC_iSCSIVirtualDisk.Tests.ps1 @@ -1,30 +1,37 @@ -$script:DSCModuleName = 'iSCSIDsc' -$script:DSCResourceName = 'DSR_iSCSIVirtualDisk' +$script:dscModuleName = 'iSCSIDsc' +$script:dscResourceName = 'DSC_iSCSIVirtualDisk' -Import-Module -Name (Join-Path -Path (Join-Path -Path (Split-Path $PSScriptRoot -Parent) -ChildPath 'TestHelpers') -ChildPath 'CommonTestHelper.psm1') -Global +function Invoke-TestSetup +{ + try + { + Import-Module -Name DscResource.Test -Force -ErrorAction 'Stop' + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' + } + + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' -#region HEADER -# Unit Test Template Version: 1.1.0 -[System.String] $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) -if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` - (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) + Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\TestHelpers\CommonTestHelper.psm1') +} + +function Invoke-TestCleanup { - & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) + Restore-TestEnvironment -TestEnvironment $script:testEnvironment } -Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force -$TestEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:DSCModuleName ` - -DSCResourceName $script:DSCResourceName ` - -TestType Unit -#endregion HEADER +Invoke-TestSetup # Begin Testing try { - #region Pester Tests - InModuleScope $script:DSCResourceName { - + InModuleScope $script:dscResourceName { # Create the Mock Objects that will be used for running tests $TestVirtualDisk = [PSObject]@{ Path = Join-Path -Path $ENV:Temp -ChildPath 'TestiSCSIVirtualDisk.vhdx' @@ -73,7 +80,7 @@ try Break } - Describe "DSR_iSCSIVirtualDisk\Get-TargetResource" { + Describe "DSC_iSCSIVirtualDisk\Get-TargetResource" { Context 'Virtual Disk does not exist' { @@ -109,7 +116,7 @@ try } } - Describe "DSR_iSCSIVirtualDisk\Set-TargetResource" { + Describe "DSC_iSCSIVirtualDisk\Set-TargetResource" { Context 'Virtual Disk does not exist but should' { @@ -287,7 +294,7 @@ try } } - Describe "DSR_iSCSIVirtualDisk\Test-TargetResource" { + Describe "DSC_iSCSIVirtualDisk\Test-TargetResource" { Context 'Virtual Disk does not exist but should' { @@ -437,7 +444,7 @@ try } } - Describe "DSR_iSCSIVirtualDisk\Get-VirtualDisk" { + Describe "DSC_iSCSIVirtualDisk\Get-VirtualDisk" { Context 'Virtual Disk does not exist' { @@ -472,11 +479,8 @@ try } } } - #endregion } finally { - #region FOOTER - Restore-TestEnvironment -TestEnvironment $TestEnvironment - #endregion + Invoke-TestCleanup } diff --git a/tests/Unit/iSCSIDsc.Common.Tests.ps1 b/tests/Unit/iSCSIDsc.Common.Tests.ps1 new file mode 100644 index 0000000..15c2af6 --- /dev/null +++ b/tests/Unit/iSCSIDsc.Common.Tests.ps1 @@ -0,0 +1,531 @@ +#region HEADER +$script:projectPath = "$PSScriptRoot\..\.." | Convert-Path +$script:projectName = (Get-ChildItem -Path "$script:projectPath\*\*.psd1" | Where-Object -FilterScript { + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try + { + Test-ModuleManifest -Path $_.FullName -ErrorAction Stop + } + catch + { + $false + }) + }).BaseName + +$script:parentModule = Get-Module -Name $script:projectName -ListAvailable | Select-Object -First 1 +$script:subModulesFolder = Join-Path -Path $script:parentModule.ModuleBase -ChildPath 'Modules' +Remove-Module -Name $script:parentModule -Force -ErrorAction 'SilentlyContinue' + +$script:subModuleName = (Split-Path -Path $PSCommandPath -Leaf) -replace '\.Tests.ps1' +$script:subModuleFile = Join-Path -Path $script:subModulesFolder -ChildPath "$($script:subModuleName)/$($script:subModuleName).psm1" + +Import-Module $script:subModuleFile -Force -ErrorAction Stop +#endregion HEADER + +InModuleScope $script:subModuleName { + Describe 'iSCSIDsc.Common\Test-DscParameterState' -Tag TestDscParameterState { + Context -Name 'When passing values' -Fixture { + It 'Should return true for two identical tables' { + $mockDesiredValues = @{ Example = 'test' } + + $testParameters = @{ + CurrentValues = $mockDesiredValues + DesiredValues = $mockDesiredValues + } + + Test-DscParameterState @testParameters | Should -Be $true + } + + It 'Should return false when a value is different for [System.String]' { + $mockCurrentValues = @{ Example = [System.String] 'something' } + $mockDesiredValues = @{ Example = [System.String] 'test' } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + } + + Test-DscParameterState @testParameters | Should -Be $false + } + + It 'Should return false when a value is different for [System.Int32]' { + $mockCurrentValues = @{ Example = [System.Int32] 1 } + $mockDesiredValues = @{ Example = [System.Int32] 2 } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + } + + Test-DscParameterState @testParameters | Should -Be $false + } + + It 'Should return false when a value is different for [Int16]' { + $mockCurrentValues = @{ Example = [System.Int16] 1 } + $mockDesiredValues = @{ Example = [System.Int16] 2 } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + } + + Test-DscParameterState @testParameters | Should -Be $false + } + + It 'Should return false when a value is different for [UInt16]' { + $mockCurrentValues = @{ Example = [System.UInt16] 1 } + $mockDesiredValues = @{ Example = [System.UInt16] 2 } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + } + + Test-DscParameterState @testParameters | Should -Be $false + } + + It 'Should return false when a value is different for [Boolean]' { + $mockCurrentValues = @{ Example = [System.Boolean] $true } + $mockDesiredValues = @{ Example = [System.Boolean] $false } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + } + + Test-DscParameterState @testParameters | Should -Be $false + } + + It 'Should return false when a value is missing' { + $mockCurrentValues = @{ } + $mockDesiredValues = @{ Example = 'test' } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + } + + Test-DscParameterState @testParameters | Should -Be $false + } + + It 'Should return true when only a specified value matches, but other non-listed values do not' { + $mockCurrentValues = @{ Example = 'test'; SecondExample = 'true' } + $mockDesiredValues = @{ Example = 'test'; SecondExample = 'false' } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + ValuesToCheck = @('Example') + } + + Test-DscParameterState @testParameters | Should -Be $true + } + + It 'Should return false when only specified values do not match, but other non-listed values do ' { + $mockCurrentValues = @{ Example = 'test'; SecondExample = 'true' } + $mockDesiredValues = @{ Example = 'test'; SecondExample = 'false' } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + ValuesToCheck = @('SecondExample') + } + + Test-DscParameterState @testParameters | Should -Be $false + } + + It 'Should return false when an empty hash table is used in the current values' { + $mockCurrentValues = @{ } + $mockDesiredValues = @{ Example = 'test'; SecondExample = 'false' } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + } + + Test-DscParameterState @testParameters | Should -Be $false + } + + It 'Should return true when evaluating a table against a CimInstance' { + $mockCurrentValues = @{ Handle = '0'; ProcessId = '1000' } + + $mockWin32ProcessProperties = @{ + Handle = 0 + ProcessId = 1000 + } + + $mockNewCimInstanceParameters = @{ + ClassName = 'Win32_Process' + Property = $mockWin32ProcessProperties + Key = 'Handle' + ClientOnly = $true + } + + $mockDesiredValues = New-CimInstance @mockNewCimInstanceParameters + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + ValuesToCheck = @('Handle','ProcessId') + } + + Test-DscParameterState @testParameters | Should -Be $true + } + + It 'Should return false when evaluating a table against a CimInstance and a value is wrong' { + $mockCurrentValues = @{ Handle = '1'; ProcessId = '1000' } + + $mockWin32ProcessProperties = @{ + Handle = 0 + ProcessId = 1000 + } + + $mockNewCimInstanceParameters = @{ + ClassName = 'Win32_Process' + Property = $mockWin32ProcessProperties + Key = 'Handle' + ClientOnly = $true + } + + $mockDesiredValues = New-CimInstance @mockNewCimInstanceParameters + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + ValuesToCheck = @('Handle','ProcessId') + } + + Test-DscParameterState @testParameters | Should -Be $false + } + + It 'Should return true when evaluating a hash table containing an array' { + $mockCurrentValues = @{ Example = 'test'; SecondExample = @('1','2') } + $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1','2') } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + } + + Test-DscParameterState @testParameters | Should -Be $true + } + + It 'Should return false when evaluating a hash table containing an array with wrong values' { + $mockCurrentValues = @{ Example = 'test'; SecondExample = @('A','B') } + $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1','2') } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + } + + Test-DscParameterState @testParameters | Should -Be $false + } + + It 'Should return false when evaluating a hash table containing an array, but the CurrentValues are missing an array' { + $mockCurrentValues = @{ Example = 'test' } + $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1','2') } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + } + + Test-DscParameterState @testParameters | Should -Be $false + } + + It 'Should return false when evaluating a hash table containing an array, but the property i CurrentValues is $null' { + $mockCurrentValues = @{ Example = 'test'; SecondExample = $null } + $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1','2') } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + } + + Test-DscParameterState @testParameters | Should -Be $false + } + } + + Context -Name 'When passing invalid types for DesiredValues' -Fixture { + It 'Should throw the correct error when DesiredValues is of wrong type' { + $mockCurrentValues = @{ Example = 'something' } + $mockDesiredValues = 'NotHashTable' + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + } + + $mockCorrectErrorMessage = ($script:localizedData.PropertyTypeInvalidForDesiredValues -f $testParameters.DesiredValues.GetType().Name) + { Test-DscParameterState @testParameters } | Should -Throw $mockCorrectErrorMessage + } + + It 'Should write a warning when DesiredValues contain an unsupported type' { + Mock -CommandName Write-Warning -Verifiable + + # This is a dummy type to test with a type that could never be a correct one. + class MockUnknownType + { + [ValidateNotNullOrEmpty()] + [System.String] + $Property1 + + [ValidateNotNullOrEmpty()] + [System.String] + $Property2 + + MockUnknownType() + { + } + } + + $mockCurrentValues = @{ Example = New-Object -TypeName MockUnknownType } + $mockDesiredValues = @{ Example = New-Object -TypeName MockUnknownType } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + } + + Test-DscParameterState @testParameters | Should -Be $false + + Assert-MockCalled -CommandName Write-Warning -Exactly -Times 1 + } + } + + Context -Name 'When passing an CimInstance as DesiredValue and ValuesToCheck is $null' -Fixture { + It 'Should throw the correct error' { + $mockCurrentValues = @{ Example = 'something' } + + $mockWin32ProcessProperties = @{ + Handle = 0 + ProcessId = 1000 + } + + $mockNewCimInstanceParameters = @{ + ClassName = 'Win32_Process' + Property = $mockWin32ProcessProperties + Key = 'Handle' + ClientOnly = $true + } + + $mockDesiredValues = New-CimInstance @mockNewCimInstanceParameters + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + ValuesToCheck = $null + } + + $mockCorrectErrorMessage = $script:localizedData.PropertyTypeInvalidForValuesToCheck + { Test-DscParameterState @testParameters } | Should -Throw $mockCorrectErrorMessage + } + } + + Assert-VerifiableMock + } + + Describe 'iSCSIDsc.Common\Get-LocalizedData' { + $mockTestPath = { + return $mockTestPathReturnValue + } + + $mockImportLocalizedData = { + $BaseDirectory | Should -Be $mockExpectedLanguagePath + } + + BeforeEach { + Mock -CommandName Test-Path -MockWith $mockTestPath -Verifiable + Mock -CommandName Import-LocalizedData -MockWith $mockImportLocalizedData -Verifiable + } + + Context 'When loading localized data for Swedish' { + $mockExpectedLanguagePath = 'sv-SE' + $mockTestPathReturnValue = $true + + It 'Should call Import-LocalizedData with sv-SE language' { + Mock -CommandName Join-Path -MockWith { + return 'sv-SE' + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' } | Should -Not -Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 3 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + + $mockExpectedLanguagePath = 'en-US' + $mockTestPathReturnValue = $false + + It 'Should call Import-LocalizedData and fallback to en-US if sv-SE language does not exist' { + Mock -CommandName Join-Path -MockWith { + return $ChildPath + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' } | Should -Not -Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 4 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + + Context 'When $ScriptRoot is set to a path' { + $mockExpectedLanguagePath = 'sv-SE' + $mockTestPathReturnValue = $true + + It 'Should call Import-LocalizedData with sv-SE language' { + Mock -CommandName Join-Path -MockWith { + return 'sv-SE' + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' -ScriptRoot '.' } | Should -Not -Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + + $mockExpectedLanguagePath = 'en-US' + $mockTestPathReturnValue = $false + + It 'Should call Import-LocalizedData and fallback to en-US if sv-SE language does not exist' { + Mock -CommandName Join-Path -MockWith { + return $ChildPath + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' -ScriptRoot '.' } | Should -Not -Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + } + } + + Context 'When loading localized data for English' { + Mock -CommandName Join-Path -MockWith { + return 'en-US' + } -Verifiable + + $mockExpectedLanguagePath = 'en-US' + $mockTestPathReturnValue = $true + + It 'Should call Import-LocalizedData with en-US language' { + { Get-LocalizedData -ResourceName 'DummyResource' } | Should -Not -Throw + } + } + + Assert-VerifiableMock + } + + Describe 'iSCSIDsc.Common\New-InvalidResultException' { + Context 'When calling with Message parameter only' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + + { New-InvalidResultException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage + } + } + + Context 'When calling with both the Message and ErrorRecord parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockExceptionErrorMessage = 'Mocked exception error message' + + $mockException = New-Object -TypeName System.Exception -ArgumentList $mockExceptionErrorMessage + $mockErrorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $mockException, $null, 'InvalidResult', $null + + { New-InvalidResultException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.Exception: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) + } + } + + Assert-VerifiableMock + } + + Describe 'iSCSIDsc.Common\New-ObjectNotFoundException' { + Context 'When calling with Message parameter only' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + + { New-ObjectNotFoundException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage + } + } + + Context 'When calling with both the Message and ErrorRecord parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockExceptionErrorMessage = 'Mocked exception error message' + + $mockException = New-Object -TypeName System.Exception -ArgumentList $mockExceptionErrorMessage + $mockErrorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $mockException, $null, 'InvalidResult', $null + + { New-ObjectNotFoundException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.Exception: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) + } + } + + Assert-VerifiableMock + } + + Describe 'iSCSIDsc.Common\New-InvalidOperationException' { + Context 'When calling with Message parameter only' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + + { New-InvalidOperationException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage + } + } + + Context 'When calling with both the Message and ErrorRecord parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockExceptionErrorMessage = 'Mocked exception error message' + + $mockException = New-Object -TypeName System.Exception -ArgumentList $mockExceptionErrorMessage + $mockErrorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $mockException, $null, 'InvalidResult', $null + + { New-InvalidOperationException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.InvalidOperationException: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) + } + } + + Assert-VerifiableMock + } + + Describe 'iSCSIDsc.Common\New-NotImplementedException' { + Context 'When called with Message parameter only' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + + { New-NotImplementedException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage + } + } + + Context 'When called with both the Message and ErrorRecord parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockExceptionErrorMessage = 'Mocked exception error message' + + $mockException = New-Object -TypeName System.Exception -ArgumentList $mockExceptionErrorMessage + $mockErrorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $mockException, $null, 'InvalidResult', $null + + { New-NotImplementedException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.NotImplementedException: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) + } + } + + Assert-VerifiableMock + } + + Describe 'iSCSIDsc.Common\New-InvalidArgumentException' { + Context 'When calling with both the Message and ArgumentName parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockArgumentName = 'MockArgument' + + { New-InvalidArgumentException -Message $mockErrorMessage -ArgumentName $mockArgumentName } | Should -Throw ('Parameter name: {0}' -f $mockArgumentName) + } + } + + Assert-VerifiableMock + } +}