#Requires -Version 2.0 function Out-IniFile { <# .Synopsis Write the contents of a Hashtable or OrderedDictionary to an INI file .Description The input can either be a standard Powershell hash created with @{}, an [ordered]@{} in Powershell 3, an OrderedDictionary created by the Get-IniContent cmdlet. Will write out the fully nested structure to an INI file .Notes Inspiration from Oliver Lipkau http://tinyurl.com/94tdhdx .Inputs Accepts either a Collections.Specialized.OrderedDictionary or a standard Powershell Hashtable .Outputs Returns an IO.FileInfo object if -PassThru is specified System.IO.FileSystemInfo .Parameter InputObject Specifies the OrderedDictionary or Hashtable to be written to the file .Parameter FilePath Specifies the path to the output file. .Parameter Encoding Specifies the type of character encoding used in the file. Valid values are "Unicode", "UTF7", "UTF8", "UTF32", "ASCII", "BigEndianUnicode", "Default", and "OEM". "Unicode" is the default. "Default" uses the encoding of the system's current ANSI code page. "OEM" uses the current original equipment manufacturer code page identifier for the operating system. .Parameter Append Adds the output to the end of an existing file, instead of replacing the file contents. .Parameter Force Allows the cmdlet to overwrite an existing read-only file. Even using the Force parameter, the cmdlet cannot override security restrictions. .Parameter PassThru Returns the newly written FileInfo. By default, this cmdlet does not generate any output. .Example @{ Section = @{ Foo = 'bar'; Baz = 1} } | Out-IniFile -FilePath .\foo.ini Description ----------- Writes the given Hashtable to foo.ini as [Section] Baz=1 Foo=bar .Example @{ Section = [ordered]@{ Foo = 'bar'; Baz = 1} } | Out-IniFile -FilePath .\foo.ini Description ----------- Writes the given Hashtable to foo.ini, in the given order [Section] Foo=bar Baz=1 .Example @{ Section = [ordered]@{ Foo = 'bar'; Baz = 1} } | Out-IniFile -FilePath .\foo.ini -Force Description ----------- Same as previous example, except that foo.ini is overwritten should it already exist .Example $file = @{ Section = [ordered]@{ Foo = 'bar'; Baz = 1} } | Out-IniFile -FilePath .\foo.ini Description ----------- Same as previous example, except that the FileInfo object is returned .Example $config = Get-IniContent .\foo.ini $config.Section.Value = 'foo' $config | Out-IniFile -Path .\foo.ini -Force Description ----------- Parses the foo.ini file contents into an OrderedDictionary with the Get-IniContent cmdlet. Manipulates the contents, then overwrites the existing file. #> [CmdletBinding()] Param( [Parameter(ValueFromPipeline=$true, Mandatory=$true)] [ValidateScript({ ($_ -is [Collections.Specialized.OrderedDictionary]) -or ` ($_ -is [Hashtable]) })] [ValidateNotNullOrEmpty()] $InputObject, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-Path $_ -IsValid })] [string] $FilePath, [Parameter(Mandatory=$false)] [ValidateSet('Unicode','UTF7','UTF8','UTF32','ASCII','BigEndianUnicode', 'Default','OEM')] [string] $Encoding = 'Unicode', [switch] $Append, [switch] $Force, [switch] $PassThru ) process { Write-Verbose "[INFO]: Out-IniFile writing file [$FilePath]" if ((New-Object IO.FileInfo($FilePath)).Extension -ne '.ini') { Write-Warning "Out-IniFile [$FilePath] does not end in .ini extension" } if ((Test-Path $FilePath) -and (!$Force)) { throw "The -Force switch must be applied to overwrite $outFile" } $outFile = $null if ($append) { $outFile = Get-Item $FilePath -ErrorAction SilentlyContinue } if ([string]::IsNullOrEmpty($outFile) -or (!(Test-Path $outFile))) { $outFile = New-Item -ItemType File -Path $FilePath -Force:$Force } #recursive function write sections at various depths function WriteKeyValuePairs($dictionary, $sectionName = $null, $depth = 0) { #Sections - take into account nested depth if ((![string]::IsNullOrEmpty($sectionName)) -and ($depth -gt 0)) { $sectionName = "$('[' * $depth)$sectionName$(']' * $depth)" Write-Verbose "[INFO]: writing section $sectionName to $outFile" Add-Content -Path $outFile -Value $sectionName -Encoding $Encoding } $dictionary.GetEnumerator() | % { if ($_.Value -is [Collections.Specialized.OrderedDictionary] -or $_.Value -is [Hashtable]) { Write-Verbose "[INFO]: Writing section [$($_.Key)] of $sectionName" WriteKeyValuePairs $_.Value $_.Key ($depth + 1) } elseif ($_.Value.IsComment -or ($_.Key -match '^\{Comment\-[\d]+\}')) { Write-Verbose "[INFO]: Writing comment $($_.Value)" Add-Content -Path $outFile -Value $_.Value -Encoding $Encoding } else { Write-Verbose "[INFO]: Writing key $($_.Key)" Add-Content -Path $outFile -Value "$($_.Key) = $($_.Value)" -Encoding $Encoding } } } WriteKeyValuePairs $InputObject Write-Verbose "[SUCCESS]: Out-IniFile wrote file [$outFile]" if ($PassThru) { return $outFile } } }