views:

204

answers:

2

Is it possible to register a new PowerShell module just by defining a new module manifest and putting it in the specified directory?

E.g. a a new module named SampleModule should be created. I put the empty manifest file SampleModule.psd1 in the directory <%PSModulePath%>\SampleModule. (It doesn't matter which (user or global) module path is used).

This is enough for PowerShell to list my new module with the Get-Module -ListAvailable command.

In the next step I try to fill the manifest and set the ModuleToProcess property to an assembly, which is located in another directory. Calling Import-Module fails, PowerShell cannot find the assembly.

+1  A: 

It is indeed. In fact, I have a really nifty trick that does this for the SQL snapin. The first chunk of the code is a function that will create a module manifest for you from one of of the module locations. It will still prompt you (as the New-ModuleManifest cmdlet does) for some required parameters, like description. It finds any types and format files, as well any .dll's named .ps.dll (most snapins follow this convention), and then it creates a module manifest that wraps the original module. The second chunk of code does this specificially for the SQL PSSnapin.

function New-ModuleManifestFromSnapIn {

    [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
    param(

    [Parameter(Mandatory=$true, Position=0)]
    [System.String]
    ${Path},

    # The location in the filesystem where the V1 snapin resides
    [Parameter(Mandatory=$true)]
    [System.String]
    ${OriginalPath},

    [System.Guid]
    ${Guid},

    [Parameter(Mandatory=$true)]
    [AllowEmptyString()]
    [System.String]
    ${Author},

    [Parameter(Mandatory=$true)]
    [AllowEmptyString()]
    [System.String]
    ${CompanyName},

    [Parameter(Mandatory=$true)]
    [AllowEmptyString()]
    [System.String]
    ${Copyright},

    [ValidateNotNull()]
    [System.Version]
    ${ModuleVersion},

    [Parameter(Mandatory=$true)]
    [AllowEmptyString()]
    [System.String]
    ${Description},

    [System.Reflection.ProcessorArchitecture]
    ${ProcessorArchitecture},

    [System.Version]
    ${PowerShellVersion},

    [System.Version]
    ${ClrVersion},

    [System.Version]
    ${DotNetFrameworkVersion},

    [System.String]
    ${PowerShellHostName},

    [System.Version]
    ${PowerShellHostVersion},

    [System.Object[]]
    ${RequiredModules},

    [AllowEmptyCollection()]
    [System.String[]]
    ${ScriptsToProcess},

    [AllowEmptyCollection()]
    [System.Object[]]
    ${ModuleList},

    [AllowNull()]
    [System.Object]
    ${PrivateData},

    [Switch]
    ${PassThru}
    )

    process {
        $types = Get-ChildItem $originalPath -Filter *.types.ps1xml
        $formats = Get-ChildItem $originalPath -Filter *.format.ps1xml
        $dlls = Get-ChildItem $originalPath -Filter *.ps*.dll
        $null = $psBoundParameters.Remove("OriginalPath")
        $psBoundParameters += @{
            VariablesToExport = "*"
            FunctionsToExport = "*"
            AliasesToExport = "*"
            CmdletsToExport = "*"
            FileList = @()
            RequiredAssemblies = @()
            ModuleToProcess = ""
            NestedModules = @($dlls | Select-Object -ExpandProperty FullName)
            TypesToProcess = @($types | Select-Object -ExpandProperty FullName)
            FormatsToProcess = @($formats | Select-Object -ExpandProperty FullName)
        }
        New-ModuleManifest @psBoundParameters
    }
}

This chunk will show create a manifest by pointing the tool @ SQL.

$basePath = $env:SqlSamplesSourceDataPath | Split-Path
if (${env:ProgramFiles(x86)}) {
    $basePath = $basePath.Replace($env:ProgramFiles, ${env:ProgramFiles(x86)})
} 

$path = Join-Path $basePath "Binn"


$basicMetaData = @{
    Author = "Microsoft Corporation"
    Description = "A Manifest to enable using the SQL PowerShell snapin in V2"
    CompanyName = "Microsoft Corporation"
    Copyright = (Get-Date).Year 
}
New-ModuleManifestFromSnapin -Path $psScriptRoot\Sql.psd1 -OriginalPath $path @BasicMetaData

The final chunk will copy any files named the current culture (i.e. SQL\en-us) into the module directory, which will make Get-Help work for the cmdlets.

This trick has worked for a few snapins, but may require some customizations for each snapin you want to re-expose as a module. Luckily, this is a one time cost.

$Culture = Get-Culture
$CultureList = "$path\$culture", 
    (Join-Path $path $culture.ThreeLetterIsoLanguageName), 
    (Join-Path $path $culture.TwoLetterIsoLanguageName)

$CultureDirectory = Get-Item $CultureList -ErrorAction SilentlyContinue |
    Select-Object -First 1 

if ($CultureDirectory) {
    $localDir = Join-Path $psScriptRoot (Split-Path $CultureDirectory -Leaf)
    $item = Get-Item $localDir -ErrorAction SilentlyContinue 
    if ($item) {
        Remove-Item $item -Recurse -Force 
    }
    Copy-Item $CultureDirectory $LocalDir -Recurse
}

Hope this helps

Start-Automating
+1  A: 

The Get-Module cmdlet with the -ListAvailable parameter works nice. The problem was the architecture, i.e. 32-bit PowerShell host could not list 64-bit modules...

Michael Damatov