-
Notifications
You must be signed in to change notification settings - Fork 226
Add Assert-SqlDscLogin command to validate SQL Server login existence #2123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
johlju
merged 21 commits into
main
from
copilot/fix-11455cec-d078-4daf-820b-08aaf88b8409
Aug 3, 2025
Merged
Changes from 7 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
2571727
Initial plan
Copilot 29c116a
Add Assert-SqlLogin command with unit and integration tests
Copilot cf8760e
Update unit and integration tests to follow existing patterns
Copilot 21ec499
Final implementation of Assert-SqlLogin command with validated tests
Copilot 3ac3109
Merge branch 'main' into copilot/fix-11455cec-d078-4daf-820b-08aaf88b…
johlju 3e5d2b5
Add Assert-SqlLogin integration test to CI pipeline and include SqlAd…
Copilot aab9a74
Fix missing newlines in Assert-SqlLogin files and update copilot inst…
Copilot f12f442
Update CHANGELOG.md with Assert-SqlLogin release notes and add change…
Copilot c4defdf
Apply suggestion from @Copilot
johlju 43991fa
Improve copilot instructions for better AI understanding and add SQL …
Copilot 59d8ba4
Fix FullyQualifiedErrorId test expectation to match PowerShell's erro…
Copilot 8ac5882
Fix integration test authentication and add README reference to copil…
Copilot 519de24
Rename Assert-SqlLogin to Assert-SqlDscLogin with Name parameter and …
Copilot b1fb4dc
Fix localized string key names and add missing newlines
Copilot c0e5b4a
Fix SqlAdmin credential format to use computer name prefix for local …
Copilot a4ddc3e
Refactor Assert-SqlDscLogin tests to manage SQL Server service lifecy…
johlju 86b4e1c
Add "DSCSQLTEST" to cSpell words in settings.json
johlju 90470e8
Rename test suite from 'Install-SqlDscServer' to 'Uninstall-SqlDscSer…
johlju fa0ca66
Update Assert-SqlDscLogin tests to start and stop the named instance …
johlju 7d6f7ba
Update Assert-SqlDscLogin tests for Pester v6 compatibility and add p…
Copilot f3a9647
Fix unit tests for Pester v6 compatibility and improve copilot instru…
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
johlju marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| <# | ||
| .SYNOPSIS | ||
| Assert that the specified SQL Server principal exists as a login. | ||
|
|
||
| .DESCRIPTION | ||
| This command asserts that the specified SQL Server principal exists as a | ||
| login. If the principal does not exist as a login, a terminating error | ||
| is thrown. | ||
|
|
||
| .PARAMETER ServerObject | ||
| Specifies current server connection object. | ||
|
|
||
| .PARAMETER Principal | ||
| Specifies the principal that need to exist as a login. | ||
johlju marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| .EXAMPLE | ||
| $serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance' | ||
| $serverObject | Assert-SqlLogin -Principal 'MyLogin' | ||
|
|
||
| Asserts that the principal 'MyLogin' exists as a login. | ||
|
|
||
| .EXAMPLE | ||
| $serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance' | ||
| Assert-SqlLogin -ServerObject $serverObject -Principal 'MyLogin' | ||
|
|
||
| Asserts that the principal 'MyLogin' exists as a login. | ||
|
|
||
| .NOTES | ||
| This command throws a terminating error if the specified SQL Server | ||
| principal does not exist as a SQL server login. | ||
| #> | ||
| function Assert-SqlLogin | ||
| { | ||
| [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('UseSyntacticallyCorrectExamples', '', Justification = 'Because the rule does not yet support parsing the code when a parameter type is not available. The ScriptAnalyzer rule UseSyntacticallyCorrectExamples will always error in the editor due to https://github.com/indented-automation/Indented.ScriptAnalyzerRules/issues/8.')] | ||
| [CmdletBinding()] | ||
| param | ||
| ( | ||
| [Parameter(Mandatory = $true, ValueFromPipeline = $true)] | ||
| [Microsoft.SqlServer.Management.Smo.Server] | ||
| $ServerObject, | ||
|
|
||
| [Parameter(Mandatory = $true)] | ||
| [System.String] | ||
| $Principal | ||
| ) | ||
|
|
||
| process | ||
| { | ||
| Write-Verbose -Message ($script:localizedData.AssertLogin_CheckingLogin -f $Principal, $ServerObject.InstanceName) | ||
|
|
||
| if (-not $ServerObject.Logins[$Principal]) | ||
| { | ||
| $missingLoginMessage = $script:localizedData.AssertLogin_LoginMissing -f $Principal, $ServerObject.InstanceName | ||
|
|
||
| $PSCmdlet.ThrowTerminatingError( | ||
| [System.Management.Automation.ErrorRecord]::new( | ||
| $missingLoginMessage, | ||
| 'ASL0001', # cspell: disable-line | ||
| [System.Management.Automation.ErrorCategory]::ObjectNotFound, | ||
| $Principal | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| Write-Debug -Message ($script:localizedData.AssertLogin_LoginExists -f $Principal) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
tests/Integration/Commands/Assert-SqlLogin.Integration.Tests.ps1
johlju marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')] | ||
| param () | ||
|
|
||
| BeforeDiscovery { | ||
| try | ||
| { | ||
| if (-not (Get-Module -Name 'DscResource.Test')) | ||
| { | ||
| # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. | ||
| if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) | ||
| { | ||
| # Redirect all streams to $null, except the error stream (stream 2) | ||
| & "$PSScriptRoot/../../../build.ps1" -Tasks 'noop' 3>&1 4>&1 5>&1 6>&1 > $null | ||
| } | ||
|
|
||
| # If the dependencies has not been resolved, this will throw an error. | ||
| Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' | ||
| } | ||
| } | ||
| catch [System.IO.FileNotFoundException] | ||
| { | ||
| throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' | ||
| } | ||
| } | ||
|
|
||
| BeforeAll { | ||
| $script:dscModuleName = 'SqlServerDsc' | ||
|
|
||
| Import-Module -Name $script:dscModuleName | ||
| } | ||
|
|
||
| Describe 'Assert-SqlLogin' -Tag @('Integration_SQL2016', 'Integration_SQL2017', 'Integration_SQL2019', 'Integration_SQL2022') { | ||
| BeforeAll { | ||
| $script:instanceName = 'DSCSQLTEST' | ||
| $script:computerName = Get-ComputerName | ||
| } | ||
|
|
||
| Context 'When connecting to SQL Server instance' { | ||
| BeforeAll { | ||
| $script:serverObject = Connect-SqlDscDatabaseEngine -InstanceName $script:instanceName | ||
johlju marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| Context 'When a login exists' { | ||
| It 'Should not throw an error for sa login' { | ||
| { Assert-SqlLogin -ServerObject $script:serverObject -Principal 'sa' } | Should -Not -Throw | ||
| } | ||
|
|
||
| It 'Should not throw an error when using pipeline' { | ||
| { $script:serverObject | Assert-SqlLogin -Principal 'sa' } | Should -Not -Throw | ||
| } | ||
|
|
||
| It 'Should not throw an error for NT AUTHORITY\SYSTEM login' { | ||
| { Assert-SqlLogin -ServerObject $script:serverObject -Principal 'NT AUTHORITY\SYSTEM' } | Should -Not -Throw | ||
| } | ||
|
|
||
| It 'Should not throw an error for SqlAdmin login' { | ||
| { Assert-SqlLogin -ServerObject $script:serverObject -Principal ('{0}\SqlAdmin' -f $script:computerName) } | Should -Not -Throw | ||
| } | ||
| } | ||
|
|
||
| Context 'When a login does not exist' { | ||
| It 'Should throw a terminating error for non-existent login' { | ||
| { Assert-SqlLogin -ServerObject $script:serverObject -Principal 'NonExistentLogin123' } | Should -Throw -ExpectedMessage "*does not exist as a login*" | ||
| } | ||
|
|
||
| It 'Should throw an error with ObjectNotFound category' { | ||
| try | ||
| { | ||
| Assert-SqlLogin -ServerObject $script:serverObject -Principal 'NonExistentLogin123' | ||
| } | ||
| catch | ||
| { | ||
| $_.CategoryInfo.Category | Should -Be 'ObjectNotFound' | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
johlju marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] | ||
| param () | ||
|
|
||
| BeforeDiscovery { | ||
| try | ||
| { | ||
| if (-not (Get-Module -Name 'DscResource.Test')) | ||
| { | ||
| # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. | ||
| if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) | ||
| { | ||
| # Redirect all streams to $null, except the error stream (stream 2) | ||
| & "$PSScriptRoot/../../../build.ps1" -Tasks 'noop' 3>&1 4>&1 5>&1 6>&1 > $null | ||
| } | ||
|
|
||
| # If the dependencies has not been resolved, this will throw an error. | ||
| Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' | ||
| } | ||
| } | ||
| catch [System.IO.FileNotFoundException] | ||
| { | ||
| throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' | ||
| } | ||
| } | ||
|
|
||
| BeforeAll { | ||
| $script:dscModuleName = 'SqlServerDsc' | ||
|
|
||
| $env:SqlServerDscCI = $true | ||
|
|
||
| Import-Module -Name $script:dscModuleName | ||
|
|
||
| # Loading mocked classes | ||
| Add-Type -Path (Join-Path -Path (Join-Path -Path $PSScriptRoot -ChildPath '../Stubs') -ChildPath 'SMO.cs') | ||
|
|
||
| $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName | ||
| $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName | ||
| $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName | ||
| } | ||
|
|
||
| AfterAll { | ||
| $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') | ||
| $PSDefaultParameterValues.Remove('Mock:ModuleName') | ||
| $PSDefaultParameterValues.Remove('Should:ModuleName') | ||
|
|
||
| # Unload the module being tested so that it doesn't impact any other tests. | ||
| Get-Module -Name $script:dscModuleName -All | Remove-Module -Force | ||
|
|
||
| Remove-Item -Path 'env:SqlServerDscCI' | ||
| } | ||
|
|
||
| Describe 'Assert-SqlLogin' -Tag 'Public' { | ||
| Context 'When a login exists' { | ||
| BeforeAll { | ||
| $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | ||
| $mockServerObject | Add-Member -MemberType 'NoteProperty' -Name 'InstanceName' -Value 'TestInstance' -Force | ||
| $mockServerObject | Add-Member -MemberType 'ScriptProperty' -Name 'Logins' -Value { | ||
| return @{ | ||
| 'TestLogin' = New-Object -TypeName Object | | ||
| Add-Member -MemberType 'NoteProperty' -Name 'Name' -Value 'TestLogin' -PassThru -Force | ||
| } | ||
| } -Force | ||
| } | ||
|
|
||
| It 'Should not throw an error when the login exists' { | ||
| { Assert-SqlLogin -ServerObject $mockServerObject -Principal 'TestLogin' } | Should -Not -Throw | ||
| } | ||
|
|
||
| It 'Should accept ServerObject from pipeline' { | ||
| { $mockServerObject | Assert-SqlLogin -Principal 'TestLogin' } | Should -Not -Throw | ||
| } | ||
| } | ||
|
|
||
| Context 'When a login does not exist' { | ||
| BeforeAll { | ||
| $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | ||
| $mockServerObject | Add-Member -MemberType 'NoteProperty' -Name 'InstanceName' -Value 'TestInstance' -Force | ||
| $mockServerObject | Add-Member -MemberType 'ScriptProperty' -Name 'Logins' -Value { | ||
| return @{ | ||
| 'ExistingLogin' = New-Object -TypeName Object | | ||
| Add-Member -MemberType 'NoteProperty' -Name 'Name' -Value 'ExistingLogin' -PassThru -Force | ||
| } | ||
| } -Force | ||
| } | ||
|
|
||
| It 'Should throw a terminating error when the login does not exist' { | ||
| { Assert-SqlLogin -ServerObject $mockServerObject -Principal 'NonExistentLogin' } | Should -Throw -ExpectedMessage "*does not exist as a login*" | ||
| } | ||
|
|
||
| It 'Should throw an error with the correct error category' { | ||
| try | ||
| { | ||
| Assert-SqlLogin -ServerObject $mockServerObject -Principal 'NonExistentLogin' | ||
| } | ||
| catch | ||
| { | ||
| $_.CategoryInfo.Category | Should -Be 'ObjectNotFound' | ||
| $_.FullyQualifiedErrorId | Should -Be 'ASL0001' | ||
johlju marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
|
|
||
| It 'Should include the principal name in the error message' { | ||
| { Assert-SqlLogin -ServerObject $mockServerObject -Principal 'NonExistentLogin' } | Should -Throw -ExpectedMessage "*NonExistentLogin*" | ||
| } | ||
|
|
||
| It 'Should include the instance name in the error message' { | ||
| { Assert-SqlLogin -ServerObject $mockServerObject -Principal 'NonExistentLogin' } | Should -Throw -ExpectedMessage "*TestInstance*" | ||
| } | ||
| } | ||
|
|
||
| Context 'When validating parameters' { | ||
| BeforeAll { | ||
| $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | ||
| } | ||
|
|
||
| It 'Should have ServerObject as a mandatory parameter' { | ||
| $parameterInfo = (Get-Command -Name 'Assert-SqlLogin').Parameters['ServerObject'] | ||
| $parameterInfo.Attributes.Mandatory | Should -Contain $true | ||
| } | ||
|
|
||
| It 'Should have Principal as a mandatory parameter' { | ||
| $parameterInfo = (Get-Command -Name 'Assert-SqlLogin').Parameters['Principal'] | ||
| $parameterInfo.Attributes.Mandatory | Should -Contain $true | ||
| } | ||
|
|
||
| It 'Should accept ServerObject from pipeline' { | ||
| $parameterInfo = (Get-Command -Name 'Assert-SqlLogin').Parameters['ServerObject'] | ||
| $parameterInfo.Attributes.ValueFromPipeline | Should -Contain $true | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.