尝试用 Powershell 替换连接字符串设置

Trying to replace connection string settings with Powershell

我是 Powershell 的业余爱好者,但我正在尝试找出一种使用 Powershell 使用通配符查找连接字符串值并将其替换为全新连接字符串的方法。我和一位同事拼凑了一个脚本,用于在连接字符串中查找服务器名称并将其替换为新的服务器名称。现在我需要用一个全新的字符串替换字符串中的全部内容。奖励是我们想对 xml 配置文件和 json 配置文件执行此操作。

我想首先搜索特定的文本,以便我只能找到属于特定数据库的连接。一旦找到它想要替换特定通配符模式内的整个文本。

示例:在

中查找“SpecificServiceName”的值
<add name="DbConnection" connectionString="USER ID=SomeId;PASSWORD=abc123;DATA SOURCE=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=servername.mydomain.com)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=SpecificServiceName)));PERSIST SECURITY INFO=True" providerName="Oracle.ManagedDataAccess.Client" />

如果找到了,那么我想用新的东西替换 connectionString 值中的所有内容。因此,寻找 connectionString=”*” 并将 “*” 替换为

"USER ID=myNewUserId;PASSWORD=newPassword;DATA SOURCE=(DESCRIPTION_LIST = (LOAD_BALANCE = off)(FAILOVER = on)(DESCRIPTION =(CONNECT_TIMEOUT = 5)(TRANSPORT_CONNECT_TIMEOUT = 3)(RETRY_COUNT = 3)(ADDRESS_LIST =(LOAD_BALANCE = on)(ADDRESS = (PROTOCOL = TCP)(HOST = primaryserver.domain.com)(PORT = 1521)))(CONNECT_DATA =(SERVICE_NAME = SERVICENAME_SVC)))(DESCRIPTION =(CONNECT_TIMEOUT = 5)(TRANSPORT_CONNECT_TIMEOUT = 3)(RETRY_COUNT = 3)(ADDRESS_LIST =(LOAD_BALANCE = on)(ADDRESS = (PROTOCOL = TCP)(HOST = drserver.domain.com)(PORT = 1521)))(CONNECT_DATA =(SERVICE_NAME = SERVICENAME_SVC))));PERSIST SECURITY INFO=True" providerName="Oracle.ManagedDataAccess.Client"

对于这样的事情,您有什么建议或示例可以向我指出吗?

这是我们用来在连接字符串中查找特定服务器名称的方法。它适用于仅替换服务器名称,但我正在尝试做类似的事情,但在上述情况下。

#After this runs, run it again to double check that all the words to replace were actually replaced. You can just run it, and then choose n, or close the script and check the ResultFile.csv. If its empty, then all of them should be updated.
 
$folderPath = "D:\Inetpub\websites"
$wordToReplace = "oldServerName"
$wordToUpdate = "newServerName"
$exportCSVPath = "D:\Script Files\ResultFile.csv"
 
#Send list of files to csv
$filesToUpdate = Get-ChildItem -Path $folderPath -include ('*.config', '*.json') -Recurse | Select-String $wordToReplace -List | Select Path | Export-CSV $exportCSVPath
 
 
#Ask for validation
Write-Output "Replacing $($wordToReplace) with $($wordToUpdate) in $($folderPath)"
$response = Read-Host "Check D:\ResultFile.csv and make sure everything listed should be updated! Confirm (y/n)"
 
#If response = y
if($response -eq "y"){
 
#Get list again, for some reason, the above list gets deleted...maybe because we are sending to CSV?
$files = Get-ChildItem -Path $folderPath -include ('*.config', '*.json') -Recurse | Select-String $wordToReplace -List | Select Path
 
#Print out each file name and update
foreach($file in $files){
Write-Output "Updating file: $($file.Path)"
 
#probably a better way to do upper vs lower, but didnt spend time researching...for some reason this isnt case insensitive, but the above ones are...
Get-ChildItem $file.Path -Recurse | ForEach {(Get-Content $_).Replace($wordToReplace.ToUpper(), $wordToUpdate)  |  Set-Content $_ }
 
Get-ChildItem $file.Path -Recurse | ForEach {(Get-Content $_).Replace($wordToReplace.ToLower(), $wordToUpdate)  |  Set-Content $_ }
}
}
else{
Write-Output "Update Aborted"
}
 
#just pauses to see output
$response = Read-Host

应用程序设置示例

    {
      "FolderLocation": {
        "Input": "D:\ImportFiles\AppName",
        "Export": "D:\ImportFiles\AppName\Export",
      },
      "FileName": {
        "Input": "InputFileName.csv",
        "Export": "ExportFileName.txt"
      },
      "ConnectionStrings": {
        "DbConnection": "user id=MyUserId;password=ABC123;data source=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=Server1Name.com)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=MyServiceName)))",
"OtherConnection": "user id=MyUserId;password=ABC123;data source=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=Server1Name.com)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=MyServiceName)))"
      },
      "Logging": {
        "IncludeScopes": false,
        "LogLevel": {
          "Default": "Trace",
          "Microsoft": "Warning",
          "Microsoft.Hosting.Lifetime": "Information"
        }
      },
      "AllowedHosts": "*"
    }

继续我的评论,你真的应该操纵 XML 和 JSON 而不是常规的 plain-text。

假设您的 (XML).config 文件如下所示:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="DbConnection" connectionString="USER ID=SomeId;PASSWORD=abc123;DATA SOURCE=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=servername.mydomain.com)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=SpecificServiceName)));PERSIST SECURITY INFO=True" providerName="Oracle.ManagedDataAccess.Client" />
  </connectionStrings>
</configuration>

你的 .json 文件是这样的:

{
    "configuration": {
        "connectionStrings": {
            "add": {
                "name": "DbConnection",
                "connectionString": "USER ID=SomeId;PASSWORD=abc123;DATA SOURCE=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=servername.mydomain.com)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=SpecificServiceName)));PERSIST SECURITY INFO=True",
                "providerName": "Oracle.ManagedDataAccess.Client"
            }
        }
    }
}

如果代码中没有那些 Read-Host 行(我认为您应该在输入代码之前完成所有用户问题),我会这样做:

$folderPath          = 'D:\Inetpub\websites'
$exportCSVPath       = 'D:\somewhere\UpdateResults.csv'
$wordToSearch        = 'SpecificServiceName'
$newConnectionString = 'USER ID=myNewUserId;PASSWORD=newPassword;DATA SOURCE=(DESCRIPTION_LIST = (LOAD_BALANCE = off)(FAILOVER = on)(DESCRIPTION =(CONNECT_TIMEOUT = 5)(TRANSPORT_CONNECT_TIMEOUT = 3)(RETRY_COUNT = 3)(ADDRESS_LIST =(LOAD_BALANCE = on)(ADDRESS = (PROTOCOL = TCP)(HOST = primaryserver.domain.com)(PORT = 1521)))(CONNECT_DATA =(SERVICE_NAME = SERVICENAME_SVC)))(DESCRIPTION =(CONNECT_TIMEOUT = 5)(TRANSPORT_CONNECT_TIMEOUT = 3)(RETRY_COUNT = 3)(ADDRESS_LIST =(LOAD_BALANCE = on)(ADDRESS = (PROTOCOL = TCP)(HOST = drserver.domain.com)(PORT = 1521)))(CONNECT_DATA =(SERVICE_NAME = SERVICENAME_SVC))));PERSIST SECURITY INFO=True" providerName="Oracle.ManagedDataAccess.Client"'

# get a list of the files
$configFiles = Get-ChildItem -Path $folderPath -Include ('*.config', '*.json') -File -Recurse

$result = foreach ($file in $configFiles) {
    # do NOT use Write-Output because that will mess up the $result
    Write-Host "Processing file '$($file.FullName)'"

    # create a flag to track if the file is updated or not
    $updated = $false
    # prepare an object for the CSV
    $objOut = [PsCustomObject]@{
                  File    = $file.FullName
                  Updated = $updated
                  Status  = $null
              }

    # what filetype?
    if ($file.Extension -eq '.json') {
        try {
            $json = (Get-Content -Path $file.FullName -Raw) | ConvertFrom-Json -ErrorAction Stop
            $json.configuration.connectionStrings.add | Where-Object { $_.connectionString -like "*$wordToSearch*" } | ForEach-Object {
                $_.connectionString = $newConnectionString
                $updated = $true
            }
            if ($updated) { 
                $json | ConvertTo-Json -Depth 99 | Set-Content -Path $file.FullName -Force   # Depth 99 for safety..
            }
            # fill the output objects details
            $objOut.Updated = $updated
            $objOut.Status  = 'OK'
        }
        catch {
            # set the error in the output object
            $objOut.Status  = "Error: $($_.Exception.Message)"
        }
    }
    else {  
        # assume XML
        try {
            $xml = [System.Xml.XmlDocument]::new()
            $xml.Load($file.FullName)
            $xml.DocumentElement.connectionStrings.add | Where-Object { $_.connectionString -like "*$wordToSearch*" } | ForEach-Object {
                $_.connectionString = $newConnectionString
                $updated = $true
            }
            if ($updated) { 
                $xml.Save($file.FullName) 
            }
            # fill the output objects details
            $objOut.Updated = $updated
            $objOut.Status  = 'OK'
        }
        catch {
            # set the error in the output object
            $objOut.Status  = "Error: $($_.Exception.Message)"
        }
    }
    # output the object so it gets collected in variable $result
    $objOut
}

# output on screen
$result

# save the results as CSV file
$result | Export-Csv -Path $exportCSVPath -NoTypeInformation

不用说,您应该先 复制 D:\Inetpub\websites 文件夹的内容!

P.S。 PowerShell 创建有效 JSON,但格式非常难看。如果你想美化它,你可以使用我之前发布的功能


使用您的示例 JSON,更改 if ($file.Extension -eq '.json') {..} 块中的这一部分

 $json.configuration.connectionStrings.add | Where-Object { $_.connectionString -like "*$wordToSearch*" } | ForEach-Object {
    $_.connectionString = $newConnectionString
    $updated = $true
}

进入

$json.connectionStrings | Where-Object { $_.DbConnection -like "*$wordToSearch*" } | ForEach-Object {
    $_.DbConnection = $newConnectionString
    $updated = $true
}

如果您不能依赖元素的名称 DbConnection,您可以改用此循环:

# get the actual element name and value, filter the values that pass the comparison
$json.connectionStrings.PsObject.Properties | Where-Object { $_.Value -like "*$wordToSearch*" } | ForEach-Object {
    $json.connectionStrings.$($_.Name) = $newConnectionString
    $updated = $true
}