Azure Runbook Error, AzureRmStorageAccountKey Cannot Index into null array - powershell

I am trying to run this command within an Azure Run book
(Get-AzureRmStorageAccountKey -Name $defaultStorageAccountName -ResourceGroupName $defaultResourceGroupName).Value[0]
It runs fine on my local machine and I can see the Storage Account Key. However, when I run the same command in Azure Runbook it throws the following error
Cannot index into a null array
If I just run Get-AzureRmStorageAccountKey -Name $defaultStorageAccountName -ResourceGroupName $defaultResourceGroupName in the Azure Runbook, it runs fine and I can see the Keys. It is just when I am selecting an element from an Array that it fails and throws an error.
The PowerShell version is 5 on my local machine and on Azure Runbook.
Update:
My issue was resolved after updating the Modules from the Gallery in my automation account.

According to your description, I test your cmdlets in my runbook, I could get storage key.
You could use the following commands to login your subscriptions.
Write-Verbose "Get connection asset: $ConnectionAssetName" -Verbose
$connectionName = Get-AutomationConnection -Name "AzureRunAsConnection"
$AzureCredentialAssetName = "AzureRunAsConnection"
try
{
# Get the connection "AzureRunAsConnection "
$servicePrincipalConnection=Get-AutomationConnection -Name $AzureCredentialAssetName
"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
}
}
$key=(Get-AzureRmStorageAccountKey -Name <storage account name> -ResourceGroupName <resource group name>).Value[0]
"The storage account key is $key"
More information please refer to this link.
Update
You need update Azure Storage account module version in automation account, then you could solve this issue.

Related

Azure Automation Runbooks, Connect-AzAccount, Assigning rights

I am trying to do some basic group management using Azure Automation, but I'm having a heck of a time getting the script to authenticate correctly.
I have added the Azure.Account modules to the runbook, and the connection seems to get established (at least, it doesn't throw an exception, and the returned object is not null).
When using "Get-AzAdGroup", I am getting:
Get-AzADGroup : Insufficient privileges to complete the operation.
The app account created is a "Contributor" in AAD, so as far as I understand, has full rights to the directory.
I have tried the solution listed at How to connect-azaccount in Azure DevOps release pipeline, to the same effect (Insufficient privileges). I have also applied "Group.Read.All", "Group.ReadWrite.All", "GroupMember.Read.All", "GroupMember.ReadWrite.All" based on what I can read from https://learn.microsoft.com/en-us/graph/permissions-reference#group-permissions - but I'm not 100% clear if the Az* cmdlets use the Microsoft Graph, or if that's separate altogether.
Code is as follows:
$connectionName = "AzureRunAsConnection"
try
{
# Get the connection "AzureRunAsConnection "
$servicePrincipalConnection=Get-AutomationConnection -Name $connectionName
"Logging in to Azure..."
<#
# Original, technically legacy.
Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
#>
$connectState = Connect-AzAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
<#
# From https://stackoverflow.com/questions/56350960/how-to-connect-azaccount-in-azure-devops-release-pipeline, same result.
$AzurePassword = ConvertTo-SecureString "*****" -AsPlainText -force
$psCred = New-Object System.Management.Automation.PSCredential($servicePrincipalConnection.ApplicationId , $AzurePassword)
$connectState = Connect-AzAccount -Credential $psCred -TenantId $servicePrincipalConnection.TenantId -ServicePrincipal
#>
if ($connectState) {
"Connected."
} else {
"Doesn't seem to be connected."
}
}
catch {
if (!$servicePrincipalConnection)
{
$ErrorMessage = "Connection $connectionName not found."
throw $ErrorMessage
} else{
Write-Error -Message $_.Exception
throw $_.Exception
}
}
# Get groups
Get-AzADGroup
My gut tells me that since both connect-azaccount methods yield the same result (connected, but no access) my issue isn't necessarily in the script, but short of creating a service account (which presents challenges with MFA), I don't know how to fix this.
From the solution How to connect-azaccount in Azure DevOps release pipeline I provided, in the screenshot, it is clear that you need to add the API permission of Azure Active Directory Graph, not Microsoft Graph.
Please add the Directory.Read.All in Azure Active Directory Graph for the AD App of your automation run as account.
I have faced a very similar issue. You have a problem with the API permissions Azure APP has.
In my case, my azure App was working as a Service Principal, and not only modifying some stuff in the Azure AD, but also some Azure resources, therefore, these were the api permissions that I had to grant:
Remember that you also need to grant admin consent from the Azure tenant for these permissions update.
If you just assign Contributor role to the service principal, you just can use the sp to get Azure resource(such as VM, app service). So if you want to use the sp tp get Azure AD resource, we need to assign Azure AD role (sucah as Directory Readers) to the sp. For more details, please refer to the document and the document
The detailed steps are as below
Get the RunAsAccount sp object id
Confugure Permisisons for the application
connect-AzureAD
$sp=Get-AzureADServicePrincipal -ObjectId <the sp object id your copy>
$role=Get-AzureADDirectoryRole | Where-Object{$_.DisplayName -eq "Directory Readers"}
Add-AzureADDirectoryRoleMember -ObjectId $role.ObjectId -RefObjectId $sp.ObjectId
Test
a. create a new runbook
$servicePrincipalConnection=Get-AutomationConnection -Name 'AzureRunAsConnection'
$connectState = Connect-AzAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
if ($connectState) {
"Connected."
} else {
"Doesn't seem to be connected."
}
Get-AzADGroup
Go to Azure portal --> Azure AD --> roles and Administrator-->Directory Readers role --> assign this role to the runbook account name

Auto Azure SQL DB Creation -DACPAC task how to configure database Azure pricing model

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

Azure Runbook : What credential to use?

I'm trying to create a runbook to restart my web app. I'm new to this so I created a credential in the automation blade but I don't know what is the username/pwd is supposed to be? Is it the same with my azure login account?
Tried that and obviously when I test the runbook this error shows up :
Add-AzureAccount : unknown_user_type: Unknown User Type
At RestartJob:13 char:13
+ + CategoryInfo : CloseError: (:) [Add-AzureAccount],
AadAuthenticationFailedException
+ FullyQualifiedErrorId : Microsoft.WindowsAzure.Commands.Profile.AddAzureAccount
Been trying to figure this out on msdn as well.. any help?
According to your description, I test in my lab. The fowwling cmdlets work for me.
$ConnectionAssetName = "shuitest"
# Get the connection
$connection = Get-AutomationConnection -Name $connectionAssetName
# Authenticate to Azure with certificate
Write-Verbose "Get connection asset: $ConnectionAssetName" -Verbose
$Conn = Get-AutomationConnection -Name $ConnectionAssetName
if ($Conn -eq $null)
{
throw "Could not retrieve connection asset: $ConnectionAssetName. Assure that this asset exists in the Automation account."
}
$CertificateAssetName = $Conn.CertificateAssetName
Write-Verbose "Getting the certificate: $CertificateAssetName" -Verbose
$AzureCert = Get-AutomationCertificate -Name $CertificateAssetName
if ($AzureCert -eq $null)
{
throw "Could not retrieve certificate asset: $CertificateAssetName. Assure that this asset exists in the Automation account."
}
Write-Verbose "Authenticating to Azure with certificate." -Verbose
Set-AzureSubscription -SubscriptionName $Conn.SubscriptionName -SubscriptionId $Conn.SubscriptionID -Certificate $AzureCert
Select-AzureSubscription -SubscriptionId $Conn.SubscriptionID
Before you execute the runbook, you should create AssetName and Certificate on Azure Portal.
1.Certificate assets in Azure Automation
Please select your runbook-->ASSETS--Certificate.
2.Create Connection(AssetName). Please select your runbook-->ASSETS--Connections. According to your scenario, you should select AzureClassicCertificate

Azure Powershell how to get running services of a VM via Runbook?

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)

Get job status in Azure Runbook

I would like to have a runbook run on a schedule but exit if the last job hasn't finished running (i.e. if the schedule is every 2 hours and a job takes 2.5 hours then the next job shouldn't run).
I've tried to use Get-AzureAutomationJob to get the last job status (https://azure.microsoft.com/en-gb/documentation/articles/automation-runbook-execution/#retrieving-job-status-using-windows-powershell), but I can't get it to work. I presume all the preamble to get the subscription etc is required.
"Get-AzureAutomationJob : The Automation account was not found."
$ConnectionAssetName = "AzureClassicRunAsConnection"
# Get the connection
$connection = Get-AutomationConnection -Name $connectionAssetName
# Authenticate to Azure with certificate
Write-Verbose "Get connection asset: $ConnectionAssetName" -Verbose
$Conn = Get-AutomationConnection -Name $ConnectionAssetName
if ($Conn -eq $null)
{
throw "Could not retrieve connection asset: $ConnectionAssetName. Assure that this asset exists in the Automation account."
}
$CertificateAssetName = $Conn.CertificateAssetName
Write-Verbose "Getting the certificate: $CertificateAssetName" -Verbose
$AzureCert = Get-AutomationCertificate -Name $CertificateAssetName
if ($AzureCert -eq $null)
{
throw "Could not retrieve certificate asset: $CertificateAssetName. Assure that this asset exists in the Automation account."
}
Write-Verbose "Authenticating to Azure with certificate." -Verbose
Set-AzureSubscription -SubscriptionName $Conn.SubscriptionName - SubscriptionId $Conn.SubscriptionID -Certificate $AzureCert
Select-AzureSubscription -SubscriptionId $Conn.SubscriptionID
$job = (Get-AzureAutomationJob –AutomationAccountName "THE NAME OF THE AUTOMATION ACCOUNT AS IT APPEARS IN THE PORTAL" –Name "JobStatusTest" | sort LastModifiedDate –desc)[0]
Well, you would need to use Get-AzureRMAutomation job for that. Let me elaborate on that, I think in march 2016 Microsoft removed Azure Automation from the OLD azure model and now it is present only in the new one. so you would need to add RM to your commandlets