Generating Printer Preferences XML - powershell

I'm writing a script that takes the name of a printer from a csv file and for each printer it is supposed to modify a xml printer preference, change the name of the printer, generate a new guid and output the new xml file. It almost works. Problem is that the script also changes the clsid, because it has the same regex as the uid. Only the uid is supposed to change, in other words: only the second occurrence of the matching regex.
Heres what the xml file looks like:
<SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="cop-fibu-eink" status="cop-fibu-eink" image="1" changed="2021-02-18 14:52:29" uid="{7D783062-5840-4CBD-BE9A-6334FD87A8D6}" removePolicy="1" userContext="1" bypassErrors="1"><Properties action="R" comment="" path="\\srv-print01\cop-fibu-eink" location="" default="1" skipLocal="0" deleteAll="0" persistent="0" deleteMaps="0" port=""/><Filters><FilterGroup bool="AND" not="0" name="EKBDOM1\cop-fibu-eink" sid="S-1-5-21-356607561-1380008178-1848903544-16152" userContext="0" primaryGroup="0" localGroup="0"/></Filters></SharedPrinter>
And this is the Powershell script:
Import-Csv .\printer_groups.csv | ForEach-Object {
$uid = ([guid]::NewGuid()).guid
((Get-Content -path C:\pc_inst\druckerliste\cop-it.xml -Raw) -replace "cop-fibu-eink", $($_.name)` -replace "\{[0-9a-z\-]*\}", $uid ) | Set-Content -Path .\xmls\$($_.name).xml
}
I can't figure it out. Hope you guys can help me. (:

My colleague figured it out. In case anyone is wondering:
Import-Csv C:\pc_inst\druckerliste\printer_groups.csv | ForEach-Object {
$uid = ([guid]::NewGuid()).guid
((Get-Content -path C:\pc_inst\druckerliste\cop-it.xml -Raw) -replace "cop-fibu-eink", $($_.name)` -replace 'uid="\{[0-9a-z\-]*\}"', ('uid="{' + $uid + '}"') ) | Set-Content -Path C:\pc_inst\druckerliste\drucker_xml_standard\$($_.name).xml
}

If your file content is xml, why not treat it as xml as opposed to plain text?
$oldText = #"
<SharedPrinter clsid="{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}" name="cop-fibu-eink" status="cop-fibu-eink" image="1" changed="2021-02-18 14:52:29" uid="{7D783062-5840-4CBD-BE9A-6334FD87A8D6}" removePolicy="1" userContext="1" bypassErrors="1">
<Properties action="R" comment="" path="\\srv-print01\cop-fibu-eink" location="" default="1" skipLocal="0" deleteAll="0" persistent="0" deleteMaps="0" port="" />
<Filters>
<FilterGroup bool="AND" not="0" name="EKBDOM1\cop-fibu-eink" sid="S-1-5-21-356607561-1380008178-1848903544-16152" userContext="0" primaryGroup="0" localGroup="0" />
</Filters>
</SharedPrinter>
"#
$xml = [xml] $oldText;
and then you can change attribute values easily like this:
$xml.SharedPrinter.uid = "{" + [Guid]::NewGuid().ToString().ToUpperInvariant() + "};
and serialize it back to xml:
$newText = $xml.OuterXml;
write-host $newText;
Note - it might not matter, but your original uid attribute has enclosing braces and upper case letters (uid="{7D783062-5840-4CBD-BE9A-6334FD87A8D6}") so I've matched that in the new value...

I used something like this. It's not well-structured but it Just Works(tm). It does all off the GPP stuff instead of your problem, but you can get the overall idea.
# Configuration variables
$PrintServer = ""
$CSVFilePath = "printers.csv"
$XMLFilePath = "printers.xml"
# The code
Import-Module ActiveDirectory
$Domain = (Get-ADDomain).name
$ScriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$Now = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
# Set The Formatting
$XMLSettings = New-Object System.Xml.XmlWriterSettings
$XMLSettings.Indent = $true
$XMLSettings.IndentChars = " "
# Set the File Name Create The Document
$XMLWriter = [System.XML.XmlWriter]::Create($XMLfilePath, $XMLSettings)
# Write the XML Declaration
$XMLWriter.WriteStartDocument()
# Start the Root Element
$XMLWriter.WriteComment('Root of the policy')
$XMLWriter.WriteStartElement('Printers') # Start root
$XMLWriter.WriteAttributeString('clsid', '{1F577D12-3D1B-471e-A1B7-060317597B9C}')
Import-Csv (Join-Path -Path $ScriptPath -ChildPath $CSVFilePath) | ForEach-Object {
$GroupName = $_.Group
[Microsoft.ActiveDirectory.Management.ADGroup]$Group = Get-ADGroup -Filter { name -like $_.GroupName }
# Start SharedPrinter Element
$XMLWriter.WriteStartElement('SharedPrinter') # Start SharedPrinter
$XMLWriter.WriteAttributeString('clsid', '{9A5E9697-9095-436d-A0EE-4D128FDFBCE5}')
$XMLWriter.WriteAttributeString('name', $_.Printer)
$XMLWriter.WriteAttributeString('status', $_.Printer)
$XMLWriter.WriteAttributeString('image', '1')
$XMLWriter.WriteAttributeString('bypassErrors', '1')
$XMLWriter.WriteAttributeString('changed', $Now)
$XMLWriter.WriteAttributeString('uid', [System.Guid]::NewGuid().ToString())
# Start Properties Element
$XMLWriter.WriteStartElement('Properties') # Start Properties
$XMLWriter.WriteAttributeString('action', 'R')
$XMLWriter.WriteAttributeString('comment', '')
$XMLWriter.WriteAttributeString('path', "\\$PrintServer\$($_.Printer)")
$XMLWriter.WriteAttributeString('location', '')
$XMLWriter.WriteAttributeString('default', $_.Default)
$XMLWriter.WriteAttributeString('skipLocal', '0')
$XMLWriter.WriteAttributeString('deleteAll', '0')
$XMLWriter.WriteAttributeString('persistent', '0')
$XMLWriter.WriteAttributeString('deleteMaps', '0')
$XMLWriter.WriteAttributeString('port', '')
$XMLWriter.WriteEndElement() # End Properties
# Start Filters Element
$XMLWriter.WriteStartElement('Filters') # Start Filters
# Start FilterGroup Element
$XMLWriter.WriteStartElement('FilterGroup')
$XMLWriter.WriteAttributeString('bool', 'AND')
$XMLWriter.WriteAttributeString('not', '0')
$XMLWriter.WriteAttributeString('name', "$Domain\$($Group.Name)")
$XMLWriter.WriteAttributeString('sid', $Group.SID)
$XMLWriter.WriteAttributeString('userContext', '1')
$XMLWriter.WriteAttributeString('primaryGroup', '0')
$XMLWriter.WriteAttributeString('localGroup', '0')
$XMLWriter.WriteEndElement() # End FilterGroup
$XMLWriter.WriteEndElement() # End Filters
$XMLWriter.WriteEndElement() # End SharedPrinter
}
$XMLWriter.WriteEndElement() # End root
# End, Finalize and close the XML Document
$XMLWriter.WriteEndDocument()
$XMLWriter.Flush()
$XMLWriter.Close()

Related

Powershell script to search and replace text in a file using two columns in a separate reference file

I want a script that can help me check for the name of keyset (column a) in Sample.cvs and then replace the current command(column b) with new command (column c) in the Source text file.
CSV file: Sample.csv
A. | B. | C.
Manock | 2B | 2ab
Sterling | 3F | 3sf
Source file text: Source.txt
keyset "Manock"
(
key("SELECT")
command ("display/app=%disapp% "2B")
);
So desired output:
keyset "Manock"
(
key("SELECT")
command ("display/app=%disapp% "2ab")
);
Powershell Script:
New-Item -Path "C:\Users\e076200\Desktop\ks_update\source.txt" -ItemType File -Force
$data = Get-Content C:\Users\e076200\Desktop\ks_update\source.ddl
Add-Content -Value $data -Path "C:\Users\e076200\Desktop\ks_update\source.txt"
$foundline = $false
$a = 0
$Etxt = foreach($line in Get-Content C:\Users\e076200\Desktop\ks_update\source.txt)
{
if ($line -match 'keyset "Manock"' )
{
$a = 0
$foundline = $true
}
$a= $a + 1
if($line -match "display/app" -and $a -eq 5 -and $foundline -eq $true)
{
$line = $line.replace('2b' , '2ab')
$line
}
else
{
$line
}
}
$Etxt | Set-Content C:\Users\e076200\Desktop\ks_update\source.txt -Force
$users = Import-CSV -Path:\Users\e076200\Desktop\ks_update\sample.csv
I've figured out how to find and replace one line in the file directly. I've also figured out how to import the csv. I need help on how to make the logic parameterized and use column A of CSV as the match piece and column c as the replacement piece.
Script Explanation.
New-Item -Path "C:\Users\e076200\Desktop\ks_update\source.txt" -ItemType File -Force
New-Item creates new text file # location defined by -Path using name specified at the end, source.
ItemType to define type of document, -Force is force command.
$data = Get-Content C:\Users\e076200\Desktop\ks_update\source.ddl
Retrieves ddl and stores in variable.
Add-Content -Value $data -Path "C:\Users\e076200\Desktop\ks_update\source.txt"
Transfers content from variable to new text file created.
$foundline = $false
conditional variable defined for when keyset identifier is found.
$a = 0
counter defined for if statement.
$Etxt = foreach($line in Get-Content C:\Users\e076200\Desktop\ks_update\source.txt)
$Etxt - for loop
$line - variable for each line in txt
{
if ($line -match 'keyset "Manock"' )
{
$a = 0
$foundline = $true
}
If keyset identifier is found, set counter to 0 and set conditional variable to true
$a= $a + 1
if($line -match "display/app" -and $a -eq 5 -and $foundline -eq $true)
{
$line = $line.replace('2b' , '2ab')
$line
Match found, PS runs logic, line with keyset identifier == 0 + 1....up until line = 5 where we find item to be replaced.
For redundancy, line reader set to check for line identifier, ("display/app") on expected line.
If Redundant check if met and counter is 5 then we replace word with the line.replace function.
Overwritten data is returned in $line
}
else
{
$line
}
Else retain line
}
$Etxt | Set-Content C:\Users\e076200\Desktop\ks_update\source.txt -Force
Updated text file
$users = Import-CSV -Path:\Users\e076200\Desktop\ks_update\sample.csv
Imports Reference csv file
Please make explanation as dumbed down as possible. Thank you.

Powershell replace from array strange behaviour

Problem is solved, but I don't understand why :-)
I have a Powershell script that perform replacements inside files (language metadata):
loads a list of replacement from a txt file into an array
gets all xml files from a Start folder
performs all the replacements from the array
performs a replacement on the filename based on the array first entry
saves the resulting files in a End folder
I've been using successfully variations of the exact same script for many years, with the only thing changing being the replacement file name and content... except today when creating another variant. The only change was the content of the substitution file, and suddenly the replacement did not happen anymore in the filename.
Here is the code:
#load the replacements from file
$data = Import-Csv -Path substitutions.txt -Header "Source", "Target", "Safe", "Count" -Delimiter "|"
#load the files to be processed
$xmlfiles = -join ($Startfolder, "*.xml")
$Fileset = Get-ChildItem $xmlfiles -recurse
foreach ($File in $Fileset) {
$NewFileName = ""
$WipFile = Get-Content $File
# set safe replacement flag to nothing
$flag = ""
#perform replacements
foreach ($item in $data) {
if ($WipFile -cmatch $item.Source) {
if ($item.Safe -eq 'yes') {
$WipFile = $WipFile -creplace $item.Source, $item.Target
$item.Count = $item.Count + 1
}
else {
$WipFile = $WipFile -creplace $item.Source, $item.Target
$item.Count = $item.Count + 1
$flag = "TOCHECK "
}
}
}
#replace language code in filename, based on first entry in the substitution list
$NewFileName = -join ($Endfolder, $flag, $file.name -creplace $data.Source[0], $data.Target[0])
Write-Host $NewFileName
#save file with updated content
$WipFile | Set-Content -Encoding Unicode ($File)
#move file to End folder
Move-Item $File $NewFileName
}
The substitution file is formatted as follows:
nl-NL|nl-BE|yes
After testing more, I discovered my new variant was failing if my substitution file had only one line. Add another one, and it works. How come?

Powershell Files fetch

Am looking for some help to create a PowerShell script.
I have a folder where I have lots of files, I need only those file that has below two content inside it:
must have any matching string pattern as same as in file file1 (the content of file 1 is -IND 23042528525 or INDE 573626236 or DSE3523623 it can be more strings like this)
also have date inside the file in between 03152022 and 03312022 in the format mmddyyyy.
file could be old so nothing to do with creation time.
then save the result in csv containing the path of the file which fulfill above to conditions.
Currently am using the below command that only gives me the file which fulfilling the 1 condition.
$table = Get-Content C:\Users\username\Downloads\ISIN.txt
Get-ChildItem `
-Path E:\data\PROD\server\InOut\Backup\*.txt `
-Recurse |
Select-String -Pattern ($table)|
Export-Csv C:\Users\username\Downloads\File_Name.csv -NoTypeInformation
To test if a file contains a certain keyword from a range of keywords, you can use regex for that. If you also want to find at least one valid date in format 'MMddyyyy' in that file, you need to do some extra work.
Try below:
# read the keywords from the file. Ensure special characters are escaped and join them with '|' (regex 'OR')
$keywords = (Get-Content -Path 'C:\Users\username\Downloads\ISIN.txt' | ForEach-Object {[regex]::Escape($_)}) -join '|'
# create a regex to capture the date pattern (8 consecutive digits)
$dateRegex = [regex]'\b(\d{8})\b' # \b means word boundary
# and a datetime variable to test if a found date is valid
$testDate = Get-Date
# set two variables to the start and end date of your range (dates only, times set to 00:00:00)
$rangeStart = (Get-Date).AddDays(1).Date # tomorrow
$rangeEnd = [DateTime]::new($rangeStart.Year, $rangeStart.Month, 1).AddMonths(1).AddDays(-1) # end of the month
# find all .txt files and loop through. Capture the output in variable $result
$result = Get-ChildItem -Path 'E:\data\PROD\server\InOut\Backup'-Filter '*.txt'-File -Recurse |
ForEach-Object {
$content = Get-Content -Path $_.FullName -Raw
# first check if any of the keywords can be found
if ($content -match $keywords) {
# now check if a valid date pattern 'MMddyyyy' can be found as well
$dateFound = $false
$match = $dateRegex.Match($content)
while ($match.Success -and !$dateFound) {
# we found a matching pattern. Test if this is a valid date and if so
# set the $dateFound flag to $true and exit the while loop
if ([datetime]::TryParseExact($match.Groups[1].Value,
'MMddyyyy',[CultureInfo]::InvariantCulture,
[System.Globalization.DateTimeStyles]::None,
[ref]$testDate)) {
# check if the found date is in the set range
# this tests INCLUDING the start and end dates
$dateFound = ($testDate -ge $rangeStart -and $testDate -le $rangeEnd)
}
$match = $match.NextMatch()
}
# finally, if we also successfully found a date pattern, output the file
if ($dateFound) { $_.FullName }
elseif ($content -match '\bUNKNOWN\b') {
# here you output again, because unknown was found instead of a valid date in range
$_.FullName
}
}
}
# result is now either empty or a list of file fullnames
$result | set-content -Path 'C:\Users\username\Downloads\MatchedFiles.txt'

How to split through the whole list using PowerShell

In my CSV file I have "SharePoint Site" column and a few other columns. I'm trying to split the ID from "SharePoint Site" columns and put it to the new column call "SharePoint ID" but not sure how to do it so I'll be really appreciated If I can get any help or suggestion.
$downloadFile = Import-Csv "C:\AuditLogSearch\New folder\Modified-Audit-Log-Records.csv"
(($downloadFile -split "/") -split "_") | Select-Object -Index 5
CSV file
SharePoint Site
Include:[https://companyname-my.sharepoint.com/personal/elksn7_nam_corp_kl_com]
Include:[https://companyname-my.sharepoint.com/personal/tzksn_nam_corp_kl_com]
Include:[https://companyname.sharepoint.com/sites/msteams_c578f2/Shared%20Documents/Forms/AllItems.aspx?id=%2Fsites%2Fmsteams%5Fc578f2%2FShared%20Documents%2FBittner%2DWilfong%20%2D%20Litigation%20Hold%2FWork%20History&viewid=b3e993a1%2De0dc%2D4d33%2D8220%2D5dd778853184]
Include:[https://companyname.sharepoint.com/sites/msteams_c578f2/Shared%20Documents/Forms/AllItems.aspx?id=%2Fsites%2Fmsteams%5Fc578f2%2FShared%20Documents%2FBittner%2DWilfong%20%2D%20Litigation%20Hold%2FWork%20History&viewid=b3e993a1%2De0dc%2D4d33%2D8220%2D5dd778853184]
Include:[All]
After spliting this will show it under new Column call "SharePoint ID"
SharePoint ID
2. elksn
3. tzksn
4. msteams_c578f2
5. msteams_c578f2
6. All
Try this:
# Import csv into an array
$Sites = (Import-Csv C:\temp\Modified-Audit-Log-Records.csv).'SharePoint Site'
# Create Export variable
$Export = #()
# ForEach loop that goes through the SharePoint sites one at a time
ForEach($Site in $Sites){
# Clean up the input to leave only the hyperlink
$Site = $Site.replace('Include:[','')
$Site = $Site.replace(']','')
# Split the hyperlink at the fifth slash (Split uses binary, so 0 would be the first slash)
$SiteID = $Site.split('/')[4]
# The 'SharePoint Site' Include:[All] entry will be empty after doing the split, because it has no 4th slash.
# This If statement will detect if the $Site is 'All' and set the $SiteID as that.
if($Site -eq 'All'){
$SiteID = $Site
}
# Create variable to export Site ID
$SiteExport = #()
$SiteExport = [pscustomobject]#{
'SharePoint ID' = $SiteID
}
# Add each SiteExport to the Export array
$Export += $SiteExport
}
# Write out the export
$Export
A concise solution that appends a Sharepoint ID column to the existing columns by way of a calculated property:
Import-Csv 'C:\AuditLogSearch\New folder\Modified-Audit-Log-Records.csv' |
Select-Object *, #{
Name = 'SharePoint ID'
Expression = {
$tokens = $_.'SharePoint Site' -split '[][/]'
if ($tokens.Count -eq 3) { $tokens[1] } # matches 'Include:[All]'
else { $tokens[5] -replace '_nam_corp_kl_com$' }
}
}
Note:
To see all resulting column values, pipe the above to Format-List.
To re-export the results to a CSV file, pipe to Export-Csv
You have 3 distinct patterns you are trying to extract data from. I believe regex would be an appropriate tool.
If you are wanting the new csv to just have the single ID column.
$file = "C:\AuditLogSearch\New folder\Modified-Audit-Log-Records.csv"
$IdList = switch -Regex -File ($file){
'Include:.+(?=/(\w+?)_)(?<=personal)' {$matches.1}
'Include:(?=\[(\w+)\])' {$matches.1}
'Include:.+(?=/(\w+?)/)(?<=sites)' {$matches.1}
}
$IdList |
ConvertFrom-Csv -Header "Sharepoint ID" |
Export-Csv -Path $newfile -NoTypeInformation
If you want to add a column to your existing CSV
$file = "C:\AuditLogSearch\New folder\Modified-Audit-Log-Records.csv"
$properties = ‘*’,#{
Name = 'Sharepoint ID'
Expression = {
switch -Regex ($_.'sharepoint Site'){
'Include:.+(?=/(\w+?)_)(?<=personal)' {$matches.1}
'Include:(?=\[(\w+)\])' {$matches.1}
'Include:.+(?=/(\w+?)/)(?<=sites)' {$matches.1}
}
}
}
Import-Csv -Path $file |
Select-Object $properties |
Export-Csv -Path $newfile -NoTypeInformation
Regex details
.+ Match any amount of any character
(?=...) Positive look ahead
(...) Capture group
\w+ Match one or more word characters
? Lazy quantifier
(?<=...) Positive look behind
This would require more testing to see if it works well, but with the input we have it works, the main concept is to use System.Uri to parse the strings. From what I'm seeing, the segment you are looking for is always the third one [2] and depending on the previous segments, perform a split on _ or trim the trailing / or leave the string as is if IsAbsoluteUri is $false.
$csv = Import-Csv path/to/test.csv
$result = foreach($line in $csv)
{
$uri = [uri]($line.'SharePoint Site' -replace '^Include:\[|]$')
$id = switch($uri)
{
{-not $_.IsAbsoluteUri} {
$_
break
}
{ $_.Segments[1] -eq 'personal/' } {
$_.Segments[2].Split('_')[0]
break
}
{ $_.Segments[1] -eq 'sites/' } {
$_.Segments[2].TrimEnd('/')
}
}
[pscustomobject]#{
'SharePoint Site' = $line.'SharePoint Site'
'SharePoint ID' = $id
}
}
$result | Format-List

How to change powershell script output behavior

I am not a scripter, please if anyone can help me with modify below script by removing UCPID value but keep only servername
Currently below script looking two columns from csv file, now I want to change the behavior to only look for ServerName because now CSV file have only one column which containing server only in each row and update related XML.
$data = Import-Csv .\MyFile.csv
$luTable = #{}
# Create Keys in Lookup Table
$data | % {
if (!$luTable.ContainsKey("$($_.ServerName)")) { $luTable["$($_.UCPID)"] = New-Object System.Collections.ArrayList }
}
$luTable.Keys | % {
$key = $_ # Store Key
$data | where UCPID -Match $_ | select ServerName | % {
$luTable[$key].Add($_.ServerName)
}
}
# Build XML Files
$luTable.Keys | % {
$key = $_
$filetext = gc ".\MyXML.xml"
$filetext = $filetext.Replace("#Title#", $key)
$targets = ""
$luTable[$key] | % {
$targets += "<ComputerName>$($_)</ComputerName>"
}
$filetext = $filetext.Replace("#computername#", $targets)
sc -Path ".\$($key).xml" -Value $filetext
}
I tried deleting below code but its not helping.
# Create Keys in Lookup Table
$data | % {
if (!$luTable.ContainsKey("$($_.ServerName)")) { $luTable["$($_.UCPID)"] = New-Object System.Collections.ArrayList }
}
//CSV file content
ServerName
Server1
Server2
Server3
Server4
Server5
//XML - location where I want server to be copied
<AnnounceOffer>false</AnnounceOffer>
<OfferCategory>false</OfferCategory>
<OfferDescriptionHTML>false</OfferDescriptionHTML>
</SettingsLocks>
<IsUrgent>false</IsUrgent>
<Target>
#computername#
</Target>
</SingleAction>
</BES>
#computername# must be replaced with below-
<ComputerName>Server1</ComputerName>
<ComputerName>Server2</ComputerName>
<ComputerName>Server3</ComputerName>
<ComputerName>Server4</ComputerName>
If your XML looks like this:
<BES>
<SingleAction>
<SettingsLocks>
<AnnounceOffer>false</AnnounceOffer>
<OfferCategory>false</OfferCategory>
<OfferDescriptionHTML>false</OfferDescriptionHTML>
</SettingsLocks>
<IsUrgent>false</IsUrgent>
<Target>
#computername#
</Target>
</SingleAction>
</BES>
Then here's two alternatives for you:
Method 1: use XML functionality of PowerShell
# load the xml from file
$xml= New-Object System.XML.XMLDocument
$xml.Load("D:\Test\MyXML.xml")
# select the node with the #computername# placeholder inside
$targetNode = $xml.SelectSingleNode('//BES/SingleAction/Target')
$targetNode.'#text' = '' # remove the placeholder text
# read the servernames from file and create and insert new nodes for each of them
(Import-Csv -Path 'D:\Test\AllServers.csv').ServerName |
ForEach-Object {
$newNode = $xml.CreateElement('ComputerName')
$newNode.InnerText = $_
$targetNode.AppendChild($newNode)
}
# save the updated XML
$xml.Save('D:\Test\NewXml.xml')
Method 2: treat the xml as plain text and do a simple textual -replace on it
# read the XML as multiline text
$xml = Get-Content -Path 'D:\Test\MyXML.xml' -Raw
# find the line where the #computername# placeholder is and get the indentation value
$indent = ' ' * ($xml | Select-String -Pattern '(?m)^(\s+)#computername#').Matches[0].Groups[1].Length
# read the servernames from file and construct a multiline string
$servers = ((Import-Csv -Path 'D:\Test\AllServers.csv').ServerName |
ForEach-Object { "$indent<ComputerName>$_</ComputerName>" }) -join [environment]::NewLine
# now replace in the xml and write to (new) file
$xml -replace "(?m)^$indent#computername#", $servers | Set-Content -Path 'D:\Test\NewXml.xml' -Encoding UTF8
Result in both cases:
<BES>
<SingleAction>
<SettingsLocks>
<AnnounceOffer>false</AnnounceOffer>
<OfferCategory>false</OfferCategory>
<OfferDescriptionHTML>false</OfferDescriptionHTML>
</SettingsLocks>
<IsUrgent>false</IsUrgent>
<Target>
<ComputerName>Server1</ComputerName>
<ComputerName>Server2</ComputerName>
<ComputerName>Server3</ComputerName>
<ComputerName>Server4</ComputerName>
<ComputerName>Server5</ComputerName>
</Target>
</SingleAction>
</BES>