I am attempting to use the Remove-Item cmdlet as part of an automation for a system. The files are stored on a server that requires elevated rights to perform the file deletion. I have access to a domain admin account that I use for such automation scripts.
The code below will build the PSCredential object:
$password = New-Object System.Security.SecureString
"passwordhere".ToCharArray() | ForEach-Object { $password.AppendChar($_) }
$cred = New-Object System.Management.Automation.PSCredential("domain\username",$password)
$cred
I am passing this object to the following action:
Remove-Item -LiteralPath $path -Force -Credential $cred
Any ideas?
It's not clear to me if the files are local (you're running the script on the server) or remote (on another machine). If local try running the command using a background job and pass in the credentials to Start-Job:
$job = Start-Job { Remove-Item -LiteralPath $path -force } -cred $cred
Wait-Job $job
Receive-Job $job
If they're remote, try using remoting:
Invoke-Command -computername servername `
-scriptblock { Remove-Item -LiteralPath $path -force } `
-Cred $cred
Note: This requires that you execute Enable-PSRemoting on the remote machine.
In general, putting raw passwords in your script isn't a great idea. You can store the password in an encrypted manner using DPAPI and later, only that user account can decrypt the password e.g.:
# Stick password into DPAPI storage once - accessible only by current user
Add-Type -assembly System.Security
$passwordBytes = [System.Text.Encoding]::Unicode.GetBytes("Open Sesame")
$entropy = [byte[]](1,2,3,4,5)
$encrytpedData = [System.Security.Cryptography.ProtectedData]::Protect( `
$passwordBytes, $entropy, 'CurrentUser')
$encrytpedData | Set-Content -enc byte .\password.bin
# Retrieve and decrypted password
$encrytpedData = Get-Content -enc byte .\password.bin
$unencrytpedData = [System.Security.Cryptography.ProtectedData]::Unprotect( `
$encrytpedData, $entropy, 'CurrentUser')
$password = [System.Text.Encoding]::Unicode.GetString($unencrytpedData)
$password
Remove-Item can fail due to authorisation. Alternatively, either find the reference for each file and hit it with a .Delete() or move all of the files to the recycle bin.
foreach ($svr in $computers)
{
Invoke-Command -ComputerName $svr {
$folderitems = Get-ChildItem $cachefolder -Recurse
# Method 1: .Delete
foreach ($cachefolderitem in $cachefolderitems)
{
if ($cachefolderitem -like "*.ini")
{
$cachefolderitem.Delete()
}
}
# Method 2: Move all matching files to the recycle bin
Move-Item "$cachefolder\*.ini" 'C:\$Recycle.Bin' -Force
}
Related
I wanted to reach out and see if anyone has some tips on impersonation/runas cmds. I am working on a script that exports, then imports a .pfx certificate over to the users profile from the admin profile. Right now, I have everything working except for the import portion.
As seen below, I am showing only the import portion. $x and $y variables are defined earlier in the script by user input and that works okay.
Everything works up until the import-pfxcertificate cmdlet and scriptblock. Running that scriptblock as the other use is proving to be difficult. If anyone has any advice on how to structure that scriptblock cmd so that it will runas the user, that would be great!
I have an error log written into the script as well (not shown) Unfortunately, it is not picking up any errors because I believe it is pulling a local machine cert rather than the cert I specified - so no real error messages.
<#Cache credentials in IE and Import new or existing cert as client#>
function importcert
{
certpath = "C:\Temp\$x.pfx"
$password = $y | ConvertTo-SecureString -AsPlainText -Force
<#Enter your credentials#>
Credentials = Get-Credential -Credential corp\$x
<#Export to Secure XML#>
$Credentials | Export-Clixml -path 'C:\Temp\creds.xml'
<#Import credentials and run application using those credentials#>
Set-Location C:\
$creds = Import-Clixml -Path 'C:\Temp\creds.xml'
$ie = Start-Process -FilePath 'C:\Program Files (x86)\Internet Explorer\IEXPLORE.EXE' -Credential $creds
$ie
Start-Sleep -Seconds 30
<#Imports the certificate as the client#>
Start-Job -ScriptBlock { Import-PfxCertificate -FilePath $certpath -Exportable -CertStoreLocation Cert:\CurrentUser\My -Password $password } -Credential $creds
<#Search For Client Credential and if path is false, the credential file was removed successfully.#>
$clientXML = Test-Path -Path "C:\Temp\creds.xml"
Remove-Item -Path "C:\Temp\creds.xml"
if (-not ($clientXML))
{
Write-Output "Credential XML was removed"
}
}
importcert
It looks like all you're missing is some arguments for your Start-Job. I just tested this out locally and got it to install mycert.pfx for the other user TomServo:
<#Cache credentials in IE and Import new or existing cert as client#>
$Certpath = Get-Item "C:\Projects\Sandbox\mycert.pfx"
$Password = '{Password}' | ConvertTo-SecureString -AsPlainText -Force
<#Enter your credentials#>
$Credentials = Get-Credential -UserName TomServo
<#Export to Secure XML#>
$Credentials | Export-Clixml -path 'C:\Projects\Sandbox\creds.xml'
<#Import credentials and run application using those credentials#>
Set-Location C:\
$Creds = Import-Clixml -Path 'C:\Projects\Sandbox\creds.xml'
$Ie = Start-Process -FilePath 'C:\Program Files (x86)\Internet Explorer\IEXPLORE.EXE' -Credential $Creds
$Ie
Start-Sleep -Seconds 30
<#Imports the certificate as the client#>
Start-Job -ScriptBlock {
param($certpath, $Password)
Import-PfxCertificate -FilePath $Certpath -Exportable -CertStoreLocation Cert:\CurrentUser\My -Password $Password
} -Credential $Creds -ArgumentList $Certpath, $Password
<#Search For Client Credential and if path is false, the credential file was removed successfully.#>
$ClientXML = Test-Path -Path "C:\Projects\Sandbox\creds.xml"
Remove-Item -Path "C:\Projects\Sandbox\creds.xml"
if (-not ($ClientXML))
{
Write-Output "Credential XML was removed"
}
I am new to PowerShell. I can't connect to a server that requires a username and password.
I wrote a script that moves files from 5 different servers to 5 different sources.
Out of these 5 source servers, one of them requires a username and password to connect to it.
This script is supposed to run every hour. I want the authentication to go through so when it comes to transferring files the script runs as is without errors.
The code below gives the following error:
Connecting to remote server xx.xx.xx.x failed with the following error message : The WinRM client cannot
process the request. Default authentication may be used with an IP address under the following conditions: the transport
is HTTPS or the destination is in the TrustedHosts list, and explicit credentials are provided. Use winrm.cmd to configure
TrustedHosts. Note that computers in the TrustedHosts list might not be authenticated.
The complete code block is:
$logPath = "C:\Users\Log.txt"
$trancriptPath = "C:\Users\LogTranscript.txt"
$getDate = Get-Date -Format "dddd MM/dd/yyyy HH:mm "
$counter = 0
Start-Transcript -Path $trancriptPath -Append
Add-Content -Path $logPath -Value ("LOG CREATED $getDate") -PassThru
#Credentials For Server5
$password = ConvertTo-SecureString “password” -AsPlainText -Force
$userName = "username"
[pscredential]$cred = New-Object System.Management.Automation.PSCredential -ArgumentList #($userName, $password)
Enter-PSSession -ComputerName "xx.xx.xx.x" -Credential $cred
#Sources
$srcMt01 = "\\Server2\programs\RECEIVE\*"
$srcMt01NameChg ="\\Server2\programs\RECEIVE"
$srcMC2o = "\\Server3\Predator\Revised Programs\MC2o\*"
$srcMC2oNameChg ="\\Server3\Predator\Revised Programs\MC2o"
$srcHm03 = "\\Server4\Predator\Revised Programs\H3\*"
$srcHm03NameChg ="\\Server4\Predator\Revised Programs\H3"
$srcMca = "\\Server5\Public\NcLib\FromNC\*"
$srcMcaNameChg ="\\Server5\Public\NcLib\FromNC"
$srcMt02 = "\\Server6\programs\RECEIVE\*"
$srcMt02NameChg ="\\Server6\programs\RECEIVE"
#Destination
$destMt01 = "\\Sever1\MfgLib\RevisedPrograms\MT01"
$destMC2o = "\\Server1\MfgLib\RevisedPrograms\MC2old"
$destHm03 = "\\Sever1\MfgLib\RevisedPrograms\H3"
$destMca = "\\Sever1\MfgLib\RevisedPrograms\MC-A"
$destMt02 = "\\Sever1\MfgLib\RevisedPrograms\MT02"
Function MoveFiles{
Param(
[string]$src,
[string]$dest,
[string]$srcNameChange
)
Get-ChildItem -Force -Recurse $src -ErrorAction Stop -ErrorVariable SearchError | ForEach-Object{
$counter++
$fileName = $_.Name
# Check for duplicate files
$file = Test-Path -Path $dest\$fileName
Write-Output $file
if($file)
{
"$srcNameChange\$fileName" | Rename-Item -NewName ("Copy_"+$fileName);
Add-Content -Path $logPath -Value ("$fileName exists in destination folder. Name change was successful") -PassThru
}
}
Move-Item -Path $src -Destination $dest -Force
Add-Content -Path $logPath -Value ("$counter file(s) moved to $dest") -PassThru
}
MoveFiles -src $srcMt01 -dest $destMt01 -srcNameChange $srcMt01NameChg
MoveFiles -src $srcMC2o -dest $destMC2o -srcNameChange $srcMC2oNameChg
MoveFiles -src $srcHm03 -dest $destHm03 -srcNameChange $srcHm03NameChg
MoveFiles -src $srcMca -dest $destMca -srcNameChange $srcMcaNameChg
MoveFiles -src $srcMt02 -dest $destMt02 -srcNameChange $srcMt02NameChg
Stop-Transcript
Any help is appreciated.
You might find it easier to remote into the server using Invoke-Command and then running your file copy using a script block on the remote server. You will probably need to use CredSSP authentication so the copy process can connect to the destination server.
The server running the script will need to be configured as a WinRM client and the remote servers will need WinRM configured to accept connections. This is most likely where your current WinRM error is coming from. That's a pretty involved discussion so do some research and post specific questions as you uncover them.
Ex.
$Destination = "\\Sever1\MfgLib\RevisedPrograms\MT01"
$SourceServer = "Server2"
$password = ConvertTo-SecureString “password” -AsPlainText -Force
$userName = "username"
[pscredential]$cred = New-Object System.Management.Automation.PSCredential -ArgumentList #($userName, $password)
$ScriptBlock = {
param ( [string]$dest )
Code to move the files from source to $dest
}
Invoke-Command -ComputerName $SourceServer -ScriptBlock $ScriptBlock -Authentication CredSSP -Credentials $Cred -ArgumentList $Destination
I am attempting to create a generic script that will process registry files on a list of remote servers. The script will take the input of the path/filename to process and a list of servers to process it on. Copies the .reg file to the remote server and then attempts to process the .reg file on the remote server. In order to make it generic for use with any .reg file I want to pass the path\filename to process in a variable in the script block. I have seen examples of passing named variables and positional parameters but that doesn't seem to meet my requirements. I tried creating the script block contents as a Scriptblock type and calling it but it does not work. Most of the errors I have been getting are related to invalid parsing, ambiguous parameter, etc. I have seen a couple of working examples in which the .reg file path/filename is hard-coded but that defeats the purpose of what I am attempting to accomplish. I have tried the Invoke-Command with a session and with the -ComputerName variable in order to try the :using:" command with no success. Is there any way to pass a variable that is basically the command line values (switch params & filepath/name) for an executable within the scriptblock or am I going about this in the wrong manner? In code below I am crossing domains so having to open session for those servers in order to create directory if it doesn't exist.
while ($true)
{
$regFile = Read-Host -Prompt "Enter the path & name of registry file to be processed"
If(Test-Path -Path $regFile) { break }
Write-Host "You must enter valid path & filename of a registry file to process."
}
$servers = Get-Content D:\MLB\Scripts\servers.txt
$fileNm = [System.IO.Path]::GetFileName($regFile)
$pass = ConvertTo-SecureString "blahblah" -AsPlainText -Force
$Creds = new-object -typename System.Management.Automation.PSCredential( "domain\username", $pass)
foreach ($server in $servers)
{
$dirPath = ''
$newfile = '\\' + $server + '\d$\MLB\RegFiles\' + $fileNm
if($server.ToLower().Contains("web"))
{
$Session = New-PSSession -ComputerName $server -Credential $Creds
Invoke-Command -Session $Session -ScriptBlock { New-Item -ErrorAction SilentlyContinue -ItemType directory -Path D:\MLB\RegFiles }
$newfile = "d:\MLB\RegFiles\" + $fileNm
Copy-Item $regFile -Destination $newfile -ToSession $Session -Force
Remove-PSSession -Session $Session
}
else
{
$dirPath = "\\$server\d`$\MLB\RegFiles"
New-Item -ErrorAction SilentlyContinue -ItemType directory -Path $dirPath
$newfile = "\\$server\d`$\MLB\RegFiles\$fileNm"
Copy-Item $regFile -Destination $newfile -Force
}
Invoke-Command -ComputerName $server -Credential $Creds -ScriptBlock {
$args = "s/ $newfile"
Start-Process -filepath "C:\Windows\regedit.exe" -Argumentlist $args
}
I am trying to run a script that searches/downloads/installs windows updates on remote computers using WinRM. I am running this script as a domain user with Admin access. However, I get an ACCESS Denied error.
Now, I have the script copied over to the remote servers but I am unable to view output to see whether the script is running or not.
OUTPUT I want to see:
# Continue running on other servers on error
$ErrorActionPreference = "Continue"
# Server list
$servers = Get-Content "C:\Users\admin\Desktop\vm-nonprod.txt"
# Logs
$log = "C:\Users\admin\Desktop\log-nonprod.txt"
# Path to script on server list
$scriptpath = "C:\Patch.ps1"
$results = #()
foreach ($server in $servers) {
try {
$Credential = Import-CliXml -Path "C:\Users\admin\Desktop\admin.Cred"
#New-PSSession -ComputerName $server -Credential $Credential
Invoke-Command -ComputerName $server -Credential $Credential -ScriptBlock {$scriptpath} -ArgumentList "Y" | Out-File -FilePath C:\Users\admin\Desktop\WinPatch.txt
#Invoke-Command -ComputerName $server -Credential hhq\admin -FilePath "C:\Users\admin\Documents\Patch.ps1"
#Copy-Item -Path C:\Users\admin\Documents\Patch.ps1 -Destination 'C:\' -ToSession (New-PSSession –ComputerName $server -Credential $Credential)
}
catch {
Write-Output ("Error running script on remote host: " + $server)
}
}
$results | Export-Csv -NoTypeInformation $log
There's a few issues here.
Does the script exist on the server?
Sounds like yes, you have Patch.ps1 in C:\ on each $server
The scriptblock does not run the script - just prints the variable.
To run it, change {$scriptpath} to {. $scriptpath} or {& $scriptpath}
The variable $scriptpath is not in the scriptblock scope - you will have to pass it in the -ArgumentList
Change: {$scriptpath} -ArgumentList "Y"
____To: {param($p); . $p} -ArgumentList $scriptpath
The argument "Y" is being passed to the scriptbock, not the script. The scriptblock is not looking for it, so this value is being lost.
Assume you want it to be passed to the script - this needs to be done in the scriptblock:
{$scriptpath "Y"}
I would recommend getting rid of Out-File until you are happy with the output in the console.
Putting it all together:
-ScriptBlock {$scriptpath} -ArgumentList "Y" | Out-File -FilePath C:\Users\admin\Desktop\WinPatch.txt
-ScriptBlock {param($p); . $p "Y"} -ArgumentList $scriptpath
I believe you have the wrong Invoke-Command commented out. The one that is running only has the user name hhq\admin in the credential parameter. It might be failing due to that because it would be prompting for the password during run-time.
I need your help.
I need to write a script that will from a windows server,
Will connect to a list of servers (provided by a csv that has IP Addresses)
on those servers will overwrite a file located either on
C:\Program Files (x86)\Folder1 or C:\Program Files\Folder1
Taking the correct version from the server Running the script from D:\
and then restart a service called sname1.
(we can assume all target servers have the WinRM Active)
Your help will be greatly appriciated.
What I have so far is this, but I'm not sure it's correct assuming I'm working with IP Addresses and not Hostnames (Hostnames aren't an option as I'm not using this in a domain environment)
$servers=Get-Content D:\List.txt
foreach ($server in $servers)
{
if (Test-Path "\\$_\c$\Program Files\Folder1")
{
Copy-Item \\127.0.0.1\D$\file1.ini "\\$_\c$\Program Files\Folder1\file1.ini"
}
if (Test-Path "\\$_\c$\Program Files (x86)\Folder1")
{
Copy-Item \\127.0.0.1\D$\file1.ini "\\$_\c$\Program Files (x86)\Folder1\file1.ini"
}
Restart-Service -InputObject $(Get-Service -Computer $server -Name sname1)
}
REF:
$servers=Import-Csv E:\Temp\serverlist.csv
$Source = "D:\file1.ini"
foreach ($server in $servers)
{
$Password = ConvertTo-SecureString -AsPlainText -Force -String "$server.Password"
$User = "$server.Domain\$server.Usernaem"
$credentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $user,$password
if (Test-Path "\\$server.Alias\c$\Program Files\Folder1")
{
$Destination = "\\$server.Alias\c$\Program Files\Folder1"
}
if (Test-Path "\\$server.Alias\c$\Program Files (x86)\Folder1")
{
$Destination = "\\$server.Alias\c$\Program Files (x86)\Folder1"
}
Copy-Item $Source -Destination $Destination -Credential $credentials -Force
Restart-Service -InputObject $(Get-Service -Computer $server,Alias -Name sname1)
}
Note that you CANNOT access properties or methods of variables if you enclose it in quotes. For example, if you say;
$test = 1234
Write-Output "House $test.ToString()"
You will get the output "House 1234.ToString()" because the method is not resolved, it just assumes it's a string. You need to either split it up or enclose it:
Write-Output ("House " + $test.ToString())
Write-Output "House $($test.ToString())"
Additionally you've misspelled UserName when declaring $User. Fix those up and it should work fine.
I recommend checking out Microsoft's MVAs PowerShell 3.0 JumpStart and Advanced Tools & Scripting as they explain a lot of this stuff, get you into good habits and are actually pretty fun to watch.