IniManager.psm1
#REQUIRES -Version 4.0 <# .Synopsis Reads an ini file and creates an object based on the content of the file .DESCRIPTION Reads an ini file and creates an object based on the content of the file. One property per key/value. Sections will be named with surrounding brackets and will contain a list of objects based on the keys within that section. Comments will be ignored. Created by John Roos Email: john@roostech.se Web: http://blog.roostech.se .EXAMPLE get-ini -Path "C:\config.ini" Opens the file config.ini and creates an object based on that file. .OUTPUTS Outputs an custom object of the type File.Ini #> function Get-Ini { [CmdletBinding()] param( # Enter the path for the ini file [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)] [string]$Path ) Process{ if (!(Test-Path $Path)) { Write-Error 'Invalid path' break } $iniFile = Get-Content $Path -Verbose:$false $currentSection = '' $currentKey = '' $currentValue = '' [hashtable]$iniSectionHash = [ordered]@{} [hashtable]$iniConfigArray = [ordered]@{} foreach ($line in $iniFile) { if ( $line.Trim().StartsWith('[') -and $line.EndsWith(']') ) { Write-Verbose "Found new section." if ($currentSection -ne ''){ Write-Verbose "Creating section property based on array:" $keyobj = New-Object PSObject -Property $iniConfigArray $keyobj.PSObject.TypeNames.Insert(0,'File.Ini.Config') $iniSectionHash.Add($currentSection,$keyobj) [hashtable]$iniConfigArray = @{} Write-Verbose "Created section property: $currentSection" } if ($iniConfigArray.count -gt 0) { $rootSection = $iniConfigArray [hashtable]$iniConfigArray = [ordered]@{} } $currentSection = $line Write-Verbose "Current section: $currentSection" continue } Write-Verbose "Parsing line: $line" if ( $line.Contains('=') ){ $keyvalue = $line.Split('=') [string]$currentKey = $keyvalue[0] [string]$currentValue = $keyvalue[1] $valuehash = @{ $currentKey = $currentValue } $iniConfigArray.Add($currentKey, $currentValue) Write-Verbose "Added keyvalue: $($keyvalue[0]) = $($keyvalue[1])" } <# below was for handling comments, but I wont do it... elseif ($line.Contains('#') -or $line.Contains(';')) { [string]$currentKey = $line [string]$currentValue = "" $valuehash = @{ $currentKey = $currentValue } $iniConfigArray.Add($currentKey, $currentValue) Write-Verbose "Added comment: $currentKey" }#> } $keyobj = New-Object PSObject -Property $iniConfigArray $keyobj.PSObject.TypeNames.Insert(0,'File.ini.Section') $iniSectionHash.Add($currentSection,$keyobj) Write-Verbose "Created last section property: $currentSection" $result = New-Object PSObject -Property $iniSectionHash if ($rootSection) { foreach ($key in $rootSection.keys){ Add-Member -InputObject $result -MemberType NoteProperty -Name $key -Value $rootSection.$key } } $result.PSObject.TypeNames.Insert(0,'File.ini') Return $result } } <# .Synopsis Sets a specific key to a value in a ini file .DESCRIPTION Sets a specific key to a value in a ini file Comments will be ignored. Warning: Even comments in the target ini file will be removed! Created by John Roos Email: john@roostech.se Web: http://blog.roostech.se .EXAMPLE Set-IniKey -Path "C:\config.ini" -Key LoggingLevel -Value Debug -Section Logging -Encoding UTF8 Opens the file config.ini and changes the key "LoggingLevel" in the [Logging] section of the file. The file will be saved with UTF8 encoding. .OUTPUTS Creates a ini file. Keeps the original content of the ini file and only replaces the value for the key matching the parameter Key #> function Set-IniKey { [CmdletBinding()] param( [Parameter(Mandatory=$true, Position=0)] [string]$Path, [Parameter(Mandatory=$true, Position=1)] [String]$Key, [Parameter(Mandatory=$true, Position=2)] [String]$Value, [Parameter(Mandatory=$false, Position=3)] [String]$Section, [Parameter(Mandatory=$false, ValueFromPipeline=$false, Position=4)] [ValidateSet("Unicode", "UTF7", "UTF8", "UTF32", "ASCII", "BigEndianUnicode", "Default", "OEM")] [psobject]$Encoding = "UTF8", [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=5)] [switch]$Force ) Process { if ($Section){ $Section = $Section.Replace('[','').Replace(']','') $Section = "[$Section]" } Write-Verbose "Checking if path exists" if (Test-Path $Path){ Write-Verbose "Path exists" Write-Verbose "Reading ini file with Get-Ini" $ini = Get-Ini -Path $Path Write-Verbose "Get-Ini completed" } else { Write-Error 'Path does not exist' break } if ($Section){ [array]$availableSections = ($ini | Get-Member -MemberType Properties | Where-Object {$_.MemberType -eq 'Property' -or $_.MemberType -eq 'NoteProperty'} | Select-Object -ExpandProperty Name | Where-Object {$_.Contains(']')}).tolower() Write-Verbose "Checking if the section $Section already exist" if (!$availableSections.Contains($Section.tolower())) { Write-Verbose "Section does not exist" Write-Host 'Creating new section' $props = [hashtable]@{$Key = $Value} $newValue = New-Object PSObject -Property $props Add-Member -InputObject $ini -MemberType NoteProperty -Name $Section -Value $newValue } else { Write-Verbose "Section exist" [array]$availableProperties = ($ini.$Section | Get-Member -MemberType Properties | Where-Object { $_.MemberType -eq 'Property' -or $_.MemberType -eq 'NoteProperty' } | Select-Object -ExpandProperty Name).ToLower() if (!$availableProperties.Contains($Key.ToLower())) { Write-Verbose "Property $Property exist" Write-Verbose "Setting property value to $Value" $ini.$Section | Add-Member -MemberType NoteProperty -Name $Key -Value $Value } else { Write-Verbose 'Adding property' $ini.$Section.$Key = $Value } } } else { [array]$availableProps = ($ini | Get-Member -MemberType Properties | Where-Object {$_.MemberType -eq 'Property' -or $_.MemberType -eq 'NoteProperty'} | Select-Object -ExpandProperty Name | Where-Object {!$_.Contains(']')}).ToLower() if (!$availableProps.Contains($Key.ToLower())) { Write-Verbose "Property $Key does not exist" Write-Verbose "Creating new property $Key" Add-Member -InputObject $ini -MemberType NoteProperty -Name $Key -Value $Value } else { Write-Verbose "Property $Key exist" Write-Verbose "Setting property value to $Value" $ini.$Key = $Value } } Write-Verbose 'Creating ini file with New-Ini' New-Ini -Path $Path -Content $ini -Encoding $Encoding -Force:$Force } } <# .Synopsis Removes a key (entire line) in ini file .DESCRIPTION Removes a key (entire line) in ini file Comments will be ignored. Warning: Even comments in the target ini file will be removed! Created by John Roos Email: john@roostech.se Web: http://blog.roostech.se .EXAMPLE Remove-IniKey -Path "c:\config.ini" -Key [system]Proxy -Encoding ASCII Opens the file config.ini and removes the key "Proxy" in the [system] section of the file. The file will be saved with ASCII encoding. .OUTPUTS Overwrites the original ini file #> function Remove-IniKey { [CmdletBinding()] param( [Parameter(Mandatory=$true, Position=0)] [string]$Path, [Parameter(Mandatory=$true, Position=1)] [string]$Key, [Parameter(Mandatory=$false, Position=2)] [string]$Section, [Parameter(Mandatory=$false, Position=3)] [ValidateSet("Unicode", "UTF7", "UTF8", "UTF32", "ASCII", "BigEndianUnicode", "Default", "OEM")] [string]$Encoding = "UTF8", [Parameter(Mandatory=$False, Position=4)] [switch]$Force ) Process { Write-Verbose "Checking path to ini file" if (Test-Path $Path) { Write-Verbose "Ini file found" Write-Verbose "Reading ini file with Get-Ini" $ini = Get-Ini -Path $Path -Verbose:$false Write-Verbose "Get-Ini completed" } else { Write-Error 'Cannot find ini file' break } if ($Section) { $Section = $Section.Replace('[','').Replace(']','') $inisection = "[$Section]" Write-Verbose "Key belongs to section $inisection" } else { $inisection = $null Write-Verbose 'Key does not belong to any section' } $iniproperty = $Key # check if the value contains a [section] <# if ($Value -like '*]*') { $tempvalue = $Key.Trim().Split(']') $inisection = "$($tempvalue[0].Trim())]" $iniproperty = "$($tempvalue[1].Trim())" Write-Verbose "Section: $inisection" Write-Verbose "Property: $iniproperty" } else { $inisection = "" $iniproperty = $Key Write-Verbose "No section selected" Write-Verbose "Property: $iniproperty" } #> Write-Verbose 'Key check completed' Write-Verbose 'Searching for matching keys in ini file' if ($inisection) { # if section was included in the Value parameter [array]$availableSections = ($ini | Get-Member -MemberType Properties | Where-Object {$_.MemberType -eq 'Property' -or $_.MemberType -eq 'NoteProperty'} | Select-Object -ExpandProperty Name | Where-Object {$_.Contains(']')}).tolower() if ($availableSections.Contains($inisection.ToLower())) { # Section exists in the ini file Write-Verbose "Section $inisection exists" [array]$availableProps = ($ini.$inisection | Get-Member -MemberType Properties | Where-Object {$_.MemberType -eq 'Property' -or $_.MemberType -eq 'NoteProperty'} | Select-Object -ExpandProperty Name | Where-Object {!$_.Contains(']')}).ToLower() if ($availableProps.Contains($iniproperty.ToLower())) { # Property exists in the correct section in the ini file Write-Verbose "Property $iniproperty exists" $ini.$inisection.PSObject.Properties.Remove($iniproperty) Write-Verbose 'Key removed from ini object' } else { Write-Error "ini file contains the section but does not contain requested key: $iniproperty" break } } else { Write-Error "ini file does not contain requested section: $inisection" break } } else { # if no section was included in the Value parameter if ($ini.psobject.properties.name.Contains($Key)) { $ini.PSObject.Properties.Remove($Key) Write-Verbose 'Key removed from ini object' } else { Write-Error 'Key does not exist in ini file' break } } #$ini # Recreate the ini file Write-Verbose 'Saving file' New-Ini -Path $Path -Content $ini -Encoding $Encoding -Force:$Force -Verbose:$false } } <# .Synopsis Renames a key in ini file .DESCRIPTION Renames a key in ini file Comments will be ignored. Warning: Even comments in the target ini file will be removed! Created by John Roos Email: john@roostech.se Web: http://blog.roostech.se .EXAMPLE Rename-IniKey -Path c:\config.ini -Key Prixy -NewKey Proxy -Section system -Encoding UTF8 Opens the file config.ini and renames the key "Prixy" to "Proxy" in the [system] section of the file. The file will be saved with UTF8 encoding. .OUTPUTS Overwrites the original ini file #> function Rename-IniKey { [CmdletBinding()] param( [Parameter(Mandatory=$true, Position=0)] [string]$Path, [Parameter(Mandatory=$true, Position=1)] [string]$Key, [Parameter(Mandatory=$true, Position=2)] [string]$NewKey, [Parameter(Mandatory=$false, Position=3)] [string]$Section, [Parameter(Mandatory=$false, Position=4)] [ValidateSet("Unicode", "UTF7", "UTF8", "UTF32", "ASCII", "BigEndianUnicode", "Default", "OEM")] [string]$Encoding = "UTF8", [Parameter(Mandatory=$False, Position=5)] [switch]$Force ) Process { Write-Verbose "Checking if path exists" if (Test-Path $Path) { Write-Verbose "Path exists" Write-Verbose "Reading ini file with Get-Ini" $ini = Get-Ini -Path $Path -Verbose:$false Write-Verbose "Get-Ini completed" } else { Write-Error 'Path does not exist' break } Write-Verbose 'Checking value to remove' if ($Section) { $Section = $Section.Replace('[','').Replace(']','') $inisection = "[$Section]" } else { $inisection = $null } $iniproperty = $Key # check if the value contains a [section] <# if ($Value -like '*]*') { $tempvalue = $Key.Trim().Split(']') $inisection = "$($tempvalue[0].Trim())]" $iniproperty = "$($tempvalue[1].Trim())" Write-Verbose "Section: $inisection" Write-Verbose "Property: $iniproperty" } else { $inisection = "" $iniproperty = $Key Write-Verbose "No section selected" Write-Verbose "Property: $iniproperty" } #> Write-Verbose 'Value check completed' Write-Verbose 'Checking object for matching properties' if ($inisection) { # if section was included in the Value parameter [array]$availableSections = ($ini | Get-Member -MemberType Properties | Where-Object {$_.MemberType -eq 'Property' -or $_.MemberType -eq 'NoteProperty'} | Select-Object -ExpandProperty Name | Where-Object {$_.Contains(']')}).tolower() if ($availableSections.Contains($inisection.ToLower())) { # Section exists in the ini file Write-Verbose "Section $inisection exists" [array]$availableProps = ($ini.$inisection | Get-Member -MemberType Properties | Where-Object {$_.MemberType -eq 'Property' -or $_.MemberType -eq 'NoteProperty'} | Select-Object -ExpandProperty Name | Where-Object {!$_.Contains(']')}).ToLower() if ($availableProps.Contains($iniproperty.ToLower())) { # Property exists in the correct section in the ini file Write-Verbose "Property $iniproperty exists" $tempvalue = $ini.$inisection.$iniproperty Write-Verbose "Removing property" $ini.$inisection.PSObject.Properties.Remove($iniproperty) Write-Verbose "Recreating property with new name and same value" $ini.$inisection | Add-Member -MemberType NoteProperty -Name $NewKey -Value $tempvalue } else { Write-Error "ini file contains the section but does not contain requested configuration: $iniproperty" break } } else { Write-Error "ini file does not contain requested section: $inisection" break } } else { # if no section was included in the Value parameter if ($ini.psobject.properties.name.Contains($Key)) { $tempvalue = $ini.$iniproperty Write-Verbose "Removing property" $ini.PSObject.Properties.Remove($iniproperty) Write-Verbose "Recreating property with new name and same value" $ini | Add-Member -MemberType NoteProperty -Name $NewKey -Value $tempvalue Write-Verbose 'Value removed from ini object' } else { Write-Error 'Key does not exist in ini file' break } } #$ini # Recreate the ini file Write-Verbose 'Saving file' New-Ini -Path $Path -Content $ini -Encoding $Encoding -Force:$Force -Verbose:$false } } <# .Synopsis Sets a number of keys and values in ini file based on a hash table .DESCRIPTION Sets a number of keys and values in ini file based on a hash table. Sections are separated by naming them within brackets, like this: [section]key The keys will be added if they do not exist. Comments will be ignored. Warning: Even comments in the target ini file will be removed! Created by John Roos Email: john@roostech.se Web: http://blog.roostech.se .EXAMPLE Set-IniFromHash -Path c:\config.ini -Values @{'DebugLog'='false';'[settings]Hostname'='localhost'} -Encoding UTF8 Opens the file config.ini and sets the key DebugLog to false and in the [settings] section sets the Hostname to localhost. .OUTPUTS Overwrites the original ini file #> function Set-IniFromHash { [CmdletBinding()] param( [Parameter(Mandatory=$true, Position=0)] [string]$Path, [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=1)] [hashtable]$Values, [Parameter(Mandatory=$false, ValueFromPipeline=$false, Position=2)] [ValidateSet("Unicode", "UTF7", "UTF8", "UTF32", "ASCII", "BigEndianUnicode", "Default", "OEM")] [psobject]$Encoding = "UTF8", [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=3)] [switch]$Force ) Process { [string]$section = '' Write-Verbose "Checking if path exists" if (Test-Path $Path) { Write-Verbose "File exists" $iniexist = $true Write-Verbose "Reading ini file with Get-Ini" $ini = Get-Ini -Path $Path -Verbose:$false Write-Verbose "Get-Ini completed" } else { if ($Force){ $ini = New-Object -TypeName psobject Write-Verbose 'File does not exist (new file will be created)' $iniexist = $false } else { Write-Error 'File does not exist. Use the Force parameter to create file.' break } } [array]$availableSections = @() if ($iniexist) { $availableSections = ( $ini | Get-Member -MemberType Properties | Where-Object {$_.MemberType -eq 'Property' -or $_.MemberType -eq 'NoteProperty'} | Select-Object -ExpandProperty Name | Where-Object {$_.Contains(']')} ).tolower() } foreach ($key in $Values.keys) { Write-Verbose "Processing $key" if ($key -like '*]*') { Write-Verbose "Section selected" $keysplit = $key.Split(']') $section = $keysplit[0] + ']' $property = $keysplit[1] if ($availableSections.Contains($section.ToLower())) { Write-Verbose "Section $section exists" [array]$availableProps = ( $ini.$section | Get-Member -MemberType Properties | Where-Object {$_.MemberType -eq 'Property' -or $_.MemberType -eq 'NoteProperty'} | Select-Object -ExpandProperty Name | Where-Object {!$_.Contains(']')} ).ToLower() if (!$availableProps.Contains($property.ToLower())) { Write-Verbose "Property $property does not exist" Write-Verbose "Creating new property $property" Add-Member -InputObject $ini.$section -MemberType NoteProperty -Name $property -Value $Value } else { Write-Verbose "Property $property exist" Write-Verbose "Setting property value to $Value" $ini.$section.$Property = $Value } } else { Write-Verbose "Section $section does not exist !one!" $props = [hashtable]@{$property = $Values.$key} $newValue = New-Object PSObject -Property $props $availableSections += $section.ToLower() Add-Member -InputObject $ini -MemberType NoteProperty -Name $Section -Value $newValue } $ini.$section.$property = $Values.$key } else { Write-Verbose "No section selected" $property = $key [array]$availableProps = @() if ($iniexist) { [array]$availableProps = ($ini | Get-Member -MemberType Properties | Where-Object {$_.MemberType -eq 'Property' -or $_.MemberType -eq 'NoteProperty'} | Select-Object -ExpandProperty Name | Where-Object {!$_.Contains(']')}).ToLower() } if (!$availableProps.Contains($property.ToLower())) { Write-Verbose "Property $property does not exist" Write-Verbose "Creating new property $property" Add-Member -InputObject $ini -MemberType NoteProperty -Name $property -Value $Values.$key } else { Write-Verbose "Property $property exist" Write-Verbose "Setting property value to $($Values.$key)" $ini.$Property = $Values.$key } } } # uncomment below for debug # $ini Write-Verbose "Saving file $Path with encoding $Encoding" New-Ini -Path $Path -Content $ini -Encoding $Encoding -Force:$Force -Verbose:$false } } <# .Synopsis Creates an ini file based on a custom object of the type File.Ini .DESCRIPTION Creates an ini file based on a custom object of the type File.Ini Comments will be ignored. Created by John Roos Email: john@roostech.se Web: http://blog.roostech.se .EXAMPLE get-ini -Path "C:\config.ini" | new-ini -Path c:\config_new.ini -Encoding UTF8 Opens the file config.ini and which creates a File.Ini object. The object is then piped to New-Ini which will create a new file based on that object. .OUTPUTS Creates a new ini file #> function New-Ini { [CmdletBinding()] param( [Parameter(Mandatory=$true, Position=0)] [string]$Path, [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=1)] [psobject]$Content, [Parameter(Mandatory=$false, ValueFromPipeline=$false, Position=2)] [ValidateSet("Unicode", "UTF7", "UTF8", "UTF32", "ASCII", "BigEndianUnicode", "Default", "OEM")] [string]$Encoding = "UTF8", [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=3)] [switch]$Force ) Process { [array]$result = @() $sections = $Content | Get-Member -MemberType Properties | Where-Object {$_.MemberType -eq 'Property' -or $_.MemberType -eq 'NoteProperty'} | Select-Object -ExpandProperty Name foreach ($section in $sections){ if ($section -notlike '*]*') { $result += "$section=$($Content.$section)" } } if ($result.count -gt 0) { $result += " " } foreach ($section in $sections){ if ($section -like '*]*') { $result += "$section" $keys = $Content.$section | Get-Member -MemberType Properties | Where-Object {$_.MemberType -eq 'Property' -or $_.MemberType -eq 'NoteProperty'} | Select-Object -ExpandProperty Name foreach ($key in $keys){ $result += "$key=$($Content.$section.$key)" } $result += " " } } $result | Out-File -FilePath $Path -Encoding $Encoding -Force:$Force } } <# .Synopsis Returns true or false if the ini file has the provided configuration .DESCRIPTION Reads the configuration from an ini file and compares the content with the provided hashtable. Comments will be ignored. Created by John Roos Email: john@roostech.se Web: http://blog.roostech.se .EXAMPLE Test-Ini -Path "C:\config.ini" -Values @{'DebugLog'='false';'[settings]Hostname'='localhost'} Opens the file config.ini and checks the values for 'DebugLog' (not in any section) and 'Hostname' (in section 'settubgs'). If the values are the same as the provided values the function will return True, otherwise it will return False. .OUTPUTS Boolean #> function Test-Ini { [CmdletBinding()] param( [Parameter(Mandatory=$true, Position=0)] [string]$Path, [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=1)] [hashtable]$Values ) Process { Write-Verbose "Checking if path exists" if (Test-Path $Path) { Write-Verbose "Path exists" Write-Verbose "Reading ini file with Get-Ini" $ini = Get-Ini -Path $Path -Verbose:$false Write-Verbose "Get-Ini completed" } else { Write-Error 'Path does not exist' return $false } foreach ($key in $Values.keys) { Write-Verbose "Processing $key" if ($key -like '*]*') { Write-Verbose "Section selected" $keysplit = $key.Split(']') $section = $keysplit[0] + ']' $property = $keysplit[1] [array]$availableSections = ($ini | Get-Member -MemberType Properties | Where-Object {$_.MemberType -eq 'Property' -or $_.MemberType -eq 'NoteProperty'} | Select-Object -ExpandProperty Name | Where-Object {$_.Contains(']')}).tolower() if ($availableSections.Contains($section.ToLower())) { Write-Verbose "Section $section exists" [array]$availableProps = ($ini.$section | Get-Member -MemberType Properties | Where-Object {$_.MemberType -eq 'Property' -or $_.MemberType -eq 'NoteProperty'} | Select-Object -ExpandProperty Name | Where-Object {!$_.Contains(']')}).ToLower() if (!$availableProps.Contains($property.ToLower())) { Write-Verbose "Property $property does not exist" Write-Verbose "Property $property NOT OK (ref 1)" return $false } else { Write-Verbose "Property $property exist" if ($ini.$section.$Property -ne $Values.$key){ Write-Verbose "Property $property NOT OK (ref 2)" return $false } else { Write-Verbose "Property $property OK (ref 1)" } } } else { Write-Verbose "Section $section does not exist" return $false } $ini.$section.$property = $Values.$key } else { Write-Verbose "No section selected" $property = $key [array]$availableProps = ($ini | Get-Member -MemberType Properties | Where-Object {$_.MemberType -eq 'Property' -or $_.MemberType -eq 'NoteProperty'} | Select-Object -ExpandProperty Name | Where-Object {!$_.Contains(']')}).ToLower() if (!$availableProps.Contains($property.ToLower())) { Write-Verbose "Property $property does not exist" Write-Verbose "Property $property NOT OK (ref 3)" return $false } else { Write-Verbose "Property $property exist" if ($ini.$Property -ne $Values.$key) { Write-Verbose "Property $property NOT OK (ref 4)" return $false } else { Write-Verbose "Property $property OK (ref 2)" } } } } return $true } } |