I am new to Azure power shell world. I am trying to create power shell script to automate VM creation. All my script running well, VM is getting created as well, but it’s getting hanged on its last line. So even though the virtual machine gets created my power shell script is keep on running. Please advise me how to address this issue.
Write-Verbose 'Creating VM...'
$result = New-AzureRmVM -ResourceGroupName $resourceGroupName -Location $location -VM $vm
if($result.Status -eq 'Succeeded') {
Write-Verbose $result.Status
Write-Verbose ('VM named ''{0}'' is now ready, you can connect using username: {1} and password: {2}' -f $vmName, $adminUsername, $adminPassword)
}
else {
Write-Error 'Virtual machine was not created successfully.'
}
you can take another way, to verify the state of the VM
New-AzureRmVM -ResourceGroupName $resourceGroupName -Location $location -VM $vm
if((Get-AzureRmVM -Name $vmName).Status -eq "ReadyRole"){
#Do something awesome here :)
}
and have a look at: https://4sysops.com/archives/how-to-create-an-azure-vm-with-powershell/
return type of $result will be a psobject. After successful VM creation the output will be in below format
$result[0] = "Provisioning succeeded"
$result[1] = "VM running"
So, try this-
if($result.ProvisioningState -eq "Succeeded")
{
Write-Verbose ('VM named ''{0}'' is now ready, you can connect using username: {1} and password: {2}' -f $VMName, $username, $password)
}
else
{
Write-Error 'Virtual machine was not created successfully.'
}
Related
Running this script in Azure:
Write-Host "Running ps_example.ps1"
$resourceGroupName = 'myGroupName'
$storageName = "psexample"
$storageType = "Standard_LRS"
$location = "centralus"
if (Test-AzureName -Storage $storageName) {
Write-Host "Use existing storage account - $storageName"
} Else {
Write-Host "Make new storage account - $storageName"
New-AzureRmStorageAccount -ResourceGroupName $resourceGroupName -Name $storageName -Type $storageType -Location $location
}
The first run shows:
Running ps_example.ps1
Make new storage account - psexample
The second run shows:
Running ps_example.ps1
Make new storage account - psexample
The storage account named psexample is already taken.
Why? That would seem to indicate that if (Test-AzureName -Storage $storageName) always returns false.
If I tell Azure to use powershell 1, the version is 1.113.5. Requesting version 2.0 results in 2.0.11. The behavior is the same for both.
EDIT:
Running this:
$result = Test-AzureName -Storage $storageName
Write-Host $result
always prints False, whether psexample exists or not.
You are combining RM and SM cmdlets in Azure. Test-AzureName is a Service Management cmdlet, while New-AzureRmStorageAccount is a Resource Manager cmdlet.
You may want to try to use
if ((Get-AzureRmStorageAccountNameAvailability -Name $storageName).NameAvailable) {
Write-Host "Make new storage account - $storageName"
New-AzureRmStorageAccount -ResourceGroupName $resourceGroupName -Name $storageName -Type $storageType -Location $location
} Else {
Write-Host "Use existing storage account - $storageName"
}
to check for the name or you can create your storage account with:
New-AzureStorageAccount
Depending on what you want to use, SM or RM.
I'm tying to change a value on a tag, using an automation script. The users will have a startup script, which will change the shutdown tag key from true to false.
When I set the tags individually using the script below it sets the tag value to false. The current setting is true.
When I use the automation script it wipes all the tags, however If I specify the vm in the script the automaton account works and changes the key value from false to true.
I can't see what I'm missing. This is from a webhook and is running as a powershell script, not a workflow.
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[object]$WebhookData
)
Write-Output "------------------------------------------------"
Write-Output "`nConnecting to Azure Automation"
$Connection = Get-AutomationConnection -Name AzureRunAsConnection
Add-AzureRMAccount -ServicePrincipal -Tenant $Connection.TenantID `
-ApplicationId $Connection.ApplicationID -CertificateThumbprint $Connection.CertificateThumbprint
$RunbookVersion = "0.0.17"
$timeStartUTC = (Get-Date).ToUniversalTime()
Write-Output "Workflow started: Runbook Version is $RunbookVersion"
Write-Output "System time is: $(Get-Date)"
Write-Output "`nGetting tagged resources"
Write-Output "------------------------------------------------"
$ResourceGroupFilter = ""
$SupportedEnvironments = "DEV, Test, PREProd, Prod"
$isWebhookDataNull = $WebhookData -eq $null
Write-Output "Is webhook data null ? : $($isWebhookDataNull)"
# If runbook was called from Webhook, WebhookData will not be null.
If ($WebhookData -ne $null) {
# Collect properties of WebhookData
$WebhookName = $WebhookData.WebhookName
$WebhookHeaders = $WebhookData.RequestHeader
$WebhookBody = $WebhookData.RequestBody
$body = $WebhookBody | ConvertFrom-Json
$UserEmail = $body.user.email
Write-Output "Runbook started from webhook '$WebhookName' by '$($body.user.email)' for environment '$($body.environment)'"
Write-Output "Message body: " $WebhookBody
}
else {
Write-Error "Runbook mean to be started only from webhook."
}
If ($body.environment.ToUpper() -eq 'DEV') {
$ResourceGroupFilter = 'The-DEV-RG'
}
if ($ResourceGroupFilter -eq "") {
Exit 1
}
if($VMRG -eq ''){
Write-Output "No resource groups matched for selected environment. Webhook cant progress further, exiting.."
Write-Error "No resource groups matched for selected environment. Webhook cant progress further, exiting.."
Exit 1
}
$rgs = Get-AzureRmResourceGroup | Where-Object {$_.ResourceGroupName -like "*$rg*"}
foreach ($rg in $rgs)
{
$vms = Get-AzureRmVm -ResourceGroupName $rg.ResourceGroupName
$vms.ForEach({
$tags = $_.Tags
$tags['ShutdownSchedule_AllowStop'] = "$False";
Set-AzureRmResource -ResourceId $_.Id -Tag $tags -Force -Verbose
})
}
ForEach ($vm in $vms) {
Start-AzureRmVM -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -Verbose
}
Thanks in advance :)
The root reason is your local Azure Power Shell is latest version, but in Azure automation account, it is not latest version. I test in my lab, older version does not support this.
You need upgrade Azure Power Shell version. More information about this please see this answer.
I have a Powershell script that enumerates running services and their current state using Get-WmiObject Win32_Service. Initial version based on this one and then modified for Azure. When I run the script in Powershell (without the azure automation parts) on my location machine it works fine and I can connect to all the machines of interest, but when I port it to a runbook i get the following error: "Get-WmiObject : The RPC server is unavailable."
Q: Is the problem with permissions for the automation account? If so, what account should I add to the local machines to resolve the issue?
Q: Is Get-WmiObject not a valid way to initiate the connection? If not, what should I try instead?
The code I'm using is below:
[CmdletBinding(SupportsShouldProcess = $true)]
param(
# Servers to check
[Parameter(Mandatory=$true)][string[]]$ServerList,
# Services to check for
[Parameter(Mandatory=$true)][string[]]$includeService
)
# Following modifies the Write-Verbose behavior to turn the messages on globally for this session
$VerbosePreference = "Continue"
$connectionName = "AzureRunAsConnection"
# retry
$retry = 6
$syncOk = $false
$servicePrincipalConnection = Get-AutomationConnection -Name $connectionName
do
{
try
{
Add-AzureRmAccount -ServicePrincipal -TenantId $servicePrincipalConnection.TenantId -ApplicationId $servicePrincipalConnection.ApplicationId -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
$syncOk = $true
}
catch
{
$ErrorMessage = $_.Exception.Message
$StackTrace = $_.Exception.StackTrace
Write-Warning "Error during sync: $ErrorMessage, stack: $StackTrace. Retry attempts left: $retry"
$retry = $retry - 1
Start-Sleep -s 60
}
} while (-not $syncOk -and $retry -ge 0)
Select-AzureRMSubscription -SubscriptionId $SubscriptionId -TenantId $servicePrincipalConnection.TenantId
$currentSubscription = Get-AzureRMSubscription -SubscriptionId $SubscriptionId -TenantId $servicePrincipalConnection.TenantId
Set-AzureRmContext -SubscriptionId $SubscriptionId;
$props=#()
[System.Collections.ArrayList]$unreachableServers = #()
Foreach($ServerName in ($ServerList))
{
try
{
$service = Get-WmiObject Win32_Service -ComputerName $servername
}
catch
{}
if ($Service -ne $NULL)
{
foreach ($item in $service)
{
#$item.DisplayName
Foreach($include in $includeService)
{
#write-host $include
if(($item.name).Contains($include) -eq $TRUE)
{
$props += [pscustomobject]#{
servername = $ServerName
name = $item.name
Status = $item.Status
startmode = $item.startmode
state = $item.state
serviceaccount=$item.startname
DisplayName =$item.displayname}
}
}
}
}
else
{
Write-host "Failed to contact server: "$ServerName
$unreachableServers.Add($ServerName)
}
}
$props | Format-Table Servername,Name,startmode,state,serviceaccount,displayname -AutoSize
I am assuming that you are using the Azure Automation Hybrid Worker functionality. Be default it runs under the System account. However you can use a different account to run the runbook under. This is documented here: Azure Automation Hybrid Worker; Look under the RunAs account section. Use the same account that works when you try it directly.
have you considered using OMS? this sounds like a better thing to do.
Anyway, to answer your questions, I would probably create a local user and create a PS configuration endpoint for that user to connect to, and connect impersonating that user from the Automation Account, but again, I wouldn't even go this route, I'd rather use OMS.
Recently we did a move and a rename of an AppFabric configuration database.
The rename was from default name "AppFabricConfigurationDatabase" to "AppFabricPreOrdersConfiguration"
DistirbutedCacheService.exe.config was changed with the new database and server name
<clusterConfig provider="System.Data.SqlClient" connectionString="Data Source=NEWSERVER;Initial Catalog=AppFabricPreOrdersConfiguration;Integrated Security=True" />
and the service starts succesfully.
But from this point on the "caching administration powershell" does not start anymore because when use-cachecluster is called it still tries to connect to the old server / database.
Test connection failed for ConnectionString Data Source=OLDSERVER;Initial Catalog
=AppFabricCacheConfigurationDatabase;
Use-CacheCluster : ErrorCode:SubStatus:Invalid provider and c
onnection string read.
Where does powershell read those values from? Apparently not from the config file of the service but where then?
Since I can't stop the cluster I've tried to see if the connection string would be changed without restarting and basically just calling Remove-CacheAdmin and Add-CacheAdmin....it worked!
Of course the script would have to be run on each host so not good for large setups but a restart is not really needed apparently
param ([string] $provider, [string] $newConnectionString)
function Main {
if ( (! $provider) -or (! $newConnectionString))
{
Write-Host "Usage: ChangeConnString.ps1 <provider> <newConnectionString>"
exit(1)
}
Import-Module "DistributedCacheAdministration"
Import-Module "DistributedCacheConfiguration"
[Reflection.Assembly]::LoadWithPartialName('Microsoft.ApplicationServer.Caching.Management') | Out-Null
[Reflection.Assembly]::LoadWithPartialName('System.Management.Automation') | Out-Null
[Reflection.Assembly]::LoadWithPartialName('System.Management.Automation.Runspaces') | Out-Null
Remove-CacheAdmin
Add-CacheAdmin -Provider $provider -ConnectionString $newConnectionString
}
The scripts provided by the other users did not work for me. I encountered exceptions. I was able to work around this issue by editing the registry on each host and restarting the service.
The connection string is stored here: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AppFabric\V1.0\Configuration
The value is named "ConnectionString"
Under the user hive, there's another instance of the connection string. I don't know if you need to change this or not, but I did. HKEY_CURRENT_USER\Software\Microsoft\AppFabric\V1.0\Temp
That worked for me. Don't forget you also need to edit the ClusterConfig ConnectionString in DistributedCacheService.exe.config under C:\Program Files\AppFabric 1.1 for Windows Server
You need to call Remove-CacheAdmin and then Add-CacheAdmin to change the cache admin connection on each admin host
This Microsoft Powershell script - (.EXE download, script reproduced below) - changes the connection string on all hosts in a cluster.
param ([string] $provider, [string] $newConnectionString)
function Main {
if ( (! $provider) -or (! $newConnectionString))
{
Write-Host "Usage: ChangeConnString.ps1 <provider> <newConnectionString>"
exit(1)
}
Import-Module "DistributedCacheAdministration"
Import-Module "DistributedCacheConfiguration"
Use-CacheCluster
Write-Host "Stop the cache cluster if it is running"
$clusterRunnig=$true
&{
Stop-CacheCluster -EA Stop
}
trap [DataCacheException] {
#'Error Category {0}, Error Type {1}, ID: {2}, Message: {3} {4}' -f $_.CategoryInfo.Category, $_.Exception.GetType().FullName, $_.FullyQualifiedErrorID, $_.Exception.Message, $_.Exception.ErrorCode;
#12008: ErrorCode<ERRCAdmin008>:SubStatus<ES0001>:No hosts running in cluster
if ($_.Exception.ErrorCode -eq 12008)
{
write-host "Cluster is not running"
$clusterRunnig=$false
continue
}
}
[Reflection.Assembly]::LoadWithPartialName('Microsoft.ApplicationServer.Caching.Management') | Out-Null
[Reflection.Assembly]::LoadWithPartialName('System.Management.Automation') | Out-Null
[Reflection.Assembly]::LoadWithPartialName('System.Management.Automation.Runspaces') | Out-Null
SetCacheConnectionString $provider $newConnectionString
Write-Host "Connection string is altered on all the cache hosts. Now changing the connection string for cache admin"
Remove-CacheAdmin
Add-CacheAdmin -Provider $provider -ConnectionString $newConnectionString
if ($clusterRunnig -eq $true)
{
Write-Host "Starting the cache cluster..."
Start-CacheCluster
}
}
function SetCacheConnectionString {
param ([string] $provider, [string] $newConnectionString)
Write-Host "Parameters: " $provider " " $newConnectionString
$powerShell = [System.Management.Automation.PowerShell]::Create()
# Import the admin cmdlets module
$powerShell.AddCommand("Import-Module", $true);
$powerShell.AddParameter("Name", "DistributedCacheAdministration")
# Call the Invoke method to run the commands
$powerShell.Invoke();
$powerShell.Commands.AddCommand("Use-CacheCluster")
$powerShell.Commands.AddCommand("Get-CacheHost")
$commandResults = $powerShell.Invoke()
$powerShell.Dispose()
Write-Host "Number of hosts in the cluster " $commandResults.Count
foreach ($cacheHost in $commandResults)
{
Write-Host "Configuring the host " $cacheHost.HostName
Invoke-Command -ComputerName $cacheHost.HostName -ScriptBlock {param ($provider, $newConnectionString) Import-Module DistributedCacheConfiguration;Remove-CacheHost;Add-CacheHost -Provider $provider -ConnectionString $newConnectionString -Account 'NT Authority\NETWORK SERVICE'} -ArgumentList $provider, $newConnectionString
}
}
#
# Entry
#
Main
I am building a power shell script to automate the setup of a website environment in Azure. This web uses an account storage. I want to the script not to create the account storage if exists.
I thought that using Get-AzureStorageAccount this way may work but it does not:
Write-Verbose "[Start] creating $Name storage account $Location location"
$storageAcct = Get-AzureStorageAccount –StorageAccountName $Name
if (!$storageAcct)
{
$storageAcct = New-AzureStorageAccount -StorageAccountName $Name -Location $Location -Verbose
if ($storageAcct)
{
Write-Verbose "[Finish] creating $Name storage account in $Location location"
}
else
{
throw "Failed to create a Windows Azure storage account. Failure in New-AzureStorage.ps1"
}
}
else
{
Write-Verbose "$Name storage account in $Location location already exists, skipping creation"
}
The issue is I don't know how to handle the return of Get-AzureStorageAccount.
Thank you very much in advance!
I would suggest using the Test-AzureName cmdlet to determine if it exists. So, something like this.
if (!(Test-AzureName -Storage $Name))
{
Write-Host "Creating Storage Account $Name"
New-AzureStorageAccount -StorageAccountName $Name -Location $Location
}
You can use Test-AzureName for other services too, such as Cloud Services, WebSites, and ServiceBus. It returns True if it exists, False otherwise.
Get-AzureRmStorageAccountNameAvailability -Name "accountname"
Try this:
$Name = "myStorageAccount"
$Location = "myLocation"
Write-Host "[Start] creating $Name storage account $Location location"
try{
Get-AzureStorageAccount –StorageAccountName $Name -ErrorAction Stop | Out-Null
Write-Host "$Name storage account in $Location location already exists, skipping creation"
}
catch{
Write-Host "[Finish] creating $Name storage account in $Location location"
New-AzureStorageAccount -StorageAccountName $Name -Location $Location -Verbose
}
Test-AzureName didn't work with our build agents and we already had a try/catch in code so a second one would require building it out as a function. I opted for that standard get and check if null, use -ErrorAction Ignore to stop it throwing an exception
# Check for storage account and create if not found
$StorageAccount = Get-AzureRmStorageAccount -Name $StorageAccountName -ResourceGroupName $StorageAccountRG -ErrorAction Ignore
if ($StorageAccount -eq $null)
{
New-AzureRmStorageAccount -Location "West Europe" -Name $StorageAccountName -ResourceGroupName $StorageAccountRG -SkuName Standard_LRS -Kind Storage
$StorageAccount = Get-AzureRmStorageAccount -Name $StorageAccountName -ResourceGroupName $StorageAccountRG
}
#Rick Rainey's solution works if you're logged in using Add-AzureAccount. However, Azure and powershell have a conflicting and confusing suite of login accounts (Windows Live versus AD) and login mechanisms (Classic: Add-AzureAccount; Resource manager: Login-AzureRmAccount). Some Azure powershell cmdlets require a specific login; further, some require a specific account type!
To clear through this thicket of complicated, undocumented, and confusing permission issues, we always use an AD account, logging in via Login-AzureRmAccount. We also use Azure resource manager (ARM) resources and cmdlets, following Microsoft's movement to ARM as its recommended and strategic approach. However, #RIck's solution is one which the ARM login doesn't work with. :-( So you need another approach, which is #Darren's (for storage). However, for a generic replacement for Test-AzureName I'd suggest Find-AzureRmResource. In the case of storage
$StorageObject = Find-AzureRmResource -ResourceType "Microsoft.Storage/storageAccounts" | Where-Object {$_.Name -eq $storageName}
if ( !$StorageObject ) {
$storageLocation = (Get-AzureRmResourceGroup -ResourceGroupName $resourceGroup).Location
$storageType = "Standard_LRS"
New-AzureRmStorageAccount -ResourceGroupName $resourceGroup -Name $storageName -Location $storageLocation -Type $storageType
}
You should use the latest Powershell module Az.
if ($(Get-AzStorageAccount -ResourceGroupName $resourceGroupName -Name $storageAccountName) -eq $null)
{
# does not exist
}
With the current Az module for PowerShell Version 7, the Get-AzStorageAccountNameAvailability cmdlet might offer a more efficient solution as it was designed specifically for this task. Here is an example:
# ... declare variables and specify values ...
$checkNameAvail = (Get-AzStorageAccountNameAvailability -Name $storageAccountName) | `
Select-Object NameAvailable
if ($checkNameAvail.NameAvailable)
{
Write-Host 'Account name available! Please wait while your resource is being created'
# Create account. Variables used in this example would have been declared earlier in the script.
$storageAccount = (New-AzStorageAccount -ResourceGroupName $resourceGroupName `
-AccountName $storageAccountName `
-Location $location `
-SkuName $skuType `
-AllowBlobPublicAccess $false -EnableHttpsTrafficOnly $true)
# ...
}
else
{
# This section of the script executes if the name is not available
Write-Host "The name <$storageAccountName> is not available. Suggest a new globally unique name!"
}
The condition above will return False, and execute the else statement because the boolean value returned by the cmdlet is in [0] as shown in the PowerShell command-line test below. The availability information (boolean) can thus be stripped from the object returned by the cmdlet and (as in this example) used as a condition in the rest of the script.
PS C:\> Get-AzStorageAccountNameAvailability -Name testaccount1
NameAvailable Reason Message
------------- ------ -------
False AlreadyExists The storage account named testaccount1 is already taken.
Use the error variable
Get-AzStorageAccount -ResourceGroupName 'RG-QA-TEST' -Name 'staccountfor12334ff' -ErrorVariable ev1 -ErrorAction SilentlyContinue
if ($ev1) {
Write-Host "-------------------------- Creating OEM Storage"
//create storage account
}
I had this challenge when setting up Azure storage accounts for Static website hosting using Powershell in Octopus Deploy.
Here's how I fixed it:
Using the Az module for Azure Powershell I did the following:
# Define Variables
$RESOURCE_GROUP_NAME = my-resource-group
$LOCATION = northeurope
$STORAGE_ACCOUNT_NAME = myapplication
$SKU_NAME = Standard_GRS
$STORAGE_KIND = StorageV2
# Check Storage Account and Create if not Found
$STORAGE_ACCOUNT = Get-AzStorageAccount -ResourceGroupName $RESOURCE_GROUP_NAME -Name $STORAGE_ACCOUNT_NAME -ErrorAction Ignore
if ($STORAGE_ACCOUNT -eq $null) {
Write-Host 'Creating storage account'
New-AzStorageAccount -ResourceGroupName $RESOURCE_GROUP_NAME -AccountName $STORAGE_ACCOUNT_NAME -Location $LOCATION -SkuName $SKU_NAME -Kind $STORAGE_KIND
Write-Host "$STORAGE_ACCOUNT_NAME storage account successfully created"
}
else {
Write-Host "$STORAGE_ACCOUNT_NAME storage account already exists"
}
Note:
-ErrorAction Ignore - This ignores the exception that would arise if the storage account does not exist
Write-Host " " - Double quotes were used to allow for string interpolation since we are connecting strings and variables.
That's all.
I hope this helps