How to import automatically Az Powershell modules in azure automation account? - azure

I am trying to install az powershell modules from my automation runbook with powershell script. But i am failing to do that. I have tried the solution in a similar topic which is here but it didn't work for me. The code is below:
$AAccName = "my-aa"
$RGName = "my-rg"
$deps1 = #("Az.Accounts","Az.Profile")
foreach($dep in $deps1){
$module = Find-Module -Name $dep
$link = $module.RepositorySourceLocation + "/package/" + $module.Name + "/" + $module.Version
New-AzAutomationModule -AutomationAccountName $AAccName -Name $module.Name -ContentLinkUri $link -ResourceGroupName $RGName
}
The error , i get:
Exception calling "ShouldContinue" with "2" argument(s): "A command
that prompts the user failed because the host program or the command
type does not support user interaction. The host was attempting to
request confirmation with the following message: PowerShellGet
requires NuGet provider version '2.8.5.201' or newer to interact with
NuGet-based repositories. The NuGet provider must be available in
'C:\Program Files\PackageManagement\ProviderAssemblies' or
'C:\Users\Client\AppData\Roaming\PackageManagement\ProviderAssemblies'.
You can also install the NuGet provider by running
'Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201
-Force'. Do you want PowerShellGet to install and import the NuGet provider now?" At C:\Program
Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.psm1:7455
char:8 + if($Force -or
$psCmdlet.ShouldContinue($shouldContinueQueryMessag ... +
I have executed the command given in this message but i get this error:
Install-PackageProvider : No match was found for the specified search
criteria for the provider 'NuGet'. The package provider requires
'PackageManagement' and 'Provider' tags.
Do you have any idea how to add module in Azure Automation Account with script?
Update:
When i use Import-Module -Name Az.Profile -Force command, I get this error:
Import-Module : The specified module 'Az.Profile' was not loaded
because no valid module file was found in any module directory.
This should be because that the module is not installed on the module directory. When i manually add the module from the module gallery, it works.

Check this MS recommended PS script to update modules in Automation account. It's reference is given here: Use the update runbook to update a specific module version.
Script:
<#
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
#>
<#
.SYNOPSIS
Update Azure PowerShell modules in an Azure Automation account.
.DESCRIPTION
This Azure Automation runbook updates Azure PowerShell modules imported into an
Azure Automation account with the module versions published to the PowerShell Gallery.
Prerequisite: an Azure Automation account with an Azure Run As account credential.
.PARAMETER ResourceGroupName
The Azure resource group name.
.PARAMETER AutomationAccountName
The Azure Automation account name.
.PARAMETER SimultaneousModuleImportJobCount
(Optional) The maximum number of module import jobs allowed to run concurrently.
.PARAMETER AzureModuleClass
(Optional) The class of module that will be updated (AzureRM or Az)
If set to Az, this script will rely on only Az modules to update other modules.
Set this to Az if your runbooks use only Az modules to avoid conflicts.
.PARAMETER AzureEnvironment
(Optional) Azure environment name.
.PARAMETER Login
(Optional) If $false, do not login to Azure.
.PARAMETER ModuleVersionOverrides
(Optional) Module versions to use instead of the latest on the PowerShell Gallery.
If $null, the currently published latest versions will be used.
If not $null, must contain a JSON-serialized dictionary, for example:
'{ "AzureRM.Compute": "5.8.0", "AzureRM.Network": "6.10.0" }'
or
#{ 'AzureRM.Compute'='5.8.0'; 'AzureRM.Network'='6.10.0' } | ConvertTo-Json
.PARAMETER PsGalleryApiUrl
(Optional) PowerShell Gallery API URL.
.LINK
https://learn.microsoft.com/en-us/azure/automation/automation-update-azure-modules
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "")]
param(
[Parameter(Mandatory = $true)]
[string] $ResourceGroupName,
[Parameter(Mandatory = $true)]
[string] $AutomationAccountName,
[int] $SimultaneousModuleImportJobCount = 10,
[string] $AzureModuleClass = 'AzureRM',
[string] $AzureEnvironment = 'AzureCloud',
[bool] $Login = $true,
[string] $ModuleVersionOverrides = $null,
[string] $PsGalleryApiUrl = 'https://www.powershellgallery.com/api/v2'
)
$ErrorActionPreference = "Continue"
#region Constants
$script:AzureRMProfileModuleName = "AzureRM.Profile"
$script:AzureRMAutomationModuleName = "AzureRM.Automation"
$script:GetAzureRmAutomationModule = "Get-AzureRmAutomationModule"
$script:NewAzureRmAutomationModule = "New-AzureRmAutomationModule"
$script:AzAccountsModuleName = "Az.Accounts"
$script:AzAutomationModuleName = "Az.Automation"
$script:GetAzAutomationModule = "Get-AzAutomationModule"
$script:NewAzAutomationModule = "New-AzAutomationModule"
$script:AzureSdkOwnerName = "azure-sdk"
#endregion
#region Functions
function ConvertJsonDictTo-HashTable($JsonString) {
try{
$JsonObj = ConvertFrom-Json $JsonString -ErrorAction Stop
} catch [System.ArgumentException] {
throw "Unable to deserialize the JSON string for parameter ModuleVersionOverrides: ", $_
}
$Result = #{}
foreach ($Property in $JsonObj.PSObject.Properties) {
$Result[$Property.Name] = $Property.Value
}
$Result
}
# Use the Run As connection to login to Azure
function Login-AzureAutomation([bool] $AzModuleOnly) {
try {
$RunAsConnection = Get-AutomationConnection -Name "AzureRunAsConnection"
Write-Output "Logging in to Azure ($AzureEnvironment)..."
if (!$RunAsConnection.ApplicationId) {
$ErrorMessage = "Connection 'AzureRunAsConnection' is incompatible type."
throw $ErrorMessage
}
if ($AzModuleOnly) {
Connect-AzAccount `
-ServicePrincipal `
-TenantId $RunAsConnection.TenantId `
-ApplicationId $RunAsConnection.ApplicationId `
-CertificateThumbprint $RunAsConnection.CertificateThumbprint `
-Environment $AzureEnvironment
Select-AzSubscription -SubscriptionId $RunAsConnection.SubscriptionID | Write-Verbose
} else {
Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $RunAsConnection.TenantId `
-ApplicationId $RunAsConnection.ApplicationId `
-CertificateThumbprint $RunAsConnection.CertificateThumbprint `
-Environment $AzureEnvironment
Select-AzureRmSubscription -SubscriptionId $RunAsConnection.SubscriptionID | Write-Verbose
}
} catch {
if (!$RunAsConnection) {
$RunAsConnection | fl | Write-Output
Write-Output $_.Exception
$ErrorMessage = "Connection 'AzureRunAsConnection' not found."
throw $ErrorMessage
}
throw $_.Exception
}
}
# Checks the PowerShell Gallery for the latest available version for the module
function Get-ModuleDependencyAndLatestVersion([string] $ModuleName) {
$ModuleUrlFormat = "$PsGalleryApiUrl/Search()?`$filter={1}&searchTerm=%27{0}%27&targetFramework=%27%27&includePrerelease=false&`$skip=0&`$top=40"
$ForcedModuleVersion = $ModuleVersionOverridesHashTable[$ModuleName]
$CurrentModuleUrl =
if ($ForcedModuleVersion) {
$ModuleUrlFormat -f $ModuleName, "Version%20eq%20'$ForcedModuleVersion'"
} else {
$ModuleUrlFormat -f $ModuleName, 'IsLatestVersion'
}
$SearchResult = Invoke-RestMethod -Method Get -Uri $CurrentModuleUrl -UseBasicParsing
if (!$SearchResult) {
Write-Verbose "Could not find module $ModuleName on PowerShell Gallery. This may be a module you imported from a different location. Ignoring this module"
} else {
if ($SearchResult.Length -and $SearchResult.Length -gt 1) {
$SearchResult = $SearchResult | Where-Object { $_.title.InnerText -eq $ModuleName }
}
if (!$SearchResult) {
Write-Verbose "Could not find module $ModuleName on PowerShell Gallery. This may be a module you imported from a different location. Ignoring this module"
} else {
$PackageDetails = Invoke-RestMethod -Method Get -UseBasicParsing -Uri $SearchResult.id
# Ignore the modules that are not published as part of the Azure SDK
if ($PackageDetails.entry.properties.Owners -ne $script:AzureSdkOwnerName) {
Write-Warning "Module : $ModuleName is not part of azure sdk. Ignoring this."
} else {
$ModuleVersion = $PackageDetails.entry.properties.version
$Dependencies = $PackageDetails.entry.properties.dependencies
#($ModuleVersion, $Dependencies)
}
}
}
}
function Get-ModuleContentUrl($ModuleName) {
$ModuleContentUrlFormat = "$PsGalleryApiUrl/package/{0}"
$VersionedModuleContentUrlFormat = "$ModuleContentUrlFormat/{1}"
$ForcedModuleVersion = $ModuleVersionOverridesHashTable[$ModuleName]
if ($ForcedModuleVersion) {
$VersionedModuleContentUrlFormat -f $ModuleName, $ForcedModuleVersion
} else {
$ModuleContentUrlFormat -f $ModuleName
}
}
# Imports the module with given version into Azure Automation
function Import-AutomationModule([string] $ModuleName, [bool] $UseAzModule = $false) {
$NewAutomationModule = $null
$GetAutomationModule = $null
if ($UseAzModule) {
$GetAutomationModule = $script:GetAzAutomationModule
$NewAutomationModule = $script:NewAzAutomationModule
} else {
$GetAutomationModule = $script:GetAzureRmAutomationModule
$NewAutomationModule = $script:NewAzureRmAutomationModule
}
$LatestModuleVersionOnGallery = (Get-ModuleDependencyAndLatestVersion $ModuleName)[0]
$ModuleContentUrl = Get-ModuleContentUrl $ModuleName
# Find the actual blob storage location of the module
do {
$ModuleContentUrl = (Invoke-WebRequest -Uri $ModuleContentUrl -MaximumRedirection 0 -UseBasicParsing -ErrorAction Ignore).Headers.Location
} while (!$ModuleContentUrl.Contains(".nupkg"))
$CurrentModule = & $GetAutomationModule `
-Name $ModuleName `
-ResourceGroupName $ResourceGroupName `
-AutomationAccountName $AutomationAccountName
if ($CurrentModule.Version -eq $LatestModuleVersionOnGallery) {
Write-Output "Module : $ModuleName is already present with version $LatestModuleVersionOnGallery. Skipping Import"
} else {
Write-Output "Importing $ModuleName module of version $LatestModuleVersionOnGallery to Automation"
& $NewAutomationModule `
-ResourceGroupName $ResourceGroupName `
-AutomationAccountName $AutomationAccountName `
-Name $ModuleName `
-ContentLink $ModuleContentUrl > $null
}
}
# Parses the dependency got from PowerShell Gallery and returns name and version
function GetModuleNameAndVersionFromPowershellGalleryDependencyFormat([string] $Dependency) {
if ($null -eq $Dependency) {
throw "Improper dependency format"
}
$Tokens = $Dependency -split":"
if ($Tokens.Count -ne 3) {
throw "Improper dependency format"
}
$ModuleName = $Tokens[0]
$ModuleVersion = $Tokens[1].Trim("[","]")
#($ModuleName, $ModuleVersion)
}
# Validates if the given list of modules has already been added to the module import map
function AreAllModulesAdded([string[]] $ModuleListToAdd) {
$Result = $true
foreach ($ModuleToAdd in $ModuleListToAdd) {
$ModuleAccounted = $false
# $ModuleToAdd is specified in the following format:
# ModuleName:ModuleVersionSpecification:
# where ModuleVersionSpecification follows the specifiation
# at https://learn.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges-and-wildcards
# For example:
# AzureRm.profile:[4.0.0]:
# or
# AzureRm.profile:3.0.0:
# In any case, the dependency version specification is always separated from the module name with
# the ':' character. The explicit intent of this runbook is to always install the latest module versions,
# so we want to completely ignore version specifications here.
$ModuleNameToAdd = $ModuleToAdd -replace '\:.*', ''
foreach($AlreadyIncludedModules in $ModuleImportMapOrder) {
if ($AlreadyIncludedModules -contains $ModuleNameToAdd) {
$ModuleAccounted = $true
break
}
}
if (!$ModuleAccounted) {
$Result = $false
break
}
}
$Result
}
# Creates a module import map. This is a 2D array of strings so that the first
# element in the array consist of modules with no dependencies.
# The second element only depends on the modules in the first element, the
# third element only dependes on modules in the first and second and so on.
function Create-ModuleImportMapOrder([bool] $AzModuleOnly) {
$ModuleImportMapOrder = $null
$ProfileOrAccountsModuleName = $null
$GetAutomationModule = $null
# Use the relevant module class to avoid conflicts
if ($AzModuleOnly) {
$ProfileOrAccountsModuleName = $script:AzAccountsModuleName
$GetAutomationModule = $script:GetAzAutomationModule
} else {
$ProfileOrAccountsModuleName = $script:AzureRmProfileModuleName
$GetAutomationModule = $script:GetAzureRmAutomationModule
}
# Get all the non-conflicting modules in the current automation account
$CurrentAutomationModuleList = & $GetAutomationModule `
-ResourceGroupName $ResourceGroupName `
-AutomationAccountName $AutomationAccountName |
?{
($AzModuleOnly -and ($_.Name -eq 'Az' -or $_.Name -like 'Az.*')) -or
(!$AzModuleOnly -and ($_.Name -eq 'AzureRM' -or $_.Name -like 'AzureRM.*' -or
$_.Name -eq 'Azure' -or $_.Name -like 'Azure.*'))
}
# Get the latest version of the AzureRM.Profile OR Az.Accounts module
$VersionAndDependencies = Get-ModuleDependencyAndLatestVersion $ProfileOrAccountsModuleName
$ModuleEntry = $ProfileOrAccountsModuleName
$ModuleEntryArray = ,$ModuleEntry
$ModuleImportMapOrder += ,$ModuleEntryArray
do {
$NextAutomationModuleList = $null
$CurrentChainVersion = $null
# Add it to the list if the modules are not available in the same list
foreach ($Module in $CurrentAutomationModuleList) {
$Name = $Module.Name
Write-Verbose "Checking dependencies for $Name"
$VersionAndDependencies = Get-ModuleDependencyAndLatestVersion $Module.Name
if ($null -eq $VersionAndDependencies) {
continue
}
$Dependencies = $VersionAndDependencies[1].Split("|")
$AzureModuleEntry = $Module.Name
# If the previous list contains all the dependencies then add it to current list
if ((-not $Dependencies) -or (AreAllModulesAdded $Dependencies)) {
Write-Verbose "Adding module $Name to dependency chain"
$CurrentChainVersion += ,$AzureModuleEntry
} else {
# else add it back to the main loop variable list if not already added
if (!(AreAllModulesAdded $AzureModuleEntry)) {
Write-Verbose "Module $Name does not have all dependencies added as yet. Moving module for later import"
$NextAutomationModuleList += ,$Module
}
}
}
$ModuleImportMapOrder += ,$CurrentChainVersion
$CurrentAutomationModuleList = $NextAutomationModuleList
} while ($null -ne $CurrentAutomationModuleList)
$ModuleImportMapOrder
}
# Wait and confirm that all the modules in the list have been imported successfully in Azure Automation
function Wait-AllModulesImported(
[Collections.Generic.List[string]] $ModuleList,
[int] $Count,
[bool] $UseAzModule = $false) {
$GetAutomationModule = if ($UseAzModule) {
$script:GetAzAutomationModule
} else {
$script:GetAzureRmAutomationModule
}
$i = $Count - $SimultaneousModuleImportJobCount
if ($i -lt 0) { $i = 0 }
for ( ; $i -lt $Count; $i++) {
$Module = $ModuleList[$i]
Write-Output ("Checking import Status for module : {0}" -f $Module)
while ($true) {
$AutomationModule = & $GetAutomationModule `
-Name $Module `
-ResourceGroupName $ResourceGroupName `
-AutomationAccountName $AutomationAccountName
$IsTerminalProvisioningState = ($AutomationModule.ProvisioningState -eq "Succeeded") -or
($AutomationModule.ProvisioningState -eq "Failed")
if ($IsTerminalProvisioningState) {
break
}
Write-Verbose ("Module {0} is getting imported" -f $Module)
Start-Sleep -Seconds 30
}
if ($AutomationModule.ProvisioningState -ne "Succeeded") {
Write-Error ("Failed to import module : {0}. Status : {1}" -f $Module, $AutomationModule.ProvisioningState)
} else {
Write-Output ("Successfully imported module : {0}" -f $Module)
}
}
}
# Uses the module import map created to import modules.
# It will only import modules from an element in the array if all the modules
# from the previous element have been added.
function Import-ModulesInAutomationAccordingToDependency([string[][]] $ModuleImportMapOrder, [bool] $UseAzModule) {
foreach($ModuleList in $ModuleImportMapOrder) {
$i = 0
Write-Output "Importing Array of modules : $ModuleList"
foreach ($Module in $ModuleList) {
Write-Verbose ("Importing module : {0}" -f $Module)
Import-AutomationModule -ModuleName $Module -UseAzModule $UseAzModule
$i++
if ($i % $SimultaneousModuleImportJobCount -eq 0) {
# It takes some time for the modules to start getting imported.
# Sleep for sometime before making a query to see the status
Start-Sleep -Seconds 20
Wait-AllModulesImported -ModuleList $ModuleList -Count $i -UseAzModule $UseAzModule
}
}
if ($i -lt $SimultaneousModuleImportJobCount) {
Start-Sleep -Seconds 20
Wait-AllModulesImported -ModuleList $ModuleList -Count $i -UseAzModule $UseAzModule
}
}
}
function Update-ProfileAndAutomationVersionToLatest([string] $AutomationModuleName) {
# Get the latest azure automation module version
$VersionAndDependencies = Get-ModuleDependencyAndLatestVersion $AutomationModuleName
# Automation only has dependency on profile
$ModuleDependencies = GetModuleNameAndVersionFromPowershellGalleryDependencyFormat $VersionAndDependencies[1]
$ProfileModuleName = $ModuleDependencies[0]
# Create web client object for downloading data
$WebClient = New-Object System.Net.WebClient
# Download AzureRM.Profile to temp location
$ModuleContentUrl = Get-ModuleContentUrl $ProfileModuleName
$ProfileURL = (Invoke-WebRequest -Uri $ModuleContentUrl -MaximumRedirection 0 -UseBasicParsing -ErrorAction Ignore).Headers.Location
$ProfilePath = Join-Path $env:TEMP ($ProfileModuleName + ".zip")
$WebClient.DownloadFile($ProfileURL, $ProfilePath)
# Download AzureRM.Automation to temp location
$ModuleContentUrl = Get-ModuleContentUrl $AutomationModuleName
$AutomationURL = (Invoke-WebRequest -Uri $ModuleContentUrl -MaximumRedirection 0 -UseBasicParsing -ErrorAction Ignore).Headers.Location
$AutomationPath = Join-Path $env:TEMP ($AutomationModuleName + ".zip")
$WebClient.DownloadFile($AutomationURL, $AutomationPath)
# Create folder for unzipping the Module files
$PathFolderName = New-Guid
$PathFolder = Join-Path $env:TEMP $PathFolderName
# Unzip files
$ProfileUnzipPath = Join-Path $PathFolder $ProfileModuleName
Expand-Archive -Path $ProfilePath -DestinationPath $ProfileUnzipPath -Force
$AutomationUnzipPath = Join-Path $PathFolder $AutomationModuleName
Expand-Archive -Path $AutomationPath -DestinationPath $AutomationUnzipPath -Force
# Import modules
Import-Module (Join-Path $ProfileUnzipPath ($ProfileModuleName + ".psd1")) -Force -Verbose
Import-Module (Join-Path $AutomationUnzipPath ($AutomationModuleName + ".psd1")) -Force -Verbose
}
#endregion
#region Main body
if ($ModuleVersionOverrides) {
$ModuleVersionOverridesHashTable = ConvertJsonDictTo-HashTable $ModuleVersionOverrides
} else {
$ModuleVersionOverridesHashTable = #{}
}
$UseAzModule = $null
$AutomationModuleName = $null
# We want to support updating Az modules. This means this runbook should support upgrading using only Az modules
if ($AzureModuleClass -eq "Az") {
$UseAzModule = $true
$AutomationModuleName = $script:AzAutomationModuleName
} elseif ( $AzureModuleClass -eq "AzureRM") {
$UseAzModule = $false
$AutomationModuleName = $script:AzureRMAutomationModuleName
} else {
Write-Error "Invalid AzureModuleClass: '$AzureModuleClass'. Must be either Az or AzureRM" -ErrorAction Stop
}
# Import the latest version of the Az automation and accounts version to the local sandbox
Update-ProfileAndAutomationVersionToLatest $AutomationModuleName
if ($Login) {
Login-AzureAutomation $UseAzModule
}
$ModuleImportMapOrder = Create-ModuleImportMapOrder $UseAzModule
Import-ModulesInAutomationAccordingToDependency $ModuleImportMapOrder $UseAzModule
#endregion
Further, if you want to refer to another approach, you can check this script.
Here is Reddit question regarding the same.

I have used azure devops to install the modules with powershell. I was trying to install it within the runbook but that didn't work so i kind of changed the method.
Here is the code:
#necessary modules list
$deps1 = #("Az.Accounts","Az.Storage","Az.Compute")
foreach($dep in $deps1){
$module = Find-Module -Name $dep
$link = $module.RepositorySourceLocation + "/package/" + $module.Name + "/" + $module.Version
New-AzAutomationModule -AutomationAccountName $AutomationAccountName -Name $module.Name -ContentLinkUri $link -ResourceGroupName $ResourceGroupName
if ($dep -eq "Az.Accounts") {
#Az.Accounts is a dependency for Az.Storage and Az.Compute modules
Write-Host "Sleeping for 180 sec in order to wait the installation of the Az.Accounts module"
Start-Sleep 180
}
}
This answer in this post helped me for the code.

Related

Do we need to dispose azure powershell commands?

We have over 400 app services and we are creating a deployment tool to deploy to all of these instances as soon as possible.
We have 4 projects running in separate app service and each customer need to have all 4 projects up and running so we have distributed the instances by customer name and each instance deploy to 4 app services internally (See the code snippet below)
We've created a powershell function app with queue trigger that is calling azure powershell commands to do the deployment. The deployment steps are:
Create deployment slot
Publish to the deployment slot
Version check (Have an endpoint in all projects which returns assembly version)
Swap slot
Version check again
Delete slot
The steps are straight forward but when I deploy to single instance it runs pretty quickly but when I am trying to deploy to multiple instances (5+) then it starts becoming really slow and eventually stops execution. My assumption is that there is a memory issue. I am using premium (EP1) plan for my function so it shouldn't be that slow.
I am totally new to powershell so the question is do I need to dispose the azure powershell stuff? Or anything else to freed the memory. If yes, how can I call dispose to azure powershell commands?
Update
Adding the code snippets.
Some more context:
So the run.ps1 file has the code that downloads the ps1 script file from blob storage and dynamically executes it:
run.ps1
# Input bindings are passed in via param block.
param($QueueItem, $TriggerMetadata)
# Write out the queue message and insertion time to the information log.
Write-Host "PowerShell queue trigger function processed work item: $QueueItem"
Write-Host "Queue item insertion time: $($TriggerMetadata.InsertionTime)"
$currentContext = Get-AzContext -ListAvailable | Where-Object {$_.Name -contains 'Subscriptionname'}
$storageAccount = Get-AzStorageAccount -ResourceGroupName "<ResourceGroup>" -Name "<ResourceName>" -DefaultProfile $currentContext
$Context = $storageAccount.Context
$queue = Get-AzStorageQueue -Name logs -Context $Context
function Log {
param (
[string]$message, [string]$isDetailed, [string]$hasError = "false"
)
$jsonMessge = #"
{
"InstanceId": "$instanceId",
"TaskId": "$taskId",
"Note": "$message",
"IsDetailed": $isDetailed,
"HasError": $hasError
}
"#
# Create a new message using a constructor of the CloudQueueMessage class
$queueMessage = [Microsoft.Azure.Storage.Queue.CloudQueueMessage]::new($jsonMessge)
# # Add a new message to the queue
$queue.CloudQueue.AddMessageAsync($queueMessage)
}
try {
#Extracting data from Queue message
$queueMessage = $QueueItem
$instanceId = $queueMessage.instanceId
$taskId = $queueMessage.taskId
$siteName = $queueMessage.siteName
$buildNo = $queueMessage.buildNo
$pass = $queueMessage.password
$dbPassword = convertto-securestring "$pass" -asplaintext -force
$servicePlanName = $queueMessage.servicePlanName
$subscription = $queueMessage.subscription
$key = $queueMessage.key
$rg = $queueMessage.resourceGroup
$sqlServerName = $queueMessage.sqlServerName
Log -message "Deployment started for $($siteName)" -isDetailed "false"
$tempFolder = $env:temp
# $artifactFolderPath = "$($tempFolder)\$($siteName)\$($buildNo)"
#$artifactFilePath = "$($tempFolder)\$($siteName)\$($buildNo).zip"
$tempDownloadPath = "$($tempFolder)\$($siteName)"
$scriptFileName = "DeployScripts.ps1"
if (Test-Path $tempDownloadPath) {
Remove-Item $tempDownloadPath -Force -Recurse
}
$storageAccount = Get-AzStorageAccount -ResourceGroupName "KineticNorthEurope" -Name "KineticDeployment" -DefaultProfile $currentContext
New-Item -Path $tempFolder -Name $siteName -ItemType "directory"
$Context = $storageAccount.Context
$blobContent = #{
Blob = "DeployScripts.ps1"
Container = 'builds'
Destination = "$($tempDownloadPath)\$($scriptFileName)"
Context = $Context
}
Get-AzStorageBlobContent #blobContent -DefaultProfile $currentContext
#[System.IO.Compression.ZipFile]::ExtractToDirectory($artifactFilePath, $artifactFolderPath)
$arguments = "-rg $rg -site $siteName -buildNo $buildNo -dbPassword `$dbPassword -instanceId $instanceId -taskId $taskId -sqlServerName $sqlServerName -subscription $subscription"
$path = "$($tempDownloadPath)\$($scriptFileName)"
Unblock-File -Path $path
"$path $ScriptFilePath $arguments" | Invoke-Expression
if (Test-Path $tempDownloadPath) {
Remove-Item $tempDownloadPath -Force -Recurse
}
Log -message "Resources cleaned up" -isDetailed "false"
}
catch {
Log -message $_.Exception.message -isDetailed "false" -hasError "true"
if (Test-Path $tempDownloadPath) {
Remove-Item $tempDownloadPath -Force -Recurse
}
}
And here is the actual DeployScripts.ps1 file:
param($rg, $site, $buildNo, [SecureString] $dbPassword, $instanceId, $taskId, $sqlServerName, $subscription)
$siteNameWeb = "${site}"
$siteNamePortal = "${site}Portal"
$siteNamePortalService = "${site}PortalService"
$siteNameSyncService = "${site}SyncService"
$kineticNorthContext = Get-AzContext -ListAvailable | Where-Object {$_.Name -contains 'SubscriptionName'}
$storageAccount = Get-AzStorageAccount -ResourceGroupName "<ResourceGroup>" -Name "<ResourceName>" -DefaultProfile $kineticNorthContext
$Context = $storageAccount.Context
$queue = Get-AzStorageQueue -Name logs -Context $Context
Set-AzContext -SubscriptionName $subscription
Function CreateDeploymentSlots() {
Log -message "Creating deployment slots" -isDetailed "false"
Log -message "Creating deployment slot for web" -isDetailed "true"
New-AzWebAppSlot -ResourceGroupName $rg -name $siteNameWeb -slot develop
Log -message "Creating deployment slot for portal" -isDetailed "true"
New-AzWebAppSlot -ResourceGroupName $rg -name $siteNamePortal -slot develop
Log -message "Creating deployment slot for portal service" -isDetailed "true"
New-AzWebAppSlot -ResourceGroupName $rg -name $siteNamePortalService -slot develop
Log -message "Creating deployment slot for sync service" -isDetailed "true"
New-AzWebAppSlot -ResourceGroupName $rg -name $siteNameSyncService -slot develop
Log -message "Deployment slots created" -isDetailed "false"
}
Function DeleteDeploymentSlots() {
Log -message "Deleting deployment slots" -isDetailed "false"
Log -message "Deleting slot web" -isDetailed "true"
Remove-AzWebAppSlot -ResourceGroupName $rg -Name $siteNameWeb -Slot "develop"
Log -message "Deleting slot portal" -isDetailed "true"
Remove-AzWebAppSlot -ResourceGroupName $rg -Name $siteNamePortal -Slot "develop"
Log -message "Deleting slot portal service" -isDetailed "true"
Remove-AzWebAppSlot -ResourceGroupName $rg -Name $siteNamePortalService -Slot "develop"
Log -message "Deleting slot sync service" -isDetailed "true"
Remove-AzWebAppSlot -ResourceGroupName $rg -Name $siteNameSyncService -Slot "develop"
Log -message "Slots deployment deleted" -isDetailed "false"
}
Function SwapDeploymentSlots {
Log -message "Switching deployment slots" -isDetailed "false"
Log -message "Switch slot web" -isDetailed "true"
Switch-AzWebAppSlot -SourceSlotName "develop" -DestinationSlotName "production" -ResourceGroupName $rg -Name $siteNameWeb
Log -message "Switch slot portal" -isDetailed "true"
Switch-AzWebAppSlot -SourceSlotName "develop" -DestinationSlotName "production" -ResourceGroupName $rg -Name $siteNamePortal
Log -message "Switch slot portal service" -isDetailed "true"
Switch-AzWebAppSlot -SourceSlotName "develop" -DestinationSlotName "production" -ResourceGroupName $rg -Name $siteNamePortalService
Log -message "Switch slot sync service" -isDetailed "true"
Switch-AzWebAppSlot -SourceSlotName "develop" -DestinationSlotName "production" -ResourceGroupName $rg -Name $siteNameSyncService
Log -message "Deployment slots switched" -isDetailed "false"
}
function Log {
param (
[string]$message, [string]$isDetailed, [string]$isDeployed = "false", [string]$hasError = "false"
)
$jsonMessge = #"
{
"InstanceId": "$instanceId",
"TaskId": "$taskId",
"Note": "$message",
"IsDetailed": $isDetailed,
"IsDeployed": $isDeployed,
"HasError": $hasError
}
"#
# Create a new message using a constructor of the CloudQueueMessage class
$queueMessage = [Microsoft.Azure.Storage.Queue.CloudQueueMessage]::new($jsonMessge)
# # Add a new message to the queue
$queue.CloudQueue.AddMessageAsync($queueMessage)
}
function VersionCheckWeb() {
$tls = Invoke-WebRequest -URI "https://${site}.net/Home/Info" -UseBasicParsing
$content = $tls.Content | ConvertFrom-Json
$versionCheck = $content.VersionNo -eq $buildNo
return $versionCheck
}
function VersionCheckPortal() {
$tls = Invoke-WebRequest -URI "https://${site}portal.net/Home/Info" -UseBasicParsing
$content = $tls.Content | ConvertFrom-Json
$versionCheck = $content.VersionNo -eq $buildNo
return $versionCheck
}
function VersionCheckPortalService() {
$tls = Invoke-WebRequest -URI "https://${site}portalservice.net/Home/Info" -UseBasicParsing
$content = $tls.Content | ConvertFrom-Json
$versionCheck = $content.VersionNo -eq $buildNo
return $versionCheck
}
function VersionCheckSyncService() {
$tls = Invoke-WebRequest -URI "https://${site}syncservice.net/SyncService.svc/Info" -UseBasicParsing
$tls = $tls -replace '[?]', ""
$content = "$tls" | ConvertFrom-Json
$versionCheck = $content.VersionNo -eq $buildNo
return $versionCheck
}
function VersionCheck() {
$versionCheckWeb = VersionCheckWeb
$versionCheckPortal = VersionCheckPortal
$versionCheckPortalService = VersionCheckPortalService
$versionCheckSyncService = VersionCheckSyncService
if (($versionCheckWeb -eq "True") -and ($versionCheckPortal -eq "True") -and ($versionCheckPortalService -eq "True") -and ($versionCheckSyncService -eq "True")) {
Log -message "Version correct" -isDetailed "false"
}
else {
Log -message "Version check failed, exception not thrown" -isDetailed "false"
}
}
function VersionCheckWebSlot() {
$tls = Invoke-WebRequest -URI "https://${site}-develop.azurewebsites.net/Home/Info" -UseBasicParsing
$content = $tls.Content | ConvertFrom-Json
$versionCheck = $content.VersionNo -eq $buildNo
return $versionCheck
}
function VersionCheckPortalSlot() {
$tls = Invoke-WebRequest -URI "https://${site}portal-develop.azurewebsites.net/Home/Info" -UseBasicParsing
$content = $tls.Content | ConvertFrom-Json
$versionCheck = $content.VersionNo -eq $buildNo
return $versionCheck
}
function VersionCheckPortalServiceSlot() {
$tls = Invoke-WebRequest -URI "https://${site}portalservice-develop.azurewebsites.net/Home/Info" -UseBasicParsing
$content = $tls.Content | ConvertFrom-Json
$versionCheck = $content.VersionNo -eq $buildNo
return $versionCheck
}
function VersionCheckSyncServiceSlot() {
$tls = Invoke-WebRequest -URI "https://${site}syncservice-develop.azurewebsites.net/SyncService.svc/Info" -UseBasicParsing
$tls = $tls -replace '[?]', ""
$content = "$tls" | ConvertFrom-Json
$versionCheck = $content.VersionNo -eq $buildNo
return $versionCheck
}
function VersionCheckSlot() {
$versionCheckWeb = VersionCheckWebSlot
$versionCheckPortal = VersionCheckPortalSlot
$versionCheckPortalService = VersionCheckPortalServiceSlot
$versionCheckSyncService = VersionCheckSyncServiceSlot
if (($versionCheckWeb -eq "True") -and ($versionCheckPortal -eq "True") -and ($versionCheckPortalService -eq "True") -and ($versionCheckSyncService -eq "True")) {
Log -message "Slot version correct" -isDetailed "false"
}
else {
Log -message "Slot version check failed, exception not thrown" -isDetailed "false"
}
}
function PublishToAzure() {
$webPublishProfile = Get-AzWebAppPublishingProfile -ResourceGroupName $rg -Name $siteNameWeb
$webXml = $webPublishProfile -as [Xml]
$webUserName = $webXml.publishData.publishProfile[0].userName
$webUserPwd = $webXml.publishData.publishProfile[0].userPWD
$webpair = "$($webUserName):$($webUserPwd)"
$webencodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($webpair))
$webauthHeader = "Basic $webencodedCreds"
$webHeaders = #{
Authorization = $webauthHeader
}
$portalPublishProfile = Get-AzWebAppPublishingProfile -ResourceGroupName $rg -Name $siteNamePortal
$portalXml = $portalPublishProfile -as [Xml]
$portalUserName = $portalXml.publishData.publishProfile[0].userName
$portalUserPwd = $portalXml.publishData.publishProfile[0].userPWD
$portalpair = "$($portalUserName):$($portalUserPwd)"
$portalencodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($portalpair))
$portalauthHeader = "Basic $portalencodedCreds"
$portalHeaders = #{
Authorization = $portalauthHeader
}
$portalServicePublishProfile = Get-AzWebAppPublishingProfile -ResourceGroupName $rg -Name $siteNamePortalService
$portalServiceXml = $portalServicePublishProfile -as [Xml]
$portalServiceUserName = $portalServiceXml.publishData.publishProfile[0].userName
$portalServiceUserPwd = $portalServiceXml.publishData.publishProfile[0].userPWD
$portalServicepair = "$($portalServiceUserName):$($portalServiceUserPwd)"
$portalServiceencodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($portalServicepair))
$portalServiceauthHeader = "Basic $portalServiceencodedCreds"
$portalServiceHeaders = #{
Authorization = $portalServiceauthHeader
}
$syncServicePublishProfile = Get-AzWebAppPublishingProfile -ResourceGroupName $rg -Name $siteNameSyncService
$syncServiceXml = $syncServicePublishProfile -as [Xml]
$syncServiceUserName = $syncServiceXml.publishData.publishProfile[0].userName
$syncServiceUserPwd = $syncServiceXml.publishData.publishProfile[0].userPWD
$syncServicepair = "$($syncServiceUserName):$($syncServiceUserPwd)"
$syncServiceencodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($syncServicepair))
$syncServiceauthHeader = "Basic $syncServiceencodedCreds"
$syncServiceHeaders = #{
Authorization = $syncServiceauthHeader
}
$bodyWeb = '{"packageUri": "<blobUrl>"}'
$bodyPortal = '{"packageUri": "blobUrl"}'
$bodyPortalService = '{"packageUri": "blobUrl"}'
$bodySyncService = '{"packageUri": "blobUrl"}'
$web = Invoke-RestMethod -URI "https://${siteNameWeb}.scm.azurewebsites.net/api/publish?type=zip" -Method POST -Body $bodyWeb -Headers $webHeaders -ContentType "application/json"
Log -message "Published to Web" -isDetailed "false"
$portal = Invoke-RestMethod -URI "https://${siteNamePortal}.scm.azurewebsites.net/api/publish?type=zip" -Method POST -Body $bodyPortal -Headers $portalHeaders -ContentType "application/json"
Log -message "Published to Portal" -isDetailed "false"
$portalService = Invoke-RestMethod -URI "https://${siteNamePortalService}.scm.azurewebsites.net/api/publish?type=zip" -Method POST -Body $bodyPortalService -Headers $portalServiceHeaders -ContentType "application/json"
Log -message "Published to PortalService" -isDetailed "false"
$syncService = Invoke-RestMethod -URI "https://${siteNameSyncService}.scm.azurewebsites.net/api/publish?type=zip" -Method POST -Body $bodySyncService -Headers $syncServiceHeaders -ContentType "application/json"
Log -message "Published to SyncService" -isDetailed "false"
}
try {
CreateDeploymentSlots
PublishToAzure
VersionCheckSlot
SwapDeploymentSlots
VersionCheck
DeleteDeploymentSlots
Log -message "Instance deployed successfully" -isDetailed "false" -isDeployed "true"
}
catch {
Log -message $_.Exception.message -isDetailed "false" -hasError "true"
}
I know the code is little mess right now and that's because I've changed the implementation to faster the process but no major luck.
Question2
I actually have another question as well. We've multiple subscriptions and appservices are distributed accordingly. So when function app tries to deploy to a instance exists in another subscription, it throws error. I'm setting AzContext before starting the processing and it seems to be working fine. But i also have to put message into a queue after each step and the queue exists in a storage account that belongs to same subscription as of the function app. Currently i'm getting the AzContent and passing it as -DefaultProfile while getting the storage account. Is there a better way to handle this?
Also I am writing powershell for the first time so any suggestion would be appreciated. Thanks.

How to pull all tags at resource level in azure

I am Trying to pull All the Tags resource level in azure
$subscripionList = Get-AzSubscription
foreach ($subscriptionId in $subscripionList) {
Write-Host 'Getting details about SubscriptionId :' $subscriptionId
Set-AzContext -Subscription $subscriptionId
#Select-AzureRmSubscription -SubscriptionName $subscriptionId
$resourcesgroups = Get-AzResourceGroup
foreach($resourcesgroup in $resourcesgroups){
Write-Host 'resourcegroup :' $resourcesgroup
$resources = Get-AzResource -ResourceGroupName $resourcesgroup.ResourceGroupName
#$azure_resources = Get-AzResource
foreach($resource in $resources){
Write-Host $resource
{
#Fetching Tags
$Tags = $resource.Tags
#Checkign if tags is null or have value
if($Tags -ne $null)
{
foreach($Tag in $Tags)
{
$TagsAsString += $Tag.Name + ":" + $Tag.Value + ";"
}
}
else
{
$TagsAsString = "NULL"
}
}
}
}
I am Trying to get all the subscription then
I am Trying to get all the resource groups then
I am Trying to get all the resource present in resource group
Trying to get all the tags
But i am unable to get Tags and can anyone guide me how to export all tags into csv file.
As there is no $tag.Name and $Tag.Value , Your code doesn't store any values. The output you are looking are stored as $tags.keys and $tags.values. So, to collect them you will have to use for loop again . I tested the same by replacing the for each loop for the resources with the below script:
$TagsAsString=#()
$tagkeys =#()
$TagValues =#()
$resources = Get-AzResource -ResourceGroupName <resourcegroupname>
foreach($resource in $resources){
Write-host ("ResourceName :")$resource.Name
if($resource.Tags -ne $null){
foreach($Tag in $resource.Tags){
foreach($key in $Tag.keys){
$tagkeys += $key
}
foreach($value in $Tag.values){
$TagValues += $value
}
}
}
else{
Write-Host ("No Tags")
}
}
for($i = 0; $i -lt $tagkeys.Length; $i++) {
$TagsAsString=( '{0} : {1}' -f $tagkeys[$i], $tagvalues[$i] )
Write-Host $TagsAsString
}
Output:

Starting azure VM in parallel and validate after in PowerShell

Context : I am automating starting of 3 Azure VM through a PowerShell script, and each VM is taking 4-5min to start. I want to run the start command parallel and after 5-6min verify them whether they are started.
function restartLoadAgents ($AzuresecretValue,$AzureApplicationID,$AzureObjectID,$AzureDirectoryID,$AzureUserName,$AzureSubscriptionID) {
$password = ConvertTo-SecureString $AzuresecretValue -AsPlainText -Force;
$LoadAgentResourceGroup = "test-performance-rg01";
#Connecting to the Azure VM using the Service Principle
$pscredential = New-Object -TypeName System.Management.Automation.PSCredential($AzureApplicationID, $password);
Connect-AzAccount -ServicePrincipal -Tenant $AzureDirectoryID -Credential $pscredential | Out-null;
#List all the Load Agents
$VMList = Get-AzVm -ResourceGroupName $LoadAgentResourceGroup -Status;
ForEach($VM in $VMList) {
#Skipping the Master Machine and DB machine
if ($VM.Name -eq "test-load-appmachine01" -or $VM.Name -eq "test-load-appmachine02") {
continue;
}
$VMLoadAgentStatus = (Get-AzVm -ResourceGroupName $LoadAgentResourceGroup -Name $VM.Name -status).Statuses
$CurrentLoadAgentRunningStatus = $VMLoadAgentStatus[1].DisplayStatus;
if($CurrentLoadAgentRunningStatus -match "deallocated" -or $CurrentLoadAgentRunningStatus -match "VM deallocated"){
Start-AzVM -ResourceGroupName $LoadAgentResourceGroup -Name $VM.Name | Out-null
commandVerifier;
checkVMStatus($VM.Name);
}
else {
Write-Host $VM.Name " Current State is "$CurrentLoadAgentRunningStatus;
}
}
}
function commandVerifier() {
if ($?){
Write-Host "Successfully Started "$VM.Name;
}
else {
Write-Host "Start Unsuccessful "$VM.Name;
}
}
function checkVMStatus($VM_NAME) {
$VMLoadAgentStatus = (Get-AzVm -ResourceGroupName $LoadAgentResourceGroup -Name $$VM_NAME -status).Statuses
$VMRunningStatusAfterTriggered = $VMLoadAgentStatus[1].DisplayStatus;
if($VMRunningStatusAfterTriggered -eq "running" -or $VMRunningStatusAfterTriggered -eq "VM running"){
Write-Host "Successfully Started VM"
}
else{
Write-Host "Something went with starting VM and current status is"$VMRunningStatusAfterTriggered
}
}
function getServicePrincipleDetails () {
$AzuresecretValue = "<secretValue>";
$AzureApplicationID = "<app_id>";
$AzureObjectID = "<obj_id>";
$AzureDirectoryID = "<dir_id>";
$AzureUserName = "SVCUSER";
$AzureSubscriptionID = "<sub_id>";
restartLoadAgents $AzuresecretValue $AzureApplicationID $AzureObjectID $AzureDirectoryID $AzureUserName $AzureSubscriptionID
}
getServicePrincipleDetails
There are 5 VM in total and first two need not to be stopped or started. test-load-appmachine03,test-load-appmachine04 & test-load-appmachine05 are the target VM and I want to start them parallelly and check after.

Azure Powershell Tagging VM's from CSV file

I'm quite new to Powershell scripting. I'm trying to generate tags for azure vm's from a CSV file.
Inside the CSV I have the following column headings:
VMName
Application
SubCat
Environment
AppOwner
Location
I've got a test CSV which literally has the following data in it:
MattTestVM, TestApp, TestSubapp, Dev, Matt, UK South
I'm not sure what i've put wrong in my code to get it to add the tags.
Code
#Set Credentials
$cred = Get-credential
# Sign-in with Azure account credentials
add-azurermaccount -credential $cred
# Select Azure Subscription
$subscriptionId = (Get-AzureRmSubscription | Out-GridView -Title "Select an Azure Subscription ..." -PassThru).SubscriptionId
#Select specified subscription ID
Select-AzureRmSubscription -SubscriptionId $subscriptionId
$InputCSVFilePath = "C:\test\Tagging1.csv"
#Start loop
foreach ($eachRecord in $InputCSVFilePath)
{
$VMName = $eachrecord.VMName
$Application = $eachrecord.Application
$SubCat = $eachrecord.SubCat
$Environment = $eachrecord.Environment
$AppOwner = $eachrecord.AppOwner
$Location = $eachrecord.Location
$r = Get-AzureRmResource -ResourceId $ResourceId -ErrorAction Continue
if($r -ne $null)
{
if($r.tags)
{
# Tag - Application
if($r.Tags.ContainsKey("Application"))
{
$r.Tags["Application"] = $Application
}
else
{
$r.Tags.Add("Application", $Application)
}
# Tag - SubCat
if($r.Tags.ContainsKey("subCat"))
{
$r.Tags["subCat"] = $subCat
}
else
{
$r.Tags.Add("subCat", $subCat)
}
# Tag - Environment
if($r.Tags.ContainsKey("Environment"))
{
$r.Tags["Environment"] = $Environment
}
else
{
$r.Tags.Add("Environment", $Environment)
}
# Tag - AppOwner
if($r.Tags.ContainsKey("AppOwner"))
{
$r.Tags["AppOwner"] = $AppOwner
}
else
{
$r.Tags.Add("AppOwner", $AppOwner)
}
# Tag - Location
if($r.Tags.ContainsKey("Location"))
{
$r.Tags["Location"] = $Location
}
else
{
$r.Tags.Add("Location", $Location)
}
#Setting the tags on the resource
Set-AzureRmResource -Tag $r.Tags -ResourceId $r.ResourceId -Force
}
else
{
#Setting the tags on a resource which doesn't have tags
Set-AzureRmResource -Tag #{ Application=$Application; subCat=$subCat; Environment=$Environment; AppOwner=$AppOwner; Location=$Location } -ResourceId $r.ResourceId -Force
}
}
else
{
Write-Host "Resource Not Found with Resource Id: " + $ResourceId
}
}
Error message
Get-AzureRmResource : Cannot validate argument on parameter 'ResourceId'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At line:10 char:43
+ $r = Get-AzureRmResource -ResourceId $ResourceId -ErrorAction Co ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-AzureRmResource], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.GetAzureResourceCmdlet
OK, first thing's first: You can't do this without the ResourceGroupName, and the best way to add it in your case is as a column in your csv.
Secondly, everybody starts somewhere so never apologize. :) I commented about most of the issues I fixed in the code below. Your biggest problems were a) you didn't actually load the data from the CSV (you only created a variable with the file path in it), and b) you were attempting to use $ResourceID without ever populating it.
I cleaned things up a bit and it works now, and will throw an error if it can't find the VM. Here are the contents of my test.csv, with one good VM and one that doesn't exist.
VMName,ResourceGroup,Application,SubCat,Environment,AppOwner,Location
test-rm-01,test-rg,Thing,Subs,Dev,Nick,Europe
clambake,mygroup,,,,,
And here's the re-worked script.
# Set Credentials (Fixed this up)
$context = Get-AzureRmContext
if ($context.Account -eq $null) {
Login-AzureRmAccount
}
# Select Azure Subscription
$subscriptionId = (Get-AzureRmSubscription | Out-GridView -Title "Select an Azure Subscription ..." -PassThru).SubscriptionId
#Select specified subscription ID
Select-AzureRmSubscription -SubscriptionId $subscriptionId
$InputCSVFilePath = "C:\Users\Nick\Desktop\test.csv"
################
#
# Here you were missing loading the actual data. Also renamed the items in the loop, nitpicking. :)
#
################
$csvItems = Import-Csv $InputCSVFilePath
################
#Start loop
foreach ($item in $csvItems)
{
################
#
# Change here, you need ResourceGroupName
# Also cleared r because if you don't, it will still have a value if no matching VM is found, which would re-tag the previous item from the loop, instead of throwing an error that the VM was not found
#
################
Clear-Variable r
$r = Get-AzureRmResource -ResourceGroupName $item.ResourceGroup -Name $item.VMName -ErrorAction Continue
################
if ($r -ne $null)
{
if ($r.Tags)
{
# Tag - Application
if ($r.Tags.ContainsKey("Application"))
{
$r.Tags["Application"] = $item.Application
}
else
{
$r.Tags.Add("Application", $item.Application)
}
# Tag - SubCat
if ($r.Tags.ContainsKey("subCat"))
{
$r.Tags["subCat"] = $item.subCat
}
else
{
$r.Tags.Add("subCat", $item.subCat)
}
# Tag - Environment
if ($r.Tags.ContainsKey("Environment"))
{
$r.Tags["Environment"] = $item.Environment
}
else
{
$r.Tags.Add("Environment", $item.Environment)
}
# Tag - AppOwner
if ($r.Tags.ContainsKey("AppOwner"))
{
$r.Tags["AppOwner"] = $item.AppOwner
}
else
{
$r.Tags.Add("AppOwner", $item.AppOwner)
}
# Tag - Location
if ($r.Tags.ContainsKey("Location"))
{
$r.Tags["Location"] = $item.Location
}
else
{
$r.Tags.Add("Location", $item.Location)
}
#Setting the tags on the resource
Set-AzureRmResource -Tag $r.Tags -ResourceId $r.ResourceId -Force
}
else
{
#Setting the tags on a resource which doesn't have tags
Set-AzureRmResource -Tag #{ Application = $Application; subCat = $subCat; Environment = $Environment; AppOwner = $AppOwner; Location = $Location } -ResourceId $r.ResourceId -Force
}
}
else
{
Write-Host "No VM found named $($item.VMName) in ResourceGroup $($item.ResourceGroup)!"
}
}

Check if Azure VM name exists before deploying arm template

I am trying to use Azure powershell to get VM name (Eg: demovm01, where the VM name is demovm and the suffix is 01.
I want to get the output and automatically append a new suffix of 02 if 01 already exists.
Sample script to get vm name:
$getvm = Get-AzVM -Name "$vmname" -ResourceGroupName "eodemofunction" -ErrorVariable notPresent -ErrorAction SilentlyContinue
if ($notPresent) {
Write-Output "VM not found. Creating now"
}
else {
Write-Output "VM exists."
return $true
}
I want to be able to inject this new vm name to an arm deploy to deploy
This should do it. Will increment until no VM is found and use a simple -replace to inject to your json file. Will also return all thr VM values that are already present in Azure
$i=1
$vmname_base = "vmserver"
$VMexists = #()
do {
#invert int value to double digit string
$int = $i.tostring('00')
$getvm = Get-AzVM -Name "$vmname_base$int" -ResourceGroupName "eodemofunction" -ErrorVariable notPresent -ErrorAction SilentlyContinue
if ($notPresent) {
Write-Output "VM not found. Creating now"
Write-Output "VM created name is $vmname_base$int"
#Set condition to end do while loop
VMcreated = "true"
#commands to inject to json here. I always find replace method the easiest
$JSON = Get-Content azuredeploy.parameters.json
$JSON = $JSON -replace ("Servername","$vmname_base$int")
$JSON | Out-File azuredeploy.parameters.json -Force
}
else {
Write-Output "VMexists."
# Add existing VM to array
$VMexists += "$vmname_base$int"
# Increment version, ie. 01 to 02 to 03 etc
$i++
}
} while ($VMcreated -ne "true")
return $VMexists
Your command could be like below, the $newvmname is that you want.
$vmname = "demovm01"
$getvm = Get-AzVM -Name "$vmname" -ResourceGroupName "<group name>" -ErrorVariable notPresent -ErrorAction SilentlyContinue
if ($notPresent) {
Write-Output "VM not found. Creating now"
}
else {
Write-Output "VM exists."
if($getvm.Name -like '*01'){
$newvmname = $vmname.TrimEnd('01')+"02"
}
Write-Output $newvmname
return $true
}

Resources