Multiple Azure VM base images with the same name - azure

When i use a script to create a VM in Azure and select as the base image "Windows 10, Pro Version 1903" I get the following error: "Found more than 1 image with name 'Windows 10 Pro, Version 1903'. Please pick one from ['Windows 10 Pro, Version 1903', 'Windows 10 Pro, Version 1903']"
If I create a VM from the portal there are two base images with exactly the same name.
az lab vm create --resource-group MyTestRG --lab-name MyTestLab --name 'MyScriptVM' --image "Windows 10 Pro, Version 1903" --image-type gallery --size 'Standard_B2s' --admin-username '****' --admin-password '****'
How are you supposed to use that base image if you can't tell it exactly which one to use?

When you filter the image via CLI command: az lab gallery-image list --lab-name mytestlab --resource-group myrg --query "[?name == 'Windows 10 Pro, Version 1903']", you will find the similar image reference except for the SKU.
However, there is no SKU parameter for Azure CLI az lab vm create. As a workaround, you could create a VM with DevTest Labs using Azure PowerShell.
The sample PowerShell script:
[CmdletBinding()]
Param(
[Parameter(Mandatory = $false)] $SubscriptionId,
[Parameter(Mandatory = $true)] $LabResourceGroup,
[Parameter(Mandatory = $true)] $LabName,
[Parameter(Mandatory = $true)] $NewVmName,
[Parameter(Mandatory = $true)] $UserName,
[Parameter(Mandatory = $true)] $Password
)
pushd $PSScriptRoot
try {
if ($SubscriptionId -eq $null) {
$SubscriptionId = (Get-AzContext).Subscription.SubscriptionId
}
$API_VERSION = '2016-05-15'
$lab = Get-AzResource -ResourceId "/subscriptions/$SubscriptionId/resourceGroups/$LabResourceGroup/providers/Microsoft.DevTestLab/labs/$LabName"
if ($lab -eq $null) {
throw "Unable to find lab $LabName resource group $LabResourceGroup in subscription $SubscriptionId."
}
#For this example, we are getting the first allowed subnet in the first virtual network
# for the lab.
#If a specific virtual network is needed use | to find it.
#ie $virtualNetwork = #(Get-AzResource -ResourceType 'Microsoft.DevTestLab/labs/virtualnetworks' -ResourceName $LabName -ResourceGroupName $lab.ResourceGroupName -ApiVersion $API_VERSION) | Where-Object Name -EQ "SpecificVNetName"
$virtualNetwork = #(Get-AzResource -ResourceType 'Microsoft.DevTestLab/labs/virtualnetworks' -ResourceName $LabName -ResourceGroupName $lab.ResourceGroupName -ApiVersion $API_VERSION)[0]
$labSubnetName = $virtualNetwork.properties.allowedSubnets[0].labSubnetName
#Prepare all the properties needed for the createEnvironment
# call used to create the new VM.
# The properties will be slightly different depending on the base of the vm
# (a marketplace image, custom image or formula).
# The setup of the virtual network to be used may also affect the properties.
# This sample includes the properties to add an additional disk under dataDiskParameters
$parameters = #{
"name" = $NewVmName;
"location" = $lab.Location;
"properties" = #{
"labVirtualNetworkId" = $virtualNetwork.ResourceId;
"labSubnetName" = $labSubnetName;
"notes" = "Windows Server 2016 Datacenter";
"osType" = "windows"
"expirationDate" = "2019-12-01"
"galleryImageReference" = #{
"offer" = "WindowsServer";
"publisher" = "MicrosoftWindowsServer";
"sku" = "2016-Datacenter";
"osType" = "Windows";
"version" = "latest"
};
"size" = "Standard_DS2_v2";
"userName" = $UserName;
"password" = $Password;
"disallowPublicIpAddress" = $true;
"dataDiskParameters" = #(#{
"attachNewDataDiskOptions" = #{
"diskName" = "adddatadisk"
"diskSizeGiB" = "1023"
"diskType" = "Standard"
}
"hostCaching" = "ReadWrite"
})
}
}
#The following line is the same as invoking
# https://azure.github.io/projects/apis/#!/Labs/Labs_CreateEnvironment rest api
Invoke-AzResourceAction -ResourceId $lab.ResourceId -Action 'createEnvironment' -Parameters $parameters -ApiVersion $API_VERSION -Force -Verbose
}
finally {
popd
}
The following command provides an example of running the script saved in a file name: Create-LabVirtualMachine.ps1.
PS> .\Create-LabVirtualMachine.ps1 -ResourceGroupName 'MyLabResourceGroup' -LabName 'MyLab' -userName 'AdminUser' -password 'Password1!' -VMName 'MyLabVM'

Related

Assistance with looping through multiple subscriptions

I'm using the below script from http://vcloud-lab.com/entries/microsoft-azure/get-azure-virtual-machine-backup-reports-using-powershell to pull Azure VM backup details. Currently I have to run the script against each subscription, I would love to have it loop though all subscriptions.
I've been trying to get it working using this example https://www.jpaul.me/2019/05/azure-automation-how-to-quickly-work-with-many-subscriptions/ but I'm new to powershell and am struggling. Any suggestions would be really appreciated.
[CmdletBinding(SupportsShouldProcess=$True,
ConfirmImpact='Medium',
HelpURI='http://vcloud-lab.com',
DefaultParameterSetName = 'AllVirtualMachines'
)]
<#
.SYNOPSIS
Collect Azure VM Backup Information
.DESCRIPTION
This Script collects Azure Virtual Machine Backup Recovery service vault information, This report includes the complete backup status Information of VM.
.PARAMETER AllVirtualMachines
Collect Backup information of the all Azure Virtual Machines, This is default parameter.
.PARAMETER VirtualMachineList
You can specify for which virtual machine you want backup information.
.INPUTS
None. Provides virtual machine information.
.OUTPUTS
Generate Backup information. You can pipe information to Export-CSV.
.EXAMPLE
PS> .\Get-AzVMBackupInformation.ps1
VM_Name : vcloud-lab-vm01
VM_Location : uksouth
VM_ResourceGroupName : VCLOUD-LAB.COM
VM_BackedUp : True
VM_RecoveryVaultName : vault828
VM_RecoveryVaultPolicy : DailyPolicy-kosrnox0
VM_BackupHealthStatus : Passed
VM_BackupProtectionStatus : Healthy
VM_LastBackupStatus : Completed
VM_LastBackupTime : 27-05-2021 19:32:34
VM_BackupDeleteState : NotDeleted
VM_BackupLatestRecoveryPoint : 27-05-2021 19:32:37
VM_Id : /subscriptions/9e22xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/VCLOUD-LAB.COM/providers/Microsoft.Compute/virtualMachines/vcloud-lab-vm01
RecoveryVault_ResourceGroupName : vCloud-lab.com
RecoveryVault_Location : uksouth
RecoveryVault_SubscriptionId : /subscriptions/9e22xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/vCloud-lab.com/providers/Microsoft.RecoveryServices/vaults/vault828
.EXAMPLE
PS> .\Get-AzVMBackupInformation.ps1 -AllVirtualMachines
This produces same result as .\Get-AzVMBackupInformation.ps1 from all VMs
.EXAMPLE
PS> .\Get-AzVMBackupInformation.ps1 -VirtualMachineList
Provide either single virtual machine name or in list
.LINK
Online version: http://vcloud-lab.com
.LINK
Get-AzVMBackupInformation.ps1
#>
Param
(
[parameter(Position=0, ParameterSetName = 'AllVMs' )]
[Switch]$AllVirtualMachines,
[parameter(Position=0, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, ParameterSetName = 'VM' )]
[alias('Name')]
[String[]]$VirtualMachineList
) #Param
Begin
{
#Collecing Azure virtual machines Information
Write-Host "Collecing Azure virtual machine Information" -BackgroundColor DarkGreen
if (($PSBoundParameters.ContainsKey('AllVirtualMachines')) -or ($PSBoundParameters.Count -eq 0))
{
$vms = Get-AzVM
} #if ($PSBoundParameters.ContainsKey('AllVirtualMachines'))
elseif ($PSBoundParameters.ContainsKey('VirtualMachineList'))
{
$vms = #()
foreach ($vmname in $VirtualMachineList)
{
$vms += Get-AzVM -Name $vmname
} #foreach ($vmname in $VirtualMachineList)
} #elseif ($PSBoundParameters.ContainsKey('VirtualMachineList'))
#Collecing All Azure backup recovery vaults Information
Write-Host "Collecting all Backup Recovery Vault information" -BackgroundColor DarkGreen
$backupVaults = Get-AzRecoveryServicesVault
} #Begin
Process
{
$vmBackupReport = [System.Collections.ArrayList]::new()
foreach ($vm in $vms)
{
$recoveryVaultInfo = Get-AzRecoveryServicesBackupStatus -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -Type 'AzureVM'
if ($recoveryVaultInfo.BackedUp -eq $true)
{
Write-Host "$($vm.Name) - BackedUp : Yes"
#Backup Recovery Vault Information
$vmBackupVault = $backupVaults | Where-Object {$_.ID -eq $recoveryVaultInfo.VaultId}
#Backup recovery Vault policy Information
$container = Get-AzRecoveryServicesBackupContainer -ContainerType AzureVM -VaultId $vmBackupVault.ID -FriendlyName $vm.Name #-Status "Registered"
$backupItem = Get-AzRecoveryServicesBackupItem -Container $container -WorkloadType AzureVM -VaultId $vmBackupVault.ID
} #if ($recoveryVaultInfo.BackedUp -eq $true)
else
{
Write-Host "$($vm.Name) - BackedUp : No" -BackgroundColor DarkRed
$vmBackupVault = $null
$container = $null
$backupItem = $null
} #else if ($recoveryVaultInfo.BackedUp -eq $true)
[void]$vmBackupReport.Add([PSCustomObject]#{
VM_Name = $vm.Name
VM_Location = $vm.Location
VM_ResourceGroupName = $vm.ResourceGroupName
VM_BackedUp = $recoveryVaultInfo.BackedUp
VM_RecoveryVaultName = $vmBackupVault.Name
VM_RecoveryVaultPolicy = $backupItem.ProtectionPolicyName
VM_BackupHealthStatus = $backupItem.HealthStatus
VM_BackupProtectionStatus = $backupItem.ProtectionStatus
VM_LastBackupStatus = $backupItem.LastBackupStatus
VM_LastBackupTime = $backupItem.LastBackupTime
VM_BackupDeleteState = $backupItem.DeleteState
VM_BackupLatestRecoveryPoint = $backupItem.LatestRecoveryPoint
VM_Id = $vm.Id
RecoveryVault_ResourceGroupName = $vmBackupVault.ResourceGroupName
RecoveryVault_Location = $vmBackupVault.Location
RecoveryVault_SubscriptionId = $vmBackupVault.ID
}) #[void]$vmBackupReport.Add([PSCustomObject]#{
} #foreach ($vm in $vms)
} #Process
end
{
$vmBackupReport
} #end
You can try this sample script from the MS doc for looping through multiple subscriptions.
Connect-AzAccount
$SubscriptionList = Get-AzSubscription
foreach ($Id in $SubscriptionList)
{
#Provide the subscription Id where the VMs reside
$subscriptionId = $Id
#Provide the name of the csv file to be exported
$reportName = "myReport.csv"
Select-AzSubscription $subscriptionId
$report = #()
$vms = Get-AzVM
$publicIps = Get-AzPublicIpAddress
$nics = Get-AzNetworkInterface | ?{ $_.VirtualMachine -NE $null}
foreach ($nic in $nics)
{
$info = "" | Select VmName, ResourceGroupName, Region, VmSize, VirtualNetwork, Subnet, PrivateIpAddress, OsType, PublicIPAddress, NicName, ApplicationSecurityGroup, subscriptionId
$vm = $vms | ? -Property Id -eq $nic.VirtualMachine.id
foreach($publicIp in $publicIps)
{
if($nic.IpConfigurations.id -eq $publicIp.ipconfiguration.Id)
{
$info.PublicIPAddress = $publicIp.ipaddress
}
}
$info.subscriptionId = $subscriptionId
$info.OsType = $vm.StorageProfile.OsDisk.OsType
$info.VMName = $vm.Name
$info.ResourceGroupName = $vm.ResourceGroupName
$info.Region = $vm.Location
$info.VmSize = $vm.HardwareProfile.VmSize
$info.VirtualNetwork = $nic.IpConfigurations.subnet.Id.Split("/")[-3]
$info.Subnet = $nic.IpConfigurations.subnet.Id.Split("/")[-1]
$info.PrivateIpAddress = $nic.IpConfigurations.PrivateIpAddress
$info.NicName = $nic.Name
$info.ApplicationSecurityGroup = $nic.IpConfigurations.ApplicationSecurityGroups.Id
$report+=$info
}
$report | ft subscriptionId, VmName, ResourceGroupName, Region, VmSize, VirtualNetwork, Subnet, PrivateIpAddress, OsType, PublicIPAddress, NicName, ApplicationSecurityGroup
$report | Export-CSV "$home/$reportName"
}
References: Collect details about all VMs in a subscription with PowerShell - Azure Virtual Machines | Microsoft Docs , Iterate through subscriptions using an Array to avoid manual work · Issue #50670 · MicrosoftDocs/azure-docs · GitHub and powershell - How to loop through multiple Azure subscriptions on Azure Pipelines Yaml? - Stack Overflow

How to customize the image with Log Analytics virtual machine extension using Azure Image Builder

I have created a Windows VM with Azure Image Builder using PowerShell by following this documentation.
param (
[Parameter(Mandatory = $true)]
[string]
$imageResourceGroup,
[Parameter(Mandatory = $true)]
[string]
$location,
[Parameter(Mandatory = $true)]
[string]
$imageTemplateName,
[Parameter(Mandatory = $true)]
[string]
$runOutputName,
[Parameter(Mandatory = $true)]
[string]
$myGalleryName,
[Parameter(Mandatory = $true)]
[string]
$imageDefName
)
## Register features
Get-AzResourceProvider -ProviderNamespace Microsoft.Compute, Microsoft.KeyVault, Microsoft.Storage, Microsoft.VirtualMachineImages, Microsoft.Network |
Where-Object RegistrationState -ne Registered |
Register-AzResourceProvider
## Install modules
#Install-Module -Name Az.ManagedServiceIdentity -RequiredVersion 0.7.2 -Force
#Install-Module -Name Az.ImageBuilder -Force
## Your Azure Subscription ID
$subscriptionID = (Get-AzContext).Subscription.Id
Write-Output $subscriptionID
## Create a resource group
New-AzResourceGroup -Name $imageResourceGroup -Location $location
## Create user identity and set role permissions
[int]$timeInt = $(Get-Date -UFormat '%s')
$imageRoleDefName = "Azure Image Builder Image Def $timeInt"
$identityName = "myIdentity$timeInt"
## Create a user identity.
New-AzUserAssignedIdentity -ResourceGroupName $imageResourceGroup -Name $identityName
## Store the identity resource and principal IDs in variables.
$identityNameResourceId = (Get-AzUserAssignedIdentity -ResourceGroupName $imageResourceGroup -Name $identityName).Id
$identityNamePrincipalId = (Get-AzUserAssignedIdentity -ResourceGroupName $imageResourceGroup -Name $identityName).PrincipalId
## Assign permissions for identity to distribute images
$myRoleImageCreationUrl = 'https://raw.githubusercontent.com/azure/azvmimagebuilder/master/solutions/12_Creating_AIB_Security_Roles/aibRoleImageCreation.json'
$myRoleImageCreationPath = "$env:TEMP\myRoleImageCreation.json"
Invoke-WebRequest -Uri $myRoleImageCreationUrl -OutFile $myRoleImageCreationPath -UseBasicParsing
$Content = Get-Content -Path $myRoleImageCreationPath -Raw
$Content = $Content -replace '<subscriptionID>', $subscriptionID
$Content = $Content -replace '<rgName>', $imageResourceGroup
$Content = $Content -replace 'Azure Image Builder Service Image Creation Role', $imageRoleDefName
$Content | Out-File -FilePath $myRoleImageCreationPath -Force
## Create the role definition.
New-AzRoleDefinition -InputFile $myRoleImageCreationPath
## Grant the role definition to the image builder service principal.
$RoleAssignParams = #{
ObjectId = $identityNamePrincipalId
RoleDefinitionName = $imageRoleDefName
Scope = "/subscriptions/$subscriptionID/resourceGroups/$imageResourceGroup"
}
New-AzRoleAssignment #RoleAssignParams
## Create an Azure Compute Gallery
## Create the gallery.
#$myGalleryName = 'myImageGallery'
#$imageDefName = 'winSvrImages'
New-AzGallery -GalleryName $myGalleryName -ResourceGroupName $imageResourceGroup -Location $location
## Create a gallery definition.
$GalleryParams = #{
GalleryName = $myGalleryName
ResourceGroupName = $imageResourceGroup
Location = $location
Name = $imageDefName
OsState = 'generalized'
OsType = 'Windows'
Publisher = 'myCo'
Offer = 'Windows'
Sku = 'Win2019'
}
New-AzGalleryImageDefinition #GalleryParams
## Create an image
## Create an Azure image builder source object.
$SrcObjParams = #{
SourceTypePlatformImage = $true
Publisher = 'MicrosoftWindowsServer'
Offer = 'WindowsServer'
Sku = '2019-Datacenter'
Version = 'latest'
}
$srcPlatform = New-AzImageBuilderSourceObject #SrcObjParams
## Create an Azure image builder distributor object.
$disObjParams = #{
SharedImageDistributor = $true
ArtifactTag = #{tag = 'dis-share' }
GalleryImageId = "/subscriptions/$subscriptionID/resourceGroups/$imageResourceGroup/providers/Microsoft.Compute/galleries/$myGalleryName/images/$imageDefName"
ReplicationRegion = $location
RunOutputName = $runOutputName
ExcludeFromLatest = $false
}
$disSharedImg = New-AzImageBuilderDistributorObject #disObjParams
## Create an Azure image builder customization object.
$ImgCustomParams01 = #{
PowerShellCustomizer = $true
CustomizerName = 'settingUpMgmtAgtPath'
RunElevated = $false
Inline = #("mkdir c:\\buildActions", "mkdir c:\\buildArtifacts", "echo Azure-Image-Builder-Was-Here > c:\\buildActions\\buildActionsOutput.txt")
}
$Customizer01 = New-AzImageBuilderCustomizerObject #ImgCustomParams01
## Create a second Azure image builder customization object.
$ImgCustomParams02 = #{
FileCustomizer = $true
CustomizerName = 'downloadBuildArtifacts'
Destination = 'c:\\buildArtifacts\\index.html'
SourceUri = 'https://raw.githubusercontent.com/azure/azvmimagebuilder/master/quickquickstarts/exampleArtifacts/buildArtifacts/index.html'
}
$Customizer02 = New-AzImageBuilderCustomizerObject #ImgCustomParams02
## Create an Azure image builder template.
$ImgTemplateParams = #{
ImageTemplateName = $imageTemplateName
ResourceGroupName = $imageResourceGroup
Source = $srcPlatform
Distribute = $disSharedImg
Customize = $Customizer01, $Customizer02
Location = $location
UserAssignedIdentityId = $identityNameResourceId
}
New-AzImageBuilderTemplate #ImgTemplateParams
## To determine if the template creation process was successful, you can use the following example.
Get-AzImageBuilderTemplate -ImageTemplateName $imageTemplateName -ResourceGroupName $imageResourceGroup |
Select-Object -Property Name, LastRunStatusRunState, LastRunStatusMessage, ProvisioningState
## Start the image build
## Submit the image configuration to the VM image builder service.
Start-AzImageBuilderTemplate -ResourceGroupName $imageResourceGroup -Name $imageTemplateName
## Create a VM
## Store login credentials for the VM in a variable. The password must be complex.
$Cred = Get-Credential
## Create the VM using the image you created.
$ArtifactId = (Get-AzImageBuilderRunOutput -ImageTemplateName $imageTemplateName -ResourceGroupName $imageResourceGroup).ArtifactId
New-AzVM -ResourceGroupName $imageResourceGroup -Image $ArtifactId -Name myWinVM01 -Credential $Cred
## Verify the customizations
Get-Content -Path C:\buildActions\buildActionsOutput.txt
Get-ChildItem c:\buildArtifacts\
## Delete the image builder template
#Remove-AzImageBuilderTemplate -ResourceGroupName $imageResourceGroup -Name $imageTemplateName
## Delete the image resource group
#Remove-AzResourceGroup -Name $imageResourceGroup
I want to add the Log Analytics virtual machine extension for Windows to the custom image.
Did you tried the same custom params that I see in your script, something in inline (Silent installation commands) comments with workid or key?
however, the easiest way is to enabled log analytics auto provisioning in security center now it's called microsoft defender for cloud (I am using it)

AccessRestrictions AddIP for Multiple App Services in Azure Powershell

I have an Azure powershell script where the user has to add in the app service name, IP address and so on. I have 4 web apps where I would like to add the same IP address to. I would like to hard code the app service name so the user does not have to input it. How can I achieve this in powershell? Here is my code:
Param
(
# Name of the resource group that contains the App Service.
[Parameter(Mandatory=$true)]
$RGName,
# Name of your Web or API App.
[Parameter(Mandatory=$true)]
$WebAppName,
# priority value.
[Parameter(Mandatory=$true)]
$priority,
# WhitelistIp values.
[Parameter(Mandatory=$true)]
$IPList,
# rule to add.
[PSCustomObject]$rule
)
function Add-AzureIpRestrictionRule
{
$ApiVersions = Get-AzureRmResourceProvider -ProviderNamespace Microsoft.Web |
Select-Object -ExpandProperty ResourceTypes |
Where-Object ResourceTypeName -eq 'sites' |
Select-Object -ExpandProperty ApiVersions
$LatestApiVersion = $ApiVersions[0]
$WebAppConfig = Get-AzureRmResource -ResourceType 'Microsoft.Web/sites/config' -ResourceName $WebAppName -ResourceGroupName $RGName -ApiVersion $LatestApiVersion
$WebAppConfig.Properties.ipSecurityRestrictions = $WebAppConfig.Properties.ipSecurityRestrictions + #($rule) |
Group-Object name |
ForEach-Object { $_.Group | Select-Object -Last 1 }
Set-AzureRmResource -ResourceId $WebAppConfig.ResourceId -Properties $WebAppConfig.Properties -ApiVersion $LatestApiVersion -Force
}
$IPList= #($IPList-split ",")
Write-Host "IPList found "$IPList"."
$increment = 1
foreach ($element in $IPList)
{
if ($element -eq "" -OR $element -eq " ") {continue}
else
{
$element=$element.Trim()
$rule = [PSCustomObject]#{
ipAddress = "$($element)/32"
action = "Allow"
priority = "$priority"
name = "WhitelistIP"+ $increment}
$increment++
Add-AzureIpRestrictionRule -ResourceGroupName "$RGName" -AppServiceName "$WebAppName" -rule $rule
}
}
$OutboundIP = #(Get-AzureRmWebApp -Name "$WebAppName" -ResourceGroupName "$RGName").possibleOutboundIPAddresses -split ","
$increment = 1
foreach ($element in $OutboundIP)
{
$rule = [PSCustomObject]#{
ipAddress = "$($element)/32"
action = "Allow"
priority = "$priority"
name = "OutboundIP"+ $increment}
$increment++
Add-AzureIpRestrictionRule -ResourceGroupName "$RGName" -AppServiceName "$WebAppName" -rule $rule
}
so my $WebAppName i want it to be hard coded, but I have 4 of these web-apps, how do I get this script to run 4 times with just having the user input his IP address once, all variables stay the same, just the $WebAppName should change to the hard coded values i give
You could something like the following (If I've correctly understood the problem).
$webAppNames = 'a,b,c,d'
$webAppList = $webAppNames.split(',')
Foreach($webAppName in $webAppList) { ....do stuff }

Passing multiple Parameters in single Azure Storage Script for various environments

I have a powershell script that creates the storage and blob account for a given subscription that works fine . Subscription Name, resource group keeps changing for different environments like DEV,UAT,PROD
STRUCTURE OF MY TEMPLATE / CODE :
param(
[string] $subscriptionName ="ABC",
[string] $resourceGroupName = "XYZ",
[string] $resourceGroupLocation ="westus",
[string] $templateFilePath = "template.json",
[string] $parametersFilePath = "parameters.json"
)
Function RegisterRP {
Param(
[string]$ResourceProviderNamespace
)
Write-Host "Registering resource provider '$ResourceProviderNamespace'";
Register-AzureRmResourceProvider -ProviderNamespace $ResourceProviderNamespace;
}
$ErrorActionPreference = "Stop"
$confirmExecution = Read-Host -Prompt "Hit Enter to continue."
if($confirmExecution -ne '') {
Write-Host "Script was stopped by user." -ForegroundColor Yellow
exit
}
# sign in
Write-Host "Logging in...";
Login-AzureRmAccount;
# select subscription
Write-Host "Selecting subscription '$subscriptionName'";
Select-AzureRmSubscription -SubscriptionName $subscriptionName;
# Register RPs
$resourceProviders = #("microsoft.storage");
if($resourceProviders.length) {
Write-Host "Registering resource providers"
foreach($resourceProvider in $resourceProviders) {
RegisterRP($resourceProvider);
}
}
#Create or check for existing resource group
$resourceGroup = Get-AzureRmResourceGroup -Name $resourceGroupName -ErrorAction SilentlyContinue
if(!$resourceGroup)
{
Write-Host "Resource group '$resourceGroupName' does not exist. To create a new resource group, please enter a location.";
if(!$resourceGroupLocation) {
$resourceGroupLocation = Read-Host "resourceGroupLocation";
}
Write-Host "Creating resource group '$resourceGroupName' in location '$resourceGroupLocation'";
New-AzureRmResourceGroup -Name $resourceGroupName -Location $resourceGroupLocation
}
else{
Write-Host "Using existing resource group '$resourceGroupName'";
}
# Start the deployment
Write-Host "Starting deployment...";
if(Test-Path $parametersFilePath) {
New-AzureRmResourceGroupDeployment -ResourceGroupName $resourceGroupName -Name $deploymentName -TemplateFile $templateFilePath -TemplateParameterFile $parametersFilePath -storageAccounts_name $storageAccountName
} else {
New-AzureRmResourceGroupDeployment -ResourceGroupName $resourceGroupName -Name $deploymentName -TemplateFile $templateFilePath; -storageAccounts_name $storageAccountName
}
Approach 1 :
Created multiple powershell scripts for each denvironment
Created 1 Menu Based powershell script that calls the other script and executes like : Select 1 for Dev , 2 for UAt , 3 for PROD , this approach works but is not effective .
Approach 2 :
I would like to combine all scripts and just have one script for all environments and based on select should allow me to create the storage accounts. Only Subscription and resource group change rest all structure of the powershell remains same .
I tried using GET function commandlets and it selects but still throws the error
[string] $subscriptionName = Get-AzureSubscription,
[string] $resourceGroupName = Get-AzureRmLocation,
If i try to use it using an array based approach like passing the values as below im unable to understand how do i pass these array based values to the code and get it to work .
$environment=#('DEV','TEST','QA','PROD')
$resourcegroupname = #('test','test1','test2','test3')
$subscriptionName = #('devsub1','devsub2','test3','prod4')
I'm trying to call the functions using :
$environment[0]
$subscriptionName[0]
It returns the value as below if i execute it seperately but how do i pass these values to my script to create storage account ?
DEV
devsub1
Requesting expert help if anyone has come across such scenarios earlier and if you can help in changing the above code and provide a tested code that would be of great help.
APPROACH 3:
$subscription = #(Get-AzureRmSubscription)
$resourcegroup = #(Get-AzureRmResourceGroup)
$Environment = #('DEV','TEST','QA','PROD')
$resourceGroupName = $resourcegroup | Out-GridView -PassThru -Title 'Pick the environment'
$subscriptionName = $subscription | Out-GridView -PassThru -Title 'Pick the subscription'
Write-Host "Subscription:" $subscriptionName
Write-Host "ResourceGroup:" $resourcegroup
OUTPUT :
If you look at resource group it fails to give the selection option for resource group .
Subscription: < it returns the subscription name >
ResourceGroup: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceGroup Microsoft.Azure.Commands.ResourceManager.Cmd
lets.SdkModels.PSResourceGroup Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceGroup Microsoft.Azure.Commands.Res
ourceManager.Cmdlets.SdkModels.PSResourceGroup
What you are proposing is an interesting approach. I would likely an input parameter that defines which environment the work will be done in, and then have a conditional block that sets the dynamic variables for that environment. There would be some duplication of initialization code for each environment, but the main code block would still be unified.

Create a consumption based App Service Plan with Powershell

I found this answer for the question
Does anyone know how to create a Consumption app-service-plan with Azure?
When I look at the properties (using https://resources.azure.com/ ) of one I made (by the Gui), I see the following properties;
},
"sku": {
"name": "Y1",
"tier": "Dynamic",
"size": "Y1",
"family": "Y",
"capacity": 0
}
{
"id": "/subscriptions/xxxxxxxxxxxxxxxxxxxxxxxx/resourceGroups/MyResourceGroup/providers/Microsoft.Web/serverfarms/MyHandMadeConsumptionAppServicePlan",
"name": "MyHandMadeConsumptionAppServicePlan",
"type": "Microsoft.Web/serverfarms",
"kind": "functionapp",
"location": "East US",
But if I try (the important part being "-Tier Dynamic")
$plan = New-AzureRmAppServicePlan -Name 'MyPowershellCreatedAppServicePlan' -ResourceGroupName 'MyResourceGroup' -Location 'PickALocation' -Tier Dynamic
I get the exception:
Exception - + Cannot validate argument on parameter 'Tier'. The
argument "Dynamic" does not belong to the set
"Free,Shared,Basic,Standard,Premium,PremiumV2" specified by the
ValidateSet attribute. Supply an argument that is in the set and then
try the command again.
Geeze Louise.
This has been racking me for 3 days. I finally found some help (urls I mention).
It looks like this cannot be accomplished (currently) with New-AzureRmAppServicePlan.
But you can fall back onto the more generic New-AzureRmResource.
The code I finally got to work:
function SafeCreateAppServicePlan(
[Parameter(Mandatory = $true)]
[System.String]$location,
[Parameter(Mandatory = $true)]
[System.String]$resourceGroupName,
[Parameter(Mandatory = $true)]
[String]$appServicePlanName
)
{
Write-Host "SafeCreateAppServicePlan.Parameter:location: $location"
Write-Host "SafeCreateAppServicePlan.Parameter:resourceGroupName: $resourceGroupName"
Write-Host "SafeCreateAppServicePlan.Parameter:appServicePlanName: $appServicePlanName"
$SkuName = "Y1"
$SkuTier = "Dynamic"
$WebAppApiVersion = "2015-08-01"
$fullObject = #{
location = $location
sku = #{
name = $SkuName
tier = $SkuTier
}
}
Write-Host "Ensuring the $appServicePlanName app service plan exists"
$plan = Get-AzureRmAppServicePlan -Name $appServicePlanName -ResourceGroupName $resourceGroupName -ErrorAction SilentlyContinue
if(-not $plan) {
Write-Host "Creating $appServicePlanName app service plan"
New-AzureRmResource -ResourceGroupName $resourceGroupName -ResourceType Microsoft.Web/serverfarms -Name $appServicePlanName -IsFullObject -PropertyObject $fullObject -ApiVersion $WebAppApiVersion -Force
}
else {
Write-Host "$appServicePlanName app service plan already exists"
}
}
The help I got:
https://github.com/davidebbo/AzureWebsitesSamples/blob/master/PowerShell/HelperFunctions.ps1
(search the url above for "SkuName" to find the magic lines.
Please note, this is only part of the overall equation to deploy the INFRASTRUCTURE for azure functions (if you are not using arm templates). When I say the infrastructure, the code below does not deploy the azure-functions themselves, but the below will setup the infrastructure needed to do this.
This guy does a good job of explaining:
https://clouddeveloper.space/2017/10/26/deploy-azure-function-using-powershell/
"clouddeveloper" basically says that for consumption plan azure functions, you need to have a storage account. This lines up with the "info" button you get when you manually add a Function-App via the azure portal.
"A storage account that supports Blob, Queue, and Table Storage is
required. When using a Consumption plan function definitions are
stored in File Storage."
So my full code, that will create an App-Service-Plan, create a Storage Account, create an App-Service (that is azure functions/function app friendly) and match up the storage-account to the app-service is:
function SafeCreateAppServicePlan(
[Parameter(Mandatory = $true)]
[System.String]$location,
[Parameter(Mandatory = $true)]
[System.String]$resourceGroupName,
[Parameter(Mandatory = $true)]
[String]$appServicePlanName
)
{
Write-Host "SafeCreateAppServicePlan.Parameter:location: $location"
Write-Host "SafeCreateAppServicePlan.Parameter:resourceGroupName: $resourceGroupName"
Write-Host "SafeCreateAppServicePlan.Parameter:appServicePlanName: $appServicePlanName"
$SkuName = "Y1"
$SkuTier = "Dynamic"
$WebAppApiVersion = "2015-08-01"
$fullObject = #{
location = $location
sku = #{
name = $SkuName
tier = $SkuTier
}
}
Write-Host "Ensuring the $appServicePlanName app service plan exists"
$plan = Get-AzureRmAppServicePlan -Name $appServicePlanName -ResourceGroupName $resourceGroupName -ErrorAction SilentlyContinue
if(-not $plan) {
Write-Host "Creating $appServicePlanName app service plan"
New-AzureRmResource -ResourceGroupName $resourceGroupName -ResourceType Microsoft.Web/serverfarms -Name $appServicePlanName -IsFullObject -PropertyObject $fullObject -ApiVersion $WebAppApiVersion -Force
}
else {
Write-Host "$appServicePlanName app service plan already exists"
}
}
function SafeCreateAzureFunctionAppService(
[Parameter(Mandatory = $true)]
[System.String]$location,
[Parameter(Mandatory = $true)]
[System.String]$resourceGroupName,
[Parameter(Mandatory = $true)]
[String]$appServicePlanName,
[Parameter(Mandatory = $true)]
[String]$functionAppName
)
{
Write-Host "SafeCreateAzureFunctionAppService.Parameter:location: $location"
Write-Host "SafeCreateAzureFunctionAppService.Parameter:resourceGroupName: $resourceGroupName"
Write-Host "SafeCreateAzureFunctionAppService.Parameter:appServicePlanName: $appServicePlanName"
Write-Host "SafeCreateAzureFunctionAppService.Parameter:functionAppName: $functionAppName"
[String]$planId = ''
$plan = Get-AzureRmAppServicePlan -Name $appServicePlanName -ResourceGroupName $resourceGroupName -ErrorAction SilentlyContinue
if(-not $plan) {
throw [System.ArgumentOutOfRangeException] "Missing App Service Plan. (ResourceGroupName='$resourceGroupName', AppServicePlan.Name = '$appServicePlanName')"
}
else {
Write-Host "START AzureRmAppServicePlan Properties"
$plan.PSObject.Properties
Write-Host "END AzureRmAppServicePlan Properties"
#get the planId, so that can be used as the backing-app-service-plan for this AppService
[String]$planId = $plan.Id
}
#wire up the necessary properties for this AppService
$props = #{
ServerFarmId = $planId
}
$functionAppResource = Get-AzureRmResource | Where-Object { $_.ResourceName -eq $functionAppName -And $_.ResourceType -eq 'Microsoft.Web/Sites' }
if ($functionAppResource -eq $null)
{
New-AzureRmResource -ResourceType 'Microsoft.Web/Sites' -ResourceName $functionAppName -kind 'functionapp' -Location $location -ResourceGroupName $resourceGroupName -Properties $props -force
}
}
function SafeCreateStorageAccountToBackConsumptionAzureFunctions(
[Parameter(Mandatory = $true)]
[System.String]$location,
[Parameter(Mandatory = $true)]
[System.String]$resourceGroupName,
[Parameter(Mandatory = $true)]
[String]$storageAccountName
)
{
Write-Host "SafeCreateStorageAccount.Parameter:location: $location"
Write-Host "SafeCreateStorageAccount.Parameter:resourceGroupName: $resourceGroupName"
Write-Host "SafeCreateStorageAccount.Parameter:storageAccountName: $storageAccountName"
$azureRmStorageAccountGetCheck = Get-AzureRmStorageAccount -ResourceGroupName $resourceGroupName -AccountName $storageAccountName -ErrorAction SilentlyContinue
if(-not $azureRmStorageAccountGetCheck)
{
New-AzureRmStorageAccount -ResourceGroupName $resourceGroupName -AccountName $storageAccountName -Location $location -SkuName 'Standard_LRS'
}
else
{
Write-Host "$storageAccountName storage account already exists"
}
}
function MatchStorageSettingsToAppService(
[Parameter(Mandatory = $true)]
[System.String]$location,
[Parameter(Mandatory = $true)]
[System.String]$resourceGroupName,
[Parameter(Mandatory = $true)]
[String]$functionAppName,
[Parameter(Mandatory = $true)]
[String]$storageAccountName
)
{
Write-Host "MatchStorageSettingsToAppService.Parameter:location: $location"
Write-Host "MatchStorageSettingsToAppService.Parameter:resourceGroupName: $resourceGroupName"
Write-Host "MatchStorageSettingsToAppService.Parameter:functionAppName: $functionAppName"
Write-Host "MatchStorageSettingsToAppService.Parameter:storageAccountName: $storageAccountName"
$keys = Get-AzureRmStorageAccountKey -ResourceGroupName $resourceGroupName -AccountName $storageAccountName
$accountKey = $keys | Where-Object { $_.KeyName -eq "Key1" } | Select Value
$storageAccountConnectionString = 'DefaultEndpointsProtocol=https;AccountName=' + $storageAccountName + ';AccountKey=' + $accountKey.Value
$AppSettings = #{}
$AppSettings = #{'AzureWebJobsDashboard' = $storageAccountConnectionString;
'AzureWebJobsStorage' = $storageAccountConnectionString;
'FUNCTIONS_EXTENSION_VERSION' = '~1';
'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' = $storageAccountConnectionString;
'WEBSITE_CONTENTSHARE' = $storageAccountName;
}
Set-AzureRMWebApp -Name $functionAppName -ResourceGroupName $resourceGroupName -AppSettings $AppSettings
}
I got a few hints from this ARM template as well:
https://github.com/Azure/azure-quickstart-templates/blob/master/101-function-app-create-dynamic/azuredeploy.json
This is not really an answer to your questions, but just some research...
You will notice that in older Microsoft.Web schemas, there was no support for "Dynamic". Instead, we see a list similar to what you see in the error message.
https://github.com/Azure/azure-resource-manager-schemas/blob/master/schemas/2015-08-01/Microsoft.Web.json#L37
In the newer schema (from 2016-09-01), which is what I'm seeing when I view my Comsumption App Service Plan in resources.azure.com, you'll find "Dynamic" as a valid value under computeMode: https://github.com/Azure/azure-resource-manager-schemas/blob/master/schemas/2016-09-01/Microsoft.Web.json#L189
I'm not sure how you can set the computeMode value in PowerShell, since the documentation doesn't show that this is exposed:
https://learn.microsoft.com/en-us/powershell/module/azurerm.websites/new-azurermappserviceplan?view=azurermps-5.0.0

Resources