Managing INI Files with PowerShell

Traditionally, INI files are used as configuration sources, where an INI file consists of one or more sections with key pair settings.
Here’s an example of an INI file:
[General]
Action = Start
Directory = c:\work
ID = 123ABC
[Application]
Name = foo.exe
Version = 1.0
[User]
Name = Jeff
Company = Globomantics

If your application uses an INI file, then there’s not much you can do except let it do its thing. But let’s say you have an INI file that you want to move to an object-based PowerShell world. In this article, I’ll show you how to use an INI file in the world of objects, and I expect the process will serve more as a learning tool than a practical one.
As I approached the problem, I thought about what I wanted the end result to look like. I knew I had to write an object to the pipeline. The key pair combinations already look like a hash table that can easily be turned into a custom object.

​
The section name, [General], is also a likely candidate for a property. Let's make this the property name, and the setting object the value.
Making the [General] section name the property. (Image Credit: Jeff Hicks)
Making the [General] section name the property. (Image Credit: Jeff Hicks)
This makes it easy to reference individual settings.
Giving [General] a property lets us easily reference individual settings. (Image Credit: Jeff Hicks)
Giving [General] a property lets us easily reference individual settings. (Image Credit: Jeff Hicks)
Now that I had manually drafted and tested commands to turn an INI section into an object, I need to create a tool to automate this process. I know my command needs a Path parameter to get the INI file. To simplify, I decided to strip off any comment starting with a semi-colon, as well as any blank or empty lines. In the following line of code, I'm using a regular expression pattern.
​
Next, I initialized an empty object and hash table.
​
I'll be processing the INI file from top to bottom, so I need a ordered hash table to keep all of the settings in order.
Because this process walks through the file line-by-line, I need to know the section heading name by using another regular expression pattern.
​
Next, let's use another pattern with the –Replace operator to strip off the [ and ] characters.
​
If the line was a data setting, I could split each line and add it to my hash table.
​
The challenge is now that I need to finish creating the previous object before going onto the next section.

Here's my logic: If the line matches my pattern for a section heading, and the hash table contained something, then create the custom object.
<
​
I then reinitalized the hash table and saved the section name, since I am now processing the next object.
​
This process continues for each line in the INI file. However, when I reached the end of the last setting, there's a new section heading to trigger anything. Now, I need to add code to create the final object property.
Here's my complete function, which includes comment-based help.
​
<#
.Synopsis
Convert an INI file to an object
.Description
Use this command to convert a legacy INI file into a PowerShell custom object. Each INI section will become a property name. Then each section setting will become a nested object. Blank lines and comments starting with ; will be ignored.
It is assumed that your ini file follows a typical layout like this:
;This is a sample ini
[General]
Action = Start
Directory = c:\work
ID = 123ABC
 ;this is another comment
[Application]
Name = foo.exe
Version = 1.0
[User]
Name = Jeff
Company = Globomantics
.Parameter Path
The path to the INI file.
.Example
PS C:\> $sample = ConvertFrom-IniFile c:\scripts\sample.ini
PS C:\> $sample
General                           Application                      User
-------                           -----------                      ----
@{Directory=c:\work; ID=123ABC... @{Version=1.0; Name=foo.exe}     @{Name=Jeff; Company=Globoman...
PS C:\> $sample.general.action
Start
In this example, a sample ini file is converted to an object with each section a separate property.
.Example
PS C:\> ConvertFrom-IniFile c:\windows\system.ini | export-clixml c:\work\system.ini
Convert the System.ini file and export results to an XML format.
.Notes
Last Updated: June 5, 2015
Version     : 1.0
Learn more about PowerShell:
Essential PowerShell Learning Resources
**************************************************************** * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED * * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF * * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, * * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING. * **************************************************************** .Link Get-Content .Inputs [string] .Outputs [pscustomobject] #> [cmdletbinding()] Param( [Parameter(Position=0,Mandatory,HelpMessage="Enter the path to an INI file", ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias("fullname","pspath")] [ValidateScript({ if (Test-Path $_) { $True } else { Throw "Cannot validate path $_" } })] [string]$Path ) Begin { Write-Verbose "Starting $($MyInvocation.Mycommand)" } #begin Process { Write-Verbose "Getting content from $(Resolve-Path $path)" #strip out comments that start with ; and blank lines $all = Get-content -Path $path | Where {$_ -notmatch "^(\s+)?;|^\s*$"} $obj = New-Object -TypeName PSObject -Property @{} $hash = [ordered]@{} foreach ($line in $all) { Write-Verbose "Processing $line" if ($line -match "^\[.*\]$" -AND $hash.count -gt 0) { #has a hash count and is the next setting #add the section as a property write-Verbose "Creating section $section" Write-verbose ([pscustomobject]$hash | out-string) $obj | Add-Member -MemberType Noteproperty -Name $Section -Value $([pscustomobject]$Hash) -Force #reset hash Write-Verbose "Resetting hashtable" $hash=[ordered]@{} #define the next section $section = $line -replace "\[|\]","" Write-Verbose "Next section $section" } elseif ($line -match "^\[.*\]$") { #Get section name. This will only run for the first section heading $section = $line -replace "\[|\]","" Write-Verbose "New section $section" } elseif ($line -match "=") { #parse data $data = $line.split("=").trim() $hash.add($data[0],$data[1]) } else { #this should probably never happen Write-Warning "Unexpected line $line" } } #foreach #get last section If ($hash.count -gt 0) { Write-Verbose "Creating final section $section" Write-Verbose ([pscustomobject]$hash | Out-String) #add the section as a property $obj | Add-Member -MemberType Noteproperty -Name $Section -Value $([pscustomobject]$Hash) -Force } #write the result to the pipeline $obj } #process End { Write-Verbose "Ending $($MyInvocation.Mycommand)" } #end } #end function

Once loaded, I can create a custom object out of the INI file.

Creating a custom object out of an INI file. (Image Credit: Jeff Hicks)
Creating a custom object out of an INI file. (Image Credit: Jeff Hicks)

I now have an objectified version of my original INI file. If I want to persist it, I can export it:

​
Later, I can easily re-import it.
Importing the INI file. (Image Credit: Jeff Hicks)
Importing the INI file. (Image Credit: Jeff Hicks)
Even if you don't have a need for this functionality, I hope you learned something about PowerShell syntax and scripting concepts. Although I like the option to use the Export-Clixml cmdlet, you can only use the XML file in PowerShell. Next time, we'll take this a step further and export the INI file to a more traditional XML format that you can use anywhere.