Skip to content

Instantly share code, notes, and snippets.

@PanosGreg
Created December 2, 2023 14:14
Show Gist options
  • Save PanosGreg/76f85323296d2a453d1f7a31523143a3 to your computer and use it in GitHub Desktop.
Save PanosGreg/76f85323296d2a453d1f7a31523143a3 to your computer and use it in GitHub Desktop.
Microsoft.Extensions.Configuration in PowerShell
## Drilling down into the Microsoft.Extensions.Configuration namespace from a PowerShell point-of-view
## Note: the inspiration for this came up by watching a .net video about configuration from Chris Ayers
# [.NET Configuration In Depth | .NET Conf 2023](https://www.youtube.com/watch?v=aOXaBZFB0-0)
## How to load a .dll which has some dependencies
## you just have to get the package from nuget which will also retrieve all of the dependencies
## We'll be using the Microsoft.Extensions.Configuration namespace
## by default the namespace and its classes, don't exist in the current session
## so we need to load it, but from where ?
## in this case we can simply just create a new CS class project and add the package
## and then compile it. This will give us the .dlls we need to load in PowerShell
## for example
cd D:\Temp
cd (md scratch)
dotnet new classlib
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet publish
Add-Type -Path D:\Temp\scratch\bin\Debug\net7.0\publish\Microsoft.Extensions.Configuration.dll
Add-Type -Path D:\Temp\scratch\bin\Debug\net7.0\publish\Microsoft.Extensions.Configuration.Json.dll
# or whatever target framework (the net7.0) is just an example in my case when I run that command
# instantiate the builder class
$bld = [Microsoft.Extensions.Configuration.ConfigurationBuilder]::new()
# source: https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.configuration.configurationbuilder?view=dotnet-plat-ext-8.0
# create a json file as an example
[pscustomobject]@{Name='aa';Count=10} | ConvertTo-Json | Out-File test.json
# create a file provider class, with the current folder (where our json file is located)
$FileProv = [Microsoft.Extensions.FileProviders.PhysicalFileProvider]::new($PWD.Path)
# instantiate a source
$JsonSrc = [Microsoft.Extensions.Configuration.Json.JsonConfigurationSource]::new()
# configure the json source
$JsonSrc.FileProvider = $FileProv
$JsonSrc.Path = 'test.json'
# we can even tell it to automatically reload the config when the json file changes
$JsonSrc.ReloadOnChange = $true
# add the source to the configuration builder
$bld.Add($JsonSrc)
# Another way to do the above is like so:
[Microsoft.Extensions.Configuration.JsonConfigurationExtensions]::AddJsonFile($bld,'.\test.json')
# there are also more overloads for the constructur as seen here:
# source: https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.configuration.jsonconfigurationextensions.addjsonfile
[Microsoft.Extensions.Configuration.JsonConfigurationExtensions]::AddJsonFile($bld,'.\test.json',$false,$true)
# .AddJsonFile(ConfigurationBuilder builder, string path, bool optional, bool reloadOnChange)
# reloadOnChange: Whether the configuration should be reloaded if the file changes.
# optional: Whether the file is optional.
# finally build the configuration
$cfg = $bld.Build()
# show all the property items
$cfg.GetChildren()
# you can save that into an array, so you can index into it
$array = $cfg.GetChildren().ToArray()
$array[2]
# Note: the .ToArray() method comes from this: (2 ways to get it)
Write-Output $cfg.GetChildren() -NoEnumerate | gm
, $cfg.GetChildren() | gm
# get a property
$cfg['name']
$cfg['count']
# now let's update the config json
[pscustomobject]@{
Name = 'bb'
Count = 20
Version = [version]'1.0.1'
} | ConvertTo-Json | Out-File test.json -Force
# and then update the config as well
$cfg.Reload()
# let's check that we have the updated values in the config
$cfg['name']
$cfg.GetSection('name').Value
# and now let's access a nested property
$cfg['version:major']
# Note: we use a colon as the separator for the tree structure
# we can also retrieve values with the .Item() method like so:
$cfg.Item('name')
$cfg.Item('version:major')
# you can check the properties of a nested property
($cfg.GetChildren() | where Key -eq Version).GetChildren()
# show which providers are loading which values
# (as-in see where is your configuration coming from, so you can troubleshoot if needed)
[Microsoft.Extensions.Configuration.ConfigurationRootExtensions]::GetDebugView($cfg)
# once you're done you can (and should) dispose the type
$cfg.Dispose()
# Again, this is a great video about .NET configuration
# https://www.youtube.com/watch?v=aOXaBZFB0-0
## Some key points and benefits:
# 1) you can have live reloads of your configuration for your application
# since this allows for the reloadOnChange method and/or property, or just .reload() method
# 2) you can have multiple different sources for your config, like:
# json, env vars, azure key vault, azure app config, ini files, xml, custom source, or even a stream
# But in the end, you'll be working with one common API once you build the config
# (I mean API for get/set the values, reload, etc)
## Another idea, is that you could have metadata on your variables
# and then apply different configs based on that metadata
# for example, we get a VM object as input, and then we tag it (via an attribute) as either an Azure or AWS VM
# and then when you're going to do something during runtime (as-in when the script runs), you'll pick a different config
# based on that tag.
# Note: in PS we can attach a custom attribute to a variable or to a scriptblock or function, which essentially tags the variable/function
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment