PSRequiredModules.psm1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
<#
    .SYNOPSIS
        Get-RequiredModules
 
    .DESCRIPTION
        Read PSRequiredModules data from PowerShell module manifest.
 
    .PARAMETER ModuleFilePath
        Module file path expect a module file ending with .psd1 or .psm1 (to retrieve automatically associated .psd1)
 
    .EXAMPLE
        PS C:\> Get-RequiredModules -ModuleFilePath 'C:\MyModule\MyModule.psd1'
 
    .INPUTS
        System.String
 
    .OUTPUTS
        System.Boolean
 
    .NOTES
        Author: JDMSFT
        Date: 17/03/21
#>

Function Get-RequiredModules
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$ModuleFilePath
    )

    Try
    {
        If ($ModuleFilePath -like '*.psm1' -or $ModuleFilePath -like '*.psd1')
        {
            $ManifestData = Import-LocalizedData -BaseDirectory (Split-Path $ModuleFilePath) -FileName (Split-Path $ModuleFilePath -Leaf)

            $ManifestData.PrivateData.PSRequiredModules
        }
        Else
        {
            Write-Warning "Please specify a valid module file path (ending with .psm1/.psd1)"
        }
    }
    Catch { Write-Error $($_) ; throw "[message] $($_)`nException: [locator] $($_.InvocationInfo.ScriptName):$($_.InvocationInfo.ScriptLineNumber) >> $($_.InvocationInfo.Line.TrimStart())" }
}

<#
    .SYNOPSIS
        Import-RequiredModules
 
    .DESCRIPTION
        Read PSRequiredModules data from PowerShell module manifest.
 
    .PARAMETER ModuleFilePath
        Module file path expect a module file ending with .psd1 or .psm1 (to retrieve automatically associated .psd1)
 
    .PARAMETER AutoInstall
        Automatically install missing module from PowerShell environments
 
    .PARAMETER Details
        Return full state details isntead returning a boolean (usefull to troubleshoot)
 
    .EXAMPLE
        PS C:\> Import-RequiredModules -ModuleFilePath 'C:\MyModule\MyModule.psd1'
         
        Return if all required modules are imported in the PowerShell session.
 
    .EXAMPLE
        PS C:\> Import-RequiredModules -ModuleFilePath 'C:\MyModule\MyModule.psd1' -AutoInstall
         
        Install missing modules and return if all required modules are imported in the PowerShell session.
 
    .EXAMPLE
        PS C:\> Import-RequiredModules -ModuleFilePath 'C:\MyModule\MyModule.psd1' -Details
         
        Return all required modules states for the current PowerShell session instead returnin a boolean.
 
    .INPUTS
        System.String
 
    .OUTPUTS
        System.Boolean,Hastable
 
    .NOTES
        Author: JDMSFT
        Date: 17/03/21
#>

Function Import-RequiredModules
{
    [CmdletBinding()]
    param 
    (
        [Parameter(Mandatory = $true)] 
        [string]$ModuleFilePath,
        [switch]$AutoInstall,
        [switch]$Details
    )

    Try 
    {
        If ($ModuleFilePath -like '*.psm1' -or $ModuleFilePath -like '*.psd1')
        {
            $ModuleObject = @()
            $CallerFileName = (Split-Path $ModuleFilePath -Leaf)
            $CallerName = $CallerFileName.Substring(0, $CallerFileName.Length-5)
            $ManifestData = Import-LocalizedData -BaseDirectory (Split-Path $ModuleFilePath) -FileName $CallerFileName
            $CallerVersion = $ManifestData.ModuleVersion

            $ManifestData.PrivateData.PSRequiredModules.Keys | ForEach {

                $IsAlreadyImported = $false
                $IsRightVersionImported = $false
                $IsRightVersionInstalled = $false
                $IsDownloadable = 'Untested (locally present)'
                $Action = 'None'
                $State = $null

                # Get required version / prerelease
                $RequiredModuleName = $_
                $RequiredModuleVersionSemVer = $ManifestData.PrivateData.PSRequiredModules.$_
                If ($RequiredModuleVersionSemVer -like '*-*') 
                { 
                    $PreReleaseVersion = $true
                    $RequiredModuleVersion = ($RequiredModuleVersionSemVer -split '-')[0]
                    $RequiredModulePrerelease = ($RequiredModuleVersionSemVer -split '-')[1]
                } 
                Else 
                { 
                    $PreReleaseVersion = $false 
                    $RequiredModuleVersion = $RequiredModuleVersionSemVer
                }

                # Checking for imported module version ...
                $MatchVersion = $false
                $RequiredModuleImportedMatches = Get-Module $RequiredModuleName
                If ($RequiredModuleImportedMatches)
                {
                    If ($PreReleaseVersion)
                    {
                        Write-Verbose "Checking for imported module $RequiredModuleName v$RequiredModuleVersionSemVer... (prerelease)"
                        $RequiredModuleImportedMatches | ForEach { 
                            If ($_.Version -eq $RequiredModuleVersion -and $_.PrivateData.PSData.Prerelease -eq $RequiredModulePrerelease) 
                            { 
                                Write-Verbose "Module $RequiredModuleName v$RequiredModuleVersionSemVer already imported !"
                                $MatchVersion = $true
                                $RequiredModule = $_ 
                                $IsAlreadyImported = $true
                                $IsRightVersionImported = $true
                                $IsRightVersionInstalled = $true
                                $State = 'OK'
                            } 
                        }
                    }
                    Else
                    {
                        Write-Verbose "Checking for imported module $RequiredModuleName v$RequiredModuleVersionSemVer... (not prerelease)"
                        $RequiredModuleImportedMatches | ForEach { 
                            If ($_.Version -eq $RequiredModuleVersion) 
                            { 
                                Write-Verbose "Module $RequiredModuleName v$RequiredModuleVersionSemVer already imported !"
                                $MatchVersion = $true
                                $RequiredModule = $_ 
                                $IsAlreadyImported = $true
                                $IsRightVersionImported = $true
                                $IsRightVersionInstalled = $true
                                $State = 'OK'
                            } 
                        } 
                    }

                    If ($MatchVersion -ne $true) 
                    { 
                        Write-Warning "[PSRequiredModules] Wrong $RequiredModuleName module version already imported in the current PowerShell session (required version : $RequiredModuleVersionSemVer). You may encount some issue/bug by using this $RequiredModuleName module version. Please consider to remove $RequiredModuleName module (Remove-Module $RequiredModuleName) from your current PowerShell session prior importing $CallerName v$CallerVersion for an optimal experience."

                        $IsAlreadyImported = $true
                        $Action = "Skipped"
                        $State = 'Warning (wrong version already imported)'
                    }
                } Else {Write-Verbose "No $RequiredModuleName imported in the current PowerShell session"}

                If ($IsAlreadyImported -ne $true)
                {
                    # Checking for installed module version ...
                    $RequiredModuleInstalledMatches = Get-Module $RequiredModuleName -ListAvailable
                    If ($MatchVersion -ne $true) 
                    {
                        If ($PreReleaseVersion)
                        {
                            Write-Verbose "Checking for installed module $RequiredModuleName v$RequiredModuleVersionSemVer... (prerelease)"
                            $RequiredModuleInstalledMatches | ForEach { 
                                If ($_.Version -eq $RequiredModuleVersion -and $_.PrivateData.PSData.Prerelease -eq $RequiredModulePrerelease) 
                                { 
                                    Write-Verbose "Module $RequiredModuleName v$RequiredModuleVersionSemVer already installed !"
                                    $MatchVersion = $true
                                    $RequiredModule = $_ 
                                    $IsRightVersionInstalled = $true
                                } 
                            }
                        }
                        Else
                        {
                            Write-Verbose "Checking for installed module $RequiredModuleName v$RequiredModuleVersionSemVer... (not prerelease)"
                            $RequiredModuleInstalledMatches | ForEach { 
                                If ($_.Version -eq $RequiredModuleVersion) 
                                { 
                                    Write-Verbose "Module $RequiredModuleName v$RequiredModuleVersionSemVer already installed !"
                                    $MatchVersion = $true
                                    $RequiredModule = $_ 
                                    $IsRightVersionInstalled = $true
                                } 
                            } 
                        }

                        # Module installed, importing ...
                        If ($MatchVersion -eq $true) 
                        {
                            Write-Verbose "Importing module $RequiredModuleName v$RequiredModuleVersionSemVer..."
                            Import-Module $RequiredModule -Global
                            $Action = 'AutoImport'
                            If (Get-Module $RequiredModuleName) { $State = 'OK' } Else { $State = 'KO' }

                        }
                        # Module not installed, installing and importing ...
                        Else 
                        {
                            Write-Verbose "Module $RequiredModuleName v$RequiredModuleVersionSemVer not found."

                            If (Find-Module -Name $RequiredModuleName -RequiredVersion $RequiredModuleVersionSemVer -Repository PSGallery -AllowPrerelease -ea SilentlyContinue) { $IsDownloadable = $true } Else { $IsDownloadable = $false }

                            If ($AutoInstall)
                            {
                                If ($IsDownloadable)
                                {
                                    $Action = 'AutoInstall'
                                    Install-Module -Name $RequiredModuleName -RequiredVersion $RequiredModuleVersionSemVer -Repository PSGallery -AllowPrerelease

                                    If (Get-InstalledModule -Name $RequiredModuleName -RequiredVersion $RequiredModuleVersionSemVer -AllowPrerelease -ea SilentlyContinue) { Import-Module -Name $RequiredModuleName -RequiredVersion $RequiredModuleVersion -Global } 
                                }
                                Else { Write-Verbose "$RequiredModuleName can't be downloaded." }
                        
                                If (Get-Module $RequiredModuleName) { $State = 'OK' } Else { $State = 'KO' }
                            }
                            Else { Write-Verbose "Auto-install module disabled. Please installl required module manually prior using this module : Isntall-Module -Name $ModuleName -RequiredVersion $RequiredModuleVersionSemVer" ; $State = 'KO' }
                        } 
                    }
                    Else { Write-Verbose "" }
                }

                $ModuleObject += [PSCustomObject]@{Name = $RequiredModuleName; Version = $RequiredModuleVersionSemVer; IsPrerelease = $PreReleaseVersion ; IsAlreadyImported = $IsAlreadyImported ; IsRightVersionImported = $IsRightVersionImported ; IsRightVersionInstalled = $IsRightVersionInstalled ; IsDownloadable = $IsDownloadable ; Action = $Action ; State = $State }
            }

            If ($Details) { $ModuleObject } Else { $ModuleObject.State -notcontains 'KO' }
        }
        Else
        {
            Write-Warning "Please specify a valid module file path (ending with .psm1/.psd1)"
        }
    }
    Catch { Write-Error $($_) ; throw "[message] $($_)`nException: [locator] $($_.InvocationInfo.ScriptName):$($_.InvocationInfo.ScriptLineNumber) >> $($_.InvocationInfo.Line.TrimStart())" }
}