How to replace a string in file on remote computer in Powershell? - powershell

I have a VM running Windows Server 2012.And some intended services on it. I want to change a configuration file on this VM machine remotely from my desktop pc.
Currently I change this configuration file by mapping the C: drive of the remote server and then changing the file. Now this blocks me from changing multiple servers as I can't map multiple server c: simultaneously to same drive. Also, mapping hundreds of drives wouldn't be ideal.
The way I am changing the file by mapping drive is:
$password = <password text> | ConvertTo-SecureString -asPlainText -Force
$username = "admin"
$credential = New-Object System.Management.Automation.PSCredential($username,$password)
net use Z: \\$ipAddress\C$ <password> /user:admin type 'z:\Program Files\lalaland\Data\Settings.xml'
(Get-Content 'z:\Program Files\lalaland\Data\Settings.xml') | ForEach-Object { $_ -replace $oldString, $newString } | Set-Content 'z:\Program Files\lalaland\Data\Settings.xml'
type 'z:\Program Files\lalaland\Data\Settings.xml'
net use z: /delete
Therefore I searched for a better option and found this script at https://gallery.technet.microsoft.com/scriptcenter/PowerShell-Replace-String-58fbfa85 but it doesn't work for me.
I am using this script as :
.\Replace-StringInFile.ps1 -ComputerName <RemoteComputerHostName> -TargetPath 'C:\Program Files\lalaland\Data' -Fil
eName Settings.xml -Replace $oldString -ReplaceWith $newString -Credential (Get-Credential)
when I run the command credential window pops up asking for username and password. I enter the username and password that I used for mapping the drive, but it throws the following error:
New-PSSession : [<RemoteHostName>] Connecting to remote server <RemoteHostName> failed with the following error message : The user name or password is incorrect. For more information, see the
about_Remote_Troubleshooting Help topic.
At D:\workspace\Replace-StringInFile.ps1:84 char:14
+ ... $Session = New-PSSession -ComputerName $Computer -Credential $Creden ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [New-PSSession], PSRemotingTransportException
+ FullyQualifiedErrorId : LogonFailure,PSSessionOpenFailed
what I don't understand is when I map the drive with the same credentials it works fine but using the other script, which internally uses New-PSSession doesn't work.
Any idea ?

I was able to do this using the following cmdlet:
function Edit-RemoteFileStringReplace
{
[cmdletbinding()]
param([Parameter(Mandatory=$true)][ValidateScript({$_ -match [IPAddress]$_ })][string]$Address,
[Parameter(Mandatory=$true)][string]$FilePath,
[Parameter(Mandatory=$true)][string]$Replace,
[Parameter(Mandatory=$true)][string]$ReplaceWith,
[Parameter(Mandatory=$true)][System.Management.Automation.PSCredential]$Credential
)
$DriveLetter=""
$FilePathWithoutDriveLetter=""
$DriveName = $Address.replace('.','x')
$DriveName = "PSDrive_$DriveName"
$DriveLetter = (Get-DriveLetterFromPath -Path $FilePath).Substring(0,1)
$FilePathWithoutDriveLetter = Remove-DriveLetterFromPath -Path $FilePath
$MappedFilePath="$DriveName" + ":$FilePathWithoutDriveLetter"
New-PSDrive -Name $DriveName -PSProvider FileSystem -Root \\$Address\$DriveLetter$ -Credential $Credential -ErrorAction Stop
(Get-Content $MappedFilePath) | ForEach-Object { $_ -replace $Replace, $ReplaceWith } | Set-Content $MappedFilePath
Remove-PSDrive -Name $DriveName
}

Related

cannot validate argument on parameter "Path" The argument is null or empty

I was trying to create a simple script that would go to a server and get the acl details of a folder. I tested the command:
Invoke-command -Computername Servername -ScriptBlock {(Get-Acl "\\Server\Folder\user folders").access | ft- auto}
This worked ok. However when I was trying to put it into a script that would allow me to enter the path in via a variable I always get:
Cannot validate argument on parameter 'Path'. The argument is null or empty. Supply an argument that is not null or empty and then
try the command again.
+ CategoryInfo : InvalidData: (:) [Get-Acl], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetAclCommand
Here is my script:
#get folder permissions from remote computer
$serverName = read-host "Please enter the name of the target server"
$folderPath = "\\server_name\Folder\user folders"
#read-host "Please enter the full path to the target folder"
Invoke-command -ComputerName $serverName -ScriptBlock {(get-acl $folderPath).access | ft -wrap}
Its probably something very simple, but I'd appreciate the help.
The issue is because you are trying to use the $folderPath variable, but on the remote computer that variable does not exist.
You will need to pass it through as an argument. There are multiple ways to do so, two such ways are below:
# Add desired variable to ArgumentList and define it as a parameter
Invoke-command -ComputerName $serverName -ArgumentList $folderPath -ScriptBlock {
param($folderPath)
(get-acl $folderPath).access | ft -wrap
}
OR
# In PS ver >= 3.0 we can use 'using'
Invoke-command -ComputerName $serverName $folderPath -ScriptBlock {(get-acl $using:folderPath).access | ft -wrap}

O365 Mobile Device Export

The following code works on our internal exchange server minus the connection bits as thats not needed on-prem. I have added connection code to 365, which works fine. When I run this, I get the following error and it forces me to login a second time and exports only a fraction of the results:
Starting a command on the remote server failed with the following error message : The I/O operation has been aborted
because of either a thread exit or an application request. For more information, see the about_Remote_Troubleshooting
Help topic.
+ CategoryInfo : OperationStopped: (ps.outlook.com:String) [], PSRemotingTransportException
+ FullyQualifiedErrorId : JobFailure
+ PSComputerName : ps.outlook.com
Processing data from remote server ps.outlook.com failed with the following error message: WS-Management cannot
process the request. The operation failed because of an HTTP error. The HTTP error (12152) is: The server returned an
invalid or unrecognized response . For more information, see the about_Remote_Troubleshooting Help topic.
+ CategoryInfo : OperationStopped: (ps.outlook.com:String) [], PSRemotingTransportException
+ FullyQualifiedErrorId : JobFailure
+ PSComputerName : ps.outlook.com
Below is the code I am attempting to use. Do I have it formatted wrong?
#Script Created by Daniel Taylor 8/8/18
#Set Location for export:
$fLocation = "C:\temp\"
#Get username and password for 0365 connection
$cred = get-credential
#Import microsoft online
Import-module msonline
#Connect to MSonline
connect-msolservice -Credential $cred
#Connect to exchange online
$EolSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri “https://ps.outlook.com/powershell/” -Credential $cred -Authentication Basic -AllowRedirection
Import-PSSession $EolSession -DisableNameChecking
Write-host "You are now connected to Exchange. Begning ActiveSync Export:" -ForegroundColor DarkGreen
#create File to write report to:
$fName = $fLocation+"ActiveSyncDevices.txt"
$test = test-path $fName
if ($test -eq $True)
{
write-host "Removing Old File..." -ForeGroundColor Red
Remove-Item $fName
}
#Else
#{
#New-Item $fName -type file
#}
Write-host "Creating New File..." -ForeGroundColor Green
New-Item $fName -type file
#Get ActiveSync and Mailbox data
$EASDevices = ""
$AllEASDevices = #()
$EASDevices = ""| select 'User','PrimarySMTPAddress','DeviceType','DeviceModel','DeviceOS', 'LastSyncAttemptTime','LastSuccessSync'
$EasMailboxes = Get-Mailbox -ResultSize unlimited
foreach ($EASUser in $EasMailboxes) {
$EASDevices.user = $EASUser.displayname
$EASDevices.PrimarySMTPAddress = $EASUser.PrimarySMTPAddress.tostring()
foreach ($EASUserDevices in Get-MobileDevice -Mailbox $EasUser.alias) {
$EASDeviceStatistics = $EASUserDevices | Get-MobileDeviceStatistics
$EASDevices.devicetype = $EASUserDevices.devicetype
$EASDevices.devicemodel = $EASUserDevices.devicemodel
$EASDevices.deviceos = $EASUserDevices.deviceos
$EASDevices.lastsyncattempttime = $EASDeviceStatistics.lastsyncattempttime
$EASDevices.lastsuccesssync = $EASDeviceStatistics.lastsuccesssync
$AllEASDevices += $EASDevices | select user,primarysmtpaddress,devicetype,devicemodel,deviceos,lastsyncattempttime,lastsuccesssync
}
}
$AllEASDevices = $AllEASDevices | sort user
$AllEASDevices | Export-Csv $fname
write-host "The script completed successfully! The output file can be found at $fName" -ForeGroundColor Yellow
You cannot be logged in to Exchange on-prem and Exchange online at the same time. They use the same cmdlet and on-prem will take precedence.
If you are using PSRemoting, you must use two different sessions, and it's best to prefix those, so they are unique.
Or, be on the Exchange server directly and do your thing there, and open a new PSRemoting session to O365, prefixed so that the proxied commands have the prefix name.
Then make your O365 calls with the proxied cmdlets, not the on-prem ones.
Example Session setup:
# Exchange PowerShell Remote Management
#
$creds = Import-Clixml -Path $CredPath
$ExchangeSession = New-PSSession -ConfigurationName 'Microsoft.Exchange' `
-ConnectionUri ('http://' + $ExchangeServerFQDN + '/PowerShell') `
-Authentication Kerberos -Credential $Creds.ExpAdmin
Import-PSSession $ExchangeSession -Prefix 'EXP'
$creds = Import-Clixml -Path $CredPath
$ExchangeOnlineSession = New-PSSession -ConfigurationName 'Microsoft.Exchange' `
-ConnectionUri 'https://outlook.office365.com/PowerShell-liveid/' `
-Authentication Basic -Credential $Creds.ExoAdmin -AllowRedirection
Import-PSSession $ExchangeOnlineSession -Prefix 'EXO'
Get-PSSession
Each of the command will have EXP or EXO as part of the name of the proxied cmdlets.
Again, if you are doing this directly on Exchange, you only need the EXO session, and use the on-prem Exchange cmdlets normally.

Not able to Run powerSHell script using SysInternal

I need to remotely run powershell commands on multiple windows servers using SysInternal tools but I tried a lot but it seems not to work . Any help is highly appreciated .
Servers.csv (file content)
10.10.10.100
10.0.0.111
Code :
$List = Import-CSV -Path "C:\Users\javed\Desktop\New\servers.csv"
foreach ($entry in $List) {
if (test-Connection -Cn $($entry.Name) -quiet) {
& C:\Users\javed\Downloads\PsTools\psexec.exe \\$($entry.Name) -u "$($entry.Name)\Admin" -p 'P#ssword' -accepteula cmd /c " HostName >> C:\Users\javed\Desktop\New\Script.log"
} else {
"$computer is not online" >> C:\Users\javed\Desktop\New\Script.log
}
}
Output :
PS C:\Users\javed> C:\Users\javed\Desktop\New\Change-NetworkRoute.ps1
PsExec v2.2 - Execute processes remotely
Copyright (C) 2001-2016 Mark Russinovich
Sysinternals - www.sysinternals.com
psexec.exe : The handle is invalid.
At C:\Users\javed\Desktop\New\Change-NetworkRoute.ps1:26 char:9
+ & C:\Users\javed\Downloads\PsTools\psexec.exe \\$($entry.Name) -u "$(
...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (The handle is invalid.:String) [], Re
moteException
+ FullyQualifiedErrorId : NativeCommandError
Connecting to 10.10.10.100...Couldn't access 10.10.10.100:
Connecting to 10.10.10.100...
PS C:\Users\javed>
Apparently your command line isn't completely evaluated, note that PSExec shows \\$($entry.Name) in the error rather then the actual name.
The correct way to troubleshoot this is to put the first put the command is a variable, show the variable, and then execute it:
$CommandLine = "C:\Users\javed\Downloads\PsTools\psexec.exe \\$($entry.Name) -u ..."
Write-Host $CommandLine # Confirm that this is the command line you expect
if (test-Connection -Cn $($entry.Name) -quiet) {
&$CommandLine
} else {
Anyhow, it is a bad idea to use an external command line as PSExec including credentials to achieve something like this. Instead, I would use WMI to retrieve the actual name of the servers. Something like this:
$SPAdmin = "$($entry.Name)\Admin"
$Password = "P#ssword" | convertto-securestring
$Credential = new-object -typename System.Management.Automation.PSCredential -argumentlist $SPAdmin, $Password
$Computer = Get-WmiObject -Class Win32_Computersystem -ComputerName $entry.Name -Credential $Credential
Write-Host $Computer.Name

Commands in Workflow not recognized in Remote Session

We have moved our system alerting over to SCOM 2012 and receive heartbeat alerts when servers go offline. Presently there are approximately 750 servers in the vDC that I am managing. The SCOM 2012 server is in a different untrusted domain.
I have one working script where it puts the servers in Maintenance mode, but its run serially and takes about 40 minutes to put nearly 400 servers in Maintenance Mode. This is a workable solution, but I would like to use the foreach -parallel command to speed it up.
I have my workflow (to use the foreach -parallel command) created and placed in one of the default PowerShell Module locations on the Source and Destination Machines. I've tested the set of commands outside of the workflow on the SCOM server and it runs successfully. When I try to run the command remotely via an Invoke-command, the SCOM commands come back as being unrecognized.
#Get Date for usage in Connection Name
$Date = Get-Date -Format HHmmsss
#combine Name with Date to uniquify
$name = "ScomMM" + $Date
#Collect Servers from WSUS Server
[reflection.assembly]::LoadWithPartialName("Microsoft.Updateservices.Administration") | out-null
$WSUS = [Microsoft.updateservices.administration.adminproxy]::Getupdateserver("ServerName",$false,8530);
$TS = $wsus.getcomputertargetgroups() | ? {($_.name -eq "Group1") -or ($_.Name -eq "Group2")}
$computers = $TS.getcomputertargets() | Select-Object FullDomainName
#Setup Trusted host
invoke-Command -ScriptBlock {(winrm.cmd s winrm/config/client '#{TrustedHosts="*PublicIPAddress*"}')}
Enable-PSRemoting -Force
#Credentials stored in a file
$username = "username"
$password = get-content 'Path' | convertto-securestring
$creds = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $username,$password
$session = new-PSSession -ComputerName "IPAddress" -Credential $creds -Name $name
#SCOM Commands Module
Import-Module OperationsManager
#Workflow
Import-Module Set-MM
#Run the command remotely
Invoke-Command -Session $session -ScriptBlock {
Import-Module -Name Set-MM
Set-MM -computers $using:computers
}
#Workflow - Stored at C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Set-MM\Set-MM.psm1
Workflow Set-MM
{
Param($computers)
Foreach -Parallel($computer in $computers)
{
Get-SCOMClassInstance -Name $computers.FullDomainName | Start-SCOMMaintenanceMode -EndTime (Get-Date).AddMinutes(6) -Reason PlannedOperatingSystemReconfiguration
}
}
At C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Set-MM\Set-MM.psm1:6 char:3
+ Get-SCOMClassInstance -Name $computers.FullDomainName | Start ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Cannot find the 'Get-SCOMClassInstance' command. If this command is defined as a workflow, ensure it is
defined before the workflow that calls it. If it is a command intended to run directly within Windows
PowerShell (or is not available on this system), place it in an InlineScript: 'InlineScript {
Get-SCOMClassInstance }'
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : CommandNotFound
+ PSComputerName : IPAddress
The term 'Set-MM' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
+ CategoryInfo : ObjectNotFound: (Set-MM:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
+ PSComputerName : IPAddress
Get-PSSession | Remove-PSSession
If I use an Inlinescript on the script inside the foreach -Parallel
InlineScript{Get-SCOMClassInstance -Name $Using:computers.FullDomainName | Start-SCOMMaintenanceMode -EndTime (Get-Date).AddMinutes(6) -Reason PlannedOperatingSystemReconfiguration}
I get this for each computer that is attempting to get processed in the workflow:
The term 'Get-SCOMClassInstance' is not recognized as the name of a cmdlet, function, script file, or
operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
+ CategoryInfo : ObjectNotFound: (Get-SCOMClassInstance:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
+ PSComputerName : ServerName

New-PSsession not working in Script

I have a strange problem with one of my servers :
I am trying to open a PSsession with it.
If I copy my script directly in powershell everything works fine, but if i run it via a .ps1 file I get a access denied error.
The same sript works on multiple machines except this one.
Additonal information:
Executing Server : Server 2012
Target Server2003SP2
Another Server2003SP2 is working fine without a Problem
the Client Server was configured using :
Enable-PSRemoting -Force
Set-Item wsman:\localhost\client\trustedhosts MY2012Server -concatenate -force
Restart-Service WinRM
And the Error Message:
New-PSSession : [Server2003SP2] Connecting to remote server Server2003SP2 failed with the following error message : Access is denied. For more information,
Help topic.
At C:\Users\Administrator\Desktop\Script.ps1:23 char:13
+ $Session = New-PSSession -ComputerName $Servername -credential $Cred #-sessionO ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [New-PSSession], PSRemotingTransportException
+ FullyQualifiedErrorId : AccessDenied,PSSessionOpenFailed
Edit : My full SCript as requested :
$Password = "Hereismypasswordwith#and€init"
$Username = "Servername\Administrator"
$Servername = "Servername"
$Language = {
$oscode = Get-WmiObject Win32_OperatingSystem -ErrorAction continue
$oscode = $oscode.oslanguage
$switch = switch ($oscode){
1031 {"Deutsch"};
1033 {"English"};
default {"English"};
}
write-host $switch
return $switch
}
$SecurePassWord = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $Username, $SecurePassWord
$pssessionoption = new-pssessionoption -operationtimeout 7200000 -IdleTimeout 7200000
$Session = New-PSSession -ComputerName $Servername -credential $Cred -sessionOption $pssessionoption
Invoke-Command -Session $Session -Scriptblock $Language
Remove-PSSession -Session $Session
UPDATE :
it seems to be something within the Char encoding.
the password in the ps1 file produces a difrent output for the € in it :
in the ps1. ¬
in the ps window : ?
if i pass the Password as a Paramter it also works.
$password.gethash() also prouces difrent outputs. codepage is the same though (chcp)
the script was created in notepad++
Changing / Converting to ansi from UTC without BOM fixed the issue.. jesus crist who thinks about stuff like that / why the hell was it set to this value..