I'm quite new to Azure but I'm banging my head against something that seems simple. I've read a ton of resources that suggest this should be easy but I simply cannot get a runbook in the new portal (ARM) to start a specific VM. It runs and states "Completed" but nothing happens.
Here is the script:
Updated following Jack Zeng's comment which unfortunately changed little.
workflow StartDEVTC1
{
$VerbosePreference = "Continue"
$ErrorActionPreference = "Stop"
$WarnPreference = "Continue"
Write-Output "Getting credential..."
$cred = Get-AutomationPSCredential -Name 'AutomationPowershellCred1'
Write-Output "Adding account..."
Add-AzureRmAccount -Credential $cred
Write-Output "Getting VM..."
$VM = Get-AzureRmVM -ResourceGroupName "myResourceGroup" -Name "DEVTC1" -Status
$vmName = $VM.Name
$vmRG = $VM.ResourceGroupName
Write-Output "Checking state of $vmName from $vmRG..."
$VMDetail = $VM | Select-Object -ExpandProperty StatusesText | convertfrom-json
$vmPowerstate = $VMDetail[1].Code
if($vmPowerstate -like "PowerState/deallocated")
{
Write-Output "$vmName powerstate is $vmPowerstate, starting VM..."
$res = $VM | Start-AzureRmVM
if (($res.StatusCode) -ne 'OK')
{
Write-Error "Could not start VM."
}
}
else
{
Write-Output "$vmName powerstate is $vmPowerstate, not starting VM."
}
Write-Output "END."
}
As you can see I've tried adding some logging but I can't see these in the Testing Pane. Running the key steps in a Powershell on my PC works just fine so what's going on here?
The credential has been newly defined using the details of my azure account (I am an Owner on the account).
It should go without saying but I've tried some much simpler approaches to call Start-AzureRmVm and nothing works.
Thanks and appreciation in advance!
Ok I got it and the answer was frustratingly simple.
You cannot use a workflow block unless your runbook was created as a "PowerShell Workflow" runbook. In my case it was simply a "PowerShell" runbook which I guess was just ignoring the workflow block of code.
Recreating the runbook as a "PowerShell Workflow" runbook allowed the logging to be output so I could see what was happening and the VM got started as expected (although it took a really long time).
Related
I have following code to loop all my vms in all subscriptions but for some reason Runbook never goes to my loop and therefore does nothing. This managed identity has VM contributors role from the root so permission is not the issue right now.
# Ensures you do not inherit an AzContext in your runbook
Disable-AzContextAutosave -Scope Process | Out-Null
Write-Output "Connecting to azure via Connect-AzAccount -Identity"
Connect-AzAccount -Identity
Write-Output "Successfully connected with Automation account's Managed Identity"
Set-AzContext -SubscriptionId "0000-xxxx"
$Subscriptions = Get-AzSubscription
foreach ($sub in $Subscriptions) {
# suppress output on this line
Write-Host Working with Subscription $sub.Name
$null = Get-AzSubscription -SubscriptionName $sub.Name | Set-AzContext
$vms = Get-AzVM
foreach ($vm in $vms) {
Write-Host Working with virtual Machine $vm.Name in resource group $vm.ResourceGroupName
stop-azvm -Name $vm.Name -ResourceGroup $vm.ResourceGroupName
}
}
If I put Get-AzVM or Get-AzSubscription command before loop it will show all my subscriptions or vms that are in context subscriptions so I know the commands work.
Script usually run like 5 minutes and then just goes to complied without doing really anything.
If I run the code from my computer in Visual Studio Code it goes fine trough but now am I missing now.
I have just found out that MS are changing the default Azure PaaS DB config setting on create to use Vcore instead of DTU. I am using Azure Devops to build DBs from a GithHub repository via DACPACs using the Azure SQL Task. I would like to keep the DTU model for my builds.
The options I can see for implementing this are :
Configure during the DACPAC Deploy task... I have not been able to find how to do this anywhere.
Pre-create the DB and then run the DACPAC file into it.
Set an azure default for any new DBs created. I have not been able to find a way of doing this.
For the second option I have successfully used the code snippet below for DB creation though cant find how I would create one with "standard S0 10 DTUs for example.
Any advice gratefully received..
New-AzureRmSqlDatabase -ResourceGroupName $ResourceGroup -ServerName $Servername -DatabaseName $DataBaseName
In general, you want to provision your Azure resources via some variety of idempotent configuration language. ARM templates are native, Terraform is a popular alternative. Run your ARM template deployment at the start of every deployment, then all of the necessary infrastructure for your application to run will be in place and properly configured.
If you want to do it with PowerShell, though, the parameters you're looking for are Edition and RequestedServiceObjectiveName. I.E. -Edition Standard -RequestedServiceObjectiveName S0.
After many false starts due to deficiencies in the documentation due to the constant Azure churn I have a working script. This Script works and the web hooks allow it to be called from Azure Devops CI.
param
(
[Parameter (Mandatory = $false)]
[object] $WebhookData
)
Write-Output $WebhookData
# If runbook was called from Webhook, WebhookData will not be null.
if ($WebhookData) {
# Retrieve Params from Webhook request body
$params = (ConvertFrom-Json -InputObject $WebhookData.RequestBody)
foreach ($param in $params)
{
$DatabaseName = $param.DatabaseName
$Edition = $param.Edition
$PricingTier=$param.PricingTier
Write-Output "Found $DatabaseName"
Write-Output "Found $Edition"
Write-Output "Found $PricingTier"
}
$connectionName = "AzureRunAsConnection"
try
{
# Get the connection "AzureRunAsConnection "
$servicePrincipalConnection=Get-AutomationConnection -Name $connectionName
"Logging in to Azure..."
Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
}
catch {
if (!$servicePrincipalConnection)
{
$ErrorMessage = "Connection $connectionName not found."
throw $ErrorMessage
} else{
Write-Error -Message $_.Exception
throw $_.Exception
}
}
$ServerName = "ServerName"
$ResourceGroup = "ResourceGroupName"
#$PricingTierLocal ="S2"
#$EditionLocal ="Standard"
Set-AzureRmSqlDatabase -ResourceGroupName $ResourceGroup -ServerName $ServerName -DatabaseName $DatabaseName -Edition $Edition -RequestedServiceObjectiveName $PricingTier
}
else {
# Error
write-Error "This runbook is meant to be started from an Azure webhook only."
}
i've some trouble with executing command that need more privileges than the calling user has.
I wrote a "admin repository" of powershell scripts & snippets for management reasons. I create a auto importer script and also an auto update if i release a new tag. Works like a charm!
But than we decieded to split some privileges to other users to fulfill some JEA requirements.
Now our unprivileged "working" users are importing the repository and are not allowed to run every command. (eg. querying DHCP sever)
I thought it would be no issue - thought about the Get-Credential simply running the priv features with the priv user.... but i was wrong... it's not simple as i thought.
First issue was that the command does not accept a -credential param.
i ended up with something like this:
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "powershell"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Domain = $cred.UserName.Split('\')[0]
$pinfo.Password = $cred.Password
$pinfo.UserName = $cred.UserName.Split('\')[1]
$pinfo.CreateNoWindow = $true
$pinfo.Arguments = "Get-DhcpServerv4Scope -computername $server"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
$p.WaitForExit()
it took me some time to get the output of this session to a variable...
fist i wrote output to a tmp file and red it with the other session, but it feels like I do it terrible wrong.
Now I've to parse the output, create a template and build the object again - i've to change half of the script and it become slow and I dont like the way....
I can't escape the feeling that there is a better way - so im asking you :)
Best Regards
David Bla
Invoke-Command allows the passing of credentials.
Invoke-Command -ScriptBlock {
# put your code here
Write-Host "Hello World!"
} -Credential (Get-Credential)
I switched to start-job because it allows passing credentials but not using the remoteing feature:
$variable_needs_to_be_passed
$a_job = start-job -ScriptBlock{param($var) Do-Stuff-With-Other-User $var} `
-Arg $variable_needs_to_be_passed -credentials $cred
while((get-job -Id ($a_job.id)).State -eq "Running") {sleep(0.5)}
$return_value = Recieve-Job -Id ($a_job.id)
But its terrible slow compared to running command directly (due to iterative calling) maybe it would be better to call the complete script instead of using it to execute single commands.
To better understand it, this part searches all dns and dhcp servers for a specific client.
exemplary code:
if(check_permission){ GetAllDhcpScopes }else{ runas ... GetAllDhcpScopes }
foreach( AllDhcpScopes ){ if(check_permission){ GetDhcpLeases $_ }else{ runas ... GetDhcpLeases $_ }
This was anoying in performance so i started to do a permission_check at the beginning and than run the script normally or starating it as job with higher perms!
I'm trying to write an Azure Powershell Runbook that will start a VM, and then check if a windows service on the VM is running or not and start it.
I can get the VM started, but enumerating the services isn't working. I'm brand new on Azure Runbooks so I could be doing something wrong. I limited the below code to only the Get-Service bit and not the VM starting.
# Returns strings with status messages
[OutputType([String])]
param (
[Parameter(Mandatory=$false)]
[String] $AzureConnectionAssetName = "AzureRunAsConnection",
[Parameter(Mandatory=$false)]
[String] $ResourceGroupName = ""
)
try {
# Connect to Azure using service principal auth
$ServicePrincipalConnection = Get-AutomationConnection -Name $AzureConnectionAssetName
Write-Output "Logging in to Azure..."
$Null = Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $ServicePrincipalConnection.TenantId `
-ApplicationId $ServicePrincipalConnection.ApplicationId `
-CertificateThumbprint $ServicePrincipalConnection.CertificateThumbprint
}
catch {
if(!$ServicePrincipalConnection) {
throw "Connection $AzureConnectionAssetName not found."
}
else {
throw $_.Exception
}
}
# If there is a specific resource group, then get all VMs in the resource group,
# otherwise get all VMs in the subscription.
if ($ResourceGroupName) {
$VMs = Get-AzureRmVM -ResourceGroupName $ResourceGroupName
}
else {
$VMs = Get-AzureRmVM
}
# Try and enumerate the VM's services
foreach ($VM in $VMs) {
Write-Output "Listing all services..."
Write-Output ("VM: {0}" -f $VM.Name)
$VM | Get-Service | Format-Table -AutoSize
Write-Output "Listing alternative method..."
Get-Service -ComputerName $VM.Name | Format-Table -AutoSize
Write-Output "Finished listing..."
}
And the output is this:
Logging in to Azure...
Listing all services...
VM: demo-0
Listing alternative method...
Finished listing...
Well, first of all, Starting VM is asynchronous, so you need to wait for the VM to actually boot, and the Get-Service wouldn't work anyway, as to get the services from a VM you need to authenticate against that VM, so either user PSsessions or invoke-command, something like that. Just look on how to remote into servers with powershell or how to issue powershell command to remote PC's. This case is nothing different. And it has nothing to do with how Azure Automation works.
When you are running Azure Automation runbooks, you have the choice of the azure queue or creating a hybrid worker. The azure queue is good for many processes, but it will not have access to the VMs directly to run commands such as get-service.
To expand on #4c74356b41 answer, you will need to use remote powershell to execute the command (using New-PSSession) but you will also have to ensure that those commands are running on an Azure Automation Hybrid Worker
In the comments below, you asked about credentials. You can set credentials in the Azure Automation account and then have them used by your script when creating a new session. See this link
You could try to use the following cmdlets. It works for me.
# Try and enumerate the VM's services
foreach ($VM in $VMs) {
Write-Output "Listing all services..."
Write-Output ("VM: {0}" -f $VM.Name)
$ResourceGroupName=$VM.ResourceGroupName
$Name=$VM.Name
$status=(Get-AzureRmVM -ResourceGroupName $ResourceGroupName -Name $Name -Status).Statuses[1].code
if($status -like "PowerState/deallocated")
{
Write-Output "VM is stopped, starting VM"
if (Start-AzureRmVm -ResourceGroupName $ResourceGroupName -Name $Name)
{
Write-Output "Start VM successfuly"
}else
{
Write-Output "Start VM failed"
break
}
}
##get VM's Public IP
$nicName = ($VM.NetworkInterfaceIDs[0] -split '/')[-1]
$pip = (Get-AzureRmNetworkInterface -Name $nicName -ResourceGroupName $ResourceGroupName).IpConfigurations.publicipaddress.id
$PublicIP=(Get-AzureRmPublicIpAddress -ResourceGroupName shui -Name ($pip -split '/')[-1]).IpAddress
$Uri="http://$($PublicIP):5986"
Write-Output "Get ConnectionUri $Uri"
##get Credential from assets
$shui=Get-AutomationPSCredential -Name 'shui'
$session=New-PSSession -ConnectionUri $Uri -Credential $shui -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck)
$result=Invoke-Command -Session $session -ScriptBlock {ipconfig}
Write-Output "$result"
Write-Output "Finished listing..."
}
Before you use this script. Firstly,you should open ports 5896 on your VM's firewall and NSG, you could check as the following link. Please ensure you could telnet IP 5986 on your local PC.
You should import AzureRM.Network modules to your Automation Account. More information about how to import Modules please refer to this link.
3.Store your VMs's passord to Runbook, you could refer to this link. When you want to use the credentials, you could use the script below:
##get Credential from assets
$shui=Get-AutomationPSCredential -Name 'shui'
$session=New-PSSession -ConnectionUri $Uri -Credential $shui -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck)
I was working on a script which I have downloaded from the gallery to stop all VMs running in a subscription. This was running perfectly until last week, but when I ran it yesterday, it couldnt find any VM in my subscription (VMs are there in my sub and running). Script is below:
workflow Stop-AllAzureVM
{
# Add the credential used to authenticate to Azure.
# TODO: Fill in the -Name parameter with the Name of the Automation PSCredential asset
# that has access to your Azure subscription. "myPScredName" is your asset name that reflects an OrgID user
# like "someuser#somewhere.onmicrosoft.com" that has Co-Admin rights to your subscription.
$Cred = Get-AutomationPSCredential -Name "myPsCred"
# Connect to Azure
Add-AzureAccount -Credential $Cred
# Select the Azure subscription you want to work against
# TODO: Fill in the -SubscriptionName parameter with the name of your Azure subscription
Select-AzureSubscription -SubscriptionName "MySubs"
# Get all Azure VMs in the subscription that are not stopped and deallocated, and shut them down
# all at once.
$VMs = Get-AzureVM | where-object -FilterScript {$_.status -ne 'StoppedDeallocated'}
if (!$VMs)
{
Write-Output "No VM running at the moment"
}
else
{
Write-Output "VM(s) found running, proceeding for shutdown"
}
foreach -parallel ($vm in $VMs)
{
$stopRtn = Stop-AzureVM -Name $VM.Name -ServiceName $VM.ServiceName -force -ea SilentlyContinue
$count=1
if(($stopRtn.OperationStatus) -ne 'Succeeded')
{
do{
Write-Output "Failed to stop $($VM.Name). Retrying in 60 seconds..."
sleep 60
$stopRtn = Stop-AzureVM -Name $VM.Name -ServiceName $VM.ServiceName -force -ea SilentlyContinue
$count++
}
while(($stopRtn.OperationStatus) -ne 'Succeeded' -and $count -lt 5)
}
if($stopRtn){Write-Output "Stop-AzureVM cmdlet for $($VM.Name) $($stopRtn.OperationStatus) on attempt number $count of 5."}
}
}
This script always prints "No VM running at the moment"
I tried Get-AzureVM without any condition also but the result was same.
I am not sure if something has changed in past couple of weeks which is resulting in this issue.
Update-1:
I tried adding below commands in script:
$VMs = Get-AzureVM
$subscriptions = Get-AzureSubscription
Write-Output "Found [$($subscriptions.Count)] subscriptions accessible to user"
Write-Output "Found [$($VMs.Count)] VMs in Subscription"
and I got below output:
Found [] subscriptions accessible to user
Found [0] VMs in Subscription
No VM running at the moment
So it looks like something weird had happened in my automation account and I dont seem to have any clue about it!
To all who are facing this issue, not sure if this is the right way to fix this but at least as a workaround, I tried creating a new automation account and copy pasted same runbook in it and it is working fine for me.