Dynamically call Export-ModuleMember - powershell

Can you tell me why the following code does not export any functions:
# load all functions
$moduleRoot = Split-Path -Path $MyInvocation.MyCommand.Path
$functionFiles = "$moduleRoot\functions\*.ps1" | Resolve-Path
$functionFiles | ForEach-Object { . $_.ProviderPath }
$functionFiles | ForEach-Object { [io.path]::GetFileNameWithoutExtension($_) } | Export-ModuleMember
Or the difference between this:
Not working:
$test = "Cmd1", "Cmd2"
Export-ModuleMember $test
Working:
Export-ModuleMember "Cmd1", "Cmd2"
UPDATE:
When calling "import-module MyCrazyModule", I can see the exported commands (get-module).
However, when calling "get-module -ListAvailable", the exported commands property is empty. (the module is in the PSModulePath)
UPDATE 2: Evidence
$PSModulePath contains D:\powershell
Module: "D:\powershell\MyCrazyModule\MyCrazyModule.psm1"
$test = "Cmd1", "Cmd2"
Export-ModuleMember $test
No exports visible:
However with content
Export-ModuleMember "Cmd1", "Cmd2"
Thank you

Related

PowerShell logging function is not writing data

I am trying to create a reusable log function for a project I am working on in PowerShell. I am a novice with PowerShell so I am having problems figuring out why my function is not producing expected results. I am attempting to create a function that send NTFS/ACL details to a log file. This function will be incorporated in to a larger script that will change some NTFS/ACL/ACE folder rights. I have excluded some of the other code for simplification (changing rights).
Below is my current stripped down code. When it runs, it creates the log file but the file is empty. If I move the line of code that creates the log inside the log function, it creates the log file with data but it is not formatted correctly - it writes the heading (object attribute names) on one line, then the data, then then a new line with the heading, then the data. I want it to write the heading, then a line of data, line of data, .... Before I created a function for this, it worked as expected. I am a novice at PowerShell so I may not understand how to pass info in and out of the function. My Code:
#variables
$rootDir = "\\server1\share1"
$logDir = "c:\temp"
$date = (Get-Date -Format "MMddyyyy-HHmm")
$logData =#()
#My Logging Function
Function CreateLog {
#create and object to store attributes
$logObj = New-Object -TypeName psobject -Property #{
FolderPath = $Folder.Fullname
IdentityReference = $ACL.IdentityReference.ToString()
folder = $Folder[0]
FileSystemRights = $ACL.FileSystemRights.ToString()
InheritanceFlags = $ACL.InheritanceFlags.ToString()
}
$Folders=(Get-ChildItem -Path $rootDir -Directory -Recurse)
foreach ($Folder in $Folders){
$ACLs = get-acl $Folder.fullname | ForEach-Object {$_.Access}
Foreach ($ACL in $ACLs){
CreateLog
if($ACL.FileSystemRights -eq "FullControl"){
Write-Host "DO SOMETHING"
}
}
$logData | select folder,IdentityReference,FileSystemRights,InheritanceFlags,FolderPath | Format-Table -Wrap | Out-File $logDir\testlog-$date.log -Append
I assume you're looking for something like this:
#variables
$rootDir = "\\server1\share1"
$logDir = "c:\temp"
$date = Get-Date -Format "MMddyyyy-HHmm"
#My Logging Function
Function CreateLog {
param(
[Parameter(Mandatory)]
[System.Security.AccessControl.FileSystemAccessRule] $AccessRule,
[Parameter(Mandatory)]
[string] $Folder
)
[pscustomobject]#{
FolderPath = $Folder
IdentityReference = $AccessRule.IdentityReference
FileSystemRights = $AccessRule.FileSystemRights
InheritanceFlags = $AccessRule.InheritanceFlags
}
}
Get-ChildItem -Path $rootDir -Directory -Recurse | ForEach-Object {
foreach($rule in ($_ | Get-Acl).Access) {
CreateLog -AccessRule $rule -Folder $_.FullName
if($rule.FileSystemRights.HasFlag([Security.AccessControl.FileSystemRights]::FullControl)) {
# Do something here
}
}
} | Export-Csv $logDir\testlog-$date.log -NoTypeInformation
Your current code has a few syntax errors (missing }), your function seem to be assigning the objects to $logObj but then $logObj is never outputted from it, hence producing no output.
Your $logData array is defined but never used, leaving aside is not needed at all in this case. Your function should, ideally, take the arguments for the Access Rules and Path, see Functions with Parameters to learn more.
Format-Table as well as the other Format-* cmdlets should be primarily used for console display, should not be used for exporting data. In this case you should use Export-Csv, this way your data preserves its structure and can be imported back at ease, be filtered and be sorted.

Pass Variables to Powershell Pester Tests v5

I am at the last stages with my pester tests, but am having issues with the variable scoping.
I have quite a few tests and would like use something like Global scoping in order to declare variables such as $ModuleName, $ProjectRoot, and other similar things that are needed in each test.
I have tried using New-Variable -Scope Global and $Global:ModuleName but these don't seem to be visible in the Pester Tests.
I am calling Invoke-Pester from a build.ps1 file which has these declared.
Has anyone seen any good ways to use centrally defined variables, without using $env: variables?
I have found using $Env: variables to be the best approach. Thanks to the BuildHelpers module for showing me this.
This is now what the beginning of my Pester Test looks like:
Describe "Core Module Validation" {
BeforeAll {
$Script:moduleName = $ENV:BHProjectName
$Script:modulePath = $ENV:BHModulePath
Remove-Module -Name $moduleName -Force -ErrorAction SilentlyContinue
$Script:manifestTestResult = Test-ModuleManifest -Path $Env:BHPSModuleManifest
$Script:publicScriptFiles = Get-ChildItem -Path "$modulePath\Public" -Recurse
}
It "should cleanly import Module '$ENV:BHProjectName'" {
{ Import-Module -Name $modulePath -Force } | Should -Not -Throw
}
It "should export one or more functions" {
Import-Module -Name $modulePath -Force
Get-Command -Module $moduleName | Measure-Object | Select-Object -ExpandProperty Count | Should -BeGreaterThan 0
}
It "should have a valid PSD1 Module Manifest" {
{ Test-ModuleManifest -Path $ENV:BHPSModuleManifest } | Should -Not -Throw
}
These $Env: variables are set in my build.ps1 file, and are only temporary for the session.

Set multiple variables in Powershell module and return it to main program

I have written a Powershell module which has some functions and I am setting multiple variables inside the function defined as below:
#
# ConfigurationHelper.psm1
#
# Global Variables
$PackageLocation = ""
$LogFilePath = ""
$LogFileName = ""
$DestinationLocation = ""
$ExcludedBinariesFiles = ""
$ExcludedBinariesFolders = ""
$IncludeTransformsFiles = ""
# end global variables
# Function to read all the config settings
function Get-ConfigSettings {
Write-Host "Get-ConfigSetting function is called"
#logging configuration
[xml] $logConfigFile = Get-Content -Path (Join-Path ((Get-Item $PSScriptRoot).Parent.FullName) "\config\GlobalConfiguration.xml")
$LogFilePath = $logConfigFile.SelectSingleNode("/configuration/LogsPath").InnerText;
$LogFileName = $logConfigFile.SelectSingleNode("/configuration/LogsFileName").InnerText;
$PackageLocation = (Get-Item $PSScriptRoot).Parent.FullName
# BinariesConfiguration
[xml] $BinariesConfig = Get-Content -Path (Join-Path ((Get-Item $PSScriptRoot).Parent.FullName) "\config\Oakton_Environments.xml")
$environemntNodes = $BinariesConfig.SelectNodes("//environment[#Server=$env:computername]")
if ($environemntNodes -ne 1) {
throw "Server configuration missing or more than one environment configuration was found for server"
}
}
Export-ModuleMember -Function * -Variable $LogFilePath
In my main.ps1
Get-ConfigSettings
$LogFilePath #this variable is empty string
The variable is empty string even after the function is executed which sets the variable. I have done export member at the end of the module script. How can I return variables defined in the module?
I want to return multiple variables set at the top of the configurationHelper.psm1.
Read and follow Export-ModuleMember docs:
-Variable <String[]>
Specifies the variables that are exported from the script module file.
Enter the **variable names**, **without a dollar sign**.
Wildcard characters are permitted.
Use
Export-ModuleMember -Function * -Variable LogFilePath

Execute All Functions In A Script

If I have a script with some functions such as:
function FunctionOne{}
function FunctionTwo{}
How can I call them all in one line, in the same script, without having to specify the name of each function?
I'd like to do something like:
Call-AllFunctionsInCurrentScriptConsecutively #calls FunctionOne then FunctionTwo
This blog post suggests using ParseFile() for parsing the script:
$filename = 'C:\path\to\your.ps1'
[ref]$tokens = $null
[ref]$parseErrors = $null
$ast = [Management.Automation.Language.Parser]::ParseFile($filename, $tokens, $parseErrors)
Then you can invoke the functions (after you dot-sourced the script) via the call operator:
$ast.EndBlock.Statements | Where-Object { $_.Name } | ForEach-Object { & $_.Name }
For using this from within a script replace the filename with $MyInvocation.MyCommand.Path.
Parsing the file and grabbing the function names from the AST is probably the most reliable option.
A more low-tech approach would be to simply diff the function list before and after sourcing the script:
$InitialFunctions = Get-ChildItem function: -Name
. C:\path\to\script.ps1
Get-ChildItem function: -Name |Where-Object {$InitialFunctions -notcontains $_} |ForEach-Object {
& $_
}

Is it possible to create a user defined PowerShell environment?

I would like to clear a PowerShell session of mostly all alias definitions, except for common aliases like cd, sort, mkdir, ...
After I finished my session, I would like to restore all previous known the aliases.
There is no need to unload modules or to unregister CmdLets. I just want to clear the alias namespace for my session.
I could specify the allowed aliases in a list like this:
$AllowedAliases = #(
"cd", "mkdir", "rm", "rmdir",
"cd", "mkdir", "rm", "rmdir",
"where", "select",
"sort"
)
How can I save the aliases and restore them?
or
How can I start a clean PoSh and load only basic aliases?
What I have tested so far:
The following lines are from my example module called poc.psm1.
$Aliases = #()
function Register-PoC
{ foreach ($a in (Get-Item Alias:))
{ $script:Aliases += $a
Write-Host "$($a.Name) => $($a.ReferencedCommand) ($($a.Visibility))"
Remove-Item "Alias:$($a.Name)" -Force
}
}
function Unregister-PoC
{ foreach ($a in $script:Aliases)
{ Write-Host "$($a.Name) <= $($a.ReferencedCommand)"
if (Test-Path "Alias:$($a.Name)")
{ Write-Host "$($a.Name) exists." }
else
{ Set-Alias -Name $a.Name -Value $a.ReferencedCommand -Scope $a.Visibility }
}
if (Test-Path Alias:quit) { Remove-Item Alias:quit }
Remove-Module PoC
}
Export-ModuleMember -Function 'Register-PoC'
Export-ModuleMember -Function 'Unregister-PoC'
Register-PoC
Set-Alias -Name quit -Value Unregister-PoC -Description "Unload this module." -Scope Global
Usage example:
Import-Module .\poc.psm1
dir Alias:
quit
dir Alias:
Unfortunately, dir Alias: is not empty after invoking my script...
Another thing is, that I should preserve some settings of these aliases, because manual test showed, that dir does not behave like dir in before:
Remove-Item dir
Set-Alias dir Get-Item
dir
Cmdlet Get-Item an der Befehlspipelineposition 1
Geben Sie Werte für die folgenden Parameter an:
Path[0]:
So dir seams to append a default path to Get-Item if non is set to the alias.
Aliases are scoped. When you remove all aliases within a function, the aliases in the global scope aren't affected. Here is code that worked for me (simplifies your code a bit, although I didn't touch unregister-PoC, which could also be simplified I think):
function Register-PoC {
$script:aliases = get-item alias:
remove-item alias:* -force
}
function Unregister-PoC
{ foreach ($a in $script:Aliases)
{ Write-Host "$($a.Name) <= $($a.ReferencedCommand)"
if (Test-Path "Alias:$($a.Name)")
{ Write-Host "$($a.Name) exists." }
else
{ Set-Alias -Name $a.Name -Value $a.ReferencedCommand -Scope $a.Visibility }
}
if (Test-Path Alias:quit) { Remove-Item Alias:quit }
Remove-Module PoC
}
. Register-PoC
Set-Alias -Name quit -Value Unregister-PoC -Description "Unload this module." -Scope Global
Note the dot operator on Register-PoC. You will need to dot source quit to restore the aliases to global scope.
BTW, rather than the foreach loop in Unregister-PoC you could use copy-item.
For your situation, I would recommend using PowerShell profiles. These can be defined per user, per machine, and other situations. You can automatically run functions stored in the profile just by calling the function after it's defined in the profile.
For just your current user on the current machine, see Example 3 here.
New-Item -Path $PROFILE -ItemType File -Force
For other profile options, check out Understanding the Six PowerShell Profiles.
To ignore a profile, you can do that by directly running powershell.exe -NoProfile -NoExit but beware of nested sessions when doing that in another PowerShell session.
For a way to wipe all aliases except your desired list, you can export the aliases and re-import them after wiping all aliases. This can be added to the profile, if desired. Otherwise assign it to a function in the profile and call as needed. Change path if not using the profile folder(or wherever you like to keep things):
$allowedaliases = "cd","dir","rm","rmdir","where","select","sort"
Export-Alias -Path "$((Get-ChildItem $PROFILE).DirectoryName)\aliases.csv" -Name $allowedaliases
Remove-Item Alias:* -Force
Import-Alias -Path "$((Get-ChildItem $PROFILE).DirectoryName)\aliases.csv" -Force
Note: One of the original listed aliases(mkdir) is a function, not an alias, at least in Powershell v5.0. Also added dir into the list since that was mentioned by the OP.