How to run Azure PowerShell cmd from cloud service - azure

I've developed a web role to manage Azure VM's that is working locally but NOT when it's deployed in a Cloud Service.
I have executed the cmd that is in the web role in PowerShell through an RDP connection to the Cloud Service, so I know PowerShell v3.0 and Azure cmd are working fine.
First steps I had some permissions and certs issues but solved, the problem now is I can't see any error in Event Viewer.
I'm using PowerShell.Create() of System.Automation.dll
string script = "Set-ExecutionPolicy Unrestricted -Force
script = "Import-Module \"D:\\Program Files (x86)\\Microsoft SDKs\\Windows Azure\\PowerShell\\Azure\" 2 >> C:\errorp.out";
script = "Set-AzureSubscription –DefaultSubscription \"Test Environment\"";
script = "Get-AzureVM " + vm
I'm trying to get the error in all the commands with "2 >> C:\errorp.out" (actually is in all commands but didn't copy here) but it creates a blank file.
Am I missing any extra configuration to be able to do that?

The $error variable will have your error history. For example
$error | format-list -property *

Silly mistake: I was creating a new shell for each line of the script
var shell = PowerShell.Create();
So, in the second line, after do "Import-Module Azure", this second shell didn't have access to the Azure commands.
I get this thanks to #Rick for introduce me the $error, however could get working this in C#, what I did instead is:
if (shell.Streams.Error.Count > 0)
{
for (int i = 0; i < shell.Streams.Error.Count; i++)
{
ResultBox.Text += "Error: " + shell.Streams.Error[i] + "\r\n";
}
}

Related

Execute azure command in powrershell without writing error to console?

I am using powershell script in pipeline and the problem I have with this query.
$value = $(az appconfig kv show -n ThisisEnv --key thisisconfigkey) | ConvertFrom-Json
What this query does is get the data related to key if exist. If this key doesn't exist it give the error like
ERROR: Key 'abcdefg' with label 'None' does not exist.
It is work as expected. In pipeline when the key doesn't exist, it's printed a error on CLI. The pipeline see it as error and show it as failed. Is there a way I can make it work.
Is there a way I can stop it printing it on console. Any powershell operator which help me to get the value from azure command but also let me get it without print anything on console.
You could try to redirect the standard error using 2> $null
$value = $(az appconfig kv show -n ThisisEnv --key thisisconfigkey 2> $null) | ConvertFrom-Json
This will suppress the error within the console. You might also want to set the powerShellIgnoreLASTEXITCODE within the Azure CLI Task in order that the pipeline run doesn't fail - or as a workaround, set the $LASTEXITCODE to 0

how to excute powershell script on remote machine using ssh in python

When I ssh to the windows machine it will not be in powershell prompt and I don't know how I'm going to execute the powershell script I have.
I tried the following code without success.
ssh_connect(ip1, " powershell.exe <mtu-read.ps1")
def ssh_connect(address, script):
#key_filename1 = 'id_rsa' #no authentication for now testing
host_user = "Administrator"
try:
process1 = subprocess.Popen("ssh -oStrictHostKeyChecking=no " + host_user + "#" + address + script, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output1 = process1.stdout.readlines()
print(output1)
return output1
except Exception as er2:
print(str(er2) + ' connection issue ')
return "0"
The result I get is that it does not recognize the command in the powershell.
Your syntax is invalid in the powershell call:
powershell.exe -File mtu-read.ps1
Note your working directory or the script file won't be found. I suggest fully-qualifying your script path as a best-practice.
As an aside, I question why you're using python to execute powershell instead of just writing a powershell script and utilizing winrm (native to Windows) instead of ssh.

workon command doesn't work in Windows PowerShell to activate virtualenv

I have installed virtualenvwrapper-win and when I try this command
workon <envname>
In CMD it works, but not in Windows PowerShell.
In Windows PowerShell I have to do Scripts\activate.ps1 and then I get the envname before the prompt.
Can you please let me know how can I make workon command working in PowerShell?
workon is a batch script. If you run it from PowerShell it's launched in a new CMD child process, doing its thing there, then exit and return to the PowerShell prompt. Since child processes can't modify their parent you lose all modifications made by workon.bat when you return to PowerShell.
You basically have two options:
Rewrite workon.bat (and the other batch scripts it calls) in PowerShell.
Run workon.bat in without exiting from the CMD child process:
& cmd /k workon <envname>
If you just want a shortcut workon that you can call directly from PowerShell you can wrap that commandline in a function and put the function definition into your PowerShell profile:
function workon($environment) {
& cmd /k workon.bat $environment
}
Use the scriptname with extension here to avoid infinite recursion.
The answer by Ansgar Wiechers technically works but it uses cmd which means you are basically using the cmd prompt from within PowerShell and you lose the additional functionality provided by PowerShell. You can modify the function above to the following:
function workon ($env) {
& .\Envs\$env\Scripts\activate.ps1
}
This will allow you to continue to use PowerShell commands (that do not work in cmd such as ls) in your virtual environment
This also assumes that your environments are saved in .\Envs. If they are elsewhere, then adjust the path in the function accordingly, or set the WORKON_HOME environment variable, see below.
If you have set the WORKON_HOME environment variable (which you should !), you can instead use:
function workon ($env) {
& $env:WORKON_HOME\$env\Scripts\activate.ps1
}
Additionally, if you are not a Windows user (like me) and need help on where to put that function and how to get it to load when you open PowerShell. Here are some additional resources that helped me:
Background:
How to Write a PowerShell Script Module
Importing a PowerShell Module
How to get PowerShell to autoload your module when it starts:
How to Create a PowerShell Profile
Start PowerShell with modules loaded
just type "CMD" on powershell and it will bring up cmd and then workon
There is a much simpler solution! Just go to your python scripts folder, where the workon.bat file exists and create a new file named workon.ps1 and add the following line to it
iex ("~\Envs\" + $args[0] + "\Scripts\activate.ps1")
You many need to change this appropriately if you store your virtualenvs elsewhere and also set the Execution Policy to allow scripts. Now you can use workon in both cmd and powershell, since the ps1 will be executed in powershell and bat in cmd.
You can also check out my fork (full disclosure: I'm the author of the powershell part) of virtualenvwrapper-win, which contains some rewritten scripts for powershell and should work on both CMD and powershell. If you want to copy-paste, create two files, workon.ps and `cdproject.
workon.ps1:
if (-not (Test-Path env:WORKON_HOME))
{
$WORKON_HOME = '~\Envs'
} else {
$WORKON_HOME = ($env:WORKON_HOME).Replace('"','')
}
if (-not (Test-Path env:VIRTUALENVWRAPPER_PROJECT_FILENAME)) {
$VIRTUALENVWRAPPER_PROJECT_FILENAME = '.project'
} else {
$VIRTUALENVWRAPPER_PROJECT_FILENAME = ($env:VIRTUALENVWRAPPER_PROJECT_FILENAME).Replace('"','')
}
if ($args.length -eq 0) {
echo "Pass a name to activate one of the following virtualenvs:"
echo ==============================================================================
(Get-ChildItem -Path $WORKON_HOME).Name
return
}
$VENV = $args[0]
if (!(Test-Path -Path ("$($WORKON_HOME)\$($VENV)"))) {
echo ("virtualenv $($VENV) does not exist")
echo "Create it with 'mkvirtualenv $($VENV)'"
return
}
if (!(Test-Path -Path ("$($WORKON_HOME)\$($VENV)\Scripts\activate.ps1") )) {
echo "$($WORKON_HOME)$($VENV)"
echo "doesn't contain a virtualenv (yet)."
echo "Create it with 'mkvirtualenv $($VENV)'"
return
}
iex ("$($WORKON_HOME)\$($VENV)\Scripts\activate.ps1")
if (Test-Path -Path ("$($WORKON_HOME)\$($VENV)\$($VIRTUALENVWRAPPER_PROJECT_FILENAME)")) {
iex "cdproject"
}
cdproject.ps1:
function Show-Usage {
echo ""
echo "switches to the project dir of the activated virtualenv"
}
if (-not (Test-Path env:VIRTUAL_ENV)) {
echo ""
echo "a virtualenv must be activated"
Show-Usage
return
}
if (-not (Test-Path env:VIRTUALENVWRAPPER_PROJECT_FILENAME)) {
$VIRTUALENVWRAPPER_PROJECT_FILENAME = '.project'
} else {
$VIRTUALENVWRAPPER_PROJECT_FILENAME = ($env:VIRTUALENVWRAPPER_PROJECT_FILENAME).Replace('"','')
}
if (-not (Test-Path "$($env:VIRTUAL_ENV)\$($VIRTUALENVWRAPPER_PROJECT_FILENAME)")) {
echo ""
echo "No project directory found for current virtualenv"
Show-Usage
return
}
$ENVPRJDIR = Get-Content "$($env:VIRTUAL_ENV)\$($VIRTUALENVWRAPPER_PROJECT_FILENAME)" -First 1
# If path extracted from file contains env variables, the system will not find the path.
# TODO: Add this functionality
cd $ENVPRJDIR
I encountered the same problem using virtualenvwrapper in powershell on Windows10. I like the answer by #Erock but that overwrrides the workon in such a way that running workon without an argument throws error instead of showing available environments. Here is my solution.
function workon ($env) {
if ($env) {
& $env:WORKON_HOME\$env\Scripts\activate.ps1
} else {
Write-Host "Pass a name to activate one of the following virtualenvs:"
Write-Host" ================================================================"
Get-ChildItem $env:WORKON_HOME -Name
}
}
Note that I had already set WORKON_HOME envrionment variable.
I had the similar issue.
Fixed it by following some of the above steps.
Listing it down here:
install both VirtualEnv and VirtualEnvWrapper-win
Set the ExecutionPolicy to RemoteSigned Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Add environment Path 'WORKON' to 'envs directory'
Now try 'workon' command
installing:
{ pip install virtualenvwrapper-win }
Note that the commands at default can only be used in CMD.
Modify to use in the powershell:
function workon ($env) {
& .\Envs\$env\Scripts\activate.ps1
}
Creating a virtual environment:
{ workon venv }
or any other name
Activating:
{ . venv\scripts\activate }
To see a list of created virtual environments:
{ workon }
To deactivate:
{ deactivate }

How can I suspend a Hyper-V VM from a linux machine?

I've set up an NMS system on a machine running Ubuntu that responds to various UPS events by calling a Perl script to go through all of our VMWare hosts and suspend all of the VMs. VMWare was smart and provided a set of Perl modules which made this relatively easy. We also have three Hyper-V hosts, however, and I can't seem to find a way to control them that isn't specific to some Microsoft technology (e.g. a PowerShell script).
I'm hoping somebody could suggest a way to control the Hyper-V hosts from a linux box. I'd rather it didn't involve using Wine, but I'm willing to go that route if there's nothing else that will work.
I found an ugly way to do it, but at least it doesn't require anything to be installed or configured on the VM host.
First I got a utility called winexe, which lets you open a terminal connection to a windows machine.
Then I wrote a long an ugly Perl script to pipe some PowerShell code to the machine to suspend any running machines:
sub hv_suspend_host {
my $host = $_[0];
my $code = <<'END';
echo '===BEGIN'
$query = "SELECT * FROM Msvm_ComputerSystem WHERE EnabledState != 3 AND EnabledState != 32769" #Exclude off and saved VMs
$VMs = get-wmiobject -query $query -namespace "root\virtualization" -computername "."
foreach ($VM in $VMs) {
if ($VM.name -ne $VM.ElementName) { # Exclude the host itself
if ($VM.RequestStateChange(32769).ReturnValue -eq 4096) { # Put the VM in a saved state
# It worked, log success
} else {
# It didn't, log failure
}
}
}
echo '===END'
exit
END
my $recv;
run(["winexe", '-U', "DOMAIN/$win_user%$win_pass", '--interactive=0', "//$host", 'powershell -command -'], \$code, \$recv);
$recv =~ tr/\r//d; # Convert to UNIX line endings
$recv =~ /===BEGIN\n(.+)===END/s; # Now recv contains anything you logged
}
You might have to mess with this a bit to get it to work. I had to hack out some of the implementation-specific things, but I left in part of the output capturing code. This requires global variables named $win_user and $win_pass containing administrator account login info for the target VM host. It also requires that you use IPC::Run.
Hyper-V can be managed remotely using the WMI interfaces. There is a WMI Client for Linux, which should allow you to make the relevant API calls to manage Hyper-V. I have not had to do this myself, but the specific WMI calls are available at Microsoft: https://msdn.microsoft.com/en-us/library/hh850319%28v=vs.85%29.aspx

How do you call msdeploy from powershell when the parameters have spaces?

I'm running into a problem with spaces in my parameters that I try to send into msdeploy from a powershell script.
There are a number of other related articles but none of them solve the problem.
Problems Using Power Shell And MSDeploy.
Similar SO issue that doesn't work: How to run exe in powershell with parameters with spaces and quotes
PowerShell BUG: Executing commands which require quotes and variables is practically impossible
Another SO issue that doesn't work:Passing parameters in PowerShell 2.0
The simplest example that succeeds and then fails when I make it more complicated is just dumping the default web site.
$msdeploy = "C:\Program Files\IIS\Microsoft Web Deploy\msdeploy.exe"
&$msdeploy -verb:dump -source:appHostConfig=`'default web site`' -verbose
==SUCCESS
This one?
$sitename="default web site"
&$msdeploy -verb:dump -source:appHostConfig=$sitename -verbose
==FAIL with the following error
msdeploy.exe : Error: Unrecognized argument '"-source:"appHostConfig=default'. All arguments must begin with "-".
At C:\xxx\test.ps1:122 char:6
+ &
+ CategoryInfo : NotSpecified: (Error: Unrecogn...begin with "-".:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Error count: 1.
The following variations have also failed
#FAIL
$sitename=`'default web site`'
$sitename=`'"default web site"`'
$sitename="`'default web site`'"
$sitename="default web site"
$sitename="'default web site'"
&$msdeploy -verb:dump "-source:appHostConfig=$sitename" -verbose
&$msdeploy -verb:dump -source:appHostConfig="$sitename" -verbose
&$msdeploy -verb:dump -source:appHostConfig='$sitename' -verbose
&$msdeploy -verb:dump -source:appHostConfig=`'$sitename`' -verbose
&$msdeploy -verb:dump -source:appHostConfig=`"$sitename`" -verbose
I'm at a loss. Everyone I work with is at a loss. Seriously this sucks. I loved Powershell. I loved msdeploy. I can't say that I love putting them together. It looks like it may have been easier to focus on the API instead of the cli.
EDIT:
The parameters in the string array suggested by Emperor XLII works well. An alternative solution is presented in the following article: The trials and tribulations of using MSDeploy with PowerShell
function PushToTarget([string]$server, [string]$remotePath, [string]$localPath) {
cmd.exe /C $("msdeploy.exe -verb:sync -source:contentPath=`"{0}`" -dest:computerName=`"{1}`",contentPath=`"{2}`" -whatif" -f $localPath, $server, $remotePath )
}
Using the technique from Keith's answer to How to run exe in powershell with parameters with spaces and quotes question you linked to, running echoargs -verb:dump -source:appHostConfig=$sitename -verbose gave me this output:
Arg 0 is <-verb:dump>
Arg 1 is <-source:appHostConfig=default>
Arg 2 is <web>
Arg 3 is <site>
Arg 4 is <-verbose>
This would explain the invalid argument of appHostConfig=default that msdeploy was seeing.
Running echoargs -verb:dump "-source:appHostConfig=$sitename" -verbose, with $sitename = "default web site", appears to result in the desired arguments:
Arg 0 is <-verb:dump>
Arg 1 is <-source:appHostConfig=default web site>
Arg 2 is <-verbose>
Though from your list, it appears that this did not work for you.
Another method you might try is building up the list of arguments in an array, which powershell can automatically escape. For example, this gives the same output as above:
[string[]]$msdeployArgs = #(
"-verb:dump",
"-source:appHostConfig=$sitename",
"-verbose"
)
echoargs $msdeployArgs
Just adding another way in case it is helpful to anyone:
Invoke-Expression "& '[path to msdeploy]\msdeploy.exe' --% -verb:sync -source:contentPath=`'$source`' -dest:contentPath=`'$dest`'"
"--%" is new to powershell 3. From here: "You simply add a the --% sequence (two dashes and a percent sign) anywhere in the command line and PowerShell will not try to parse the remainder of that line."
Found a working solution and easy fix.
Reference: http://answered.site/all-arguments-must-begin-with--at-cwindowsdtldownloadswebserviceswebservicesidservicepublishedwebsitesidservicedeploymentidservicewsdeployps123/4231580/
$msdeploy = "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe"
$msdeployArgs = #(
"-verb:sync",
"-source:iisApp='Default Web Site/HelloWorld'",
"-verbose",
"-dest:archiveDir='c:\temp1'"
)
Start-Process $msdeploy -NoNewWindow -ArgumentList $msdeployArgs
We had faced the similar kind of issue. Our fix was like below,
$path = "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe";
$verb = "-verb:sync";
$src = "-source:contentPath=[ESC][ESC][ESC]"c:\aa aa[ESC][ESC][ESC]";
$dest = "-dest:contentPath=[ESC][ESC][ESC]"c:\aa[ESC][ESC][ESC]";
Invoke-Expression "&'$path' $verb $src $dest";
where, ESC - is escape sequence/character
I tried every technique under the sun, and this is the only one that worked for me (using PowerShell 2).
cmd.exe /C $("msdeploy.exe -verb:sync -source:package=`"{0}`" -dest:auto,IncludeAcls=`"False`" -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension -setParamFile:`"{1}`"" -f $mypackagepath, $myparamfilepath )
Here is another approach derived from the input below.
$msdeploy = "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe";
$command = "-verb:sync";
$sourcePath = "C:\aa aa\";
$source = $("-source:contentPath=`"{0}`"" -f $sourcePath);
$destPath = "C:\aa"
$destination = $("-dest:contentPath=`"{0}`" -f $destPath);
$msdeploycommand = $("`"{0}`" {1} {2} {3} -verbose" -f $msdeploy, $command, $source, $destination);
cmd.exe /C "`"$msdeploycommand`"";
This caters for the MSDeploy.exe being in its default installation folder which contains spaces. Hence the wrapping with the escape character (`).
I've used some ideas from answers above and came up with the following simpler function to do the thing.
Note that it is important to give the full path to MSDeploy as when running under the build agent it sometimes doesnt recognise the PATH to msdeploy.
function Deploy([string]$server, [string]$remotePath, [string]$localPath) {
$msdeploy = "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe";
cmd.exe /C $("`"{3}`" -verb:sync -source:contentPath=`"{0}`" -dest:computerName=`"{1}`",contentPath=`"{2}`" " -f $localPath, $server, $remotePath , $msdeploy )
}
Usage
Deploy $hostName $remotePath $source
All of the above did not work for me, this is the solution that worked:
# get msdeploy exe
$MSDeploy = ${env:ProgramFiles}, ${env:ProgramFiles(x86)} |
ForEach-Object {Get-ChildItem -Path $_ -Filter 'MSDeploy.exe' -Recurse} |
Sort-Object -Property #{Expression={[version]$_.VersionInfo.FileVersion}} -Descending |
Select-Object -First 1 -ExpandProperty FullName
#build deploy command
$deplyCmd = """""$MSDeploy"" -verb:sync -dest:iisApp=""Default Web Site"" -enableRule:DoNotDeleteRule -source:iisApp=""$ExtraWebFilesFolder"""
#execute
&cmd /c $deplyCmd
This problem has certainly been around for a long time and I spent some time battling it recently. The result has been successful for me so I'll post it here in hopes that it can help others who find this question in the future.
The first problem to resolve is getting rid of the spaces in the msdeploy path. There are two approaches here. One is persistent and requires you to have server access, the other is temporary in the context of your PowerShell script. Either will work but I'd prefer the first if it's an option for you.
For the first approach, create a junction point. Example script:
new-item -Path "c:\MS-WebDeploy" -ItemType Junction -Value "c:/Program Files (x86)/iis/microsoft web deploy v3"
For the second approach, create a PSDrive (w in this example)
New-PSDrive -Name "w" -PSProvider FileSystem -Root "C:/Program Files (x86)/iis/microsoft web deploy v3"
I'm using three PowerShell variables below. For example purposes, pretend that all three have spaces.
$ParamFilePath = "c:\deployment files\parameters.xml"
$PackageName = "c:\deployment files\My Website.zip"
$WebAppPath = "Default Web Site"
First, create an array and build up your arguments as needed.
#nothing needs to be done with these arguments so we'll start with them
[string[]]$arguments = #("-verb:sync", "-dest:auto", "-disableLink:AppPoolExtension", "-disableLink:ContentExtension", "-disableLink:CertificateExtension", "-allowUntrusted")
#double up on the quotes for these paths after the colon
$arguments += "-setParamFile:""$ParamFilePath"""
$arguments += "-source:package=""$PackageName"""
#must not have spaces with the commma, use single quotes on the name and value here
$arguments += "-setParam:name='IIS Web Application Name',value='$WebAppPath'"
#add your own logic for optional arguments
$arguments += "-EnableRule:EncryptWebConfig"
Now build the msdeploy command and put the PowerShell escape sequence to prevent PowerShell from "helping" later. Use the path you created with the junction or the PSDrive
$command = "w:\msdeploy.exe" + " --% " + $arguments -join " "
Finally, execute that command as a script block.
$sb = $ExecutionContext.InvokeCommand.NewScriptBlock($command)
& $sb
I've wrapped this and a bit more code into a script which is called like this.
.\Run-WebDeploy -WebAppPath "Default Web Site" -PackageName "c:\deployment files\My Website.zip" -ParamFilePath "c:\deployment files\parameters.xml" -EncryptWebConfig
In general, you can help yourself a lot by getting rid of the spaces in your paths/names. Sometimes, that can't be done and this should get you through.

Resources