28

Is it possible to use a configuration file with a PowerShell script?

For example, the configuration file:

#links
link1=http://www.google.com
link2=http://www.apple.com
link3=http://www.microsoft.com

And then call this information in the PS1 script:

start-process iexplore.exe $Link1
I say Reinstate Monica
  • 3,132
  • 7
  • 28
  • 52
Xavier C
  • 733
  • 2
  • 9
  • 12

8 Answers8

26

Thanks a lot for your help Dennis and Tim! Your answers put me on the good track and I found this

SETTINGS.TXT

#from http://tlingenf.spaces.live.com/blog/cns!B1B09F516B5BAEBF!213.entry
#
[General]
MySetting1=value

[Locations]
InputFile="C:\Users.txt"
OutputFile="C:\output.log"

[Other]
WaitForTime=20
VerboseLogging=True

POWERSHELL COMMAND

#from http://tlingenf.spaces.live.com/blog/cns!B1B09F516B5BAEBF!213.entry
#
Get-Content "C:\settings.txt" | foreach-object -begin {$h=@{}} -process { $k = [regex]::split($_,'='); if(($k[0].CompareTo("") -ne 0) -and ($k[0].StartsWith("[") -ne $True)) { $h.Add($k[0], $k[1]) } }

then

After executing the code snippet, a variable ($h) will contain the values in a HashTable.

Name                           Value
----                           -----
MySetting1                     value
VerboseLogging                 True
WaitForTime                    20
OutputFile                     "C:\output.log"
InputFile                      "C:\Users.txt"

*To retrieve an item from the table, use the command $h.Get_Item("MySetting1").*

Shiva
  • 103
  • 4
Xavier C
  • 733
  • 2
  • 9
  • 12
  • 6
    You can also get the settings out by the much friendlier $h.MySetting1 – Ryan Shillington Nov 13 '15 at 19:54
  • I get an array out of bounds exception in the regex parser line, despite using the exact same .txt file shown in this answer and the parser code (no changes) => `Index was outside the bounds of the array. At C:\testConfigreader.ps1:13 char:264 + ... -ne $True)) { $h.Add($k[0], $k[1]) } } + ~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : OperationStopped: (:) [], IndexOutOfRangeException + FullyQualifiedErrorId : System.IndexOutOfRangeException` Does anyone have this working correctly? – Shiva Mar 09 '17 at 00:24
  • 1
    If you config file don't have `[Sections]` or `; semicolon comments`, you can do just `$config = Get-Content $ConfigPath | ConvertFrom-StringData`. See [ConvertFrom-StringData](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertfrom-stringdata?view=powershell-6) for details. – asmironov Jun 08 '19 at 20:40
  • +1. It works! Thanks! – Roman Apr 25 '20 at 10:59
10

There is a much more convenient way to read settings from JSON-file using the ConvertFrom-Json Cmdlet in PowerShell to read a JSON formatted file:

$SettingsObject = Get-Content -Path \path\to\settings.json | ConvertFrom-Json

In your case settings in JSON would be like settings.json:

{
    "link1": "http://www.google.com",
    "link2": "http://www.apple.com",
    "link3": "http://www.microsoft.com"
}

Reading:

PS C:\> $SettingsObject = Get-Content -Path \path\to\settings.json | ConvertFrom-Json

PS C:\> $SettingsObject

link1                 link2                link3                   
-----                 -----                -----                   
http://www.google.com http://www.apple.com http://www.microsoft.com



PS C:\> $SettingsObject.link1
http://www.google.com

Source: PowerTip: Read a JSON configuration file as a PowerShell object

Vladislav
  • 201
  • 2
  • 4
6

There's a good thread here which shows this code (quoting from the linked thread):

# from http://www.eggheadcafe.com/software/aspnet/30358576/powershell-and-ini-files.aspx
param ($file)

$ini = @{}
switch -regex -file $file
{
    "^\[(.+)\]$" {
        $section = $matches[1]
        $ini[$section] = @{}
    }
    "(.+)=(.+)" {
        $name,$value = $matches[1..2]
        $ini[$section][$name] = $value
    }
}
$ini

Then you can do:

PS> $links = import-ini links.ini
PS> $links["search-engines"]["link1"]
http://www.google.com
PS> $links["vendors"]["link1"]
http://www.apple.com

Assuming an INI file that looks like this:

[vendors]
link1=http://www.apple.com
[search-engines]
link1=http://www.google.com

Unfortunately the regexes are missing from the code at the link so you'll have to reproduce them, but there's a version that handles files without section headers and lines that are comments.

Dennis Williamson
  • 62,149
  • 16
  • 116
  • 151
  • You can handle comments easily by just adding another case to the `switch` with `'^#' {}`. Also you can access hashtable contents with a dot as well, so `$links.vendors.link1` should work too which might be a little better to read. – Joey Oct 20 '10 at 07:20
  • Worked for me, but I had to trim the values before adding them to the table, otherwise it leaves spaces: $ini[$section][$name.Trim()] = $value.Trim() Maybe the regex can be modified instead? – 0kcats Feb 08 '22 at 21:10
3

yes, the cmdlets you're looking for are get-content and select-string.

$content=get-content C:\links.txt
start-process iexplore.exe $content[0]
Tim
  • 226
  • 1
  • 3
1

There is a great post by Microsoft Scripting Guys which is something I've used with great success in my own modules.

Use PowerShell to Work with Any INI File

Summary: Guest Blogger Oliver Lipkau shares two Windows PowerShell functions to simplify reading and writing to INI files. (Posted: August 20th, 2011)

The function they provide and explain is as follows:

function Get-IniContent ($filePath)
{
    $ini = @{}
    switch -regex -file $FilePath
    {
        "^\[(.+)\]" # Section
        {
            $section = $matches[1]
            $ini[$section] = @{}
            $CommentCount = 0
        }
        "^(;.*)$" # Comment
        {
            $value = $matches[1]
            $CommentCount = $CommentCount + 1
            $name = "Comment" + $CommentCount
            $ini[$section][$name] = $value
        }
        "(.+?)\s*=(.*)" # Key
        {
            $name,$value = $matches[1..2]
            $ini[$section][$name] = $value
        }
    }
    return $ini
}
Eastman
  • 60
  • 9
phbits
  • 216
  • 2
  • 8
1

For a more comprehensive approach, consider https://github.com/alekdavis/ConfigFile . This module supports config files in JSON format, as well as INI. It allows expanding variables and does a few neat tricks. The thing to remember is that the names of the key-value pairs in the INI file must match the names of the script parameters or variables.

Alek Davis
  • 153
  • 1
  • 1
  • 7
0

Powershell module psini.

Example from readme.

Returns the key "Key2" of the section "Category2" from the C:\settings.ini file:

$FileContent = Get-IniContent "C:\settings.ini"
$FileContent["Category2"]["Key2"]

My solution with ansible.

don Rumata
  • 103
  • 5
0

Here my solution to parse INI file with Powershell :

config.ini:

[readme]
; This is a sample configuration file
; Comments start with ';', as in php.ini

[asset]
; Allows you to select assets based on custom fields
environnement_field = "Environnement"
environnement_values[] = "production"
decommissioned_field = "Deco"
decommissioned_values[] = "oui"
decommissioned_values[] = "yes"
decommissioned_values[] = "vrai"
decommissioned_values[] = "true"

[form]
second_section[one] = "1 associated"
second_section[two] = "2 associated"
second_section[] = "1 unassociated"
second_section[] = "2 unassociated"
second_section[] = 1
second_section[] = 1.3
second_section[] = 2.2

[second_section]
path = "/usr/local/bin"
URL = "http://www.example.com/~username"
second_section[one] = "1 associated"
second_section[two] = "2 associated"
second_section[three] = 3
second_section[four] = 4.4
second_section[] = "1 unassociated"
second_section[] = "2 unassociated"
second_section[] = 1
second_section[] = 2.2

[third_section]
phpversion[] = "5.0"
phpversion[] = "5.1"
phpversion[] = "5.2"
phpversion[] = "5.3"

my_script.ps1 :

$PSScriptRoot

function Parse-IniFile ($filePath)
{
    $ini = [ordered]@{}
    $count = @{}
    switch -regex -file $filePath
    {
        #Section.
        "^\[(.+)\]$"
        {
            $section = $matches[1].Trim()
            $ini[$section] = [ordered]@{}
            $count[$section] = @{}
            $CommentCount = 0
            continue
        }
        # Comment
        "^(;.*)$"
        {
            $value = $matches[1]
            $CommentCount = $CommentCount + 1
            $name = "Comment" + $CommentCount
            if ($section -eq $null) {
                $section = "header"
                $ini[$section] = [ordered]@{}
            }
            $ini[$section][$name] = $value
            continue
        }

        #Array Int.
        "^\s*([^#][\w\d_-]+?)\[]\s*=\s*(\d+)\s*$"
        {
            $name,$value = $matches[1..2]
            if (!$ini[$section][$name]) {
                $ini[$section][$name] = [ordered]@{}
            }
            if (!$count[$section][$name]) {
                $count[$section][$name] = 0
            }
            $ini[$section][$name].Add($count[$section][$name], [int]$value)
            $count[$section][$name] += 1
            continue
        }
        #Array Decimal
        "^\s*([^#][\w\d_-]+?)\[]\s*=\s*(\d+\.\d+)\s*$"
        {
            $name,$value = $matches[1..2]
            if (!$ini[$section][$name]) {
                $ini[$section][$name] = [ordered]@{}
            }
            if (!$count[$section][$name]) {
                $count[$section][$name] = 0
            }
            $ini[$section][$name].Add($count[$section][$name], [decimal]$value)
            $count[$section][$name] += 1
            continue
        }
        #Array Everything else
        "^\s*([^#][\w\d_-]+?)\[]\s*=\s*(.*)"
        {
            $name,$value = $matches[1..2]
            if (!$ini[$section][$name]) {
                $ini[$section][$name] = [ordered]@{}
            }
            if (!$count[$section][$name]) {
                $count[$section][$name] = 0
            }
            $ini[$section][$name].Add($count[$section][$name], $value.Trim())
            $count[$section][$name] += 1
            continue
        }

        #Array associated Int.
        "^\s*([^#][\w\d_-]+?)\[([\w\d_-]+?)]\s*=\s*(\d+)\s*$"
        {
            $name, $association, $value = $matches[1..3]
            if (!$ini[$section][$name]) {
                $ini[$section][$name] = [ordered]@{}
            }
            $ini[$section][$name].Add($association, [int]$value)
            continue
        }
        #Array associated Decimal
        "^\s*([^#][\w\d_-]+?)\[([\w\d_-]+?)]\s*=\s*(\d+\.\d+)\s*$"
        {
            $name, $association, $value = $matches[1..3]
            if (!$ini[$section][$name]) {
                $ini[$section][$name] = [ordered]@{}
            }
            $ini[$section][$name].Add($association, [decimal]$value)
            continue
        }
        #Array associated Everything else
        "^\s*([^#][\w\d_-]+?)\[([\w\d_-]+?)]\s*=\s*(.*)"
        {
            $name, $association, $value = $matches[1..3]
            if (!$ini[$section][$name]) {
                $ini[$section][$name] = [ordered]@{}
            }
            $ini[$section][$name].Add($association, $value.Trim())
            continue
        }

        #Int.
        "^\s*([^#][\w\d_-]+?)\s*=\s*(\d+)\s*$"
        {
            $name,$value = $matches[1..2]
            $ini[$section][$name] = [int]$value
            continue
        }
        #Decimal.
        "^\s*([^#][\w\d_-]+?)\s*=\s*(\d+\.\d+)\s*$"
        {
            $name,$value = $matches[1..2]
            $ini[$section][$name] = [decimal]$value
            continue
        }
        #Everything else.
        "^\s*([^#][\w\d_-]+?)\s*=\s*(.*)"
        {
            $name,$value = $matches[1..2]
            $ini[$section][$name] = $value.Trim()
            continue
        }
    }

    return $ini
}

function Set-IniFile ($ini, $filePath)
{
    $output = @()
    foreach($section in $ini.Keys)
    {
        # Put a newline before category as seperator, only if there is null 
        $seperator = if ($output[$output.Count - 1] -eq $null) { } else { "`n" }
        $output += $seperator + "[$section]";

        foreach($key in $ini.$section.Keys)
        {
            if ( $key.StartsWith('Comment') )
            {
                $output += $ini.$section.$key
            }
            elseif ($ini.$section.$key -is [System.Collections.Specialized.OrderedDictionary]) {
                foreach($subkey in $ini.$section.$key.Keys) {
                    if ($subkey -is [int]) {
                        $output += "$key[] = " + $ini.$section.$key.$subkey
                    } else {
                        $output += "$key[$subkey] = " + $ini.$section.$key.$subkey
                    }
                }
            }
            else
            {
                $output += "$key = " + $ini.$section.$key
            }
        }
    }

    $output | Set-Content $filePath -Force
}

$ini = Parse-IniFile -filePath ".\config.ini"
Set-IniFile -ini $ini -filePath ".\config_copy.ini"

Write-Host "=first_section"
$ini["first_section"]
Write-Host "=second_section"
$ini["second_section"]
Write-Host "=second_section.second_section"
$ini["second_section"]["second_section"]
Write-Host "=third_section"
$ini["third_section"]
Write-Host "=third_section.phpversion"
$ini["third_section"]["phpversion"]

The output :

PS C:\Users\itesant> .\my_script.ps1

Name                           Value                                                                                      
----                           -----                                                                                      
=readme
Comment1                       ; This is a sample configuration file                                                      
Comment2                       ; Comments start with ';', as in php.ini                                                   
=asset
Comment1                       ; Allows you to select assets based on custom fields                                       
environnement_field            "Environnement"                                                                            
environnement_values           {0}                                                                                        
decommissioned_field           "Deco"                                                                                     
decommissioned_values          {0, 1, 2, 3}                                                                               
=form
second_section                 {one, two, 0, 1...}                                                                        
=form.second_section
one                            "1 associated"                                                                             
two                            "2 associated"                                                                             
three                          3                                                                                          
four                           4,4                                                                                        
0                              "1 unassociated"                                                                           
1                              "2 unassociated"                                                                           
2                              1                                                                                          
3                              2,2                                                                                        
=second_section
path                           "/usr/local/bin"                                                                           
URL                            "http://www.example.com/~username"                                                         
second_section                 {one, two, three, four...}                                                                 
=second_section.second_section
one                            "1 associated"                                                                             
two                            "2 associated"                                                                             
three                          3                                                                                          
four                           4,4                                                                                        
0                              "1 unassociated"                                                                           
1                              "2 unassociated"                                                                           
2                              1                                                                                          
3                              2,2                                                                                        
=third_section
phpversion                     {0, 1, 2, 3}                                                                               
=third_section.phpversion
0                              "5.0"                                                                                      
1                              "5.1"                                                                                      
2                              "5.2"                                                                                      
3                              "5.3"    

You can see some regex here :

Section: https://regex101.com/r/maYLKE/3

Comment: https://regex101.com/r/IE5FJH/2

Array Integer: https://regex101.com/r/AuuOi3/2

Array Decimal: https://regex101.com/r/Erjjym/2

Array Everything else: https://regex101.com/r/guC1Yd/2

Array associated Integer: https://regex101.com/r/Us56SL/2

Array associated Decimal: https://regex101.com/r/MrCZ9n/2

Array associated Everything else: https://regex101.com/r/TbYcyf/4

Integer: https://regex101.com/r/ZVdB2z/1

Decimal: https://regex101.com/r/TzHNb9/1

Everything else: https://regex101.com/r/S0o1Bj/1