Creating, using, and managing PowerShell modules

Advanced Template Module Generator Tool

<#
.SYNOPSIS
Advanced Template Module Generator Tool

.DESCRIPTION
This script provides an interactive tool to generate a comprehensive structure for a PowerShell module,
including advanced features like dependency management, script analyzer settings, and extensive testing setup.

.NOTES
File Name      : AdvancedTemplateModuleGenerator.ps1
Author         : [Your Name]
Prerequisite   : PowerShell V5.1 or later
Version        : 1.0
Date           : [Current Date]

.EXAMPLE
.\AdvancedTemplateModuleGenerator.ps1
#>

function Show-Menu {
    Clear-Host
    Write-Host "=== Advanced Template Module Generator Tool ===" -ForegroundColor Cyan
    Write-Host "1. Set Module Details"
    Write-Host "2. Add Public Function"
    Write-Host "3. Add Private Function"
    Write-Host "4. Add Module Dependencies"
    Write-Host "5. Configure Build Settings"
    Write-Host "6. Configure Test Settings"
    Write-Host "7. Generate Module Files"
    Write-Host "8. Exit"
}

function Set-ModuleDetails {
    $script:moduleName = Read-Host "Enter the name of the module"
    $script:moduleDescription = Read-Host "Enter a brief description of the module"
    $script:moduleAuthor = Read-Host "Enter the author's name"
    $script:moduleVersion = Read-Host "Enter the module version (e.g., 1.0.0)"
    $script:moduleLicense = Read-Host "Enter the module license (e.g., MIT)"
    $script:moduleProjectUri = Read-Host "Enter the project URI (optional)"
    Write-Host "Module details set successfully." -ForegroundColor Green
}

function Add-PublicFunction {
    $functionName = Read-Host "Enter the name of the public function"
    $script:publicFunctions += $functionName
    Write-Host "Public function '$functionName' added." -ForegroundColor Green
}

function Add-PrivateFunction {
    $functionName = Read-Host "Enter the name of the private function"
    $script:privateFunctions += $functionName
    Write-Host "Private function '$functionName' added." -ForegroundColor Green
}

function Add-ModuleDependencies {
    do {
        $dependencyName = Read-Host "Enter the name of the module dependency (or press Enter to finish)"
        if ($dependencyName -ne "") {
            $dependencyVersion = Read-Host "Enter the required version (leave blank for any version)"
            $script:moduleDependencies += [PSCustomObject]@{
                Name = $dependencyName
                Version = $dependencyVersion
            }
            Write-Host "Dependency added: $dependencyName $dependencyVersion" -ForegroundColor Green
        }
    } while ($dependencyName -ne "")
}

function Set-BuildSettings {
    $script:usePSScriptAnalyzer = (Read-Host "Use PSScriptAnalyzer for code analysis? (Y/N)") -eq 'Y'
    $script:useplatyPS = (Read-Host "Use platyPS for documentation generation? (Y/N)") -eq 'Y'
    Write-Host "Build settings configured." -ForegroundColor Green
}

function Set-TestSettings {
    $script:usePester = (Read-Host "Use Pester for testing? (Y/N)") -eq 'Y'
    $script:useCodeCoverage = (Read-Host "Include code coverage in tests? (Y/N)") -eq 'Y'
    Write-Host "Test settings configured." -ForegroundColor Green
}

function Generate-ModuleFiles {
    if ([string]::IsNullOrWhiteSpace($script:moduleName)) {
        Write-Host "Please set module details first." -ForegroundColor Yellow
        return
    }

    $modulePath = Join-Path -Path $PWD -ChildPath $script:moduleName
    New-Item -Path $modulePath -ItemType Directory -Force | Out-Null

    # Create module manifest
    $manifestPath = Join-Path -Path $modulePath -ChildPath "$($script:moduleName).psd1"
    $manifestParams = @{
        Path = $manifestPath
        RootModule = "$($script:moduleName).psm1"
        ModuleVersion = $script:moduleVersion
        Author = $script:moduleAuthor
        Description = $script:moduleDescription
        FunctionsToExport = $script:publicFunctions
        PowerShellVersion = '5.1'
    }
    if ($script:moduleLicense) { $manifestParams['LicenseUri'] = $script:moduleLicense }
    if ($script:moduleProjectUri) { $manifestParams['ProjectUri'] = $script:moduleProjectUri }
    if ($script:moduleDependencies) {
        $manifestParams['RequiredModules'] = $script:moduleDependencies | ForEach-Object {
            if ($_.Version) { @{ModuleName = $_.Name; ModuleVersion = $_.Version} } else { $_.Name }
        }
    }
    New-ModuleManifest @manifestParams

    # Create module script file
    $modulePSM1Path = Join-Path -Path $modulePath -ChildPath "$($script:moduleName).psm1"
    $modulePSM1Content = @"
# Dot source public/private functions
`$public = @(Get-ChildItem -Path `$PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue)
`$private = @(Get-ChildItem -Path `$PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue)

foreach (`$import in @(`$public + `$private)) {
    try {
        . `$import.FullName
    } catch {
        Write-Error -Message "Failed to import function `$(`$import.FullName): `$_"
    }
}

# Export public functions
Export-ModuleMember -Function `$public.BaseName
"@
    Set-Content -Path $modulePSM1Path -Value $modulePSM1Content

    # Create Public and Private folders
    New-Item -Path (Join-Path -Path $modulePath -ChildPath "Public") -ItemType Directory -Force | Out-Null
    New-Item -Path (Join-Path -Path $modulePath -ChildPath "Private") -ItemType Directory -Force | Out-Null

    # Create function files
    foreach ($function in $script:publicFunctions) {
        $functionPath = Join-Path -Path $modulePath -ChildPath "Public\$function.ps1"
        $functionContent = @"
function $function {
    <#
    .SYNOPSIS
    Brief description of $function

    .DESCRIPTION
    Detailed description of $function

    .PARAMETER Param1
    Description of Param1

    .EXAMPLE
    $function -Param1 Value
    Explanation of the example

    .NOTES
    Additional notes about the function
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=`$true)]
        [string]`$Param1
    )

    begin {
        # Initialize any prerequisites
    }

    process {
        # Main function logic
    }

    end {
        # Cleanup or final actions
    }
}
"@
        Set-Content -Path $functionPath -Value $functionContent
    }

    foreach ($function in $script:privateFunctions) {
        $functionPath = Join-Path -Path $modulePath -ChildPath "Private\$function.ps1"
        $functionContent = @"
function $function {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=`$true)]
        [string]`$Param1
    )

    # Function logic here
}
"@
        Set-Content -Path $functionPath -Value $functionContent
    }

    # Create tests folder and files
    $testsFolderPath = Join-Path -Path $modulePath -ChildPath "Tests"
    New-Item -Path $testsFolderPath -ItemType Directory -Force | Out-Null

    if ($script:usePester) {
        $testFilePath = Join-Path -Path $testsFolderPath -ChildPath "$($script:moduleName).Tests.ps1"
        $testFileContent = @"
`$ModuleName = '$($script:moduleName)'
`$ModuleManifestPath = "`$PSScriptRoot\..\`$ModuleName.psd1"

Describe "`$ModuleName Module Tests" {
    BeforeAll {
        `$Module = Import-Module `$ModuleManifestPath -PassThru -Force
    }

    It "Module manifest is valid" {
        Test-ModuleManifest -Path `$ModuleManifestPath | Should -Not -BeNullOrEmpty
        `$? | Should -Be `$true
    }

    It "Module can be imported" {
        `$Module | Should -Not -BeNullOrEmpty
    }

    Context "Function Tests" {
        # Add tests for each function
        $(foreach ($function in $script:publicFunctions) {
@"
        It "$function exists" {
            Get-Command $function -Module `$ModuleName | Should -Not -BeNullOrEmpty
        }

"@
        })
    }
}
"@
        Set-Content -Path $testFilePath -Value $testFileContent
    }

    # Create build configuration
    $buildFolderPath = Join-Path -Path $modulePath -ChildPath "Build"
    New-Item -Path $buildFolderPath -ItemType Directory -Force | Out-Null

    $buildFilePath = Join-Path -Path $buildFolderPath -ChildPath "build.ps1"
    $buildFileContent = @"
param (
    [switch]`$Test,
    [switch]`$Analyze,
    [switch]`$Documentation
)

`$ModuleName = '$($script:moduleName)'
`$ModuleRoot = "`$PSScriptRoot\.."
`$OutputPath = "`$ModuleRoot\Output"

# Clean and create output directory
if (Test-Path -Path `$OutputPath) {
    Remove-Item -Path `$OutputPath -Recurse -Force
}
New-Item -Path `$OutputPath -ItemType Directory -Force | Out-Null

# Copy module files to output directory
Copy-Item -Path "`$ModuleRoot\`$ModuleName.psd1" -Destination `$OutputPath
Copy-Item -Path "`$ModuleRoot\`$ModuleName.psm1" -Destination `$OutputPath
Copy-Item -Path "`$ModuleRoot\Public" -Destination `$OutputPath -Recurse
Copy-Item -Path "`$ModuleRoot\Private" -Destination `$OutputPath -Recurse

if (`$Test) {
    # Run Pester tests
    `$testResults = Invoke-Pester -Path "`$ModuleRoot\Tests" -PassThru
    if (`$testResults.FailedCount -gt 0) {
        throw "One or more tests failed."
    }
}

if (`$Analyze) {
    # Run PSScriptAnalyzer
    `$analysisResults = Invoke-ScriptAnalyzer -Path `$OutputPath -Recurse
    if (`$analysisResults) {
        `$analysisResults | Format-Table
        throw "PSScriptAnalyzer found one or more issues."
    }
}

if (`$Documentation) {
    # Generate documentation using platyPS
    Import-Module platyPS
    New-MarkdownHelp -Module `$ModuleName -OutputFolder "`$OutputPath\docs" -Force
}

Write-Host "Build completed successfully." -ForegroundColor Green
"@
    Set-Content -Path $buildFilePath -Value $buildFileContent

    # Create PSScriptAnalyzer settings file
    if ($script:usePSScriptAnalyzer) {
        $analyzerSettingsPath = Join-Path -Path $modulePath -ChildPath "PSScriptAnalyzerSettings.psd1"
        $analyzerSettingsContent = @"
@{
    Severity = @('Error', 'Warning')
    ExcludeRules = @('PSUseShouldProcessForStateChangingFunctions')
    Rules = @{
        PSAvoidUsingCmdletAliases = @{
            Whitelist = @('Where', 'Select')
        }
    }
}
"@
        Set-Content -Path $analyzerSettingsPath -Value $analyzerSettingsContent
    }

    Write-Host "Module files generated successfully at: $modulePath" -ForegroundColor Green
}

# Initialize variables
$script:moduleName = ""
$script:moduleDescription = ""
$script:moduleAuthor = ""
$script:moduleVersion = ""
$script:moduleLicense = ""
$script:moduleProjectUri = ""
$script:publicFunctions = @()
$script:privateFunctions = @()
$script:moduleDependencies = @()
$script:usePSScriptAnalyzer = $false
$script:useplatyPS = $false
$script:usePester = $false
$script:useCodeCoverage = $false

# Main program loop
do {
    Show-Menu
    $choice = Read-Host "`nEnter your choice (1-8)"

    switch ($choice) {
        "1" { Set-ModuleDetails }
        "2" { Add-PublicFunction }
        "3" { Add-PrivateFunction }
        "4" { Add-ModuleDependencies }
        "5" { Set-BuildSettings }
        "6" { Set-TestSettings }
        "7" { Generate-ModuleFiles }
        "8" { Write-Host "Exiting program..." -ForegroundColor Yellow; break }
        default { Write-Host "Invalid choice. Please try again." -ForegroundColor Red }
    }

    if ($choice -ne "8") {
        Read-Host "`nPress Enter to continue..."
    }
} while ($choice -ne "8")

This Advanced Template Module Generator Tool includes:

  1. A menu-driven interface for easy navigation.
  2. Enhanced functions to:
    • Set detailed module information (including license and project URI)
    • Add public and private functions
    • Specify module dependencies
    • Configure build and test settings

Key features:

  1. Advanced Module Manifest Generation:
    • Includes more detailed module information
    • Supports specifying module dependencies
  2. Comprehensive Module Structure:
    • Creates a more robust folder structure including build and test directories
  3. Enhanced Function File Generation:
    • Generates more detailed function templates
  4. Build Script Generation:
    • Creates a build.ps1 script for module building, testing, and documentation generation
  5. Test Configuration:
    • Sets up Pester tests with placeholders for each public function
    • Optionally includes code coverage settings
  6. PSScriptAnalyzer Integration:
    • Generates a PSScriptAnalyzer settings file
    • Includes PSScriptAnalyzer in the build process
  7. Documentation Support:
    • Optionally sets up platyPS for documentation generation
  8. Dependency Management:
    • Allows specifying module dependencies with version requirements

This tool is particularly useful for:

  • Experienced PowerShell module developers
  • Teams working on larger PowerShell projects
  • Developers who want to follow best practices in module development

To use this script effectively:

  1. Run the script in PowerShell
  2. Use the menu options to:
    • Set detailed module information
    • Add public and private functions
    • Specify dependencies and configure build/test settings
    • Generate the module files
  3. Review the generated module structure and customize as needed

This advanced script provides a more comprehensive starting point for PowerShell module development, incorporating best practices and tools commonly used in professional PowerShell module projects.

Template Module Generator Tool

<#
.SYNOPSIS
Template Module Generator Tool

.DESCRIPTION
This script provides an interactive tool to generate a basic structure for a PowerShell module,
including the module manifest, public and private function files, and a basic test file.

.NOTES
File Name      : TemplateModuleGenerator.ps1
Author         : [Your Name]
Prerequisite   : PowerShell V5.1 or later
Version        : 1.0
Date           : [Current Date]

.EXAMPLE
.\TemplateModuleGenerator.ps1
#>

function Show-Menu {
    Clear-Host
    Write-Host "=== Template Module Generator Tool ===" -ForegroundColor Cyan
    Write-Host "1. Set Module Details"
    Write-Host "2. Add Public Function"
    Write-Host "3. Add Private Function"
    Write-Host "4. Generate Module Files"
    Write-Host "5. Exit"
}

function Set-ModuleDetails {
    $script:moduleName = Read-Host "Enter the name of the module"
    $script:moduleDescription = Read-Host "Enter a brief description of the module"
    $script:moduleAuthor = Read-Host "Enter the author's name"
    $script:moduleVersion = Read-Host "Enter the module version (e.g., 1.0.0)"
    Write-Host "Module details set successfully." -ForegroundColor Green
}

function Add-PublicFunction {
    $functionName = Read-Host "Enter the name of the public function"
    $script:publicFunctions += $functionName
    Write-Host "Public function '$functionName' added." -ForegroundColor Green
}

function Add-PrivateFunction {
    $functionName = Read-Host "Enter the name of the private function"
    $script:privateFunctions += $functionName
    Write-Host "Private function '$functionName' added." -ForegroundColor Green
}

function Generate-ModuleFiles {
    if ([string]::IsNullOrWhiteSpace($script:moduleName)) {
        Write-Host "Please set module details first." -ForegroundColor Yellow
        return
    }

    $modulePath = Join-Path -Path $PWD -ChildPath $script:moduleName
    New-Item -Path $modulePath -ItemType Directory -Force | Out-Null

    # Create module manifest
    $manifestPath = Join-Path -Path $modulePath -ChildPath "$($script:moduleName).psd1"
    New-ModuleManifest -Path $manifestPath `
        -RootModule "$($script:moduleName).psm1" `
        -ModuleVersion $script:moduleVersion `
        -Author $script:moduleAuthor `
        -Description $script:moduleDescription `
        -FunctionsToExport $script:publicFunctions

    # Create module script file
    $modulePSM1Path = Join-Path -Path $modulePath -ChildPath "$($script:moduleName).psm1"
    $modulePSM1Content = @"
# Dot source public/private functions
`$public = @(Get-ChildItem -Path `$PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue)
`$private = @(Get-ChildItem -Path `$PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue)

foreach (`$import in @(`$public + `$private)) {
    try {
        . `$import.FullName
    } catch {
        Write-Error -Message "Failed to import function `$(`$import.FullName): `$_"
    }
}

# Export public functions
Export-ModuleMember -Function `$public.BaseName
"@
    Set-Content -Path $modulePSM1Path -Value $modulePSM1Content

    # Create Public and Private folders
    New-Item -Path (Join-Path -Path $modulePath -ChildPath "Public") -ItemType Directory -Force | Out-Null
    New-Item -Path (Join-Path -Path $modulePath -ChildPath "Private") -ItemType Directory -Force | Out-Null

    # Create function files
    foreach ($function in $script:publicFunctions) {
        $functionPath = Join-Path -Path $modulePath -ChildPath "Public\$function.ps1"
        $functionContent = @"
function $function {
    <#
    .SYNOPSIS
    Brief description of $function

    .DESCRIPTION
    Detailed description of $function

    .PARAMETER Param1
    Description of Param1

    .EXAMPLE
    $function -Param1 Value
    Explanation of the example

    .NOTES
    Additional notes about the function
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=`$true)]
        [string]`$Param1
    )

    begin {
        # Initialize any prerequisites
    }

    process {
        # Main function logic
    }

    end {
        # Cleanup or final actions
    }
}
"@
        Set-Content -Path $functionPath -Value $functionContent
    }

    foreach ($function in $script:privateFunctions) {
        $functionPath = Join-Path -Path $modulePath -ChildPath "Private\$function.ps1"
        $functionContent = @"
function $function {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=`$true)]
        [string]`$Param1
    )

    # Function logic here
}
"@
        Set-Content -Path $functionPath -Value $functionContent
    }

    # Create a basic test file
    $testsFolderPath = Join-Path -Path $modulePath -ChildPath "Tests"
    New-Item -Path $testsFolderPath -ItemType Directory -Force | Out-Null
    $testFilePath = Join-Path -Path $testsFolderPath -ChildPath "$($script:moduleName).Tests.ps1"
    $testFileContent = @"
`$ModuleName = '$($script:moduleName)'
`$ModuleManifestPath = "`$PSScriptRoot\..\`$ModuleName.psd1"

Describe "`$ModuleName Module Tests" {
    It "Module manifest is valid" {
        Test-ModuleManifest -Path `$ModuleManifestPath | Should -Not -BeNullOrEmpty
        `$? | Should -Be `$true
    }

    It "Module can be imported" {
        Import-Module `$ModuleManifestPath
        Get-Module `$ModuleName | Should -Not -BeNullOrEmpty
    }

    # Add more tests for your functions here
}
"@
    Set-Content -Path $testFilePath -Value $testFileContent

    Write-Host "Module files generated successfully at: $modulePath" -ForegroundColor Green
}

# Initialize variables
$script:moduleName = ""
$script:moduleDescription = ""
$script:moduleAuthor = ""
$script:moduleVersion = ""
$script:publicFunctions = @()
$script:privateFunctions = @()

# Main program loop
do {
    Show-Menu
    $choice = Read-Host "`nEnter your choice (1-5)"

    switch ($choice) {
        "1" { Set-ModuleDetails }
        "2" { Add-PublicFunction }
        "3" { Add-PrivateFunction }
        "4" { Generate-ModuleFiles }
        "5" { Write-Host "Exiting program..." -ForegroundColor Yellow; break }
        default { Write-Host "Invalid choice. Please try again." -ForegroundColor Red }
    }

    if ($choice -ne "5") {
        Read-Host "`nPress Enter to continue..."
    }
} while ($choice -ne "5")

This Template Module Generator Tool includes:

  1. A menu-driven interface for easy navigation.
  2. Functions to:
    • Set module details (name, description, author, version)
    • Add public functions
    • Add private functions
    • Generate module files

Key features:

  1. Module Manifest Generation:
    • Creates a .psd1 file with basic module information
  2. Module Script File (.psm1) Generation:
    • Creates a .psm1 file that dot-sources public and private functions
  3. Function File Generation:
    • Creates separate .ps1 files for each public and private function
    • Generates a basic function template with comment-based help for public functions
  4. Directory Structure Creation:
    • Creates a module folder with Public and Private subfolders
  5. Basic Test File Generation:
    • Creates a simple Pester test file for the module

This tool is particularly useful for:

  • PowerShell module developers starting new projects
  • Anyone learning to create PowerShell modules
  • Developers who want to quickly scaffold a new module structure

To use this script effectively:

  1. Run the script in PowerShell
  2. Use the menu options to:
    • Set the module details
    • Add public and private functions as needed
    • Generate the module files
  3. Review the generated module structure in the specified directory

This script provides a quick and easy way to create a basic PowerShell module structure, following best practices for module organization. It saves time in setting up the initial files and folders, allowing developers to focus on writing the actual module functionality.