Skip to content
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

Get-SqlDscPreferredModule: Should be possible to specify which version of the SqlServer module is imported #1965

Closed
YaroBear opened this issue Aug 23, 2023 · 9 comments · Fixed by #1966
Labels
enhancement The issue is an enhancement request. good first issue The issue should be easier to fix and can be taken up by a beginner to learn to contribute on GitHub

Comments

@YaroBear
Copy link
Contributor

Hello!

We have been using SqlServerDSC on systems that have the SqlServer v21.x.x powershell module installed for some time.

We have a requirement now where we need to have both SqlServer v21 and v22 installed on the system.

Is there any way we can specify which version of SqlServer module is imported in SqlServerDSC? We mainly use the SqlScriptQuery module and want to stick with using v21 for some time.

Great work, love what you guys have built! Thanks!

@YaroBear
Copy link
Contributor Author

Maybe something like Import-Module -Name "sqlserver" -RequiredVersion 21.1.18245 -Force after Import-DscResource -ModuleName SqlServerDsc would override the sqlserver module in the current session? For example:

Import-DscResource -ModuleName SqlServerDsc

Import-Module -Name "sqlserver" -RequiredVersion 21.1.18245 -Force

Node $ServerName
    {
        SqlScriptQuery DatabaseSpecs {
        ...

@kilasuit
Copy link

It should be possible as per the docs on Import-DSCResource
This though depends on how you are making use of DSC as it's changed lots in recent years

Can you add some more environment details, like is this in Azure, are you using Machine Configuration at all etc etc so that can ensure to give the right advice here.

@YaroBear
Copy link
Contributor Author

Certainly!

  • We are using Azure Windows Server 2016+ VMs hooked up to Azure Automation Account as our pull server.
  • We use DSC 1.1 and do not use Machine Configuration.

What part of the documentation of Import-DSCResource are you referring to?

Tracing the code from SqlScriptQuery.psm1 -> InvokeSqlScript -> Import-SqlDscPreferredModule is where SqlServer module is imported.
I don't think running Import-Module -Name "sqlserver" -RequiredVersion 21.1.18245 -Force before the resource block will work, because SqlScriptQuery resource is going to override that import before Invoking the Sql script.

@johlju
Copy link
Member

johlju commented Aug 24, 2023

You cannot specify what version of SqlServer to import during compilation, or prior to compilation. As mentioned above the (latest installed) module SqlServer is returned by Get-SqlDscPreferredModule and is then imported by the command Import-SqlDscPreferredModule.

So I think Get-SqlDscPreferredModule must be extended to support a specific version (probably by reading an environment variable $env:SMODefaultModuleVersion) and then make sure Import-SqlDscPreferredModule can import the specific module version returned by Get-SqlDscPreferredModule. I suggest that Get-SqlDscPreferredModule return the module object instead of just the module name.

Happy to review a PR that resolves this.

@johlju johlju added enhancement The issue is an enhancement request. help wanted The issue is up for grabs for anyone in the community. good first issue The issue should be easier to fix and can be taken up by a beginner to learn to contribute on GitHub labels Aug 24, 2023
@johlju johlju changed the title Is there a way to specify which version of the SqlServer module is imported/used? Get-SqlDscPreferredModule: Should be possible to specify which version of the SqlServer module is imported Aug 24, 2023
@YaroBear
Copy link
Contributor Author

Awesome, thanks for the help! I’ll see if I can cook up a PR here

@YaroBear
Copy link
Contributor Author

YaroBear commented Aug 24, 2023

While working on this, I found a bug in these lines of code. The version field isn't getting populated for SQLServer module as expected. Only SQLPS.

I can't tell exactly why. When I'm debugging, the code seems to exit prematurely on line 116 even though all the keys are present. No error codes.

My code changes are modifying this block of code anyway, just thought I'd bring it up.

Here is an output when I run this code on my machine:

Name      Path                                                                                        Version
----      ----                                                                                        -------
SqlServer C:\Users\yaroslav.berejnoi\Documents\PowerShell\Modules\SqlServer\22.0.49\SqlServer.psd1    
SqlServer C:\Users\yaroslav.berejnoi\Documents\PowerShell\Modules\SqlServer\21.1.18256\SqlServer.psd1 
SqlServer C:\Program Files\WindowsPowerShell\Modules\SqlServer\21.1.18256\SqlServer.psd1              
SqlServer C:\Program Files\WindowsPowerShell\Modules\SqlServer\21.1.18245\SqlServer.psd1              
SqlServer C:\Program Files\WindowsPowerShell\Modules\SqlServer\21.1.18147\SqlServer.psd1              
SQLPS     C:\Program Files (x86)\Microsoft SQL Server\130\Tools\PowerShell\Modules\SQLPS\SQLPS.psd1   130

I tried both PS 7.3.6 and 5.1.19041.3031.

As a quick test for someone else to try, this can simply be copy and pasted into any powershell window. Same code, just with the $Name variable populated:

$Name = @('sqlserver', 'SQLPS')
$availableModule = Get-Module -FullyQualifiedName $Name -ListAvailable |
        Select-Object -Property @(
            'Name',
            'Path',
            @{
                Name       = 'Version'
                Expression = {
                    if ($_.Name -eq 'SQLPS')
                    {
                        <#
                            Parse the build version number '120', '130' from the Path.
                            Older version of SQLPS did not have correct versioning.
                        #>
                        (Select-String -InputObject $_.Path -Pattern '\\([0-9]{3})\\' -List).Matches.Groups[1].Value
                    }
                    else
                    {
                        $versionToReturn = $_.Version

                        if ($_.ContainsKey('PrivateData') -and $_.PrivateData.ContainsKey('PSData') -and $_.PrivateData.PSData.ContainsKey('Prerelease'))
                        {
                            if (-not [System.String]::IsNullOrEmpty($_.PrivateData.PSData.Prerelease))
                            {
                                $versionToReturn = '{0}-{1}' -f $_.Version, $_.PrivateData.PSData.Prerelease
                            }
                        }

                        $versionToReturn
                    }
                }
            }
        )

$availableModule

@johlju
Copy link
Member

johlju commented Aug 25, 2023

It is ContainsKey that throws an error that is not caught for some reason. There were several issues, thanks for catching that.
If you would like I can push these changes, or can you incorporate them in your change? What ever is easiest for you.

Here is the working code:

$Name = @('sqlserver', 'SQLPS')
$availableModule = Get-Module -FullyQualifiedName $Name -ListAvailable |
        Select-Object -Property @(
            'Name',
            'Path',
            @{
                Name       = 'Version'
                Expression = {
                    if ($_.Name -eq 'SQLPS')
                    {
                        <#
                            Parse the build version number '120', '130' from the Path.
                            Older version of SQLPS did not have correct versioning.
                        #>
                        (Select-String -InputObject $_.Path -Pattern '\\([0-9]{3})\\' -List).Matches.Groups[1].Value
                    }
                    else
                    {
                        $versionToReturn = $_.Version.ToString()

                        if ($null -ne $_.PrivateData -and $null -ne $_.PrivateData.PSData -and $null -ne $_.PrivateData.PSData.Prerelease)
                        {
                            if (-not [System.String]::IsNullOrEmpty($_.PrivateData.PSData.Prerelease))
                            {
                                $versionToReturn = '{0}-{1}' -f $versionToReturn, $_.PrivateData.PSData.Prerelease
                            }
                        }

                        $versionToReturn
                    }
                }
            }
        )

$availableModule

This correctly returns

Name      Path                                                                                      Version
----      ----                                                                                      -------
SqlServer C:\Program Files\WindowsPowerShell\Modules\SqlServer\22.0.49\SqlServer.psd1               22.0.49-preview1
SqlServer C:\Program Files\WindowsPowerShell\Modules\SqlServer\21.1.18080\SqlServer.psd1            21.1.18080
SQLPS     C:\Program Files (x86)\Microsoft SQL Server\160\Tools\PowerShell\Modules\SQLPS\SQLPS.psd1 160
SQLPS     C:\Program Files (x86)\Microsoft SQL Server\130\Tools\PowerShell\Modules\SQLPS\SQLPS.psd1 130

@YaroBear
Copy link
Contributor Author

I can incorporate them. Thanks!

@johlju
Copy link
Member

johlju commented Aug 25, 2023

Hey, I just saw that the line if (-not [System.String]::IsNullOrEmpty($_.PrivateData.PSData.Prerelease)) seems redundant now? We are already checking that it is not null on the if statement above. 🤔  No it is not, the string can be set to empty and then it is not a prerelease, so the line should still be there. 🙂

johlju pushed a commit that referenced this issue Sep 1, 2023
…L module is imported (#1966)

- SqlServerDsc
  - `Get-SqlDscPreferredModule`
    - Optionally specify what version of the the SQL preferred module to be imported using the SMODefaultModuleVersion environment variable ([issue #1965](#1965)).
 - `Get-SqlDscPreferredModule`
    - Now returns a PSModuleInfo object instead of just the module name.
  - `Import-SqlDscPreferredModule`
    - Handles PSModuleInfo objects from `Get-SqlDscPreferredModule` instead of strings.
    - Sets -ErrorAction 'Stop' on Get-SqlDscPreferredModule to throw an error if no SQL module is found. The script-terminating error is caught and made into a statement-terminating error.
  - Removed PreferredModule_ModuleFound string in favor for more verbose PreferredModule_ModuleVersionFound.
- New private command:
  - Get-SMOModuleCalculatedVersion - Returns the version of the SMO module as a string. SQLPS version 120 and 130 do not have the correct version set, so the file path is used to calculate the version.
@johlju johlju removed the help wanted The issue is up for grabs for anyone in the community. label Sep 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement The issue is an enhancement request. good first issue The issue should be easier to fix and can be taken up by a beginner to learn to contribute on GitHub
Projects
None yet
3 participants