Configuration.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 |
# Allows you to override the Scope storage paths (e.g. for testing) param( $Converters = @{}, $EnterpriseData = $Env:AppData, $UserData = $Env:LocalAppData, $MachineData = $Env:ProgramData ) $EnterpriseData = Join-Path $EnterpriseData WindowsPowerShell $UserData = Join-Path $UserData WindowsPowerShell $MachineData = Join-Path $MachineData WindowsPowerShell $ConfigurationRoot = Get-Variable PSScriptRoot* -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq "PSScriptRoot" } | ForEach-Object { $_.Value } if(!$ConfigurationRoot) { $ConfigurationRoot = Split-Path $MyInvocation.MyCommand.Path -Parent } Import-Module "${ConfigurationRoot}\Metadata.psm1" -Args @($Converters) function Get-StoragePath { #.Synopsis # Gets an application storage path outside the module storage folder #.Description # Gets an AppData (or roaming profile) or ProgramData path for settings storage # # As a general rule, there are three scopes which result in three different root folders # User: $Env:LocalAppData # Machine: $Env:ProgramData # Enterprise: $Env:AppData (which is the "roaming" folder of AppData) # # WARNINGs: # 1. This command is only meant to be used in modules, to find a place where they can serialize data for storage. It can be used in scripts, but doing so is more risky. # 2. Since there are multiple module paths, it's possible for more than one module to exist with the same name, so you should exercise care # # If it doesn't already exist, the folder is created before the path is returned, so you can always trust this folder to exist. # The folder that is returned survives module uninstall/reinstall/upgrade, and this is the lowest level API for the Configuration module, expecting the module author to export data there using other Import/Export cmdlets. #.Example # $CacheFile = Join-Path (Get-StoragePath) Data.clixml # $Data | Export-CliXML -Path $CacheFile # # This example shows how to use Get-StoragePath with Export-CliXML to cache some data from inside a module. # [CmdletBinding(DefaultParameterSetName = 'NoParameters')] param( # The scope to save at, defaults to Enterprise (which returns a path in "RoamingData") [Security.PolicyLevelType]$Scope = "Enterprise", # A callstack. You should not ever need to pass this. # It is used to calculate the defaults for all the other parameters. [Parameter(ParameterSetName = "__CallStack")] [System.Management.Automation.CallStackFrame[]]$CallStack = $(Get-PSCallStack), # An optional module qualifier (by default, this is blank) [Parameter(ParameterSetName = "ManualOverride")] [String]$CompanyName = $( if($CallStack[0].InvocationInfo.MyCommand.Module){ $Name = $CallStack[0].InvocationInfo.MyCommand.Module.CompanyName -replace "[$([Regex]::Escape(-join[IO.Path]::GetInvalidFileNameChars()))]","_" if($Name -eq "Unknown" -or -not $Name) { $Name = $CallStack[0].InvocationInfo.MyCommand.Module.Author if($Name -eq "Unknown" -or -not $Name) { $Name = "AnonymousModules" } } $Name } else { "AnonymousScripts" } ), # The name of the module or script # Will be used in the returned storage path [Parameter(ParameterSetName = "ManualOverride")] [String]$Name = $( if($Module = $CallStack[0].InvocationInfo.MyCommand.Module) { $Module.Name } else { if(!($BaseName = [IO.Path]::GetFileNameWithoutExtension(($CallStack[0].InvocationInfo.MyCommand.Name -replace "[$([Regex]::Escape(-join[IO.Path]::GetInvalidFileNameChars()))]","_")))) { throw "Could not determine the storage name, Get-StoragePath should only be called from inside a script or module." } return $BaseName } ), # The version for saved settings -- if set, will be used in the returned path # NOTE: this is *NOT* calculated from the CallStack [Version]$Version ) begin { $PathRoot = $(switch ($Scope) { "Enterprise" { $EnterpriseData } "User" { $UserData } "Machine" { $MachineData } # This should be "Process" scope, but what does that mean? # "AppDomain" { $MachineData } default { $EnterpriseData } }) } end { $PathRoot = Join-Path $PathRoot $Type if($CompanyName -and $CompanyName -ne "Unknown") { $PathRoot = Join-Path $PathRoot $CompanyName } $PathRoot = Join-Path $PathRoot $Name if($Version) { $PathRoot = Join-Path $PathRoot $Version } # Note: avoid using Convert-Path because drives aliases like "TestData:" get converted to a C:\ file system location $null = mkdir $PathRoot -Force (Resolve-Path $PathRoot).Path } } function Import-Configuration { [CmdletBinding(DefaultParameterSetName = '__CallStack')] param( # A callstack. You should not ever need to pass this. # It is used to calculate the defaults for all the other parameters. [Parameter(ParameterSetName = "__CallStack")] [System.Management.Automation.CallStackFrame[]]$CallStack = $(Get-PSCallStack), # An optional module qualifier (by default, this is blank) [Parameter(ParameterSetName = "ManualOverride")] [Alias("Author")] [String]$CompanyName = $( if($CallStack[0].InvocationInfo.MyCommand.Module){ $Name = $CallStack[0].InvocationInfo.MyCommand.Module.CompanyName -replace "[$([Regex]::Escape(-join[IO.Path]::GetInvalidFileNameChars()))]","_" if($Name -eq "Unknown" -or -not $Name) { $Name = $CallStack[0].InvocationInfo.MyCommand.Module.Author if($Name -eq "Unknown" -or -not $Name) { $Name = "AnonymousModules" } } $Name } else { "AnonymousScripts" } ), # The name of the module or script # Will be used in the returned storage path [Parameter(ParameterSetName = "ManualOverride", Mandatory=$true)] [String]$Name = $( if($Module = $CallStack[0].InvocationInfo.MyCommand.Module) { $Module.Name } else { if(!($BaseName = [IO.Path]::GetFileNameWithoutExtension(($CallStack[0].InvocationInfo.MyCommand.Name -replace "[$([Regex]::Escape(-join[IO.Path]::GetInvalidFileNameChars()))]","_")))) { throw "Could not determine the storage name, Get-StoragePath should only be called from inside a script or module." } return $BaseName } ), # The full path to the module (in case there are dupes) # Will be used in the returned storage path [Parameter(ParameterSetName = "ManualOverride")] [String]$ModulePath = $( if($Module = $CallStack[0].InvocationInfo.MyCommand.Module) { $Module.Path } else { if(!($BaseName = [IO.Path]::GetFileNameWithoutExtension(($CallStack[0].InvocationInfo.MyCommand.Name -replace "[$([Regex]::Escape(-join[IO.Path]::GetInvalidFileNameChars()))]","_")))) { throw "Could not determine the storage name, Get-StoragePath should only be called from inside a script or module." } return $BaseName } ), # The version for saved settings -- if set, will be used in the returned path # NOTE: this is *NOT* calculated from the CallStack [Version]$Version ) Write-Verbose "PSBoundParameters $($PSBoundParameters | Out-String)" $ModulePath = Split-Path $ModulePath -Parent $ModulePath = Join-Path $ModulePath Configuration.psd1 $Module = if(Test-Path $ModulePath) { Import-Metadata $ModulePath -ErrorAction Ignore } else { @{} } Write-Verbose "Module ($ModulePath)`n$($Module | Out-String)" $Parameters = @{ CompanyName = $CompanyName Name = $Name } if($Version) { $Parameters.Version = $Version } $MachinePath = Get-StoragePath @Parameters -Scope Machine $MachinePath = Join-Path $MachinePath Configuration.psd1 $Machine = if(Test-Path $MachinePath) { Import-Metadata $MachinePath -ErrorAction Ignore } else { @{} } Write-Verbose "Machine ($MachinePath)`n$($Machine | Out-String)" $EnterprisePath = Get-StoragePath @Parameters -Scope Enterprise $EnterprisePath = Join-Path $EnterprisePath Configuration.psd1 $Enterprise = if(Test-Path $EnterprisePath) { Import-Metadata $EnterprisePath -ErrorAction Ignore } else { @{} } Write-Verbose "Enterprise ($EnterprisePath)`n$($Enterprise | Out-String)" $LocalUserPath = Get-StoragePath @Parameters -Scope User $LocalUserPath = Join-Path $LocalUserPath Configuration.psd1 $LocalUser = if(Test-Path $LocalUserPath) { Import-Metadata $LocalUserPath -ErrorAction Ignore } else { @{} } Write-Verbose "LocalUser ($LocalUserPath)`n$($LocalUser | Out-String)" $Module | Update-Object $Machine | Update-Object $Enterprise | Update-Object $LocalUser } |