EditModule.psm1
#Requires -Modules PSLogger Function Edit-Module { <# .SYNOPSIS Opens a specified PowerShell module, for editing, in the ISE .DESCRIPTION This function uses the Get-Module cmdlet to search for and retrieve information about a module (expected / available in $env:PSModulePath ) and then it opens the module from that location into a new tab in ISE for editing. Wildcard characters that resolve to a single module are supported. This function always opens the manifest file to be edited, and prompts/encourages the user/editor to update the ModuleVersion. Additional Module files such as the RootModule / module script (.psm1), and PowerShell Module properties changed in PowerShell v3, and so the behavior of the original Edit-Module function (from Scripting Guy Ed Wilson's 'PowerShellISEModule') also changed. The following updates are intended to enable easy editing of both the Data (Manifest) file as well extending similar ease of use for editing the Module script (.psm1), and other scripts included in a Module. If the Module is installed into a shared file system path (e.g. $env:ProgramFiles), Edit-Module will attempt to open the ISE with elevated permissions, which are necessary to edit a Module in place. If the user/editor cannot gain elevated permissions, then the ISE will open the module file(s) with read-only rights. .EXAMPLE Edit-Module PowerShellISEModule Edit-Module PowerShellISEModule opens the PowerShellISEModule into a new tab in the ISE for editing .EXAMPLE Edit-Module PowerShellISE* Edit-Module PowerShellISE* opens the PowerShellISEModule into a new tab in the ISE for editing by using a wild card character for the module name .PARAMETER NAME The name of the module. Wild cards that resolve to a single module are supported. .NOTES NAME: Edit-Module AUTHOR: originally authored "by ed wilson, msft" Edited by Bryan Dady (@bcdady)to extend PowerShell v3 functionality. Enhancements include Param support, a new FileType parameter, support to edit modules imported into the active session as well as from -ListAvailable. Also adds ability to search for a Module by function name, and opening files in an elevated ISE session if/as necessary. LASTEDIT: 04/29/2015 KEYWORDS: Scripting Techniques, Modules .LINK http://www.ScriptingGuys.com PSLogger #> [cmdletbinding()] Param( [Parameter( Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Specify a module to edit' )] [ValidateScript({$PSItem -in (Get-Module -ListAvailable).Name})] [string] $Name, [Parameter( Mandatory = $false, Position = 1, HelpMessage = 'Specify Script Module (.psm1), or manifest / Script Data file (.psd1) <optional>' )] [ValidateSet('Manifest','ScriptModule','Scripts','All')] [string] $FileType = 'ScriptModule' ) # Get an object for the module, for easier access in later steps $ModuleObj = (Get-Module -ListAvailable -Name $Name) # Test if we got a valid Script Module object; RootModule should point to the .psm1 file if ($ModuleObj.RootModule -ne $null) { Write-Log -Message "Preparing to edit module ($ModuleObj.Name) (from ($ModuleObj.ModuleBase))in PowerShell ISE" -Function EditModule # Now that we've got a valid module object to work with, we can pick the files we want to open in ISE # Get the Module Type - "such as a script file or an assembly. This property is introduced in Windows PowerShell 2.0". # https://msdn.microsoft.com/en-us/library/system.management.automation.psmoduleinfo.moduletype(v=vs.85).aspx if ($ModuleObj.ModuleType -eq 'Script') { # .Path property points to the .psd1 module manifest file :: https://msdn.microsoft.com/en-us/library/dd878324%28v=vs.85%29.aspx if (Test-Path -Path $ModuleObj.Path -PathType Leaf -ErrorAction SilentlyContinue ) { if (($ModuleObj.Path) | Select-String -Pattern $env:ProgramFiles -SimpleMatch) { # Path to module is Program Files, so editing the module file(s) requires elevated privileges [bool]$SharedModule = $true } # This function always opens the manifest to be edited, and prompts/encourages the user/editor to update the ModuleVersion. Write-Debug -Message "Module Manifest Path: ($ModuleObj.Path)" Write-Output -InputObject "`nOpening Module Manifest $($ModuleObj.Path), Version: $($ModuleObj.Version.ToString())`n`n`tPlease update the Version and Help comments to reflect any changes made." Start-Sleep -Seconds 2 } } else { Write-Log -Message "Unexpected ModuleType is $($ModuleObj.ModuleType)" -Function -Function EditModule -Verbose throw "Unexpected ModuleType is $($ModuleObj.ModuleType)" } Write-Log -Message "Loading FileType(s): $FileType for editing" -Function EditModule switch ($FileType) { 'ScriptModule' { Write-Log -Message "Editing Module Root Script: $($ModuleObj.RootModule)" -Function EditModule if ($SharedModule) { Open-AdminISE -File $ModuleObj.Path Open-AdminISE -File $(Join-Path -Path $ModuleObj.ModuleBase -ChildPath $ModuleObj.RootModule) } else { & powershell_ise.exe -File $ModuleObj.Path & powershell_ise.exe -File $(Join-Path -Path $ModuleObj.ModuleBase -ChildPath $ModuleObj.RootModule) } } 'Scripts' { $ModuleObj.NestedModules | ForEach-Object -Process { Write-Log -Message "Editing Nested Module: $PSItem" -Function EditModule # ($ModuleObj.NestedModules | Get-ChildItem).FullName if ($SharedModule) { Open-AdminISE -File ($PSItem | Get-ChildItem).FullName } else { & powershell_ise.exe -File ($PSItem | Get-ChildItem).FullName } } $ModuleObj.scripts | ForEach-Object -Process { Write-Log -Message "Editing script: $PSItem" -Function EditModule if ($SharedModule) { Open-AdminISE -File $PSItem.Path } else { & powershell_ise.exe -File $PSItem.Path } } } 'All' { Write-Log -Message "Editing all module files and scripts for $ModuleObj" -Function EditModule if ($SharedModule) { Open-AdminISE -File $ModuleObj.Path Open-AdminISE -File $(Join-Path -Path $ModuleObj.ModuleBase -ChildPath $ModuleObj.RootModule) $ModuleObj.NestedModules | ForEach-Object -Process { Open-AdminISE -File $PSItem.Path } $ModuleObj.scripts | ForEach-Object -Process { Open-AdminISE -File $PSItem.Path } } else { & powershell_ise.exe -File $ModuleObj.Path & powershell_ise.exe -File $(Join-Path -Path $ModuleObj.ModuleBase -ChildPath $ModuleObj.RootModule) $ModuleObj.NestedModules | ForEach-Object -Process { & powershell_ise.exe -File $PSItem.Path } $ModuleObj.scripts | ForEach-Object -Process { & powershell_ise.exe -File $PSItem.Path } } } default { Write-Log -Message "Editing Module Manifest: $($ModuleObj.Path)" -Function EditModule if ($SharedModule) { Open-AdminISE -File $ModuleObj.Path } else { & powershell_ise.exe -File $ModuleObj.Path } } } # end switch block } else { Write-Log -Message 'Failed to locate path(s) to module files for editing.' -Function EditModule -Verbose } # end if ; no matching module found } #end function Edit-Module function Open-AdminISE { <# .SYNOPSIS Launch a new PowerShell ISE window, with elevated privileges .DESCRIPTION Simplifies opening a PowerShell ISE editor instance, with Administrative permissions, from the console / keyboard, instead of having to grab the mouse to Right-Click and select 'Run as Administrator' #> [cmdletbinding()] Param( [Parameter( Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Specify a module name to edit' )] [ValidateScript({Resolve-Path -Path $PSItem})] [Alias('File','FilePath','Module')] [string] $Path ) Write-Log -Message 'Opening ""$Path""" in ISE, with elevated priveleges.' -Function EditModule -Verbose Start-Process -FilePath "$PSHOME\powershell_ise.exe" -ArgumentList "-File ""$Path""" -Verb RunAs -WindowStyle Normal } New-Alias -Name Open-AdminEditor -Value Open-AdminISE -ErrorAction Ignore function Find-Function { <# .SYNOPSIS Returns Module details, to which a specified function belongs. .DESCRIPTION Uses Get-Module and Select-String to find the RootModule which provides a specified ExportedCommand / Function name. .EXAMPLE PS C:\> Find-Function -SearchPattern 'Edit*' ModuleName : Edit-Module FunctionName : EditModule .NOTES NAME : Find-Function VERSION : 1.0.1 LAST UPDATED: 6/25/2015 AUTHOR : Bryan Dady .INPUTS None .OUTPUTS Write-Log #> Param ( [Parameter(Mandatory = $true, Position = 0)] [String] $SearchPattern, # Use SimpleMatch (non RegEx) behavior in Select-String [Parameter(Mandatory = $false, Position = 1)] [switch] $SimpleMatch = $false ) New-Variable -Name OutputObj -Description 'Object to be returned by this function' -Scope Private Get-Module -ListAvailable | Select-Object -Property Name, ExportedCommands | ForEach-Object -Process { # find and return only Module/function details where the pattern is matched in the name of the function if ($PSItem.ExportedCommands.Keys | Select-String -Pattern $SearchPattern) { # Optimize New-Object invocation, based on Don Jones' recommendation: https://technet.microsoft.com/en-us/magazine/hh750381.aspx $Private:properties = @{ 'ModuleName' = $PSItem.Name 'FunctionName' = $PSItem.ExportedCommands } $Private:RetObject = New-Object -TypeName PSObject -Property $properties return $RetObject } # end if } # end of foreach } # end function Find-Function # Export-ModuleMember -Function Edit-Module, Open-AdminISE, Find-Function, Get-ModuleMembers -Alias Open-AdminEditor |