Import-PFX as another user - Impersonation - powershell

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"
}

Related

Creating PowerShell File Using New-Item & Add-Content Cmdlets

This is the first time posting on SO (and really on any public platform), so I apologize if this question was already answered or if the question isn't structured appropriately.
I am brand new to creating PowerShell Scripts and I'm hoping someone can assist me on my new scripting adventure!
The script I am trying to create is to gather user input on OS user credentials, the OS user password, and the remote server's IP Address\FQDN\Hostname. From there I want to create a ps1 file that a scheduled task will point to, which will copy a particular folder from one server to a remote server on a daily basis (replacing the remote servers folder). I am getting caught up on properly adding the needed lines using the New-Item and Add-Content cmdlets
Here is my script I created so far:
#Clear the Screen
Clear-Host
#Create Variable for OS User Account
$User = read-host "What is the OS User that has permissions to the remote web server?"
#Clear the Screen
Clear-Host
#Password for OS User Account
$Password = Read-Host "What is the password of the OS account that has permissions to the remote web server?"
#Clear the Screen
Clear-Host
#Convert Password String
$PWord = ConvertTo-SecureString -String $Password -AsPlainText -Force
#Combine User Name and Password into One Entity
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord
#IP Address of Destination ICA Web Server
$DestServer = Read-Host "What is the IP address\FQDN\Hostname of the Destination Web Server"
#Create a PowerShell File that will be used for Task Scheduler
New-Item "C:\Test\ScheduledTask\CopyPasteDownLoadFolder.ps1" -ItemType File -Force -Value "$PWord = ConvertTo-SecureString -String $Password -AsPlainText -Force"
Add-Content "C:\Test\ScheduledTask\CopyPasteDownLoadFolder.ps1" ""
Add-Content "C:\Test\ScheduledTask\CopyPasteDownLoadFolder.ps1" "$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord"
Add-Content "C:\Test\ScheduledTask\CopyPasteDownLoadFolder.ps1" ""
Add-Content "C:\Test\ScheduledTask\CopyPasteDownLoadFolder.ps1" "$Session = New-PSSession -ComputerName $DestServer -Credential $Credential"
Add-Content "C:\Test\ScheduledTask\CopyPasteDownLoadFolder.ps1" "Copy-Item `"C:\Web\Web\Download`" -Destination `"C:\Web\Web\`" -ToSession $Session -Recurse -Force"
This is the results that I get in the ps1 that gets created (Used 'TestUser' for the OS user, 'PW' for the password, and '10.10.10.10' for the remote server):
System.Security.SecureString = ConvertTo-SecureString -String PW -AsPlainText -Force
System.Management.Automation.PSCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList TestUser, System.Security.SecureString
= New-PSSession -ComputerName 10.10.10.10 -Credential System.Management.Automation.PSCredential
Copy-Item "C:\Web\Web\Download" -Destination "C:\Web\Web\" -ToSession -Recurse -Force
On the first line, for some reason it displays 'System.Security.SecureString' rather than the $PWord variable.
The second line displays 'System.Management.Automation.PSCredential' & System.Security.SecureString rather than the $Credential & $PWord variables.
The third line does not display the $Session variable. It also displays 'System.Management.Automation.PSCredential' rather than the $Credential variable.
The fourth line does not display the $Session variable
So it looks like Powershell doesn't like me adding variables using the New-Item & Add-Content cmdlets.
Any input/suggestions is greatly appreciated!! Thank you!
UPDATE: Thank you, mklement0, for providing the missing piece to my puzzle!
Here is an updated working script based off the information provided by mklement0
#Clear the Screen
Clear-Host
#Create Variable for OS User Account
$User = read-host "What is the OS user account that has permissions to the remote web server?"
#Clear the Screen
Clear-Host
#Password for OS User Account
$Password = Read-Host "What is the password of the OS user account that has permissions to the remote web server?"
#Clear the Screen
Clear-Host
#Convert Password String
$PWord = ConvertTo-SecureString -String $Password -AsPlainText -Force
#Combine User Name and Password into One Entity
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord
#IP Address of Destination ICA Web Server
$DestServer = Read-Host "What is the IP address, FQDN, or Hostname of the remote web server"
#Create a PowerShell File that will be used for Task Scheduler
New-Item "C:\Test\ScheduledTask\CopyPasteDownLoadFolder.ps1" -ItemType File -Force -Value "`$OSUser = `"$User`""
Add-Content "C:\Test\ScheduledTask\CopyPasteDownLoadFolder.ps1" ""
Add-Content "C:\Test\ScheduledTask\CopyPasteDownLoadFolder.ps1" "`$PWord = ConvertTo-SecureString -String `"$Password`" -AsPlainText -Force"
Add-Content "C:\Test\ScheduledTask\CopyPasteDownLoadFolder.ps1" "`$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList `$OSUser, `$PWord"
Add-Content "C:\Test\ScheduledTask\CopyPasteDownLoadFolder.ps1" ""
Add-Content "C:\Test\ScheduledTask\CopyPasteDownLoadFolder.ps1" "`$Session = New-PSSession -ComputerName `"$DestServer`" -Credential `$Credential"
Add-Content "C:\Test\ScheduledTask\CopyPasteDownLoadFolder.ps1" "Copy-Item `"C:\Web\Web\Download`" -Destination `"C:\Web\Web\`" -ToSession `$Session -Recurse -Force"
$ characters that you want to retain as-is inside an expandable (double-quoted) string ("...") must be escaped as `$, otherwise they and what follows are subject to string interpolation; e.g.:
$foo = 'bar'
# Note the ` (backtick) before the first $, which escapes it.
# The second $foo, which is unescaped is expanded (interpolated).
"Variable `$foo contains $foo"
The above yields verbatim:
Variable $foo contains bar

MI_RESULT_FAILED on Copy-Item in PowerShell Script

I am running PowerShell on CentOS 7.x. I converted working individual commands when running within PowerShell via pwsh to a PowerShell script and then it no longer works. Can someone please shed me some light on what I did wrong?
Here's the working individual commands when running within PowerShell via pwsh.
PS /home/user1/Downloads> $userPw = ConvertTo-SecureString -String "user1password" -AsPlainText -Force
PS /home/user1/Downloads> cd
PS /home/user1> $userCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "user1win", $userPw
PS /home/user1> $s = New-PSSession -computerName 192.168.20.143 -credential $userCredential -Authentication Negotiate
PS /home/user1> Copy-Item -Path /home/user1/Downloads/gssntlmssp-0.7.0-1.el7.x86_64.rpm -Destination "C:\users\user1win\Desktop" -ToSession $s
PS /home/user1> exit
Here's the script when I converted to a PowerShell script so I can pass arguments into it. remote-copy.ps
$remoteHost = $args[0]
$username = $args[1]
$pwp = $args[2]
$source = $args[3]
$destination = $args[4]
Write-Host "Remote Host: '$remoteHost'"
Write-Host "Username: '$username'"
Write-Host "Password: '$pwp'"
Write-Host "Source: '$source'"
Write-Host "Destination: '$destination'"
$pw = ConvertTo-SecureString -String $pwp -AsPlainText -Force
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $pw
$s = New-PSSession -computerName $remoteHost -credential $cred -Authentication Negotiate
Copy-Item -Path $source -Destination $destination -ToSession $s
When I run the script, I got the following error.
[user1#rhel7-tm PowerShell]$ pwsh -File ./remote_copy.ps 192.168.20.143 user1win user1password /home/user1/Downloads/vte-0.28.2-10.el7.x86_64.rpm "C:\\users\user1win\Desktop"
Remote Host: '192.168.20.143'
Username: 'user1'
Password: 'user1password'
Source: '/home/user1/Downloads/vte-0.28.2-10.el7.x86_64.rpm'
Destination: 'C:\users\user1win\Desktop'
Copy-Item:
Line |
19 |
Copy-Item -Path "$source" -Destination "$destination" -ToSession $s |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
Starting a command on the remote server failed with the following error message : MI_RESULT_FAILED For more information, see the about_Remote_Troubleshooting Help topic.
Copy-Item: Line |
19 |
Copy-Item -Path "$source" -Destination "$destination" -ToSession $s |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
Failed to copy file /home/user1/Downloads/vte-0.28.2-10.el7.x86_64.rpm to remote target destination.
I would be greatly appreciate if someone can point me to where I did wrong. Thanks!
It's a false alarm. The script does work. The issue is by default the MaxEnvelopeSizekb in winrm/config is only 500kb. I need to set to a bigger value if I want to send a bigger file. To set MaxEnvelopSizekb to 500mb, you need to open PowerShell on Windows as Administrator and run the following command.
PS C:\WINDOWS\system32> Set-WSManInstance -ResourceUri winrm/config -ValueSet #{MaxEnvelopeSizekb = "500000"}

Failing to install MSI using ps1 script on remote using another PowerShell script

I am trying to install software (MSI) on remote server using a PowerShell script. The script was able to download the MSI file from the Internet, but fails to install the MSI.
When the remote script is executed directly by logging in to remote server it works fine and installs the MSI.
Script to be executed on remote servers (remote.ps1):
$base_url = "https://www.python.org/ftp/python/2.7.13/python-2.7.13.amd64.msi"
$python_filename = [System.IO.Path]::GetFileName($base_url)
$output_filepath = "D:\Temp\$python_filename"
Remove-Item -Path "C:\python27" -Force -Verbose
$installation_path = New-Item -ItemType directory -Path "C:\python27"
$get_pip_url = "https://bootstrap.pypa.io/get-pip.py"
$pip_name = [System.IO.Path]::GetFileName($get_pip_url)
$get_pip_path = "D:\Temp\$pip_name"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest $base_url -OutFile "D:\Temp\$python_filename"
Start-Process "msiexec.exe" -arg "/qn /i $output_filepath InstallAllUsers=1 TargetDir=$installation_path" -Wait
Script to be executed on the local computer (local.ps1):
$username = "user1"
$password = "password" | ConvertTo-SecureString -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($username, $password)
$script = {
Param(
[String]$Application,
[String]$Environment,
[String]$Region,
[String]$Service,
[String]$Node_Number,
[String]$proxy_sec_method,
[String]$proxy_url
)
$pyinstall = D:\Temp\remote.ps1
$pyinstall
}
Copy-Item -Path "\\localServerName\d$\Monitoring\*" -Destination "\\remoteServerName\d$\Temp" -Force -Recurse
$s = New-PSSession -ComputerName remoteServerName -Credential $cred
Invoke-Command -Session $s -ScriptBlock $script -ArgumentList $Application, $Environment, $Region, $Service, $Node_Number, $proxy_sec_method, $proxy_url -Verbose
In the local script I have tried changing the code lines $pyinstall = D:\Temp\remote.ps1 to $pyinstall = Invoke-Expression -Command 'D:\Temp\pythoninstallation.ps1' But was unable get the MSI installed.

Create Local User with Custom User Folder in Powershell

I am trying to create a new Win 2008 server local user and assign the user a different profile path. I don't want Windows to generate all the files under C:\Users\newUser, instead, I'd like to have it do that in D:\customDir\newUser. Does anyone know of a way to do this?
So far this is what I have:
$users= #("U1","U2","U3")
$computer = [ADSI]"WinNT://$env:COMPUTERNAME,computer"
$group = [ADSI]"WinNT://$env:COMPUTERNAME/MyCustomGroup,group"
$users | foreach {
$userName = $_
$userPath = "D:\customDir\$userName"
echo "Creating $userName..."
$user = $computer.Create("User",$userName)
$user.put("description","User $userName Description")
#$user.put("profilePath",$userPath) #This does not work, throws an error
#$user.put("homeDirDrive","D:") #This appears to be ignored when uncommented
$user.setpassword($userName)
$user.setInfo()
$group.add($user.Path)
#run cmd from user account to create profile files/folders
$spw = ConvertTo-SecureString $userName -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList $userName,$spw
Start-Process cmd /c -WindowStyle Hidden -Credential $cred -ErrorAction SilentlyContinue
}
The script successfully creates the users and adds them to the custom group, but all the files/folders end up in C:\Users\U*
To the OP,
Thanks for this piece, I was trying to figure out how to make this work for me:
run cmd from user account to create profile files/folders
$spw = ConvertTo-SecureString $userName -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList $userName,$spw
Start-Process cmd /c -WindowStyle Hidden -Credential $cred -ErrorAction SilentlyContinue
but I had to modify the last line to get it to work:
Start-Process cmd /c -Credential $cred -ErrorAction SilentlyContinue -LoadUserProfile
This works fine.
$loginName="ChrisMcCormack"
$newPass="123456ABCDEFG" # obviously generate a proper password...
$desc="average user"
$localHost=[ADSI]"WinNT://localhost"
$newUser=$localHost.Create("user",$loginName);
$newUser.setPassword($newPass);
$newUser.put("HomeDirectory","c:\users\$loginName");
$newUser.put("description",$desc);
$newUser.setInfo();
You can also use:
$spw = ConvertTo-SecureString $newPass -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList $loginName,$spw
Start-Process cmd /c -WindowStyle Hidden -Credential $cred -ErrorAction SilentlyContinue
to create the users directories without logging the user on.
Chris
Here is the way I use according to the underlaying COM component object model and the IADsUser interface.
$obj = [ADSI]"WinNT://$env:COMPUTERNAME"
$user1=$obj.create("user", "utilisateur1")
$user1.PasswordExpired=0
$user1.Invoke("setpassword","test.2012")
$user1.Invoke("put", "HomeDirectory", "c:\silogix")
$user1.Invoke("put", "Profile", "c:\docs")
$user1.Invoke("put", "LoginScript", "test.cmd")
$user1.CommitChanges()
Here is the result.
The only thing I can think of for you would be to change it at the registry level. Note that I'm not recommending you mess with the registry, but this does do what you want -
The default directory for a new profile is determined by the ProfilesDirectory in HKLM:\Software\Microsoft\Windows NT\CurrentVersion\ProfileList, and by default is %SystemDrive%\Users, changing it to $userpath should do what you want -
$regpath = "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\ProfileList"
$regname = "ProfilesDirectory"
set-itemproperty -path $regpath -name $regname -value $userpath
Hope that helps!

Using Remove-Item with Credentials

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
}