162 lines
7.1 KiB
PowerShell
162 lines
7.1 KiB
PowerShell
|
function Set-AppSettingPrivate {
|
|||
|
<#
|
|||
|
.SYNOPSIS
|
|||
|
Sets an appSetting Key/Value pair in the specified file. Filepath defaults to the 64 bit machine config.
|
|||
|
|
|||
|
.DESCRIPTION
|
|||
|
Set an appSetting key/value pair in the specified config file.
|
|||
|
Will default to the global 64 bit machine.config file if no config file value specified.
|
|||
|
Will not tickle files where no values have changed.
|
|||
|
Can connect to remote computers as well.
|
|||
|
|
|||
|
.PARAMETER key
|
|||
|
[string] The appSetting key
|
|||
|
|
|||
|
.PARAMETER value
|
|||
|
[string] The appSetting value to set, if it's different than the existing file
|
|||
|
|
|||
|
.PARAMETER filePath
|
|||
|
[string] The location to change settings in. Defaults to the global 64bit machine.config file.
|
|||
|
|
|||
|
.PARAMETER Force
|
|||
|
[switch] Allow forcing the write of the process. This is due to the option for ShouldProcess.
|
|||
|
|
|||
|
.PARAMETER UpdateOnly
|
|||
|
[switch] Only updates a key's value if it exists. Will not create the missing AppSetting key.
|
|||
|
#>
|
|||
|
[CmdletBinding(SupportsShouldProcess, ConfirmImpact='None')]
|
|||
|
param (
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[string]$key,
|
|||
|
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[AllowEmptyString()]
|
|||
|
[string]$value,
|
|||
|
|
|||
|
[Parameter(Mandatory = $false)]
|
|||
|
[Alias("Path")]
|
|||
|
[string]$FilePath = (Get-DotNetConfigPath -use64Bit $true),
|
|||
|
|
|||
|
[Parameter(Mandatory = $false)]
|
|||
|
[switch]$Force,
|
|||
|
|
|||
|
[Parameter(Mandatory = $false)]
|
|||
|
[switch]$UpdateOnly
|
|||
|
)
|
|||
|
|
|||
|
$logLead = (Get-LogLeadName)
|
|||
|
$dirty = $false
|
|||
|
|
|||
|
## Beware of the case of more than one appSettings element in case we run into issues, le sigh
|
|||
|
## Let's go ahead and let the person reading the logs know that there were more than one section
|
|||
|
## In the case of multiple found, we want to save the shortest-path to add our settings to in the future
|
|||
|
## We should track that as we calculate our paths
|
|||
|
|
|||
|
$shortestAppSettingsPathSize = [int]::MaxValue # this keeps us having to do 0 checks as well
|
|||
|
$appSettingsCollection = @($xml.SelectNodes('//appSettings'))
|
|||
|
$preferredAppSettingsLocation = $appSettingsCollection[0]
|
|||
|
|
|||
|
if ($appSettingsCollection.Count -gt 1) {
|
|||
|
Write-Warning "$logLead : More than one appSettings section found to manipulate in [$FilePath]"
|
|||
|
foreach($appSettingElement in $appSettingsCollection) {
|
|||
|
$fullPath = $appSettingElement.Name
|
|||
|
$parentElement = $appSettingElement.ParentNode
|
|||
|
while ($null -ne $parentElement) {
|
|||
|
if($parentElement.Path) {
|
|||
|
$fullPath = $parentElement.Name + '[path=' + $parentElement.Path + ']/' + $fullPath
|
|||
|
} else {
|
|||
|
$fullPath = $parentElement.Name + '/' + $fullPath
|
|||
|
}
|
|||
|
$parentElement = $parentElement.ParentNode
|
|||
|
}
|
|||
|
if ($shortestAppSettingsPathSize -lt $fullPath.length) {
|
|||
|
$preferredAppSettingsLocation = $appSettingElement
|
|||
|
$shortestAppSettingsPathSize = $fullPath.length
|
|||
|
}
|
|||
|
Write-Host $fullPath
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (Test-IsCollectionNullOrEmpty $appSettingsCollection) {
|
|||
|
## We don't have an appSettings element to work with
|
|||
|
## Let's add one and then store it for later use
|
|||
|
Write-Warning "$logLead : No appSettings section found to manipulate in [$FilePath], creating one on <configuration> element"
|
|||
|
[void]$xml.SelectSingleNode("configuration").AppendChild($xml.CreateElement("appSettings"))
|
|||
|
$preferredAppSettingsLocation = @($xml.SelectNodes('//appSettings'))[0]
|
|||
|
$dirty = $true
|
|||
|
}
|
|||
|
|
|||
|
## Now that we have a location to store any settings, let's go see if we have an existing value to update first
|
|||
|
$existingSettingValuesByKey = @($xml.configuration.SelectNodes("//appSettings/add[@key='$key']"))
|
|||
|
|
|||
|
## Crap, we found more than one element with the same key.
|
|||
|
## We can't edit this, but honestly the system can't work with it this way either
|
|||
|
## Something will absolutely break, but it doesn't have to be us today
|
|||
|
if ($existingSettingValuesByKey.length -gt 1) {
|
|||
|
Write-Error "$logLead : Found too many nodes with the same Key value in [$FilePath]!!"
|
|||
|
|
|||
|
foreach($appSettingElement in $existingSettingValuesByKey) {
|
|||
|
$fullPath = $appSettingElement.Name
|
|||
|
$parentElement = $appSettingElement.ParentNode
|
|||
|
while ($null -ne $parentElement) {
|
|||
|
if($parentElement.Path) {
|
|||
|
$fullPath = $parentElement.Name + '[path=' + $parentElement.Path + ']/' + $fullPath
|
|||
|
} else {
|
|||
|
$fullPath = $parentElement.Name + '/' + $fullPath
|
|||
|
}
|
|||
|
$parentElement = $parentElement.ParentNode
|
|||
|
}
|
|||
|
Write-Host "$logLead : Duplicate node found at: $fullPath"
|
|||
|
$appSettingElement.Value = $value
|
|||
|
$dirty = $true
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
# If the UpdateOnly switch is specified, and an AppSetting was not found, early out of saving anything.
|
|||
|
if ($existingSettingValuesByKey.length -eq 0 -and $UpdateOnly) {
|
|||
|
Write-Host "$logLead : Could not find appSetting key '$key'. Not creating AppSetting node because UpdateOnly was specified. Exiting.."
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
## Now we either have a value in $existingSettingValuesByKey or we don't
|
|||
|
## If we have one, we set that value to the requested value
|
|||
|
## If we don't have an element there, we create one and stuff it on $preferredAppSettingsLocation
|
|||
|
## We should track our dirty status so we only update the file if it has to be touched
|
|||
|
## This last part is important for web.config which will bounce a webapp
|
|||
|
if ($existingSettingValuesByKey.length -eq 0) {
|
|||
|
## We don't have a value yet in the file, let's add it to that $preferredAppSettingsLocation we found before
|
|||
|
Write-Host "$logLead : Could not find appSetting key '$key'. Creating one and setting value to '$value'."
|
|||
|
|
|||
|
$dirty = $true
|
|||
|
|
|||
|
$appSettingElement = $xml.CreateElement("add")
|
|||
|
$appSettingElement.SetAttribute("key", $key)
|
|||
|
$appSettingElement.SetAttribute("value", $value)
|
|||
|
[void]$preferredAppSettingsLocation.AppendChild($appSettingElement)
|
|||
|
} else {
|
|||
|
## We have the key already, let's update the value
|
|||
|
$existingValue = @($existingSettingValuesByKey)[0].Value
|
|||
|
|
|||
|
## Ensure that if the case is changed that we for sure update the value (CNE compare)
|
|||
|
if ($existingValue -cne $value) {
|
|||
|
Write-Host "$logLead : Found appSetting '$key', changing value from '$existingValue' to '$value'."
|
|||
|
$dirty = $true
|
|||
|
}
|
|||
|
@($existingSettingValuesByKey)[0].Value = $value
|
|||
|
}
|
|||
|
|
|||
|
if($dirty) {
|
|||
|
Write-Verbose "$logLead : Saving config to path $FilePath"
|
|||
|
|
|||
|
if ($Force -or $PSCmdlet.ShouldProcess("Ready to write the file [$FilePath] contents?")) {
|
|||
|
## Make sure the file gets saved with NoBOM
|
|||
|
$utfNoBOM = New-Object System.Text.UTF8Encoding($false)
|
|||
|
|
|||
|
Save-XMLFile $FilePath $xml.OuterXml $utfNoBOM
|
|||
|
} else {
|
|||
|
Write-Host "$logLead : When confirmed, will write to file [$FilePath] with contents [[$($xml.OuterXml)]]"
|
|||
|
}
|
|||
|
} else {
|
|||
|
Write-Verbose "$logLead : No changes were made to $FilePath"
|
|||
|
}
|
|||
|
}
|