Link to home
Start Free TrialLog in
Avatar of VIVEKANANDHAN_PERIASAMY
VIVEKANANDHAN_PERIASAMY

asked on

how to update particular value in the appdomain file through powershell

I have many server having appdomain, i want to update only one value on multiple servers.
eg:
<appdomai>
  <test>
    <test acc> ....</test acc>
     </test>
     <data storage sql server=' nameof sqlserver'/>
</appdomain>
i want to update name of sql server in the xml file on multiple server.
Avatar of chrismerritt
chrismerritt

Can you give me an exact copy of the XML document?

Replace any server names etc for security purposes if you want to.
Avatar of VIVEKANANDHAN_PERIASAMY

ASKER


<AppDomain Name="Name" Running="Enabled" LoggingStr="EventLog;AMDRoot\logs\AMD.log" Assembly="AMDRoot\AMDEngine\AMDAgent.exe" ReferencePath="AMDRoot\Name">

                <DataCollector CycleTime="60" CycleStartDelay="0" ThreadMaxStartDelay="evenly" EngineTimeout="50" ApplicationName="Name" SimulatorKind="DOTNET" SimulatorRunAt="Local" SimulatorAssembly="AMDRoot\Name.dll" SimulatorClassName="RelationshipManagement" SessionConfigFile="AMDRoot\Name\Personal.xml" SimulatorTraceFile="AMDRoot\logs\Name\NameTrace.txt" SimulatorLoginAccount="" SimulatorLoginDomain="" SimulatorLoginPassword="" ConnectionLimit="1" MaxServicePoints="" SessionStartBoundaryMinute="1" Debug="1">

                                <AccountDatabase Application="Name" Environment="Production" AccountType="Login" AMDEngine="ServerName" AccountGroup="" UserLoginNameWithDomain="False" ConnectStringEncrypt="False" ConnectString="Server=AMDCentral;Database=Test;Trusted_Connection=Yes;Connect Timeout=60"/>

                                <AccountGroup ScenarioID="Name" TestMode="PersonaTest" UserLoginName="" UserPassword="" Domain="Domainname" Timeout=""  PersonaURL="http://website" BatchAdminURL="http://website" URL="http://website" URL="http://website" PlatformURL="http://website" ErrorFileURL="\\AMDFILE\AMDRoot\logs\Name\Errors\" Trace="1">

                                                <Account TestDataId="Personal" UserAccountHost="Email" Transactions="Dummy_Trans_EA,CreateSync_EA,Lookup"/>
                                                <Account TestDataId="Personan" UserAccountHost="Email4" Transactions="Dummy_Trans_EA,CreateSync_EA,Lookup"/>
                                </AccountGroup>
                                <ShuffledParams></ShuffledParams>
                </DataCollector>
                <DataStorage Source="SQL" UpdateType="SQL" DataSaveMode="Own" CycleTime="" XmlSchemaFile="AMDRoot\Logs\Name\NameDataStorage.xsd" SaveDir="AMDRoot\Logs\Name" MaxLocalFileSize="" ConnectStringEncrypt="False" ConnectString="Server=sqlname;Database=databasename;Trusted_Connection=Yes;Connect Timeout=3000"/>
</AppDomain>


You can try this, however it kicks up an error with your XML saying that the "URL" attribute on Line 7 is a duplicate attribute name, and it's right it is. If I remove one of them it works just fine. Is this a problem in your real files or just the one you put here?

As normal I would highly recommend testing this against a copy of your original files, back stuff up etc before making changes in live! Can't stress this enough!

Assumptions:

I assume here we are updating the SQL server in the connection string, let me know if I've missed the mark.

Alternative Approaches

Some other approaches may include replacing the existing files with an updated copy you have, assuming all the appdomain files have the same content this may be preferable to file level modifications. Likewise if the connection string as a whole needs to be replaced it might be preferable to just update the lot instead of doing what i'm doing, which is trying to split the text out and find what needs to be replaced.

If you can't get the XML to validate in PowerShell then you can echo through the file as a normal file system object operation and replace the content anyway, but the XML method i've used should be more precise as it works against the nodes directly.

Running against multiple machines:

If you can test this against a single file, we can then worry about changing lots of files across your systems, in my experience though get the basics working first, we can use this as an inner code block for an outer query that loops through systems/files so no worries there.

Error Handling:

I included some basic error handling in this, i.e. if you don't provide a valid file path it won't process it, and if the file causes an exception when trying to parse the XML it won't proceed either, and finally any errors on the save will be picked up and return an Exception as well.

The code:

#Customise Vars

$FileName = "C:\TEMP\file1.xml"
$NewSQLServer = "NewSQLServer"

#Attempt to get XML Content

$XMLError = 0

if (Test-Path $FileName)
{
	Try
	{
		$FileContent = Get-Content $FileName
		$XMLFileContent = [xml]$FileContent
	}
	Catch [Exception]
	{
		$XMLError = 1
		Write-Host -ForeGroundColor "Red" -BackGroundColor "Black" "$($_.Exception)"
	}
}
else
{
	$XMLError = 1
	Write-Host "File not Found"
}

if ($XMLError -eq 0)
{
	#Get Current Connect String

	$ConnectString = $XMLFileContent.AppDomain.DataStorage.ConnectString

	#Get SQL Server from Connect String

	$SQLServer = $ConnectString.Substring(0, ($ConnectString.IndexOf(";")))
	$SQLServer = $SQLServer.SubString($SQLServer.IndexOf("=") + 1)

	#Replace SQL Server in Connect String

	$NewConnectString = $ConnectString.Replace($SQLServer, $NewSQLServer)
	$XMLFileContent.AppDomain.DataStorage.ConnectString = $NewConnectString

	#Save XML

	Try
	{
		$XMLFileContent.Save($FileName)
	}
	Catch [Exception]
	{
		Write-Host -ForeGroundColor "Red" -BackGroundColor "Black" "$($_.Exception)"
	}
}

Open in new window

Yes it works. But when i run under mutiple server, nodes will be the same but content inside will varies.So want to confirm will not be affected.

And now how can we make chages to multiple servers?
Are the files always in the same locations on all the servers? or would you need to feed it a CSV with content like this?

Server   | Path
Server1 | C:\Folder\file.xml
Server2 | C:\Folder\SubFolder\file.xml
Server3 | C:\Folder\SubFolder\file2.xml

If the locations are always the same it's simpler to handle but I need your guidance on this really.
chrismerritt: First of all I like to appreciate your support and help,being so patient and understanding.
Your are quite different from others.


actually i have like below

server1|e:\amdroot\messenger\messengerlogappdomain.xml
server1|e:\amdroot\microsoftmicrosoftappdomain.xml
server2|e:\amdroot\yaoo\yahoochatappdomain.xml
server3|e:\amdroot\google\google1appdomain.xml

e:\amdroot\nameoftheapplication\there will be many subfiles.
But in the subfiles we will only one appdomain file.
similarity will be nameoftheapplication+appdomain


Are you able to easily formulate an import list of these in that CSV format? or would you prefer to check the e:\amdroot\ folder for any .xml files and attempt to modify them all automatically for each server?

Either way is good for me, I would just prefer not to make any assumptions on the way you would prefer to do this :)
would prefer to check the e:\amdroot\ folder\xmlfiles for any .xml files and attempt to modify them all automatically for each server

but there are many other xml files ,which shouldn't be disturbed.

For the information: other xml file will not have same set of nodes.
Will the name of the file always contains "appdomain"?
yes it will always contain appdomain.
 like nameoftheapplication+appdomain
ASKER CERTIFIED SOLUTION
Avatar of chrismerritt
chrismerritt

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
$XMLRootPath = "E:\amdroot"
$NewSQLServer = "NewSQLServer"


for this two variable,i like to pass the value through text file.
I tried using
$XMLRootPath = gci c:\temp\path.txt
$NewSQLServer =  gci c:\temp\value.txt

It's giving exception error. how to achieve the above requirement.
You need to use gc instead of gci
yes, i tried get-content as well but it was giving me the same error
I created a file called C:\TEMP\path.txt and put the following in it:

E:\amdroot

Then I ran this in PowerShell:

PS C:\Users\Chris> $XMLRootPath = gc "C:\TEMP\path.txt"
PS C:\Users\Chris> $XMLRootPath | Out-Host
E:\amdroot
PS C:\Users\Chris>

So it appears to work fine via text file.

If you're trying to pass multiple values in those text files it won't work, as you would need a looping logic to handle multiple values.

What is the exception you get?
the below code i have used. and i getting following error

PS C:\Users\vivek\Desktop\sql> & '.\Changing SQL Server name in the appdomain.ps1'
Get-Content : Cannot find path 'C:\Users\vivek\Desktop\sql\servername' becaus
e it does not exist.
At C:\Users\vivek\Desktop\sql\Changing SQL Server name in the appdomain.ps1:1
08 char:33
+ [array]$ServerList = Get-Content <<<<  $ServerListFile
    + CategoryInfo          : ObjectNotFound: (C:\Users\vivek\Desktop\sql\servername:String) [Get-Content], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetCo
   ntentCommand

in the error i have replaced the my hostname with the servername for security reason.
Function ChangeSQL-XML
{
	param
	(
		$FileName,
		$NewSQLServer
	)
	
	#Attempt to get XML Content
	$XMLError = 0

	if (Test-Path $FileName)
	{
		Try
		{
			$FileContent = Get-Content $FileName
			$XMLFileContent = [xml]$FileContent
		}
		Catch [Exception]
		{
			$XMLError = 1
			Write-Host -ForeGroundColor "Red" -BackGroundColor "Black" "$($_.Exception)"
		}
	}
	else
	{
		$XMLError = 1
		Write-Host "File not Found"
	}

	if ($XMLError -eq 0)
	{
		#Get Current Connect String
		$ConnectString = $XMLFileContent.AppDomain.DataStorage.ConnectString

		#Get SQL Server from Connect String
		$SQLServer = $ConnectString.Substring(0, ($ConnectString.IndexOf(";")))
		$SQLServer = $SQLServer.SubString($SQLServer.IndexOf("=") + 1)

		#Replace SQL Server in Connect String
		$NewConnectString = $ConnectString.Replace($SQLServer, $NewSQLServer)
		$XMLFileContent.AppDomain.DataStorage.ConnectString = $NewConnectString
		
		Write-Host -ForeGroundColor "Yellow" "New Connection String: $NewConnectString"

		#Save XML		
		$SaveXMLError = 0

		Try
		{
			$XMLFileContent.Save($FileName)
			Write-Host -ForeGroundColor "Yellow" "Saving XML to file: $FileName"
		}
		Catch [Exception]
		{
			$SaveXMLError = 1
			Write-Host -ForeGroundColor "Red" -BackGroundColor "Black" "$($_.Exception)"
		}
		Finally
		{
			if ($SaveXMLError -eq 0)
			{
				Write-Host -ForeGroundColor "Yellow" "File saved successfully!"
			}
		}
	}
}

Function ScanFor-XML
{
	param
	(
		$NetworkPath,
		$FileSearchName,
		$Filter
	)
	
	$FileArray = @()
	
	if (Test-Path $NetworkPath)
	{
		[array]$FileCollection = gci $NetworkPath -Include $Filter -recurse
		
		if ($FileCollection.Count -gt 0)
		{
			foreach ($File in $FileCollection)
			{
				if ($File.Name -match $FileSearchName)
				{
					Write-Host -ForeGroundColor "Yellow" "Found File: $($File.FullName)"
					$FileArray += $File
				}
			}
		}
	}
	
	return $FileArray
	
}

#Customise Vars
$ServerListFile = gc C:\Users\vivek\Desktop\sql\servers.txt
$XMLRootPath = gc "C:\Users\vivek\Desktop\sql\amdpath.txt"
$FileSearchName = "appdomain"
$Filter = "*.xml"
$NewSQLServer = gc "C:\Users\vivek\Desktop\sql\sqlservername.txt"

[array]$ServerList = Get-Content $ServerListFile

#Check if ServerList contains any entries
if ($ServerList.Count -gt 0)
{
	foreach ($Server in $ServerList)
	{
		Write-Host -ForeGroundColor "Magenta" "Server: $Server"
		
		$XMLNetworkPath = $XMLRootPath.Replace(":","$")
		$NetworkPath = "\\" + $Server + "\" + $XMLNetworkPath
		
		Write-Host -ForeGroundColor "Yellow" "Network Path: $NetworkPath"
		
		[array]$XMLNetworkFiles = ScanFor-XML $NetworkPath $FileSearchName $Filter
		
		if ($XMLNetworkFiles.Count -gt 0)
		{
			foreach ($XMLNetworkFile in $XMLNetworkFiles)
			{
				ChangeSQL-XML $XMLNetworkFile $NewSQLServer
			}
		}
	}
}

Open in new window

Problem is as far as I can see is that you've said:

$ServerListFile = gc C:\Users\vivek\Desktop\sql\servers.txt

Then:

[array]$ServerList = Get-Content $ServerListFile

Basically you're getting the content of the file, then getting the content of the content of the file?

Try this:

$ServerListFile = "C:\Users\vivek\Desktop\sql\servers.txt"

[array]$ServerList = Get-Content $ServerListFile

That should work.
Experts was really very good to work with, i really appreciate his work.
I like award him more points to him.