Update multiple config files using PowerShell scripting - powershell

The task is to update the app Settings in web.config and app.config using the power-shell scripting. After some search I found some script to update single file but not for multiple files. Can anyone help?
$Config = C:\inetpub\wwwroot\TestService\Web.config
$doc = (Get-Content $Config) -as [Xml]
$obj = $doc.configuration.appSettings.add | where {$_.Key -eq 'SCVMMServerName'}
$obj.value = CPVMM02
$doc.Save($Config)

I can give you a logical set off. You can get that line which you want to update using the -match in select-string then similarly you can select the remaining things which is already there in file using -notmatch.
Put them in variables. Update the line, store it back in the variable.
Then set both(the modified line variable and the remaining values which you have not modified) back to the file using set-content
Hope you got a set off on how to approach

There are many ways to do this, for instance:
"C:\inetpub\wwwroot\TestService\Web.config",
"C:\inetpub\wwwroot\TestService\App.config" |
ForEach-Object {
$doc = (Get-Content $_) -as [Xml]
$obj = $doc.configuration.appSettings.add |
Where-Object { $_.Key -eq 'SCVMMServerName' }
$obj.value = CPVMM02
$doc.Save($_)
}

Related

PowerShell updating a csv file based from a csv file as a source

I am new to PowerShell or any scripting stuff.
I have here a DataResources(CSV File) which contains of bunch of data's that needs to be inserted into a Results.CSV
Here is my DataResources
For my Result.csv looks like this.
My goal is to export a NEW csv file based from a results.csv as a template
and if a HostName/Host match the data contains a value equivalent to UA and PWD will be inserted/updated from ExportResources CSV file to Exported new CSV File
after executing scripts that I have created it only modifies 1 row not all.
This is what I've done so far.
$DataSource = Import-Csv -Path D:\coding\Powershell\csv\Resources\ExportResources.csv
$DataResults = Import-Csv -Path D:\coding\Powershell\csv\Result\results.csv
foreach ($ItemDataResults in $DataResults)
{
$HostName = $ItemDataResults.HostName
$UA = $ItemDataResults.UA
$PASSWD = $ItemDataResults.PWD
}
$ItemDataSource = $DataSource | ? {$_.Host -eq $HostName}
if ($UA -eq "" -and $PASSWD -eq "")
{
$ItemDataResults.UA = $ItemDataSource.UA
$ItemDataResults.PWD = $ItemDataSource.Passwd
}
$DataResults | Export-Csv D:\coding\Powershell\csv\new.csv -NoTypeInformation
The result almost meet my expectation but the problem here that it only fills one hostname the rest are empty.
The issue with your script is that you loop through $DataResults once, but you have to iterate through it once for each item in $DataSource if you use the method that you're employing. I believe that a better method is to create a hashtable from one CSV, using the host name as the key, and the whole object as the value, then loop through the second array updating the value for each host. That would look something like this:
$DataSource = Import-Csv -Path D:\coding\Powershell\csv\Resources\ExportResources.csv
$DataResults = Import-Csv -Path D:\coding\Powershell\csv\Result\results.csv
$DataHT = #{}
$DataResults | ForEach-Object { $DataHT.Add($_.HostName,$_) }
ForEach( $Record in $DataSource ){
$DataHT[$Record.Host].UA = $Record.UA
$ItemDataResults.PWD = $Record.Passwd
}
$DataHT.Values | Export-Csv D:\coding\Powershell\csv\new.csv -NoTypeInformation

Powershell to present 'Net View' data

happy Easter!
I am trying to write a script in Powershell that takes a list of hosts from a txt (or csv) and then for each does a "net view /all" on it, returning the presented shares in a csv.
I got something working but I need a column to show the host its looking at for each row otherwise I cant map them back.
Attempt 1 returns the data and the host but looks VERY messy and is proving difficult to dissect in Excel:
$InputFile = 'M:\Sources\Temp\net_view_list.txt'
$addresses = get-content $InputFile
foreach($address in $addresses) {
$sharedFolders = (NET.EXE VIEW $address /all)
foreach ($item in $sharedfolders)
{
$str_list = $address + "|" + $item
$obj_list = $str_list | select-object #{Name='Name';Expression={$_}}
$obj_list | export-csv -append M:\sources\temp\netview.csv -notype
}
}
Attempt 2 works better but cant get the hostname listed, plus the comments seem to appear in the "Used as" section (only using for one host to test the theory (didnt work!)):
$command = net view hostname #/all
$netview = $command -split '\n'
$comp = $netview[0].trim().split()[-1]
$result = $netview -match '\w' | foreach {
convertfrom-string $_.trim() -delim '\s{2,}' -propertynames 'Share','Type', 'Used as', 'Comment'
}
$result[0] = $null
$result | format-table 'Share', 'Type', 'Used as', 'Comment' -hidetableheaders
Also neither of these accounts for issues where the host either isn't accessible or has 0 shares.
I have literally spent all day on these - grateful for any guidance!
I will provide the way to get what you want in the your 1st example. The main reason it is not appearing like you are expecting it to is because you are not dealing with a PowerShell object. You are getting the raw output from an external command. What you need to do is take the data and create a PS Custom object then you can use it as you will. Below is the code that you should add after you have the $SharedFolder populated heavily commented to explain what each part is for.
# Create Array to hold PSCustom Object and variable to tell when the DO loop is done
$share_list = #()
$completed = $false
# Loop through each line in the output
for($x=0;$x -lt $sharedFolders.count;$x++){
$next_line = $x + 1
# If the line is a bunch of - then we know the next line contains the 1st share name.
if($sharedFolders[$x] -like "*-------*"){
# Here we will loop until we find the end of the list of shares
do {
# Take the line and split it in to an array. Note when you
# use -split vs variable.split allows you to use regular
# expressions. the '\s+' will consider x number of spaces as one
# the single quotes are important when using regex. Double
# quotes use variable expansion. Single quotes don't
$content = $sharedFolders[$next_line] -split '\s+'
$share_name = $content[0].Trim()
# Create a PS Custom Object. This is a bit over kill for one item
# but shows you how to create a custom Object. Note the Object last
# just one loop thus you create a new one each go round then add it to
# an Array before the loop starts over.
$custom_object = new-object PSObject
$custom_object | add-member -MemberType NoteProperty -name 'Share Name' -Value $share_name
# Add the Custom Object to the Array
$share_list += $custom_object
# This exits the Do loop by setting $completed to true
if($sharedFolders[$next_line] -like "*command completed*"){
$completed = $true
}
# Set to the next line
$next_line++
} until ($completed)
}
}
$share_list

Multiple global find-replace in CSV files

I've been struggling with what I think is a really simple problem but I can't see it. I have a stack of 30-odd csv files of varying contents generated daily by different applications that I need to normalize before importing into a single reporting db. An Extract, Transform and Load (ETL) type of thing - global find and replace.
Looping through the files is no problem - not sure whether using ForEach-Object Fullname is the best way to go as outputting to an 'OUT' folder messes it up but using -Name means I have to include the path.
Basically, all 'True'/'False' text is to be replaced with 1/0, same with 'yes'/'no', poweredon/poweredoff, etc. Also we have 4 sites - each needs replacing with a ref. id, loads of stuff like that. I've tried modifying loads of scripts I've found on line - many in here. Tried using the replacement text in an array, pulling the CSV into a string, just can't see it. I've been doing the same thing for years with VBScript and it's easy. But I need to learn PowerShell so I'm going to persevere with it.
Ok, here is a quick search and replace function for you. It can read multiple CSV files and match\replace multiple values.
function Replace-CsvValue
{
[CmdletBinding()] # Enable pipeline support
Param
(
# Filename, mandatory, takes pipeline input
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
# Alias, allows to directly pipe Get-ChildItem output to this function
[Alias('FullName')]
[string]$File,
# Scriptblock, mandatory, does actual search and replace
[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
[scriptblock]$ScriptBlock
)
Process
{
# Import CSV
$CsvFile = $File | Import-Csv
# Generate new filename
$NewFileName = Join-Path -Path (Split-Path -Path $File -Parent) -ChildPath ('Processed_' + (Split-Path -Path $File -Leaf))
# Iterate over each line in CSV
$CsvFile | ForEach-Object {
# Execute scritblock against record
& $ScriptBlock
}
# Export CSV
$CsvFile | Export-Csv -Path $NewFileName -NoTypeInformation
}
}
Usage:
Write scriptblock with required replace logic
Pipe filenames or Get-ChildItem output to the function and pass scriptblock
Example:
Original CSV file:
State, Active, Available
PoweredOn, True, Yes
Function call:
# Scriptblock with replace logic
$ReplaceRule = {
# Iterate over each item in CSV line
$Item = $_
$_.PSObject.Properties.Name | ForEach-Object {
# If item name matches...
switch ($_)
{
'State' {
# If item value matches...
if($Item.$_ -eq 'PoweredOn')
{
$Item.$_ = 'Online'
}
# Or if item value matches...
elseif($Item.$_ -eq 'PoweredOff')
{
$Item.$_ = 'Offline'
}
break
}
# More replace rules, you can add your own here...
'Active' {
if($Item.$_ -eq 'True')
{
$Item.$_ = '1'
}
elseif($Item.$_ -eq 'False')
{
$Item.$_ = '0'
}
break
}
'Available' {
if($Item.$_ -eq 'Yes')
{
$Item.$_ = '1'
}
elseif($Item.$_ -eq 'No')
{
$Item.$_ = '0'
}
break
}
}
}
}
# Get all CSV files that match wildcard and
# feed them to the Replace-CsvValue function
Get-ChildItem -Path '.\' -Filter '*Report*.csv' | Replace-CsvValue -ScriptBlock $ReplaceRule
Processed CSV file:
"State","Active","Available"
"Online","1","1"
I made this csv for testing with the help of Mockaroo. Notice someones first name is True. I have that in there as a check to be sure my logic is working.
Present Name Lunch State
------- ---- ----- -----
TRUE Jesse Daniels No Powered Off
FALSE Debra Cunningham Yes Powered Off
TRUE True Jones Yes Powered Off
TRUE George Fernandez Yes Powered Off
FALSE Lisa Cox No Powered On
For the purpose of this I think it would be simple to just ignore the fact that it is a CSV and just replace the text outright. The caveat we have to be careful for is partial matches. Using regex we should be able to account for that possibility.
From comments you already know that you can chain -replace. Lets add some regex magic in there to make the process easier.
$filename = "C:\temp\MOCK_DATA.csv"
$oneKeywordPattern = "Powered On","Yes","True" -join "|"
$zeroKeywordPattern = "Powered Off","No","False" -join "|"
(Get-Content $filename) -replace "(?<=^|,)$oneKeywordPattern(?=$|,)","1" -replace "(?<=^|,)$zeroKeywordPattern(?=$|,)","0" | Set-Content $filename
To make sure that the csv structure is accounted for we only replace if the element is at the start of the line or a comma followed the end of the line or comma (This is using a lookahead and lookbehind.). This also ensures that we only change full elements and True Jones is not affected.
We used $oneKeywordPattern so that you can add elements to the array that need to be changed to a 1. We join them with a pipe so that it is treated as a alternative regex pattern. Its counterpart $zeroKeywordPattern functions just the same.
Output
Present Name Lunch State
------- ---- ----- -----
1 Jesse Daniels 0 0
0 Debra Cunningham 1 0
1 True Jones 1 0
1 George Fernandez 1 0
0 Lisa Cox 0 1
You could likely have other patterns that do not need to be changed with this logic. Just chain another -replace and remember that it supports regex so watch out for special characters.
The two caveats here is that if the files are large it could take a while to load the file and process the regexes (especially if you add more.) Also if your text is enclosed in quotes we don't currently account for that but it would be easy.
Basically, all 'True'/'False' text is to be replaced with 1/0, same with 'yes'/'no', poweredon/poweredoff, etc. Also we have 4 sites - each needs replacing with a ref. id, loads of stuff like that. I've tried modifying loads of scripts I've found on line - many in here. Tried using the replacement text in an array, pulling the csv into a string, just can't see it. I've been doing the same thing for years with vbscript and it's easy. But I need to learn PShell so I'm going to persevere with it. I'd really appreciate some help here.
If it's that static, you can probably get away with:
$changes = #{
'true' = '1';
'false' = '0';
'poweredon' = '1';
'poweredoff' = '0'
}
$folder = "" # your folder here
$csvFiles = ls $folder *.csv
foreach ($file in $csvFiles) {
$csvData = import-csv $file
foreach ($row in $csvData) {
$cells = $row | `
gm | `
?{$_.MemberType -eq 'NoteProperty'} | `
select -exp Name
foreach ( $cell in $cells ) {
$val = $row."$cell"
$valueNeedsChanging = $changes.ContainsKey($val)
if ( $valueNeedsChanging ) {
$newValue = $changes[$val]
$row."$cell" = $newValue
}
}
}
cp $file.FullName "$($file.FullName).bak" # back it up before saving
$csvData | export-csv -Path $file.FullName -NoTypeInformation
}
I chose to use Import- and Export-CSV to preserve the structure of the CSV file for files that have a lot of advanced formatting.

Powershell INI editing

I want to changing some values in an INI file. Unfortunately, I have keys in 2 different sections which share an identical name but need different values. My code uses the Get-IniContent function from PsIni.
Example INI file:
[PosScreen]
BitmapFile=C:\Temp\Random.bmp
Bitmap=1
[ControlScreen]
BitmapFile=C:\Temp\Random.bmp
Bitmap=1
I need to change the above to the following:
[PosScreen]
BitmapFile=C:\Temp\FileC.bmp
Bitmap=1
[ControlScreen]
BitmapFile=C:\Temp\FileD.bmp
Bitmap=1
The PowerShell code I am using seems to work, but it changes every value to "File D". It is obviously parsing everything twice, and the name is the same for each section.
$NewFileC = "C:\Temp\FileC.bmp"
$NewFileD = "C:\Temp\FileD.bmp"
$POSIniContent = Get-IniContent "C:\scripts\Update-EnablerImage\WINSUITE.INI"
$BOIniContent = Get-IniContent "C:\scripts\Update-EnablerImage\WINSUITE.INI"
If ($POSIniContent["PosScreen"]["BitmapFile"] -ne $NewFileC) {
Get-Content "C:\scripts\Update-EnablerImage\WINSUITE.INI" |
ForEach-Object {$_ -replace "BitmapFile=.+" , "BitmapFile=$NewFileC" } |
Set-Content "C:\scripts\Update-EnablerImage\WINSUITE.INI"
}
If ($BOIniContent["ControlScreen"]["BitmapFile"] -ne $NewFileD) {
Get-Content "C:\scripts\Update-EnablerImage\WINSUITE.INI" |
ForEach-Object {$_ -replace "BitmapFile=.+" , "BitmapFile=$NewFileD" } |
Set-Content "C:\scripts\Update-EnablerImage\WINSUITE.INI"
}
My struggle is how to change each one independently. I'm a bit of a scripting newbie, so calling out for some help. Tried using Conditional Logic (ForEach $line in $INIFile, for example), but no luck with that.
You are overcomplicating things. You can use Get-IniContent and Out-IniFile as follows:
$ini = Get-IniContent c:\temp\ini.ini
$ini["posscreen"]["BitmapFile"] = "C:\Temp\FileC.bmp"
$ini | Out-IniFile -FilePath c:\temp\ini2.ini
Note that if you want to overwrite the original file, you must add -Force to the Out-IniFile call.

Powershell: Search data in *.txt files to export into *.csv

First of all, this is my first question here. I often come here to browse existing topics, but now I'm hung on my own problem. And I didn't found a helpful resource right now. My biggest concern would be, that it won't work in Powershell... At the moment I try to get a small Powershell tool to save me a lot of time. For those who don't know cw-sysinfo, it is a tool that collects information of any host system (e.g. Hardware-ID, Product Key and stuff like that) and generates *.txt files.
My point is, if you have 20, 30 or 80 server in a project, it is a huge amount of time to browse all files and just look for those lines you need and put them together in a *.csv file.
What I have working is more like the basic of the tool, it browses all *.txt in a specific path and checks for my keywords. And here is the problem that I just can use the words prior to those I really need, seen as follow:
Operating System: Windows XP
Product Type: Professional
Service Pack: Service Pack 3
...
I don't know how I can tell Powershell to search for "Product Type:"-line and pick the following "Professional" instead. Later on with keys or serial numbers it will be the same problem, that is why I just can't browse for "Standard" or "Professional".
I placed my keywords($controls) in an extra file that I can attach the project folders and don't need to edit in Powershell each time. Code looks like this:
Function getStringMatch
{
# Loop through the project directory
Foreach ($file In $files)
{
# Check all keywords
ForEach ($control In $controls)
{
$result = Get-Content $file.FullName | Select-String $control -quiet -casesensitive
If ($result -eq $True)
{
$match = $file.FullName
# Write the filename according to the entry
"Found : $control in: $match" | Out-File $output -Append
}
}
}
}
getStringMatch
I think this is the kind of thing you need, I've changed Select-String to not use the -quiet option, this will return a matches object, one of the properties of this is the line I then split the line on the ':' and trim any spaces. These results are then placed into a new PSObject which in turn is added to an array. The array is then put back on the pipeline at the end.
I also moved the call to get-content to avoid reading each file more than once.
# Create an array for results
$results = #()
# Loop through the project directory
Foreach ($file In $files)
{
# load the content once
$content = Get-Content $file.FullName
# Check all keywords
ForEach ($control In $controls)
{
# find the line containing the control string
$result = $content | Select-String $control -casesensitive
If ($result)
{
# tidy up the results and add to the array
$line = $result.Line -split ":"
$results += New-Object PSObject -Property #{
FileName = $file.FullName
Control = $line[0].Trim()
Value = $line[1].Trim()
}
}
}
}
# return the results
$results
Adding the results to a csv is just a case of piping the results to Export-Csv
$results | Export-Csv -Path "results.csv" -NoTypeInformation
If I understand your question correctly, you want some way to parse each line from your report files and extract values for some "keys". Here are a few lines to give you an idea of how you could proceede. The example is for one file, but can be generalized very easily.
$config = Get-Content ".\config.txt"
# The stuff you are searching for
$keys = #(
"Operating System",
"Product Type",
"Service Pack"
)
foreach ($line in $config)
{
$keys | %{
$regex = "\s*?$($_)\:\s*(?<value>.*?)\s*$"
if ($line -match $regex)
{
$value = $matches.value
Write-Host "Key: $_`t`tValue: $value"
}
}
}