By design, for security reasons, ConvertFrom-StringData
does not perform string expansion (interpolation) on its input.
Assuming you trust the input string[1], you can perform the expansion on demand, after having read the values from the file.
Note that use of ConvertFrom-StringData
is problematic, as you've discovered, because the hashtable it creates invariably has unordered keys; that is, the order of the entries does not reflect the order in which the properties are defined in the file.
Therefore, processing the hashtable entries can make the on-demand expansion fail, if an out-of-order entry is processed before another entry whose value it needs for the expansion.
The solution is to roll your own ConvertFrom-StringData
variant[2] that reads the properties into an ordered hashtable.
This additionally allows you to combine the read-from-file and expansion-on-demand tasks:
# Create a sample properties file.
@'
repository.host=hostname.com/nexus
repository.api.url=https://${repository.host}/service/rest/v1
repository.url=https://${repository.host}/repository
'@ > sample.properties
# Parse the file and build an *ordered* hashtable from it.
$orderedHash = [ordered] @{}
switch -Regex -File sample.properties {
'^\s*#|^\s*$' { continue } # skip comment and blank lines.
default {
# Determine the key and value...
$key, $value = $_ -split '=', 2
# ... and create the entry while expanding ${...} references to previous
# entries.
$orderedHash[$key.Trim()] = $ExecutionContext.InvokeCommand.ExpandString((
$value.Trim() -replace '\$\{([^}]+)\}', '$$($$orderedHash[''$1''])'
))
}
}
# Output the result.
$orderedHash
Note the use of method $ExecutionContext.InvokeCommand.ExpandString
to perform on-demand string expansion (interpolation); since this method isn't easy to discover, GitHub issue #11693 proposes that this functionality be surfaced as a proper, easily discoverable cmdlet named something like Expand-String
or Expand-Template
.
- Note: In order to be able to use
$ExecutionContext
from the method of a PS custom class, you must explicitly reference it in the global scope via $global:ExecutionContext
.
For more information about the regex-based -replace
operator, see this answer.
The above yields (note that the input order was maintained):
Name Value
---- -----
repository.host hostname.com/nexus
repository.api.url https://hostname.com/nexus/service/rest/v1
repository.url https://hostname.com/nexus/repository
[1] Via $()
, the subexpression operator, it is possible to embed arbitrary commands in the input strings.
[2] The code below does not replicate all features of ConvertFrom-String
data, but it works with the sample input. While it does support skipping comment lines (those whose first non-whitespace character is a #
) and blank lines, treating \
as escape characters and supporting escape sequences such as \n
for a newline is not implemented.