How to copy file to VM using Powershell? - powershell

Sounds very simple, but most of the answers given on the internet assume both computers are on the same network. What if they are not, e.g. I want to copy a file to Azure VM. There is a thread Windows Azure Powershell Copying file to VM , but its four years old and the answers require many steps.

Agree with Rasmusgude, you can upload file to Azure File Share then mount Azure File Share to that VM.
Are you administrator of that VM?
If yes, you can enable WinRM and use WinRM to upload files to it.
About enable Azure VM WinRM, you should add port 5985 to Azure VM's NSG inbound rules and add port 5985 to windows firewall inbound rules.
Then use this script to create a session:
$username = 'user'
$pass = ConvertTo-SecureString -string 'password' -AsPlainText -Force
$cred = New-Object -typename System.Management.Automation.PSCredential -argumentlist $username, $pass
$s = New-PSSession -ConnectionUri 'http://xx.xx.xx.xx:5985' -Credential $cred -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck)
About upload files to that VM, you can use this PowerShell command Send-File -Path C:test.xml -Destination C: -Session $session.
Here a blog about sending files over WinRM, please refer to it.
Hope this helps.

You could use Azure File Share (AFS), and then:
Mount the AFS as a drive on the VM
Use the REST Api or a library to upload the files to the AFS

Related

How to copy a file from local work space to remote server (not a network shared path) with powershell

I am trying to copy a file from my local workspace to a remote server (not a network shared path) by using the powershell command through Inline Powershell" task in TFS vNext build definition.
FYI, destination path is not a network shared path
I tried with below commands
$Session = New-PSSession -ComputerName "remote server name" -Credential "domain\username"
Copy-Item "$(Build.SourcesDirectory)\Test.htm" -Destination "C:\inetpub\wwwroot\aspnet_client\" -ToSession $Session
But it's promoting for the password every time and I tried with entering the password manually and the result looks good.
How can we achieve this step without prompting password or credentials
Are you sure it's not on a network share? :)
Powershell only takes password as a secure string. You can use $credential = Get-Credential to render a really cool box to store those credentials for you, or if you want to store your login programmatically (not recommended for obvious security reasons) use this:
$passwd = ConvertTo-SecureString "<password>" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential("<username>",$passwd)
There might be a way to inherit your current domain credentials, but that's way beyond me, and a quick google search turns up nothing.
EDIT: Sorry I forgot to post the whole thing:
$passwd = ConvertTo-SecureString "<password>" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential("<username>",$passwd)
$Session = New-PSSession -ComputerName "remote server name" -Credential $credential
Copy-Item "$(Build.SourcesDirectory)\Test.htm" -Destination "C:\inetpub\wwwroot\aspnet_client\" -ToSession $Session

Attach to an Azure ShareFile using WinRM and net use

Context:
I have setup an ARM deployment from the 201-winrm-windows in azure-quickstart-template.
The deployment run fine
I can access the VM using WinRM
I can run a script remotely using WinRM
The problem is that I'm trying to setup a storage file on that VM. The official documentation ask to run this command:
net use <drive-letter>: `
\<storage-account-name>.file.core.windows.net<share-name> `
/u:<storage-account-name> <storage-account-key>
# Result:
The command completed successfully.
The issue:
When the command is run locally (local powershell on the VM), I have a success message and the mount appear.
When the command is run through WinRM, I have the same success message but when I connect to the VM, I cannot access the mount.
My code:
$resourceGroupName = "resourcegroupname"
$username = "username"
$storageAccountName = "storageaccountname"
$zone = "westeurope"
$hostName = "$resourceGroupName.$zone.cloudapp.azure.com"
$shareFileName = "test"
$winrmPort = '5986'
$storageAccountKey = "......................"
$cred = new-object `
-typename System.Management.Automation.PSCredential `
-argumentlist $username, $password
$soptions = New-PSSessionOption -SkipCACheck
Invoke-Command `
-ComputerName $hostName `
-Credential $cred `
-Port $winrmPort `
-SessionOption $soptions `
-filepath .\provision.ps1 `
-UseSSL `
-ArgumentList `
$storageAccountName, `
$storageAccountKey, `
$shareFileName
And the provision file .\provision.ps1:
Param (
[Parameter(Mandatory=$True,Position=0)]
[string]$accountStorageName,
[Parameter(Mandatory=$True,Position=1)]
[string]$accountStorageKey,
[Parameter(Mandatory=$True,Position=2)]
[string]$shareFileName
)
net use w: `
\\$accountStorageName.file.core.windows.net\$shareFileName `
/user:$accountStorageName $accountStorageKey
Note:
my issue is similar to this one but the author have no response.
The problem you are hitting into in this case is that, WinRM runs as NetworkService. When you 'net use' through WinRM, the mount operation is done for NetworkService user, and you cannot access it through another user you connect to VM with. You'll need to ensure that the mount operation is done through same user context that you'll need to access the share as.
Just tried what you've done but I can access the mount successfully. A small difference is that I connect to the VM first and then mount the file share, I didn't combine these two steps into one like you did, not sure whether this is the issue but just like to share with you and for your reference.
I uploaded a file in my file share and I can access the file share and get the file as below snapshot shows:
Update access from RDP:

Iterate through a list of VMs in Azure

i have about 10 VMs hosted on Auzre, i need to iterate through each of them and then execute a powershell script on each of them, lets say 'Set-Date'
whats the best way to connect to each VM, execute the ps script and then disconnect?
You can use PowerShell Remoting or custom scripts via extensions to execute PowerShell code on the remote VM.
For both solutions you get your list of VMs with the PowerShell command Get-AzureVM. Use a loop to iterate those VMs. I skip that part here because iterating are PowerShell basics.
1. PowerShell Remoting
For this you need PowerShell Remoting enabled on the remote VM and have an open port for PowerShell Remoting. Both is a default setting for new VMs.
Advantage: this solution is very handy for interactive sessions with a remote VM. Disadvantage of this solution is, that you need to authenticate to each VM and have to keep connected while execution.
With each VM you can do something like this. This is a shortened example where I have installed ADDS on the remote VM.
# Prepare credentials for remote session.
$secpasswd = ConvertTo-SecureString $AdminPassword -AsPlainText -Force
$credentialDC1 = New-Object System.Management.Automation.PSCredential ($AdminUsername, $secpasswd)
$EndpointDC = Get-AzureWinRMUri -ServiceName testlab-dc -Name dc1
#$EndpointDC = Get-AzureVM -ServiceName testlab-dc -Name dc1 | Get-AzureEndpoint -Name WinRmHTTPs
$psso = New-PSSessionOption -SkipCACheck
$sessionDC = New-PSSession -ComputerName testlab-dc.cloudapp.net -Port $EndpointDC.Port -Credential $credentialDC1 -UseSSL -SessionOption $psso
Invoke-Command -Session $sessionDC -ScriptBlock {
# Set-Date or other command
# or for example
# Install-WindowsFeature AD-Domain-Services
}
Remove-PSSession -Session $sessionDC
2. Custom Scripts via Extensions
Here you can upload a PowerShell file into your BLOB storage and then let execute that file on your VMs. Requirement is that the VM agent has to be installed on the VM. (Default for new VMs from the gallery.)
Advantage: you do not need to authenticate to each VM and you do not need to keep connect while execution.
Disadvantage: you have to prepare a separate PowerShell file to upload. Getting results is asynchronous.
Example:
# Upload PowerShell file
Set-AzureStorageBlobContent -Container extensions -File "Install-ADForest.ps1" -Blob "Install-ADForest.ps1"
# Install AD services and forrest
Get-AzureVM -ServiceName demoext -Name demoext |
Set-AzureVMCustomScriptExtension -ContainerName extensions -FileName "Install-ADForest.ps1" |
Update-AzureVM
The container has to exist. Create that container before you upload the file.

Powershell Server Network drive

I have a client and a server. The client will call a script like:
#Predefine necessary information
$Username = "Niels"
$Password = "password"
$ComputerName = "192.168.1.51"
$Script = {powershell c:/build/jclbuild2.bat}
#Create credential object
$SecurePassWord = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $Username, $SecurePassWord
#Create session object with this
$Session = New-PSSession -ComputerName $ComputerName -credential $Cred
#Invoke-Command
$Job = Invoke-Command -Session $Session -Scriptblock $Script
echo $Job
#Close Session
Remove-PSSession -Session $Session
On the server the jclbuild2.bat will run and access a network drive like \\otherserver\something, it says access denied if I do this command:
cmd.exe /C copy "\\server\file1.pdf" "\\server2\file1.pdf"
How do I access a network drive from a powershell file on a remote server? The user I use with the $username and $password should have access to the network drive.
I think it's a double hop issue, which I don't know how to solve.
You can't do this using the default authentication mechanism. You need to use an authentication mechanism that allows you to flow credentials, not just identity. Kerberos is one of these. CredSSP is another that is built into Windows starting from Vista/Server 2008 onwards.
I have experience setting up CredSSP. Note that there is some security risk because the target machine will have access to the credentials as plain text.
To set it up you will need to run two commands (both from an elevated shell). One on the machine you are running the above script on (the client) and another on the target that you will be connecting to via remoting (the server).
Enable-WSManCredSSP -Role Client -DelegateComputer $ComputerName -Force
This enables delegation to $ComputerName from the client (note you may have to use the FQDN). For security reasons you should avoid using the wild card '*' although you might consider using '*.mydomain.int' to enable delegation to all machines on the domain.
On the target server
Enable-WSManCredSSP -Role Server
Then when you create the session use the -Authentication flag
$Session = New-PSSession -ComputerName $ComputerName -credential $Cred -Authentication Credssp
There are questions on ServerFault on setting up CredSSP. There is also a blog post here with additional explanation. This post has troubleshooting tips for some commonly encountered error messages.
Another option is to use a delegated session on your server.
Basically, you create a custom remote session that uses the -RunAs parameter to designate the credentials that the session will run under. You can also constrain what scripts and cmdlets can be run in the session and specify who can connect to the session.
In this case, the session would run as the Niels account, and everything done in the session would be under that account authority, regardless of who was connected to the session. From that session, you can now make one hop to another server without needing CredSSP.
This also eliminates the security risk involved in storing that account password in the script file on the client computer.
http://blogs.technet.com/b/heyscriptingguy/archive/2014/04/03/use-delegated-administration-and-proxy-functions.aspx

Execute powershell script remotely on Amazon EC2 instance from my local computer

I have an Amazon EC2 instance.
Using powershell on my local workstation, I want to be able to remote into my Amazon EC2 instance and execute some commands.
I have found many articles online but none are working or I misunderstood them (probably the latter).
Some I tried are
Managing Windows EC2 Instances remotely with Powershell
Administering EC2 instance with Windows Powershell
Enabling- PSRemoting
How to Run PowerShell Commands on Remote Computers
My understanding is that I need to:
Amazon EC2 Dashboard > Network & Security > Security Groups > Add port 5985
//Local & EC2 PowerShell(Administrator)
enable-psremoting -force
//Local PowerShell(Administrator)
set-item wsman:\localhost\Client\TrustedHosts -value "*" -force
$password = convertto-securestring -asplaintext -force -string myPassword
$credential = new-object -typename system.management.automation.pscredential -argumentlist "myUsername", $password
$session = new-pssession ec2-00-00-00-000.compute-1.amazonaws.com -credential $credential
enter-pssession $session
But I get this error
new-pssession : [ec2-00-00-00-000.compute-1.amazonaws.com] Connecting to remote server
ec2-00-00-00-000.compute-1.amazonaws.com failed with the following error message : WinRM cannot complete the
operation. Verify that the specified computer name is valid, that the computer is accessible over the network, and
that a firewall exception for the WinRM service is enabled and allows access from this computer. By default, the WinRM
firewall exception for public profiles limits access to remote computers within the same local subnet. For more
information, see the about_Remote_Troubleshooting Help topic.
At line:1 char:12
+ $session = new-pssession ec2-00-00-00-000.compute-1.amazonaws.com -credential $c ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [New-PSSession], PSRemotin
gTransportException
+ FullyQualifiedErrorId : WinRMOperationTimeout,PSSessionOpenFailed
Solution found here.
The missing link was to (on the EC2 instance) open Windows Firewall with Advanced Security and edit an inbound rule.
Full Steps:
EC2 Instance
1) Open PowerShell as administrator
2) Enter enable-psremoting -force
3) Open Windows Firewall with Advanced Security
4) Inbound Rules -> Find Windows Remote Management (Http-In) - there are 2, do this for both
5) Right click -> Properties -> Advanced -> Check public
Local
6) Open PowerShell as administrator
7) Enter enable-psremoting -force
8) Enter the following:
$password = convertto-securestring -asplaintext -force -string MY_PASSWORD
$credential = new-object -typename system.management.automation.pscredential -argumentlist "MY_USERNAME", $password
$session = new-pssession MY_EC2_PUBLIC_IP -credential $credential
enter-pssession $session
Write-Host "Hello, World (from $env:COMPUTERNAME)"
I think that not exposing PowerShell via SSH was one of the biggest design mistakes MS did. Even years later they are too proud / blind to do revert that poor decision.
I suggest you to not fight with WinRM and instead, use an SSH server on your Windows machine.
You'll benefit from having a simple, standard, secure way to connect to your server from any device (I'm doing remote PS sessions from my iPad).
There is the opensource cygwin and my favorite proprietary (with free offering) PowershellServer
You'll thank me when your Windows server will play nicely with the rest of the world.
UPDATE
I got back to this old thread and would like to add another option - using the new(ish) AWS Systems Manager run-command capability.
This allows you to have no administrative port exposed to the external world so no need to fiddle with host / cloud firewalls.
It also provide other benefits like auditing, permissions etc...