From d01feacd751739bc96ebee2ef76690395823da62 Mon Sep 17 00:00:00 2001 From: cmielke Date: Tue, 10 Aug 2021 15:24:07 -0500 Subject: [PATCH 1/5] Add empty string password to .Import method --- source/Modules/CertificateDsc.Common/CertificateDsc.Common.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Modules/CertificateDsc.Common/CertificateDsc.Common.psm1 b/source/Modules/CertificateDsc.Common/CertificateDsc.Common.psm1 index 2db1f6ac..29132ff0 100644 --- a/source/Modules/CertificateDsc.Common/CertificateDsc.Common.psm1 +++ b/source/Modules/CertificateDsc.Common/CertificateDsc.Common.psm1 @@ -1082,7 +1082,7 @@ function Import-PfxCertificateEx } else { - $cert.Import($importDataValue, $flags) + $cert.Import($importDataValue, "", $flags) } $certStore = New-Object ` From 9474e0a52866d56e0b60c2a3b854866666e2e352 Mon Sep 17 00:00:00 2001 From: cmielke Date: Tue, 10 Aug 2021 15:37:27 -0500 Subject: [PATCH 2/5] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2d3e373..6dee01eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated build to use `Sampler.GitHubTasks` - Fixes [Issue #254](https://github.com/dsccommunity/CertificateDsc/issues/254). - Corrected changelog. - Updated pipeline tasks to latest pattern. +- Passed in an empty string to X509Certificate2.Import so that we do not get MethodCountCouldNotFindBest exception when using a null + password for the PFX certificate. ## [5.1.0] - 2021-02-26 From 71e5f1d55e0346bdbe6cdbac696a5bbf2e468604 Mon Sep 17 00:00:00 2001 From: cmielke Date: Tue, 10 Aug 2021 15:38:12 -0500 Subject: [PATCH 3/5] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dee01eb..fc31ea68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Corrected changelog. - Updated pipeline tasks to latest pattern. - Passed in an empty string to X509Certificate2.Import so that we do not get MethodCountCouldNotFindBest exception when using a null - password for the PFX certificate. + password for the PFX certificate. Fixes [Issue #258](https://github.com/dsccommunity/CertificateDsc/issues/258) ## [5.1.0] - 2021-02-26 From d62ba7a02e44f53086a0eec2ad3250e277776074 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Fri, 15 Apr 2022 23:14:03 +1200 Subject: [PATCH 4/5] Fix and Update Pipeline files to latest pattern - Fixes #262 (#266) --- .github/CONTRIBUTING.md | 4 +- .../ISSUE_TEMPLATE/Problem_with_resource.md | 57 ------------ .../ISSUE_TEMPLATE/Problem_with_resource.yml | 86 +++++++++++++++++++ .github/ISSUE_TEMPLATE/Resource_proposal.md | 21 ----- .github/ISSUE_TEMPLATE/Resource_proposal.yml | 39 +++++++++ .github/ISSUE_TEMPLATE/config.yml | 6 ++ .github/PULL_REQUEST_TEMPLATE.md | 31 ++++--- CHANGELOG.md | 15 +++- SECURITY.md | 30 +++++++ azure-pipelines.yml | 57 ++++++------ build.yaml | 18 +++- 11 files changed, 245 insertions(+), 119 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/Problem_with_resource.md create mode 100644 .github/ISSUE_TEMPLATE/Problem_with_resource.yml delete mode 100644 .github/ISSUE_TEMPLATE/Resource_proposal.md create mode 100644 .github/ISSUE_TEMPLATE/Resource_proposal.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 SECURITY.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 79a46819..f6895ab3 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1 +1,3 @@ -If you'd like to contribute to this project, please review the [Contribution Guidelines](https://github.com/PowerShell/DscResources/blob/master/CONTRIBUTING.md). +# Contributing + +Please check out common DSC Community [contributing guidelines](https://dsccommunity.org/guidelines/contributing). diff --git a/.github/ISSUE_TEMPLATE/Problem_with_resource.md b/.github/ISSUE_TEMPLATE/Problem_with_resource.md deleted file mode 100644 index 2431f65c..00000000 --- a/.github/ISSUE_TEMPLATE/Problem_with_resource.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -name: Problem with a resource -about: If you have a problem, bug, or enhancement with a resource in this resource module. ---- - -#### Details of the scenario you tried and the problem that is occurring - -#### Verbose logs showing the problem - -#### Suggested solution to the issue - -#### The DSC configuration that is used to reproduce the issue (as detailed as possible) -```powershell -# insert configuration here -``` - -#### The operating system the target node is running - - -#### Version and build of PowerShell the target node is running - - -#### Version of the DSC module that was used ('dev' if using current dev branch) diff --git a/.github/ISSUE_TEMPLATE/Problem_with_resource.yml b/.github/ISSUE_TEMPLATE/Problem_with_resource.yml new file mode 100644 index 00000000..3cc1ee3a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Problem_with_resource.yml @@ -0,0 +1,86 @@ +name: Problem with a resource +description: If you have a problem, bug, or enhancement with a resource in this resource module. +labels: [] +assignees: [] +body: + - type: markdown + attributes: + value: | + Please prefix the issue title (above) with the resource name, e.g. 'ResourceName: Short description of my issue'! + + Your feedback and support is greatly appreciated, thanks for contributing! + - type: textarea + id: description + attributes: + label: Problem description + description: Details of the scenario you tried and the problem that is occurring. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Verbose logs + description: | + Verbose logs showing the problem. **NOTE! Sensitive information should be obfuscated.** _Will be automatically formatted as plain text._ + placeholder: | + Paste verbose logs here + render: text + validations: + required: true + - type: textarea + id: configuration + attributes: + label: DSC configuration + description: | + The DSC configuration that is used to reproduce the issue (as detailed as possible). **NOTE! Sensitive information should be obfuscated.** _Will be automatically formatted as PowerShell code._ + placeholder: | + Paste DSC configuration here + render: powershell + validations: + required: true + - type: textarea + id: suggestedSolution + attributes: + label: Suggested solution + description: Do you have any suggestions how to solve the issue? + validations: + required: true + - type: textarea + id: targetNodeOS + attributes: + label: Operating system the target node is running + description: | + Please provide as much as possible about the target node, for example edition, version, build, and language. _Will be automatically formatted as plain text._ + + On OS with WMF 5.1 the following command can help get this information: `Get-ComputerInfo -Property @('OsName','OsOperatingSystemSKU','OSArchitecture','WindowsVersion','WindowsBuildLabEx','OsLanguage','OsMuiLanguages')` + placeholder: | + Add operating system information here + render: text + validations: + required: true + - type: textarea + id: targetNodePS + attributes: + label: PowerShell version and build the target node is running + description: | + Please provide the version and build of PowerShell the target node is running. _Will be automatically formatted as plain text._ + + To help with this information, please run this command: `$PSVersionTable` + placeholder: | + Add PowerShell information here + render: text + validations: + required: true + - type: textarea + id: moduleVersion + attributes: + label: CertificateDsc version + description: | + Please provide the version of the CertificateDsc module that was used. _Will be automatically formatted as plain text._ + + To help with this information, please run this command: `Get-Module -Name 'CertificateDsc' -ListAvailable | ft Name,Version,Path` + placeholder: | + Add module information here + render: text + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/Resource_proposal.md b/.github/ISSUE_TEMPLATE/Resource_proposal.md deleted file mode 100644 index 9f2a069a..00000000 --- a/.github/ISSUE_TEMPLATE/Resource_proposal.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: New resource proposal -about: If you have a new resource proposal that you think should be added to this resource module. ---- - -### Description - -### Proposed properties - -### Special considerations or limitations diff --git a/.github/ISSUE_TEMPLATE/Resource_proposal.yml b/.github/ISSUE_TEMPLATE/Resource_proposal.yml new file mode 100644 index 00000000..2ddd0986 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Resource_proposal.yml @@ -0,0 +1,39 @@ +name: New resource proposal +description: If you have a new resource proposal that you think should be added to this resource module. +title: "NewResourceName: New resource proposal" +labels: [] +assignees: [] +body: + - type: markdown + attributes: + value: | + Please replace `NewResourceName` in the issue title (above) with your proposed resource name. + + Thank you for contributing and making this resource module better! + - type: textarea + id: description + attributes: + label: Resource proposal + description: Provide information how this resource will/should work and how it will help users. + validations: + required: true + - type: textarea + id: proposedProperties + attributes: + label: Proposed properties + description: | + List all the proposed properties that the resource should have (key, required, write, and/or read). For each property provide a detailed description, the data type, if a default value should be used, and if the property is limited to a set of values. + value: | + Property | Type qualifier | Data type | Description | Default value | Allowed values + --- | --- | --- | --- | --- | --- + PropertyName | Key | String | Detailed description | None | None + validations: + required: true + - type: textarea + id: considerations + attributes: + label: Special considerations or limitations + description: | + Provide any considerations or limitations you can think of that a contributor should take in account when coding the proposed resource, and or what limitations a user will encounter or should consider when using the proposed resource. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..9917040e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,6 @@ +blank_issues_enabled: false +contact_links: + - name: "Virtual PowerShell User Group #DSC channel" + url: https://dsccommunity.org/community/contact/ + about: "To talk to the community and maintainers of DSC Community, please visit the #DSC channel." + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7587d88a..e9f674de 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -11,21 +11,29 @@ You may remove this comment block, and the other comment blocks, but please keep the headers and the task list. --> + #### Pull Request (PR) description + #### This Pull Request (PR) fixes the following issues + #### Task list + -- [ ] Added an entry under the Unreleased section of the CHANGELOG.md. - Entry should say what was changed, and how that affects users (if applicable). -- [ ] Resource documentation added/updated in the resource folder. -- [ ] Resource parameter descriptions added/updated in schema.mof + +- [ ] Added an entry to the change log under the Unreleased section of the + file CHANGELOG.md. Entry should say what was changed and how that + affects users (if applicable), and reference the issue being resolved + (if applicable). +- [ ] Resource documentation added/updated in README.md. +- [ ] Resource parameter descriptions added/updated in README.md, schema.mof and comment-based help. - [ ] Comment-based help added/updated. - [ ] Localization strings added/updated in all localization files as appropriate. - [ ] Examples appropriately added/updated. -- [ ] Unit tests added/updated. See [DSC Resource Testing Guidelines](https://github.com/PowerShell/DscResources/blob/master/TestsGuidelines.md). -- [ ] Integration tests added/updated (where possible). See [DSC Resource Testing Guidelines](https://github.com/PowerShell/DscResources/blob/master/TestsGuidelines.md). -- [ ] New/changed code adheres to [DSC Resource Style Guidelines](https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md) and [Best Practices](https://github.com/PowerShell/DscResources/blob/master/BestPractices.md). +- [ ] Unit tests added/updated. See [DSC Community Testing Guidelines](https://dsccommunity.org/guidelines/testing-guidelines). +- [ ] Integration tests added/updated (where possible). See [DSC Community Testing Guidelines](https://dsccommunity.org/guidelines/testing-guidelines). +- [ ] New/changed code adheres to [DSC Community Style Guidelines](https://dsccommunity.org/styleguidelines). diff --git a/CHANGELOG.md b/CHANGELOG.md index fc31ea68..fa2a599e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated build to use `Sampler.GitHubTasks` - Fixes [Issue #254](https://github.com/dsccommunity/CertificateDsc/issues/254). - Corrected changelog. - Updated pipeline tasks to latest pattern. -- Passed in an empty string to X509Certificate2.Import so that we do not get MethodCountCouldNotFindBest exception when using a null +- Build pipeline: Removed unused `dscBuildVariable` tasks. +- Updated .github issue templates to standard - Fixes [Issue #263](https://github.com/dsccommunity/CertificateDsc/issues/263). +- Added Create_ChangeLog_GitHub_PR task to publish stage of build pipeline. +- Added SECURITY.md. +- Updated pipeline Deploy_Module anb Code_Coverage jobs to use ubuntu-latest + images - Fixes [Issue #262](https://github.com/dsccommunity/CertificateDsc/issues/262). +- Updated pipeline unit tests and integration tests to use Windows Server 2019 and + Windows Server 2022 images - Fixes [Issue #262](https://github.com/dsccommunity/CertificateDsc/issues/262). + +### Fixed + +- Fixed pipeline by replacing the GitVersion task in the `azure-pipelines.yml` + with a script. +- Passed in an empty string to X509Certificate2.Import so that we do not get MethodCountCouldNotFindBest exception when using a null password for the PFX certificate. Fixes [Issue #258](https://github.com/dsccommunity/CertificateDsc/issues/258) ## [5.1.0] - 2021-02-26 diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..7bd7bc20 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,30 @@ +## Security + +The DSC Community takes the security of our modules seriously, which includes all source code repositories managed through our GitHub organization. + +If you believe you have found a security vulnerability in any DSC Community owned repository, please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to one or several members of the DSC Community organization. +The easiest way to do so is to send us a direct message via twitter or slack. + +You should receive a response within 48 hours. If for some reason you do not, please follow up to other member of the community. + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +## Preferred Languages + +We prefer all communications to be in English. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c2301d8b..5eff936d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -24,24 +24,27 @@ stages: - job: Package_Module displayName: 'Package Module' pool: - vmImage: 'ubuntu 16.04' + vmImage: 'ubuntu-latest' steps: - - task: GitVersion@5 - name: gitversion - displayName: 'Evaluate Next Version' - inputs: - runtime: 'core' - configFilePath: 'GitVersion.yml' + - pwsh: | + dotnet tool install --global GitVersion.Tool + $gitVersionObject = dotnet-gitversion | ConvertFrom-Json + $gitVersionObject.PSObject.Properties.ForEach{ + Write-Host -Object "Setting Task Variable '$($_.Name)' with value '$($_.Value)'." + Write-Host -Object "##vso[task.setvariable variable=$($_.Name);]$($_.Value)" + } + Write-Host -Object "##vso[build.updatebuildnumber]$($gitVersionObject.FullSemVer)" + displayName: Calculate ModuleVersion (GitVersion) - task: PowerShell@2 name: package displayName: 'Build & Package Module' inputs: filePath: './build.ps1' - arguments: '-Tasks pack -ResolveDependency' + arguments: '-ResolveDependency -tasks pack' pwsh: true env: - ModuleVersion: $(gitVersion.Informationalversion) + ModuleVersion: $(NuGetVersionV2) - task: PublishPipelineArtifact@1 displayName: 'Publish Pipeline Artifact' @@ -83,10 +86,10 @@ stages: testRunTitle: 'HQRM' condition: succeededOrFailed() - - job: Test_Unit_2016 - displayName: 'Unit (Windows Server 2016)' + - job: Test_Unit_2019 + displayName: 'Unit (Windows Server 2019)' pool: - vmImage: 'vs2017-win2016' + vmImage: 'windows-2019' timeoutInMinutes: 0 steps: - task: DownloadPipelineArtifact@2 @@ -109,7 +112,7 @@ stages: inputs: testResultsFormat: 'NUnit' testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml' - testRunTitle: 'Unit (Windows Server 2016)' + testRunTitle: 'Unit (Windows Server 2019)' condition: succeededOrFailed() - task: PublishPipelineArtifact@1 @@ -121,9 +124,9 @@ stages: - job: Code_Coverage displayName: 'Publish Code Coverage' - dependsOn: Test_Unit_2016 + dependsOn: Test_Unit_2019 pool: - vmImage: 'ubuntu 16.04' + vmImage: 'ubuntu-latest' timeoutInMinutes: 0 steps: - task: DownloadPipelineArtifact@2 @@ -151,10 +154,10 @@ stages: bash <(curl -s https://codecov.io/bash) -f "./$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml" displayName: 'Upload to Codecov.io' - - job: Test_Integration_2016 - displayName: 'Integration (Windows Server 2016)' + - job: Test_Integration_2019 + displayName: 'Integration (Windows Server 2019)' pool: - vmImage: 'vs2017-win2016' + vmImage: 'windows-2019' timeoutInMinutes: 0 steps: - task: DownloadPipelineArtifact@2 @@ -185,13 +188,13 @@ stages: inputs: testResultsFormat: 'NUnit' testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml' - testRunTitle: 'Integration (Windows Server 2016)' + testRunTitle: 'Integration (Windows Server 2019)' condition: succeededOrFailed() - - job: Test_Unit_2019 - displayName: 'Unit (Windows Server 2019)' + - job: Test_Unit_2022 + displayName: 'Unit (Windows Server 2022)' pool: - vmImage: 'windows-2019' + vmImage: 'windows-2022' timeoutInMinutes: 0 steps: - task: DownloadPipelineArtifact@2 @@ -214,11 +217,11 @@ stages: inputs: testResultsFormat: 'NUnit' testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml' - testRunTitle: 'Unit (Windows Server 2019)' + testRunTitle: 'Unit (Windows Server 2022)' condition: succeededOrFailed() - - job: Test_Integration_2019 - displayName: 'Integration (Windows Server 2019)' + - job: Test_Integration_2022 + displayName: 'Integration (Windows Server 2022)' pool: vmImage: 'windows-2019' timeoutInMinutes: 0 @@ -251,7 +254,7 @@ stages: inputs: testResultsFormat: 'NUnit' testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml' - testRunTitle: 'Integration (Windows Server 2019)' + testRunTitle: 'Integration (Windows Server 2022)' condition: succeededOrFailed() - stage: Deploy @@ -269,7 +272,7 @@ stages: - job: Deploy_Module displayName: 'Deploy Module' pool: - vmImage: 'ubuntu 16.04' + vmImage: 'ubuntu-latest' steps: - task: DownloadPipelineArtifact@2 displayName: 'Download Pipeline Artifact' diff --git a/build.yaml b/build.yaml index 51acc29c..0cb7837d 100644 --- a/build.yaml +++ b/build.yaml @@ -48,9 +48,10 @@ BuildWorkflow: - Pester_If_Code_Coverage_Under_Threshold publish: - - Publish_Release_to_GitHub - - Publish_Module_to_gallery + - publish_module_to_gallery + - Publish_Release_To_GitHub - Publish_GitHub_Wiki_Content + - Create_ChangeLog_GitHub_PR #################################################### # PESTER Configuration # @@ -108,3 +109,16 @@ GitHubConfig: GitHubConfigUserName: dscbot GitHubConfigUserEmail: dsccommunity@outlook.com UpdateChangelogOnPrerelease: false + +#################################################### +# DscResource.DocGenerator Configuration # +#################################################### +DscResource.DocGenerator: + Generate_Conceptual_Help: + MarkdownCodeRegularExpression: + - '\`(.+?)\`' # Match inline code-block + - '\\(\\)' # Match escaped backslash + - '\[[^\[]+\]\((.+?)\)' # Match markdown URL + - '_(.+?)_' # Match Italic (underscore) + - '\*\*(.+?)\*\*' # Match bold + - '\*(.+?)\*' # Match Italic (asterisk) From 33329ffef8d76bb0823c24e417d5512b4a3f180c Mon Sep 17 00:00:00 2001 From: Daniel C <96059559+uw-dc@users.noreply.github.com> Date: Fri, 23 Dec 2022 18:45:42 +0000 Subject: [PATCH 5/5] BREAKING CHANGE: CertReq: Improve certificate match selectivity (#271) --- CHANGELOG.md | 6 + azure-pipelines.yml | 3 +- .../DSCResources/DSC_CertReq/DSC_CertReq.psm1 | 95 ++--- .../DSC_CertReq/DSC_CertReq.schema.mof | 2 +- .../en-US/DSC_CertReq.strings.psd1 | 25 +- tests/Unit/DSC_CertReq.Tests.ps1 | 340 ++++++++++++++++-- 6 files changed, 373 insertions(+), 98 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa2a599e..a4673432 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- CertReq: + - BREAKING CHANGE: Made Certificate FriendlyName a mandatory parameter - Fixes [Issue #269](https://github.com/dsccommunity/CertificateDsc/issues/269). + - Consider FriendlyName + Template when getting existing certs - Fixes [Issue #121](https://github.com/dsccommunity/CertificateDsc/issues/121). + +### Changed + - Added support for publishing code coverage to `CodeCov.io` and Azure Pipelines - Fixes [Issue #255](https://github.com/dsccommunity/CertificateDsc/issues/255). - Updated build to use `Sampler.GitHubTasks` - Fixes [Issue #254](https://github.com/dsccommunity/CertificateDsc/issues/254). diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5eff936d..9961ed2f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -24,7 +24,8 @@ stages: - job: Package_Module displayName: 'Package Module' pool: - vmImage: 'ubuntu-latest' + # Fails on 'ubuntu-latest' as libmi shared object library not available + vmImage: 'windows-latest' steps: - pwsh: | dotnet tool install --global GitVersion.Tool diff --git a/source/DSCResources/DSC_CertReq/DSC_CertReq.psm1 b/source/DSCResources/DSC_CertReq/DSC_CertReq.psm1 index cd59f7ef..42c82a72 100644 --- a/source/DSCResources/DSC_CertReq/DSC_CertReq.psm1 +++ b/source/DSCResources/DSC_CertReq/DSC_CertReq.psm1 @@ -158,7 +158,7 @@ function Get-TargetResource [System.Boolean] $UseMachineContext, - [Parameter()] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $FriendlyName, @@ -188,7 +188,7 @@ function Get-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($script:localizedData.GettingCertReqStatusMessage -f $Subject, $CA) + $($script:localizedData.GettingCertReqStatusMessage -f $Subject, $CA, $FriendlyName, $CertificateTemplate) ) -join '' ) # If the Subject does not contain a full X500 path, construct just the CN @@ -199,11 +199,13 @@ function Get-TargetResource $cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object -FilterScript { - $_.Subject -eq $Subject -and ` - (Compare-CertificateIssuer -Issuer $_.Issuer -CARootName $CARootName) + (Compare-CertificateSubject -ReferenceSubject $_.Subject -DifferenceSubject $Subject) -and ` + (Compare-CertificateIssuer -Issuer $_.Issuer -CARootName $CARootName) -and ` + (Get-CertificateTemplateName -Certificate $PSItem) -eq $CertificateTemplate -and ` + $_.FriendlyName -eq $FriendlyName } - # If multiple certs have the same subject and were issued by the CA, return the newest + # If multiple certs have the same subject, issuer, friendly name and template, return the newest $cert = $cert | Sort-Object -Property NotBefore -Descending | Select-Object -First 1 @@ -212,7 +214,7 @@ function Get-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($script:localizedData.CertificateExistsMessage -f $Subject, $ca, $cert.Thumbprint) + $($script:localizedData.CertificateExistsMessage -f $Subject, $ca, $FriendlyName, $CertificateTemplate, $certificate.Thumbprint) ) -join '' ) $returnValue = @{ @@ -377,7 +379,7 @@ function Set-TargetResource [System.Boolean] $UseMachineContext, - [Parameter()] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $FriendlyName, @@ -407,7 +409,7 @@ function Set-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($script:localizedData.StartingCertReqMessage -f $Subject, $ca) + $($script:localizedData.StartingCertReqMessage -f $Subject, $ca, $FriendlyName, $CertificateTemplate) ) -join '' ) # If the Subject does not contain a full X500 path, construct just the CN @@ -671,7 +673,7 @@ CertificateTemplate = "$CertificateTemplate" if ($acceptRequest -match '0x') { - New-InvalidOperationException -Message ($script:localizedData.GenericErrorThrown -f ($acceptRequest | Out-String)) + New-InvalidOperationException -Message ($script:localizedData.GenericError -f ($acceptRequest | Out-String)) } else { @@ -835,7 +837,7 @@ function Test-TargetResource [System.Boolean] $UseMachineContext, - [Parameter()] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $FriendlyName, @@ -871,25 +873,28 @@ function Test-TargetResource Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($script:localizedData.TestingCertReqStatusMessage -f $Subject, $ca) + $($script:localizedData.TestingCertReqStatusMessage -f $Subject, $ca, $FriendlyName, $CertificateTemplate) ) -join '' ) - $certificate = Get-ChildItem -Path Cert:\LocalMachine\My | - Where-Object -FilterScript { - (Compare-CertificateSubject -ReferenceSubject $_.Subject -DifferenceSubject $Subject) -and ` - (Compare-CertificateIssuer -Issuer $_.Issuer -CARootName $CARootName) - } + $filterScript = { + (Compare-CertificateSubject -ReferenceSubject $_.Subject -DifferenceSubject $Subject) -and ` + (Compare-CertificateIssuer -Issuer $_.Issuer -CARootName $CARootName) -and ` + (Get-CertificateTemplateName -Certificate $PSItem) -eq $CertificateTemplate -and ` + $_.FriendlyName -eq $FriendlyName + } # Exception for standard template DomainControllerAuthentication if ($CertificateTemplate -eq 'DomainControllerAuthentication') { - $certificate = Get-ChildItem -Path Cert:\LocalMachine\My | - Where-Object -FilterScript { - (Get-CertificateTemplateName -Certificate $PSItem) -eq $CertificateTemplate -and ` - (Compare-CertificateIssuer -Issuer $_.Issuer -CARootName $CARootName) - } + $filterScript = { + (Get-CertificateTemplateName -Certificate $PSItem) -eq $CertificateTemplate -and ` + (Compare-CertificateIssuer -Issuer $_.Issuer -CARootName $CARootName) + } } + $certificate = Get-ChildItem -Path Cert:\LocalMachine\My | + Where-Object -FilterScript $filterScript + # If multiple certs have the same subject and were issued by the CA, return the newest $certificate = $certificate | Sort-Object -Property NotBefore -Descending | @@ -899,7 +904,7 @@ function Test-TargetResource { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($script:localizedData.CertificateExistsMessage -f $Subject, $ca, $certificate.Thumbprint) + $($script:localizedData.CertificateExistsMessage -f $Subject, $ca, $FriendlyName, $CertificateTemplate, $certificate.Thumbprint) ) -join '' ) if ($AutoRenew) @@ -909,7 +914,7 @@ function Test-TargetResource # The certificate was found but it is expiring within 30 days or has expired Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($script:localizedData.ExpiringCertificateMessage -f $Subject, $ca, $certificate.Thumbprint) + $($script:localizedData.ExpiringCertificateMessage -f $Subject, $ca, $FriendlyName, $CertificateTemplate ,$certificate.Thumbprint) ) -join '' ) return $false } # if @@ -921,7 +926,7 @@ function Test-TargetResource # The certificate has expired Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($script:localizedData.ExpiredCertificateMessage -f $Subject, $ca, $certificate.Thumbprint) + $($script:localizedData.ExpiredCertificateMessage -f $Subject, $ca, $FriendlyName, $CertificateTemplate ,$certificate.Thumbprint) ) -join '' ) return $false } # if @@ -943,10 +948,10 @@ function Test-TargetResource } # Find out what SANs are on the current cert + $currentDns = @() if ($certificate.Extensions.Count -gt 0) { $currentSanList = Get-CertificateSubjectAlternativeNameList -Certificate $certificate - $currentDns = @() foreach ($san in $currentSanList) { @@ -956,37 +961,37 @@ function Test-TargetResource $currentDns += $san.split('=')[1] } } + } - # Do the cert's DNS SANs and the desired DNS SANs match? - if (@(Compare-Object -ReferenceObject $currentDns -DifferenceObject $correctDns).Count -gt 0) - { - return $false - } + if ($currentDns.Count -eq 0) + { + # There are no current DNS SANs and there should be + Write-Verbose -Message ( @( + "$($MyInvocation.MyCommand): " + $($script:localizedData.NoExistingSansMessage -f $Subject, $ca, $FriendlyName, $CertificateTemplate, ($correctDns -join ',') ,$certificate.Thumbprint) + ) -join '' ) + return $false } - else + + if (@(Compare-Object -ReferenceObject $currentDns -DifferenceObject $correctDns).Count -gt 0) { - # There are no SANs and there should be + # Current DNS SANs and the desired DNS SANs do not match + Write-Verbose -Message ( @( + "$($MyInvocation.MyCommand): " + $($script:localizedData.SansMismatchMessage -f $Subject, $ca, $FriendlyName, $CertificateTemplate,($currentDns -join ',') , ($correctDns -join ',') ,$certificate.Thumbprint) + ) -join '' ) return $false } } $currentCertificateTemplateName = Get-CertificateTemplateName -Certificate $certificate - if ($CertificateTemplate -ne $currentCertificateTemplateName) - { - Write-Verbose -Message ( @( - "$($MyInvocation.MyCommand): " - $($script:localizedData.CertTemplateMismatch -f $Subject, $ca, $certificate.Thumbprint, $currentCertificateTemplateName) - ) -join '' ) - return $false - } # if - # Check the friendly name of the certificate matches if ($FriendlyName -ne $certificate.FriendlyName) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($script:localizedData.CertFriendlyNameMismatch -f $Subject, $ca, $certificate.Thumbprint, $certificate.FriendlyName) + $($script:localizedData.CertFriendlyNameMismatchMessage -f $Subject, $ca, $currentCertificateTemplateName, $certificate.Thumbprint, $certificate.FriendlyName) ) -join '' ) return $false } # if @@ -994,7 +999,7 @@ function Test-TargetResource # The certificate was found and is OK - so no change required. Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($script:localizedData.ValidCertificateExistsMessage -f $Subject, $ca, $certificate.Thumbprint) + $($script:localizedData.ValidCertificateExistsMessage -f $Subject, $ca, $FriendlyName, $CertificateTemplate, $certificate.Thumbprint) ) -join '' ) return $true } # if @@ -1002,7 +1007,7 @@ function Test-TargetResource # A valid certificate was not found Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " - $($script:localizedData.NoValidCertificateMessage -f $Subject, $ca) + $($script:localizedData.NoValidCertificateMessage -f $Subject, $ca, $FriendlyName, $CertificateTemplate) ) -join '' ) return $false } # end function Test-TargetResource @@ -1037,7 +1042,7 @@ function Assert-ResourceProperty if ((($KeyType -eq 'RSA') -and ($KeyLength -notin '1024', '2048', '4096', '8192')) -or ` (($KeyType -eq 'ECDH') -and ($KeyLength -notin '192', '224', '256', '384', '521'))) { - New-InvalidArgumentException -Message (($script:localizedData.InvalidKeySize) -f $KeyLength, $KeyType) -ArgumentName 'KeyLength' + New-InvalidArgumentException -Message (($script:localizedData.InvalidKeySizeError) -f $KeyLength, $KeyType) -ArgumentName 'KeyLength' } }# end function Assert-ResourceProperty diff --git a/source/DSCResources/DSC_CertReq/DSC_CertReq.schema.mof b/source/DSCResources/DSC_CertReq/DSC_CertReq.schema.mof index 73ceb3fa..5120f680 100644 --- a/source/DSCResources/DSC_CertReq/DSC_CertReq.schema.mof +++ b/source/DSCResources/DSC_CertReq/DSC_CertReq.schema.mof @@ -2,6 +2,7 @@ class DSC_CertReq : OMI_BaseResource { [Key, Description("Provide the text string to use as the subject of the certificate.")] String Subject; + [Key, Description("Specifies a friendly name for the certificate.")] String FriendlyName; [Write, Description("The type of CA in use, Standalone/Enterprise.")] String CAType; [Write, Description("The FQDN of the Active Directory Certificate Authority on the local area network. Leave empty to automatically locate.")] String CAServerFQDN; [Write, Description("The name of the certificate authority, by default this will be in format domain-servername-ca. Leave empty to automatically locate.")] String CARootName; @@ -17,7 +18,6 @@ class DSC_CertReq : OMI_BaseResource [Write, Description("The URL to the Certification Enrollment Policy Service.")] String CepURL; [Write, Description("The URL to the Certification Enrollment Service.")] String CesURL; [Write, Description("Indicates whether or not the flag -adminforcemachine will be used when requesting certificates. Necessary for certain templates like e.g. DomainControllerAuthentication")] Boolean UseMachineContext; - [Write, Description("Specifies a friendly name for the certificate.")] String FriendlyName; [Write, Description("Specifies if the key type should be RSA or ECDH, defaults to RSA."), ValueMap{"RSA","ECDH"}, Values{"RSA","ECDH"}] String KeyType; [Write, Description("Specifies if the request type should be CMC or PKCS10, deafults to CMC."), ValueMap{"CMC","PKCS10"},Values{"CMC","PKCS10"}] String RequestType; }; diff --git a/source/DSCResources/DSC_CertReq/en-US/DSC_CertReq.strings.psd1 b/source/DSCResources/DSC_CertReq/en-US/DSC_CertReq.strings.psd1 index 3eef42cb..e3c385c3 100644 --- a/source/DSCResources/DSC_CertReq/en-US/DSC_CertReq.strings.psd1 +++ b/source/DSCResources/DSC_CertReq/en-US/DSC_CertReq.strings.psd1 @@ -1,7 +1,7 @@ ConvertFrom-StringData @' - GettingCertReqStatusMessage = Getting Certificate with Subject '{0}' issued by {1}. - CertificateExistsMessage = Certificate with Subject '{0}' issued by {1} found with thumbprint '{2}'. - StartingCertReqMessage = Starting Certificate request with Subject '{0}' issued by {1}. + GettingCertReqStatusMessage = Getting Certificate with Subject '{0}', Issuer '{1}', FriendlyName '{2}' and CertificateTemplate '{3}'. + CertificateExistsMessage = Certificate with Subject '{0}', Issuer '{1}', FriendlyName '{2}' and CertificateTemplate '{3}' found with Thumbprint '{4}'. + StartingCertReqMessage = Starting Certificate request with Subject '{0}', Issuer '{1}', FriendlyName '{2}' and CertificateTemplate '{3}'. CreateRequestCertificateMessage = Creating certificate request '{1}' from '{0}'. CreateRequestResultCertificateMessage = Create certificate request result: {0} SubmittingRequestCertificateMessage = Submitting certificate request '{0}' returning '{1}' issued by {2}. @@ -10,16 +10,17 @@ ConvertFrom-StringData @' AcceptingRequestCertificateMessage = Accepting certificate '{1}' issued by {0}. AcceptingRequestResultCertificateMessage = Accepting certificate result: {0} CleaningUpRequestFilesMessage = Cleaning up certificate request files '{0}'. - TestingCertReqStatusMessage = Testing Certificate with Subject '{0}' issued by {1}. - ExpiringCertificateMessage = The certificate found with subject '{0}' issued by {1} with thumbprint '{2}' is about to expire. - NoValidCertificateMessage = No valid certificate found with subject '{0}' issued by {1}. - ExpiredCertificateMessage = The certificate found with subject '{0}' issued by {1} with thumbprint '{2}' has expired. - ValidCertificateExistsMessage = Valid certificate '{2}' found with subject '{0}' issued by {1}. + TestingCertReqStatusMessage = Testing Certificate with Subject '{0}', Issuer '{1}', FriendlyName '{2}' and CertificateTemplate '{3}'. + ExpiringCertificateMessage = The certificate found with Subject '{0}', Issuer '{1}', FriendlyName '{2}' and CertificateTemplate '{3}' is about to expire. + NoValidCertificateMessage = No valid certificate found with Subject '{0}', Issuer '{1}', FriendlyName '{2}' and CertificateTemplate '{3}'. + ExpiredCertificateMessage = The certificate found with Subject '{0}', Issuer '{1}', FriendlyName '{2}' and CertificateTemplate '{3}' has expired: {4}. + NoExistingSansMessage = The certificate found with Subject '{0}', Issuer '{1}', FriendlyName '{2}' and CertificateTemplate '{3}' has no SANs, yet the following SANs are specified: {4}. Certificate has the Thumbprint '{5}'. + SansMismatchMessage = The certificate found with Subject '{0}', Issuer '{1}', FriendlyName '{2}' and CertificateTemplate '{3}' has the SANs '{4}', yet the following SANs are specified: {5}. Certificate has the Thumbprint '{6}'. + ValidCertificateExistsMessage = Valid certificate found with Subject '{0}', Issuer '{1}', FriendlyName '{2}' and CertificateTemplate '{3}': {4} CertificateReqNotFoundError = Certificate Request file '{0}' not found. CertificateCerNotFoundError = Certificate file '{0}' not found. CertReqOutNotFoundError = CertReq.exe output file '{0}' not found. - CertTemplateMismatch = The certificate with subject '{0}' issued by '{1}' with thumbprint {2} has the wrong template {3}. - CertFriendlyNameMismatch = The certificate with subject '{0}' issued by '{1}' with thumbprint {2} has the wrong friendly name '{3}'. - InvalidKeySize = The key length '{0}' specified is invalid for '{1}' key types. - GenericErrorThrown = A Generic Error was thrown when accepting a Certificate. It threw the following Error message: {0} + CertFriendlyNameMismatchMessage = The certificate with Subject '{0}', Issuer '{1}', CertificateTemplate '{2}' and Thumbprint '{3}' has the wrong friendly name: {4}. + InvalidKeySizeError = The key length '{0}' specified is invalid for '{1}' key types. + GenericError = A Generic Error was thrown when accepting a Certificate. It threw the following Error message: {0} '@ diff --git a/tests/Unit/DSC_CertReq.Tests.ps1 b/tests/Unit/DSC_CertReq.Tests.ps1 index ef9ac921..3d9a6032 100644 --- a/tests/Unit/DSC_CertReq.Tests.ps1 +++ b/tests/Unit/DSC_CertReq.Tests.ps1 @@ -51,6 +51,7 @@ try $oid = '1.3.6.1.5.5.7.3.1' $keyUsage = '0xa0' $certificateTemplate = 'WebServer' + $nonDefaultCertificateTemplate = 'RDS Session Host' $certificateDCTemplate = 'DomainControllerAuthentication' $invalidCertificateTemplate = 'Invalid Template' $subjectAltUrl = 'contoso.com' @@ -58,6 +59,20 @@ try $friendlyName = "Test Certificate" $invalidFriendlyName = 'Invalid Certificate' + $certificateTemplateOid = New-Object -TypeName System.Security.Cryptography.Oid -Property @{ + Value = '1.3.6.1.4.1.311.21.7' + FriendlyName = 'Certificate Template Information' + } + + $certificateTemplateExtension = New-Object -TypeName PSObject -Property @{ + Oid = $certificateTemplateOid + Critical = $false + } + + Add-Member -InputObject $certificateTemplateExtension -MemberType ScriptMethod -Name Format -Force -Value { + return "Template=$certificateTemplate(1.3.6.1.4.1.311.21.8.9213924.8643542.5195926.6309572.11459053.244.10365877.8067662), Major VersionNumber=100, Minor Version Number=10" + } + $validCert = New-Object -TypeName PSObject -Property @{ Thumbprint = $validThumbprint Subject = "CN=$validSubject" @@ -65,6 +80,40 @@ try NotBefore = (Get-Date).AddDays(-30) # Issued on NotAfter = (Get-Date).AddDays(31) # Expires after FriendlyName = $friendlyName + Extensions = $certificateTemplateExtension + } + + Add-Member -InputObject $validCert -MemberType ScriptMethod -Name Verify -Value { + return $true + } + + $nonDefaultCertificateTemplateExtension = New-Object -TypeName PSObject -Property @{ + Oid = $certificateTemplateOid + Critical = $false + } + + Add-Member -InputObject $nonDefaultCertificateTemplateExtension -MemberType ScriptMethod -Name Format -Force -Value { + return "Template=$nonDefaultCertificateTemplate(1.3.6.1.4.1.311.21.8.9213924.8643542.5195926.6309572.11459053.244.10365877.8067662), Major VersionNumber=100, Minor Version Number=10" + } + + $validCertWithNonDefaultTemplate = New-Object -TypeName PSObject -Property @{ + Thumbprint = $validThumbprint + 2 + Subject = "CN=$validSubject" + Issuer = $validIssuer + NotBefore = (Get-Date).AddDays(-30) # Issued on + NotAfter = (Get-Date).AddDays(31) # Expires after + Extensions = @($nonDefaultCertificateTemplateExtension) + FriendlyName = $friendlyName + } + + $validCertUndesiredTemplate = New-Object -TypeName PSObject -Property @{ + Thumbprint = $validThumbprint + 3 + Subject = "CN=$validSubject" + Issuer = $validIssuer + NotBefore = (Get-Date).AddDays(-1) # Issued on + NotAfter = (Get-Date).AddDays(31) # Expires after + Extensions = @($certificateTemplateExtension) + FriendlyName = $friendlyName } $validCertWithoutSubject = New-Object -TypeName PSObject -Property @{ @@ -73,9 +122,29 @@ try Issuer = $validIssuer NotBefore = (Get-Date).AddDays(-30) # Issued on NotAfter = (Get-Date).AddDays(31) # Expires after + Extensions = @($certificateTemplateExtension) FriendlyName = $friendlyName } + $validCertUndesiredFriendlyName = New-Object -TypeName PSObject -Property @{ + Thumbprint = $validThumbprint + 5 + Subject = "CN=$validSubject" + Issuer = $validIssuer + NotBefore = (Get-Date).AddDays(-1) # Issued on + NotAfter = (Get-Date).AddDays(31) # Expires after + Extensions = @($certificateTemplateExtension) + FriendlyName = $friendlyName + ' 2' + } + + $validCertWithoutFriendlyName = New-Object -TypeName PSObject -Property @{ + Thumbprint = $validThumbprint + 6 + Subject = "CN=$validSubject" + Issuer = $validIssuer + NotBefore = (Get-Date).AddDays(-1) # Issued on + NotAfter = (Get-Date).AddDays(31) # Expires after + Extensions = @($certificateTemplateExtension) + } + $invalidCert = New-Object -TypeName PSObject -Property @{ Thumbprint = $invalidThumbprint Subject = "CN=$invalidSubject" @@ -85,16 +154,13 @@ try FriendlyName = $invalidFriendlyName } - Add-Member -InputObject $validCert -MemberType ScriptMethod -Name Verify -Value { - return $true - } - $expiringCert = New-Object -TypeName PSObject -Property @{ Thumbprint = $validThumbprint Subject = "CN=$validSubject" Issuer = $validIssuer NotBefore = (Get-Date).AddDays(-30) # Issued on NotAfter = (Get-Date).AddDays(30) # Expires after + Extensions = @($certificateTemplateExtension) FriendlyName = $friendlyName } @@ -108,6 +174,7 @@ try Issuer = $validIssuer NotBefore = (Get-Date).AddDays(-30) # Issued on NotAfter = (Get-Date).AddDays(-1) # Expires after + Extensions = @($certificateTemplateExtension) FriendlyName = $friendlyName } Add-Member -InputObject $expiredCert -MemberType ScriptMethod -Name Verify -Value { @@ -129,6 +196,7 @@ try Issuer = $validIssuer NotBefore = (Get-Date).AddDays(-30) # Issued on NotAfter = (Get-Date).AddDays(31) # Expires after + Extensions = @($certificateTemplateExtension) FriendlyName = $friendlyName } Add-Member -InputObject $validCertSubjectDifferentOrder -MemberType ScriptMethod -Name Verify -Value { @@ -141,7 +209,7 @@ try Issuer = $validIssuer NotBefore = (Get-Date).AddDays(-30) # Issued on NotAfter = (Get-Date).AddDays(31) # Expires after - Extensions = @($sanExt) + Extensions = @($sanExt,$certificateTemplateExtension) FriendlyName = $friendlyName } Add-Member -InputObject $validSANCert -MemberType ScriptMethod -Name Verify -Value { @@ -162,7 +230,7 @@ try Issuer = $validIssuer NotBefore = (Get-Date).AddDays(-30) # Issued on NotAfter = (Get-Date).AddDays(31) # Expires after - Extensions = @($incorrectSanExt) + Extensions = @($incorrectSanExt,$certificateTemplateExtension) FriendlyName = $friendlyName } Add-Member -InputObject $incorrectSANCert -MemberType ScriptMethod -Name Verify -Value { @@ -175,7 +243,7 @@ try Issuer = $validIssuer NotBefore = (Get-Date).AddDays(-30) # Issued on NotAfter = (Get-Date).AddDays(31) # Expires after - Extensions = @() + Extensions = @($certificateTemplateExtension) FriendlyName = $friendlyName } Add-Member -InputObject $emptySANCert -MemberType ScriptMethod -Name Verify -Value { @@ -188,6 +256,7 @@ try Issuer = $validIssuer NotBefore = (Get-Date).AddDays(-30) # Issued on NotAfter = (Get-Date).AddDays(31) # Expires after + Extensions = @($certificateTemplateExtension) FriendlyName = 'This name will not match' } Add-Member -InputObject $incorrectFriendlyName -MemberType ScriptMethod -Name Verify -Value { @@ -203,10 +272,14 @@ try $testCredential = New-Object System.Management.Automation.PSCredential $testUsername, (ConvertTo-SecureString $testPassword -AsPlainText -Force) $mock_getCertificateTemplateName_validCertificateTemplate = { $certificateTemplate } + $mock_getCertificateTemplateName_validNonDefaultCertificateTemplate = { $nonDefaultCertificateTemplate } $mock_getCertificateTemplateName_invalidCertificateTemplate = { $invalidCertificateTemplate } $mock_getCertificateTemplateName_validDCCertificateTemplate = { $certificateDCTemplate } $mock_GetChildItem_validCertWithoutSubject = { $validCertWithoutSubject } $mock_getChildItem_validCert = { $validCert } + $mock_getChildItem_twoCerts_OneWithUndesiredFriendlyName = { $validCert, $validCertUndesiredFriendlyName } + $mock_getChildItem_twoCerts_OneWithUndesiredTemplate = { $validCertWithNonDefaultTemplate, $validCertUndesiredTemplate } + $mock_getChildItem_twoCerts_OneWithoutFriendlyName = { $validCert, $validCertWithoutFriendlyName } $mock_getChildItem_expiredCert = { $expiredCert } $mock_getChildItem_expiringCert = { $expiringCert } $mock_getChildItem_validSANCert = { $validSANCert } @@ -225,7 +298,22 @@ try ProviderName = $providerName OID = $oid KeyUsage = $keyUsage - CertificateTemplate = $certificateTemplate + Credential = $testCredential + AutoRenew = $false + FriendlyName = $friendlyName + KeyType = 'RSA' + } + + $paramsNonDefaultCertificateTemplate = @{ + Subject = $validSubject + CAServerFQDN = $caServerFQDN + CARootName = $caRootName + KeyLength = $keyLength + Exportable = $exportable + ProviderName = $providerName + OID = $oid + KeyUsage = $keyUsage + CertificateTemplate = $nonDefaultCertificateTemplate Credential = $testCredential AutoRenew = $false FriendlyName = $friendlyName @@ -263,6 +351,21 @@ try FriendlyName = $friendlyName } + $paramsNonMatchingFriendlyNameDomainController = @{ + Subject = $validSubject + CAServerFQDN = $caServerFQDN + CARootName = $caRootName + KeyLength = $keyLength + Exportable = $exportable + ProviderName = $providerName + OID = $oid + KeyUsage = $keyUsage + CertificateTemplate = $certificateDCTemplate + Credential = $testCredential + AutoRenew = $false + FriendlyName = $friendlyName + ' 2' + } + $paramsInvalid = @{ Subject = $invalidSubject CAServerFQDN = $caServerFQDN @@ -698,6 +801,84 @@ OID = $oid } } + Context 'Two valid certs with matching Subject and Issuer, one with desired friendly name and one with undesired friendly name' { + + Mock -CommandName Get-ChildItem -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' } ` + -MockWith { $validCertUndesiredFriendlyName, $validCert } + + $result = Get-TargetResource @paramsStandard -Verbose + + It 'Should return a hashtable' { + $result | Should -BeOfType System.Collections.Hashtable + } + + It 'Should contain the input values for the cert with desired friendly name' { + $result.Subject | Should -BeExactly $validSubject + $result.CAServerFQDN | Should -BeNullOrEmpty + $result.CARootName | Should -BeExactly $caRootName + $result.KeyLength | Should -BeNullOrEmpty + $result.Exportable | Should -BeNullOrEmpty + $result.ProviderName | Should -BeNullOrEmpty + $result.OID | Should -BeNullOrEmpty + $result.KeyUsage | Should -BeNullOrEmpty + $result.CertificateTemplate | Should -BeExactly $certificateTemplate + $result.SubjectAltName | Should -BeNullOrEmpty + $result.FriendlyName | Should -BeExactly $friendlyName + } + } + + Context 'Two valid certs with matching Subject and Issuer, one with desired template and one with undesired template' { + + Mock -CommandName Get-ChildItem -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' } ` + -MockWith { $validCertUndesiredTemplate, $validCertWithNonDefaultTemplate } + + $result = Get-TargetResource @paramsNonDefaultCertificateTemplate -Verbose + + It 'Should return a hashtable' { + $result | Should -BeOfType System.Collections.Hashtable + } + + It 'Should contain the input values for the cert with desired template' { + $result.Subject | Should -BeExactly $validSubject + $result.CAServerFQDN | Should -BeNullOrEmpty + $result.CARootName | Should -BeExactly $caRootName + $result.KeyLength | Should -BeNullOrEmpty + $result.Exportable | Should -BeNullOrEmpty + $result.ProviderName | Should -BeNullOrEmpty + $result.OID | Should -BeNullOrEmpty + $result.KeyUsage | Should -BeNullOrEmpty + $result.CertificateTemplate | Should -BeExactly $nonDefaultCertificateTemplate + $result.SubjectAltName | Should -BeNullOrEmpty + $result.FriendlyName | Should -BeExactly $friendlyName + } + } + + Context 'Two valid certs with matching Subject and Issuer, one with desired friendly name and one with no friendly name' { + + Mock -CommandName Get-ChildItem -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' } ` + -MockWith { $validCertWithoutFriendlyName, $validCert } + + $result = Get-TargetResource @paramsStandard -Verbose + + It 'Should return a hashtable' { + $result | Should -BeOfType System.Collections.Hashtable + } + + It 'Should contain the input values for the cert with desired friendly name' { + $result.Subject | Should -BeExactly $validSubject + $result.CAServerFQDN | Should -BeNullOrEmpty + $result.CARootName | Should -BeExactly $caRootName + $result.KeyLength | Should -BeNullOrEmpty + $result.Exportable | Should -BeNullOrEmpty + $result.ProviderName | Should -BeNullOrEmpty + $result.OID | Should -BeNullOrEmpty + $result.KeyUsage | Should -BeNullOrEmpty + $result.CertificateTemplate | Should -BeExactly $certificateTemplate + $result.SubjectAltName | Should -BeNullOrEmpty + $result.FriendlyName | Should -BeExactly $friendlyName + } + } + Mock -CommandName Get-ChildItem -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' } ` -Mockwith { $invalidCert } @@ -1445,7 +1626,7 @@ OID = $oid Mock -CommandName CertReq.exe -ParameterFilter {@('-accept', '-m', '-q', $pathCertReqTestCer_parameterFilter)} -MockWith {'0x'} $errorRecord = Get-InvalidOperationRecord ` - -Message ($LocalizedData.GenericErrorThrown -f '0x') + -Message ($LocalizedData.GenericError -f '0x') It 'Should Throw A New-InvalidOperationException' { { Set-TargetResource @paramsAutoDiscovery -Verbose } | Should -Throw $errorRecord @@ -1469,7 +1650,7 @@ OID = $oid -Mockwith $mock_GetChildItem_validCertWithoutSubject It 'Should return false' { - Test-TargetResource @paramsStandard -Verbose | Should -Be $false + Test-TargetResource @paramsStandard -Verbose | Should -BeFalse } } @@ -1486,7 +1667,7 @@ OID = $oid -ParameterFilter $pathCertLocalMachineMy_parameterFilter It 'Should return false' { - Test-TargetResource @paramsStandard -Verbose | Should -Be $false + Test-TargetResource @paramsStandard -Verbose | Should -BeFalse } } @@ -1504,7 +1685,7 @@ OID = $oid -ParameterFilter $pathCertLocalMachineMy_parameterFilter It 'Should return false' { - Test-TargetResource @paramsStandard -Verbose | Should -Be $false + Test-TargetResource @paramsStandard -Verbose | Should -BeFalse } } @@ -1528,7 +1709,7 @@ OID = $oid -MockWith $mock_getCertificateSan_subjectAltName It 'Should return true' { - Test-TargetResource @paramsStandard -Verbose | Should -Be $true + Test-TargetResource @paramsStandard -Verbose | Should -BeTrue } } @@ -1546,7 +1727,7 @@ OID = $oid Mock -CommandName Get-CertificateSubjectAlternativeName ` -MockWith $mock_getCertificateSan_subjectAltName - Test-TargetResource @paramsStandard -Verbose | Should -Be $false + Test-TargetResource @paramsStandard -Verbose | Should -BeFalse } } @@ -1566,7 +1747,7 @@ OID = $oid Mock -CommandName Get-CertificateTemplateName ` -MockWith $mock_getCertificateTemplateName_validCertificateTemplate - Test-TargetResource @paramsAutoRenew -Verbose | Should -Be $false + Test-TargetResource @paramsAutoRenew -Verbose | Should -BeFalse } Context 'When a valid certificate already exists and X500 subjects are in a different order but match' { @@ -1586,7 +1767,7 @@ OID = $oid -MockWith $mock_getCertificateTemplateName_validCertificateTemplate It 'Should return true' { - Test-TargetResource @paramsSubjectDifferentOrder -Verbose | Should -Be $true + Test-TargetResource @paramsSubjectDifferentOrder -Verbose | Should -BeTrue } } @@ -1607,7 +1788,7 @@ OID = $oid -MockWith $mock_getCertificateTemplateName_validCertificateTemplate It 'Should return true' { - Test-TargetResource @paramsSubjectAltName -Verbose | Should -Be $true + Test-TargetResource @paramsSubjectAltName -Verbose | Should -BeTrue } } @@ -1628,7 +1809,7 @@ OID = $oid -MockWith $mock_getCertificateTemplateName_validCertificateTemplate It 'Should return false' { - Test-TargetResource @paramsSubjectAltName -Verbose | Should -Be $false + Test-TargetResource @paramsSubjectAltName -Verbose | Should -BeFalse } } @@ -1648,7 +1829,7 @@ OID = $oid -MockWith $mock_getCertificateTemplateName_validCertificateTemplate It 'Should return false' { - Test-TargetResource @paramsSubjectAltName -Verbose | Should -Be $false + Test-TargetResource @paramsSubjectAltName -Verbose | Should -BeFalse } } @@ -1669,10 +1850,75 @@ OID = $oid -MockWith $mock_getCertificateTemplateName_validCertificateTemplate It 'Should return false' { - Test-TargetResource @paramsStandard -Verbose | Should -Be $false + Test-TargetResource @paramsStandard -Verbose | Should -BeFalse + } + } + + Context 'When two valid certs exist matching Subject and Issuer, one with desired friendly name and one with undesired friendly name' { + + Mock -CommandName Find-CertificateAuthority ` + -MockWith { + return New-Object -TypeName psobject -Property @{ + CARootName = "ContosoCA" + CAServerFQDN = "ContosoVm.contoso.com" + } + } + + Mock -CommandName Get-ChildItem ` + -ParameterFilter $pathCertLocalMachineMy_parameterFilter ` + -Mockwith $mock_getChildItem_twoCerts_OneWithUndesiredFriendlyName + + Mock -CommandName Get-CertificateTemplateName ` + -MockWith $mock_getCertificateTemplateName_validCertificateTemplate + + It 'Should return true' { + Test-TargetResource @paramsStandard -Verbose | Should -BeTrue } } + Context 'When two valid certs exist matching Subject and Issuer, one with desired teplate and one with undesired template' { + + Mock -CommandName Find-CertificateAuthority ` + -MockWith { + return New-Object -TypeName psobject -Property @{ + CARootName = "ContosoCA" + CAServerFQDN = "ContosoVm.contoso.com" + } + } + + Mock -CommandName Get-ChildItem ` + -ParameterFilter $pathCertLocalMachineMy_parameterFilter ` + -Mockwith $mock_getChildItem_twoCerts_OneWithUndesiredTemplate + + Mock -CommandName Get-CertificateTemplateName ` + -MockWith $mock_getCertificateTemplateName_validNonDefaultCertificateTemplate + + It 'Should return true' { + Test-TargetResource @paramsNonDefaultCertificateTemplate -Verbose | Should -BeTrue + } + } + + Context 'When two valid certs exist matching Subject and Issuer, one with desired friendly name and one without friendly name' { + + Mock -CommandName Find-CertificateAuthority ` + -MockWith { + return New-Object -TypeName psobject -Property @{ + CARootName = "ContosoCA" + CAServerFQDN = "ContosoVm.contoso.com" + } + } + + Mock -CommandName Get-ChildItem ` + -ParameterFilter $pathCertLocalMachineMy_parameterFilter ` + -Mockwith $mock_getChildItem_twoCerts_OneWithoutFriendlyName + + Mock -CommandName Get-CertificateTemplateName ` + -MockWith $mock_getCertificateTemplateName_validCertificateTemplate + + It 'Should return true' { + Test-TargetResource @paramsStandard -Verbose | Should -BeTrue + } + } Context 'When a certificate exists but does not match the Certificate Template' { It 'Should return false' { @@ -1685,7 +1931,7 @@ OID = $oid Mock -CommandName Get-CertificateTemplateName ` -MockWith $mock_getCertificateTemplateName_invalidCertificateTemplate - Test-TargetResource @paramsStandard -Verbose | Should -Be $false + Test-TargetResource @paramsStandard -Verbose | Should -BeFalse } } @@ -1701,7 +1947,23 @@ OID = $oid Mock -CommandName Get-CertificateSubjectAlternativeName ` -MockWith $mock_getCertificateSan_subjectAltName - Test-TargetResource @paramsStandardDomainController -Verbose | Should -Be $true + Test-TargetResource @paramsStandardDomainController -Verbose | Should -BeTrue + } + } + + Context 'When a Domain Controller certificate template is used, A valid certificate already exists and has a non-matching FriendlyName' { + It 'Should return true' { + Mock -CommandName Get-ChildItem ` + -ParameterFilter $pathCertLocalMachineMy_parameterFilter ` + -Mockwith $mock_getChildItem_validCert + + Mock -CommandName Get-CertificateTemplateName ` + -MockWith $mock_getCertificateTemplateName_validDCCertificateTemplate + + Mock -CommandName Get-CertificateSubjectAlternativeName ` + -MockWith $mock_getCertificateSan_subjectAltName + + Test-TargetResource @paramsNonMatchingFriendlyNameDomainController -Verbose | Should -BeFalse } } @@ -1718,7 +1980,7 @@ OID = $oid -ParameterFilter $pathCertLocalMachineMy_parameterFilter It 'Should return false' { - Test-TargetResource @paramsAutoDiscovery -Verbose | Should -Be $false + Test-TargetResource @paramsAutoDiscovery -Verbose | Should -BeFalse } It 'Should execute the auto-discovery function' { @@ -1736,9 +1998,9 @@ OID = $oid Context 'When RSA key type and key length is invalid' { $errorRecord = Get-InvalidArgumentRecord ` - -Message (($LocalizedData.InvalidKeySize) -f '384', 'RSA') -ArgumentName 'KeyLength' + -Message (($LocalizedData.InvalidKeySizeError) -f '384', 'RSA') -ArgumentName 'KeyLength' - It 'Should not throw' { + It 'Should throw' { { Assert-ResourceProperty @paramRsaInvalid -Verbose } | Should -Throw $errorRecord } } @@ -1751,9 +2013,9 @@ OID = $oid Context 'When ECDH key type and key length is invalid' { $errorRecord = Get-InvalidArgumentRecord ` - -Message (($LocalizedData.InvalidKeySize) -f '2048', 'ECDH') -ArgumentName 'KeyLength' + -Message (($LocalizedData.InvalidKeySizeError) -f '2048', 'ECDH') -ArgumentName 'KeyLength' - It 'Should not throw' { + It 'Should throw' { { Assert-ResourceProperty @paramEcdhInvalid -Verbose } | Should -Throw $errorRecord } } @@ -1764,7 +2026,7 @@ OID = $oid It 'Should return a true' { Compare-CertificateSubject ` -ReferenceSubject 'CN=TestSubject' ` - -DifferenceSubject 'CN=TestSubject' | Should -Be $true + -DifferenceSubject 'CN=TestSubject' | Should -BeTrue } } @@ -1772,7 +2034,7 @@ OID = $oid It 'Should return a false' { Compare-CertificateSubject ` -ReferenceSubject 'CN=TestSubject' ` - -DifferenceSubject 'CN=SubjectTest' | Should -Be $false + -DifferenceSubject 'CN=SubjectTest' | Should -BeFalse } } @@ -1780,7 +2042,7 @@ OID = $oid It 'Should return a true' { Compare-CertificateSubject ` -ReferenceSubject 'CN=xyz.contoso.com, E=xyz@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, S=State, C=country' ` - -DifferenceSubject 'CN=xyz.contoso.com, E=xyz@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, S=State, C=country' | Should -Be $true + -DifferenceSubject 'CN=xyz.contoso.com, E=xyz@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, S=State, C=country' | Should -BeTrue } } @@ -1788,7 +2050,7 @@ OID = $oid It 'Should return a true' { Compare-CertificateSubject ` -ReferenceSubject 'CN=xyz.contoso.com, E=xyz@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, S=State, C=country' ` - -DifferenceSubject 'E=xyz@contoso.com, CN=xyz.contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, S=State, C=country' | Should -Be $true + -DifferenceSubject 'E=xyz@contoso.com, CN=xyz.contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, S=State, C=country' | Should -BeTrue } } @@ -1796,7 +2058,7 @@ OID = $oid It 'Should return a false' { Compare-CertificateSubject ` -ReferenceSubject 'CN=xyz.contoso.com, E=xyz@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, S=State, C=country' ` - -DifferenceSubject 'CN=xyz.contoso.com, E=test@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, S=State, C=country' | Should -Be $false + -DifferenceSubject 'CN=xyz.contoso.com, E=test@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, S=State, C=country' | Should -BeFalse } } @@ -1804,7 +2066,7 @@ OID = $oid It 'Should return a false' { Compare-CertificateSubject ` -ReferenceSubject 'CN=xyz.contoso.com, E=xyz@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, S=State, C=country' ` - -DifferenceSubject 'CN=xyz.contoso.com, E=xyz@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, C=country' | Should -Be $false + -DifferenceSubject 'CN=xyz.contoso.com, E=xyz@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, C=country' | Should -BeFalse } } @@ -1812,7 +2074,7 @@ OID = $oid It 'Should return a false' { Compare-CertificateSubject ` -ReferenceSubject $null ` - -DifferenceSubject 'CN=xyz.contoso.com, E=xyz@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, C=country' | Should -Be $false + -DifferenceSubject 'CN=xyz.contoso.com, E=xyz@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, C=country' | Should -BeFalse } } @@ -1820,7 +2082,7 @@ OID = $oid It 'Should return a false' { Compare-CertificateSubject ` -ReferenceSubject '' ` - -DifferenceSubject 'CN=xyz.contoso.com, E=xyz@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, C=country' | Should -Be $false + -DifferenceSubject 'CN=xyz.contoso.com, E=xyz@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, C=country' | Should -BeFalse } } } @@ -1830,7 +2092,7 @@ OID = $oid It 'Should return a true' { Compare-CertificateIssuer ` -Issuer 'CN=xyz.contoso.com' ` - -CARootName 'xyz.contoso.com' | Should -Be $true + -CARootName 'xyz.contoso.com' | Should -BeTrue } } @@ -1838,7 +2100,7 @@ OID = $oid It 'Should return a true' { Compare-CertificateIssuer ` -Issuer 'CN=xyz.contoso.com, E=xyz@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, S=State, C=country' ` - -CARootName 'xyz.contoso.com' | Should -Be $true + -CARootName 'xyz.contoso.com' | Should -BeTrue } } @@ -1846,7 +2108,7 @@ OID = $oid It 'Should return a false' { Compare-CertificateIssuer ` -Issuer 'CN=abc.contoso.com' ` - -CARootName 'xyz.contoso.com' | Should -Be $false + -CARootName 'xyz.contoso.com' | Should -BeFalse } } @@ -1854,7 +2116,7 @@ OID = $oid It 'Should return a false' { Compare-CertificateIssuer ` -Issuer 'CN=abc.contoso.com, E=xyz@contoso.com, OU=Organisation Unit, O=Organisation, L=Locality, S=State, C=country' ` - -CARootName 'xyz.contoso.com' | Should -Be $false + -CARootName 'xyz.contoso.com' | Should -BeFalse } } }