wiki.getshifting.com

--- Sjoerd Hooft's InFormation Technology ---

User Tools

Site Tools


start

SHIFT-WIKI - Sjoerd Hooft's InFormation Technology

This WIKI is my personal documentation blog. Please enjoy it and feel free to reach out through blue sky if you have a question, remark, improvement or observation.


Booting From VHD

Summary: How to boot directly from a VHD, a virtual hard disk.
Date: Around 2019
Refactor: 21 February 2025: Checked links and formatting.

This page is the first in a small series about Windows Server 2012 and Hyper-V 3. The end goal is to have a functional Hyper-V 3 server on my own laptop. To have that working I will use a technology that will allow me to boot directly from a VHD.

What's a VHD

As stated at wikipedia:

VHD (Virtual Hard Disk) is a file format which represents a virtual hard disk drive (HDD). It may contain what is found on a physical HDD, such as disk partitions and a file system, which in turn can contain files and folders. It is typically used as the hard disk of a virtual machine. The format was created by Connectix for Connectix Virtual PC product, which was later acquired by Microsoft in 2003, for what is now known as Microsoft Virtual PC.

It is now the standard virtual hard disk format used in Hyper-V for virtual machines. This is in more depth explained on the Microsoft site in this article:

The Virtual Hard Disk (VHD) format is a publicly-available image format specification that allows encapsulation of the hard disk into an individual file for use by the operating system as a virtual disk in all the same ways physical hard disks are used. These virtual disks are capable of hosting native file systems (NTFS, FAT, exFAT, and UDFS) while supporting standard disk and file operations. VHD API support allows management of the virtual disks. Virtual disks created with the VHD API can function as boot disks. An example of how VHD files are used is the Hyper-V feature in Windows 7, Windows Server 2008, Virtual Server, and Windows Virtual PC. These products use the VHD API to contain the Windows operating system image utilized by a virtual machine as its system boot disk.

An important feature I'll use and explore in this article is the possibility to boot directly from a VHD file. This means you don't have to install a hypervisor to run the operating system installed in the VHD. It's called “Native VHD Boot” and is supported by Windows 7 Enterprise and Ultimated Editions, as well as by Windows 8, Windows Server 2008 R2 and Windows Server 2012.

Configuring Native VHD Boot

Since my laptop is running Windows 8 this article is written for Windows 8. However, given the procedure I expect it to be the same for other Windows editions that support this.

VHD Used:

  • 9200.16384.amd64fre.win8_rtm.120725-1247_server_serverdatacentereval_en-us.vhd

Because the VHD will appear as a disk after you've boot from it and your current OS disk will appear as a different disk it is better to be really precise about your folder structure. It is best to copy the VHD to a new folder structure from the root of a current disk. If you have different disks or real partitions your data disk is fine. If you have a virtual partition it's better to start from the C root, which is also the way to go if you have only one disk.

An example of this folder structure would be:

C:\BootVHDs\WIN2012\

After you've copied the VHD to this directory follow the following steps:

Mount VHD

On Windows 8 there are two ways to get there (among others):

  • Right click the VHD and select mount

Or start Disk Management:

  • By typing “diskmgmt.msc” in a command prompt or Run Box (works on all supported Windows Editions)

or

  • By press Windows Key + X and select Disk Management
    • Note that the X stands for eXpert Tools

In disk management click on the Actions Menu and select “Attach VHD”. If you do not have this option your OS does not support this feature:

bootingfromvhd01.jpg


In the “Attach Virtual Hard Disk” windows that appears browse to the created folder and select the VHD, and click OK.

A new 40 GB disk will now appear in Disk Management:

bootingfromvhd02.jpg


Booting From the Mounted VHD

Now is the time to setup the system so you van boot from the mounted VHD. Open a command prompt as administrator. On Windows 8, the Windows Key + X will help you out again, you can select “Command Prompt (Admin)”. On Windows 7, type in CMD in the Start menu search and right click the application so you can select “Run as Administrator”.

In the command prompt, type BCDBOOT D:\WINDOWS:

bootingfromvhd03.jpg


Check the previous command by starting MSCONFIG from the same command prompt and check the boot options:

bootingfromvhd04.jpg


Note that the default OS is indeed the Windows Server 2012 installation from the D-drive. Also note I changed the timeout to 15 seconds. The default is 30.

Now you can reboot the system. Since the installation from the VHD is like a sys prepped image it will start to configure the hardware. It will also ask for a new administrator password so you van logon to the system.

Note that reviewing if all required hardware was properly installed should be your first step. Because the installation was sys prepped this is your first chance to check if everything works properly.

2025/06/01 11:59

Bicep Module for Azure App Configuration

Summary: This is a bicep module that deploys a dashboard to monitor Azure App Configurations.
Date: 9 January 2025

Check here for a previous post for modules on deploying and configuring an Azure App Configuration Store.

Bicep Module App Configuration Dashboard

The module below is the bicep module to deploy a dashboard to monitor an Azure App Configuration. The dashboard will display the following panels:

  • A markdown panel with useful information and links to the Azure App Configuration documentation and monitoring.
  • A panel showing an overview of the App Configurations in the subscription.
  • A panel showing the usage of the App Configuration.
  • A panel showing the most recent delete key-value operations and by who in the App Configuration.
  • A panel showing the HTTP error codes in the App Configuration.
/*
DESCRIPTION
  Deploys a dashboard for monitoring app configuration
 
LINKS:
  - Resource: https://learn.microsoft.com/en-us/azure/templates/microsoft.portal/dashboards
*/
 
targetScope = 'resourceGroup'
 
// parameters
param location string
param environment string
param appConfigurationName string
param appConfigurationRg string
 
// existing
resource configStore 'Microsoft.AppConfiguration/configurationStores@2023-03-01' existing = {
  scope: resourceGroup(appConfigurationRg)
  name: appConfigurationName
}
 
// defined parameters
param dashboardName string = 'AppConfig'
 
resource dashboard 'Microsoft.Portal/dashboards@2019-01-01-preview' = {
  name: 'dashboard-${environment}-${dashboardName}'
  location: location
  tags: {
    'hidden-title': 'dashboard-${environment}-${dashboardName}'
  }
  properties: {
    lenses: {
      '0': {
        order: 0
        parts: {
          '0': {
            position: {
              x: 0
              y: 0
              colSpan: 12
              rowSpan: 2
            }
            metadata: {
              inputs: []
              type: 'Extension/HubsExtension/PartType/MarkdownPart'
              settings: {
                content: {
                  content: '__Useful information__\n\n<a href=\'https://learn.microsoft.com/en-us/azure/azure-app-configuration/\' target=\'_blank\'>Azure App Configuration documentation</a><br>\n<a href=\'https://learn.microsoft.com/en-us/azure/azure-app-configuration/monitor-app-configuration?tabs=portal\' target=\'_blank\'>Monitoring App Configuration</a>\n\n'
                  title: 'App Configuration Dashboard'
                  subtitle: 'A managed service that helps developers centralize their application and feature settings simply and securely'
                  markdownSource: 1
                  markdownUri: ''
                }
              }
            }
          }
          '1': {
            position: {
              x: 0
              y: 2
              colSpan: 12
              rowSpan: 4
            }
            metadata: {
              inputs: [
                {
                  name: 'partTitle'
                  value: 'Query 1'
                  isOptional: true
                }
                {
                  name: 'query'
                  value: 'resources\r\n| where type == "microsoft.appconfiguration/configurationstores"\r\n| extend sku = sku.name\r\n| extend creationDate = todatetime(properties.creationDate)\r\n| project name, resourceGroup, creationDate, location, sku, subscriptionId'
                  isOptional: true
                }
                {
                  name: 'chartType'
                  isOptional: true
                }
                {
                  name: 'isShared'
                  isOptional: true
                }
                {
                  name: 'queryId'
                  value: ''
                  isOptional: true
                }
                {
                  name: 'formatResults'
                  value: true
                  isOptional: true
                }
                {
                  name: 'queryScope'
                  value: {
                    scope: 0
                    values: []
                  }
                  isOptional: true
                }
              ]
              type: 'Extension/HubsExtension/PartType/ArgQueryGridTile'
              settings: {}
              partHeader: {
                title: 'Overview App Configurations'
                subtitle: 'Resource Graph Explorer'
              }
            }
          }
          '2': {
            position: {
              x: 0
              y: 6
              colSpan: 12
              rowSpan: 4
            }
            metadata: {
              inputs: [
                {
                  name: 'resourceTypeMode'
                  isOptional: true
                }
                {
                  name: 'ComponentId'
                  isOptional: true
                }
                {
                  name: 'Scope'
                  value: {
                    resourceIds: [
                      configStore.id
                    ]
                  }
                  isOptional: true
                }
                {
                  name: 'PartId'
                  value: '1158470d-5c03-431a-859f-6309b00a054e'
                  isOptional: true
                }
                {
                  name: 'Version'
                  value: '2.0'
                  isOptional: true
                }
                {
                  name: 'TimeRange'
                  isOptional: true
                }
                {
                  name: 'DashboardId'
                  isOptional: true
                }
                {
                  name: 'DraftRequestParameters'
                  value: {
                    scope: 'hierarchy'
                  }
                  isOptional: true
                }
                {
                  name: 'Query'
                  value: 'AACHttpRequest\n    | where TimeGenerated > ago(14d)\n    | extend Day = startofday(TimeGenerated)\n    | summarize requestcount=sum(HitCount) by Day\n    | render columnchart \n'
                  isOptional: true
                }
                {
                  name: 'ControlType'
                  value: 'FrameControlChart'
                  isOptional: true
                }
                {
                  name: 'SpecificChart'
                  value: 'StackedColumn'
                  isOptional: true
                }
                {
                  name: 'PartTitle'
                  value: 'Analytics'
                  isOptional: true
                }
                {
                  name: 'PartSubTitle'
                  value: appConfigurationName
                  isOptional: true
                }
                {
                  name: 'Dimensions'
                  value: {
                    xAxis: {
                      name: 'Day'
                      type: 'datetime'
                    }
                    yAxis: [
                      {
                        name: 'requestcount'
                        type: 'long'
                      }
                    ]
                    splitBy: []
                    aggregation: 'Sum'
                  }
                  isOptional: true
                }
                {
                  name: 'LegendOptions'
                  value: {
                    isEnabled: true
                    position: 'Bottom'
                  }
                  isOptional: true
                }
                {
                  name: 'IsQueryContainTimeRange'
                  value: true
                  isOptional: true
                }
              ]
              type: 'Extension/Microsoft_OperationsManagementSuite_Workspace/PartType/LogsDashboardPart'
              settings: {
                content: {
                  Query: 'AACHttpRequest\n    | extend Day = startofday(TimeGenerated)\n    | summarize requestcount=sum(HitCount) by Day\n    | render columnchart \n\n'
                  IsQueryContainTimeRange: false
                }
              }
              filters: {
                MsPortalFx_TimeRange: {
                  model: {
                    format: 'local'
                    granularity: 'auto'
                    relative: '30d'
                  }
                }
              }
              partHeader: {
                title: 'Usage'
                subtitle: appConfigurationName
              }
            }
          }
          '3': {
            position: {
              x: 0
              y: 10
              colSpan: 12
              rowSpan: 8
            }
            metadata: {
              inputs: [
                {
                  name: 'resourceTypeMode'
                  isOptional: true
                }
                {
                  name: 'ComponentId'
                  isOptional: true
                }
                {
                  name: 'Scope'
                  value: {
                    resourceIds: [
                      configStore.id
                    ]
                  }
                  isOptional: true
                }
                {
                  name: 'PartId'
                  value: '12373b3a-2894-4b2b-ae71-6ea9a910aa19'
                  isOptional: true
                }
                {
                  name: 'Version'
                  value: '2.0'
                  isOptional: true
                }
                {
                  name: 'TimeRange'
                  value: 'P1D'
                  isOptional: true
                }
                {
                  name: 'DashboardId'
                  isOptional: true
                }
                {
                  name: 'DraftRequestParameters'
                  value: {
                    scope: 'hierarchy'
                  }
                  isOptional: true
                }
                {
                  name: 'Query'
                  value: '// Most recent delete key-value operations \n// List the most recent deleting key-value operations in App Config data plane. \n// This query helps retrieve the most recent 10 audit logs for deleting key-value operations in App Configuration data plane.\nAACAudit\n| where EventCategory == "ApplicationManagement" // and OperationName == "delete-keyvalue"\n| where Status == "Finished"\n| extend Target = tostring(split(TargetResource.TargetResourceName, "/")[4])\n| extend CallerType = iif( isempty(CallerIdentity[3])\n                        , CallerIdentity[0].callerIdentityType\n                        , CallerIdentity[3].callerIdentityType\n                        )\n| extend CallerId = iif( isempty( CallerIdentity[3])\n                        , CallerIdentity[0].callerIdentity\n                        , CallerIdentity[3].callerIdentity\n                        )\n| sort by TimeGenerated desc\n| project TimeGenerated, CallerType, CallerId, Target, OperationName\n\n'
                  isOptional: true
                }
                {
                  name: 'ControlType'
                  value: 'AnalyticsGrid'
                  isOptional: true
                }
                {
                  name: 'SpecificChart'
                  isOptional: true
                }
                {
                  name: 'PartTitle'
                  value: 'Analytics'
                  isOptional: true
                }
                {
                  name: 'PartSubTitle'
                  value: appConfigurationName
                  isOptional: true
                }
                {
                  name: 'Dimensions'
                  isOptional: true
                }
                {
                  name: 'LegendOptions'
                  isOptional: true
                }
                {
                  name: 'IsQueryContainTimeRange'
                  value: false
                  isOptional: true
                }
              ]
              type: 'Extension/Microsoft_OperationsManagementSuite_Workspace/PartType/LogsDashboardPart'
              settings: {
                content: {
                  GridColumnsWidth: {
                    CallerId: '276px'
                    Target: '336px'
                    CallerType: '128px'
                  }
                }
              }
              partHeader: {
                title: 'Last Changes'
                subtitle: appConfigurationName
              }
            }
          }
          '4': {
            position: {
              x: 0
              y: 18
              colSpan: 12
              rowSpan: 4
            }
            metadata: {
              inputs: [
                {
                  name: 'resourceTypeMode'
                  isOptional: true
                }
                {
                  name: 'ComponentId'
                  isOptional: true
                }
                {
                  name: 'Scope'
                  value: {
                    resourceIds: [
                      configStore.id
                    ]
                  }
                  isOptional: true
                }
                {
                  name: 'PartId'
                  value: '9e8320bf-09ea-4a19-949d-d5c0a30ad2b0'
                  isOptional: true
                }
                {
                  name: 'Version'
                  value: '2.0'
                  isOptional: true
                }
                {
                  name: 'TimeRange'
                  value: 'P7D'
                  isOptional: true
                }
                {
                  name: 'DashboardId'
                  isOptional: true
                }
                {
                  name: 'DraftRequestParameters'
                  value: {
                    scope: 'hierarchy'
                  }
                  isOptional: true
                }
                {
                  name: 'Query'
                  value: 'AACHttpRequest\n| where StatusCode != 200\n| summarize ErrorCount=count() by bin(TimeGenerated, 1d), StatusCode\n| extend StatusCode = tostring(StatusCode)\n| render columnchart with (kind=stacked, series=StatusCode)\n\n'
                  isOptional: true
                }
                {
                  name: 'ControlType'
                  value: 'FrameControlChart'
                  isOptional: true
                }
                {
                  name: 'SpecificChart'
                  value: 'StackedColumn'
                  isOptional: true
                }
                {
                  name: 'PartTitle'
                  value: 'Analytics'
                  isOptional: true
                }
                {
                  name: 'PartSubTitle'
                  value: appConfigurationName
                  isOptional: true
                }
                {
                  name: 'Dimensions'
                  value: {
                    xAxis: {
                      name: 'TimeGenerated'
                      type: 'datetime'
                    }
                    yAxis: [
                      {
                        name: 'ErrorCount'
                        type: 'long'
                      }
                    ]
                    splitBy: [
                      {
                        name: 'StatusCode'
                        type: 'string'
                      }
                    ]
                    aggregation: 'Sum'
                  }
                  isOptional: true
                }
                {
                  name: 'LegendOptions'
                  value: {
                    isEnabled: true
                    position: 'Bottom'
                  }
                  isOptional: true
                }
                {
                  name: 'IsQueryContainTimeRange'
                  value: false
                  isOptional: true
                }
              ]
              type: 'Extension/Microsoft_OperationsManagementSuite_Workspace/PartType/LogsDashboardPart'
              settings: {}
              filters: {
                MsPortalFx_TimeRange: {
                  model: {
                    format: 'local'
                    granularity: 'auto'
                    relative: '7d'
                  }
                }
              }
              partHeader: {
                title: 'HTTP error codes'
                subtitle: appConfigurationName
              }
            }
          }
        }
      }
    }
    metadata: {
      model: {
        timeRange: {
          value: {
            relative: {
              duration: 24
              timeUnit: 1
            }
          }
          type: 'MsPortalFx.Composition.Configuration.ValueTypes.TimeRange'
        }
        filterLocale: {
          value: 'en-us'
        }
        filters: {
          value: {
            MsPortalFx_TimeRange: {
              model: {
                format: 'local'
                granularity: 'auto'
                relative: '24h'
              }
              displayCache: {
                name: 'Local Time'
                value: 'Past 24 hours'
              }
            }
          }
        }
      }
    }
  }
}
2025/06/01 11:59

Bicep Module for Azure App Configuration

Summary: This is a bicep module that I use to deploy Azure App Configurations, a cloud service I use to store variables.
Date: 4 January 2025
Refactor: 9 January 2025: Added deployment information and an additional module to assign roles.

Read the post to learn more about Azure App Configuration and:

  • How to deploy an Azure App Configuration Store using a Bicep module
    • How to set the name and sku based on the environment parameter
  • How to deploy a private endpoint for an Azure App Configuration Store using a Bicep module
  • How to set permissions on the Azure App Configuration Store using a Bicep module
    • Permissions are set on an array of teams which are defined in a separate parameter file
Check here for an additional post for a dashboard to monitor the Azure App Configuration Store.

What is Azure App Configuration

Azure App Configuration is a service that allows you to centralize your application settings and feature flags. It provides a way to manage the settings of your application in a single place, and it can be used to store key-value pairs that can be accessed by your application at runtime. I found it very useful as it also allows you to import variables into your Azure DevOps pipeline, allowing you to also manage your pipeline variables in a single place.

Important to Know about Azure App Configuration

Some settings are not available for the free sku. For example, you can't configure a replica and it's also not possible to enable a private endpoint. The free sku is also limited in the amount of requests you can make. Usually I recommend to start with the free sku and then upgrade if needed. Also, when using Infrastructure as Code such as Bicep or Terraform, I would deploy the free SKU as a test deployment and use the standard edition for your production environment. Note that you are only allowed to deploy one free SKU per subscription.

Bicep Module App Configuration

The module below deploys an Azure App Configuration Store. Notice that some settings are dependent on the SKU. For example, the replica is only deployed when the SKU is set to standard. The module also deploys a diagnostic setting to log all logs and audit logs to a Log Analytics Workspace.

/*
DESCRIPTION
  Deploys a App Configuration Store
 
LINKS
  Resource: https://learn.microsoft.com/en-us/azure/templates/microsoft.appconfiguration/configurationstores
  Resource replica: https://learn.microsoft.com/en-us/azure/templates/microsoft.appconfiguration/configurationstores/replicas
  Some settings are not available for the free sku: https://azure.microsoft.com/en-us/pricing/details/app-configuration/
 
NOTES
  If your delete the replica you have to pick a new name as the old one is purge protected.
  The networking setup for a app configuration is set by default as:
    "With a private endpoint, public network access will be automatically disabled. If there is no private endpoint present,
    public network access is automatically enabled."
*/
*/
 
// input parameters
param location string
param appConfigurationName string
param lawSubscriptionId string
param lawResourceGroup string
param lawName string
@description('Specifies the SKU of the app configuration store.')
param skuName string
 
// deploy app configuration store
resource configStore 'Microsoft.AppConfiguration/configurationStores@2023-03-01' = {
  name: appConfigurationName
  location: location
  sku: {
    name: skuName
  }
  properties: (skuName == 'standard') ? {
    disableLocalAuth: true
    softDeleteRetentionInDays: 7
    enablePurgeProtection: true
  } : {
    disableLocalAuth: true
  }
}
 
// replica
resource configStoreReplica 'Microsoft.AppConfiguration/configurationStores/replicas@2023-08-01-preview' = if (skuName == 'standard') {
  parent: configStore
  name: 'eunorthreplica'
  location: 'northeurope'
}
 
resource diagnosticLogs 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  scope: configStore
  name: 'diag-${appConfigurationName}'
  properties: {
    workspaceId: resourceId(lawSubscriptionId, lawResourceGroup, 'Microsoft.OperationalInsights/workspaces', lawName)
    logs: [
      {
        categoryGroup: 'alllogs'
        enabled: true
      }
      {
        categoryGroup: 'audit'
        enabled: true
      }
    ]
    metrics: [
      {
        category: 'AllMetrics'
        enabled: true
      }
    ]
  }
}
 
output appConfigurationName string = configStore.name

Deploy Example

The module above can be deployed using the code below. Note that both the App Configuration name and the sku are set based on the environment:

targetScope = 'resourceGroup'
 
// parameters
param location string = resourceGroup().location
param environment string
 
// log analytics workspace
param lawSubId string
param lawRg string
param lawName string
 
// App Configuration parameters
var appConfigurationName = {
  'dev': 'appcs-dev'
  'prd': 'appcs-prd'
}[environment]
var appConfigSkuName = {
  'dev': 'free'
  'prd': 'standard'
}[environment]
 
// deploy app configuration
module appConfig '../modules/appconfiguration.bicep' = {
  name: 'appc-${resourceGroupName}-${deploymentSuffix}'
  params: {
    location: location
    appConfigurationName: appConfigurationName
    skuName: appConfigSkuName
    lawSubscriptionId: lawSubId
    lawResourceGroup: lawRg
    lawName: lawName
  }
}

Bicep Module App Configuration Private Endpoint

The module below deploys a private endpoint for an Azure App Configuration Store. The private endpoint allows you to access the App Configuration Store from your virtual network. Note that this module requires the vnet, subnet, and app configuration to be deployed before deploying the private endpoint. The module also deploys a DNS record so the private endpoint can be resolved. Note that the private DNS zone also already must be deployed.

/*
DESCRIPTION
  Deploys a app configuration private endpoint
 
LINKS
  Resource Private Endpoint: https://learn.microsoft.com/en-us/azure/templates/microsoft.network/privateendpoints
  Resource Private DNS Zonegroup: https://learn.microsoft.com/en-us/azure/templates/microsoft.network/privateendpoints/privatednszonegroups
  GroupId private links: https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview
 
NOTES
  DNS Zone forwarding: https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns
 
*/
 
// input parameters
param location string
// app configuration
param appConfigurationName string
// network - the virtual network where the pep/nic comes
@description('The subscription id that hosts the virtual network for the pep')
param vnetSubscriptionId string
@description('The resource group that hosts the virtual network for the pep')
param vNetRg string
@description('The name of the virtual network that hosts the private endpoints subnet')
param vnetName string
@description('The name of the private endpoints subnet')
param subnetName string
// dns
param subIdDNS string
param rgDNS string
 
// defined parameters
param privateLinkZone string = 'privatelink.azconfig.io'
 
// variables
var pepName = 'pep-${appConfigurationName}'
var nicName = 'nic-${pepName}'
var pepConnectionName = 'pep-con-${appConfigurationName}'
 
// existing
resource vNet 'Microsoft.Network/virtualNetworks@2020-11-01' existing = {
  scope: resourceGroup(vnetSubscriptionId, vNetRg)
  name: vnetName
}
resource subnetPrivateEndpoints 'Microsoft.Network/virtualNetworks/subnets@2022-09-01' existing = {
  parent: vNet
  name: subnetName
}
resource appConfig 'Microsoft.AppConfiguration/configurationStores@2023-03-01' existing = {
  name: appConfigurationName
}
 
// deploy private endpoint
resource saPrivateEndpoint 'Microsoft.Network/privateEndpoints@2022-01-01' = {
  name: pepName
  location: location
  properties: {
    privateLinkServiceConnections: [
      {
        name: pepConnectionName
        properties: {
          groupIds: [
            'configurationStores'
          ]
          privateLinkServiceId: appConfig.id
        }
      }
    ]
    customNetworkInterfaceName: nicName
    subnet: {
      id: subnetPrivateEndpoints.id
    }
  }
}
 
// deploy dns record
resource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-09-01' = {
  parent: saPrivateEndpoint
  name: 'dnsgroup'
  properties: {
    privateDnsZoneConfigs: [
      {
        name: 'config'
        properties: {
          privateDnsZoneId: resourceId(subIdDNS, rgDNS, 'Microsoft.Network/privateDnsZones', privateLinkZone)
        }
      }
    ]
  }
}

Bicep Module App Configuration Role Assignment

The module below assigns a role to a user, group, or identity on an Azure App Configuration Store. The module requires the app configuration store to be deployed before deploying the role assignment.

/*
DESCRIPTION
  Assigns a role to a user, group or identity on an azure app configuration store
 
LINKS
  Resource: https://learn.microsoft.com/en-us/azure/templates/microsoft.authorization/roleassignments
*/
 
targetScope = 'resourceGroup'
 
// input parameters
param appConfigurationName string
@allowed([
  'App Configuration Data Owner'
  'App Configuration Data Reader'
  'Contributor'
])
param roleName string
param principalId string
@allowed([
  'Device'
  'ForeignGroup'
  'Group'
  'ServicePrincipal'
  'User'
])
param principalType string
 
// variables
var roleIds = {
  'App Configuration Data Owner': resourceId('Microsoft.Authorization/roleAssignments', '5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b')
  'App Configuration Data Reader': resourceId('Microsoft.Authorization/roleAssignments', '516239f1-63e1-4d78-a4de-a74fb236a071')
  'Contributor': resourceId('Microsoft.Authorization/roleAssignments', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
}
 
// exists
resource appConfig 'Microsoft.AppConfiguration/configurationStores@2023-03-01' existing = {
  name: appConfigurationName
}
 
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
  scope: appConfig
  name: guid(roleIds[roleName], principalId, resourceId('Microsoft.AppConfiguration/configurationStores', appConfigurationName))
  properties: {
    roleDefinitionId: roleIds[roleName]
    principalId: principalId
    principalType: principalType
  }
}

Deploy Example

The following code is used to deploy permissions to the app configuration store. Note that a uniqueString function is used on the devTeam.teamName to ensure that the role assignment name is not too long as the maximum length is 64 characters, and that the teamName is coming from an array which is defined in a separate parameter file:

// scrum teams
param scrumTeams array
// permissions
param AzureADEnterpriseAppId string
 
// Permissions for a set of teams
module roleAssignmentTeamAccess '../modules/role-assignment-appconfiguration.bicep' = [for devTeam in devTeams: {
  name: 'rbac-team-${uniqueString(devTeam.teamName)}'
  dependsOn: [ appConfig ]
  params: {
    appConfigurationName: appConfig.outputs.appConfigurationName
    roleName: 'App Configuration Data Reader'
    principalId: devTeam.objectId
    principalType: 'Group'
  }
}]
 
// Permissions for a service principal
module rolePipeline '../modules/role-assignment-appconfiguration.bicep' = {
  name: 'rbac-sp-${appConfigurationName}'
  dependsOn: [ appConfig ]
  params: {
    appConfigurationName: appConfig.outputs.appConfigurationName
    roleName: 'App Configuration Data Owner'
    principalId: AzureADEnterpriseAppId
    principalType: 'ServicePrincipal'
  }
}

This is the parameters file for the devTeams parameter above:

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        // these are the names and objectIds of the dev teams
        "devTeams": {
            "value": [
                {
                    "teamName": "Dev team frontend",
                    "objectId": "4e3b4ef3-4495-4cdc-a981-f009ad6f3ebf"
                },
                {
                    "teamName": "Dev team backend",
                    "objectId": "fecefe42-46bf-4ff7-af8a-cc0b7bf6ea85"
                },
                {
                    "teamName": "Dev team api",
                    "objectId": "a704c4a1-9397-43cd-a37f-3b210bf6eb04"
                }
            ]
        }
    }
}

As such, to deploy such a bicep file using the azure cli you would use the following command:

az deployment group create \
  --resource-group myResourceGroup \
  --name "appconfig-permissions" \
  --template-file appconfiguration.bicep \
  --parameters AzureADEntAppId="d60692dc-666c-410f-bb14-bb045fd36dd9" \
  --parameters devteams-parameterfile.jsonc
2025/06/01 11:59

AzureRM PowerShell

Summary: How to use PowerShell to manage Azure Resources.
Date: Around 2022
Refactor: 20 February 2025: Checked links and formatting.

Note that this page still uses the old AzureRM cmdlets. This page is now only meant as an archive and window to the past.

Azure PowerShell can be used to manage Azure Resources, and because it provides more functionality then the web interface you really can't without. This tutorial shows you how to install and use it on a Windows 10 professional installation. Note that this already has a PowerShell included.

Note: Azure Powershell, as Azure as a whole has two versions, each for managing a different Azure version. The normal Azure PowerShell cmdlets are for managing the classic portal, https://manage.windowsazure.com , the AzureRM PowerShell cmdlets are for managing the Azure Resource Manager on https://portal.azure.com .

Download the Installer

Download the installer from http://aka.ms/webpi-azps and run it keeping all the defaults.

Connect to Your Azure Account

Use the Login-AzureRmAccount cmdlet to logon to your account. The cmdlet will ask you for your credentials but if you don't want to provide them every time you cab store them in a variable:

PS C:\Users\sjoer> Login-AzureRmAccount
WARNING: Microsoft Azure PowerShell collects data about how users use PowerShell cmdlets and some problems they
encounter.  Microsoft uses this information to improve our PowerShell cmdlets.  Participation is voluntary and when you
 choose to participate your device automatically sends information to Microsoft about how you use Azure PowerShell.
 
If you choose to participate, you can stop at any time by using Azure PowerShell as follows:
1. Use the Disable-AzureDataCollection cmdlet to turn the feature Off. The cmdlet can be found in the
AzureResourceManager module
To disable data collection: PS > Disable-AzureDataCollection
 
If you choose to not participate, you can enable at any time by using Azure PowerShell as follows:
1. Use the Enable-AzureDataCollection cmdlet to turn the feature On. The cmdlet can be found in the
AzureResourceManager module
To enable data collection: PS > Enable-AzureDataCollection
 
Select Y to enable data collection [Y/N]:
WARNING: You choose not to participate in Microsoft Azure PowerShell data collection.
WARNING: The setting profile has been saved to the following path 'C:\Users\sjoer\AppData\Roaming\Windows Azure Powershell\AzureDataCollectionProfile.json'.
 
 
Environment               : AzureCloud
Account                   : sjoerd.hooft@xxxxx.com
TenantId                  : wwwwwwww-2222-dddd-4444-jjjjjjjjjjjjjj
SubscriptionId            : xxxxxxxx-yyyy-zzzz-wwww-aaaaaaaaaaaa
SubscriptionName          : Visual Studio Enterprise
CurrentStorageAccount     :
Note that the first time you use this you might be asked if you agree that Microsoft collects data on you.

View Installed Azure PowerShell Modules

PS C:\Users\sjoer> get-module -listavailable Azure*
 
 
    Directory: C:\Program Files (x86)\Microsoft SDKs\Azure\PowerShell\ResourceManager\AzureResourceManager
 
 
ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Manifest   0.0.3      AzureRM.AnalysisServices            {Resume-AzureRmAnalysisServicesServer, Suspend-AzureRmAnal...
Manifest   3.3.0      AzureRM.ApiManagement               {Add-AzureRmApiManagementRegion, Get-AzureRmApiManagementS...
Manifest   2.5.0      AzureRM.Automation                  {Get-AzureRMAutomationHybridWorkerGroup, Get-AzureRmAutoma...
Manifest   2.5.0      AzureRM.Backup                      {Backup-AzureRmBackupItem, Enable-AzureRmBackupContainerRe...
Manifest   2.5.0      AzureRM.Batch                       {Remove-AzureRmBatchAccount, Get-AzureRmBatchAccount, Get-...
Manifest   2.5.0      AzureRM.Cdn                         {Get-AzureRmCdnProfile, Get-AzureRmCdnProfileSsoUrl, New-A...
Manifest   0.4.3      AzureRM.CognitiveServices           {Get-AzureRmCognitiveServicesAccount, Get-AzureRmCognitive...
Manifest   2.5.0      AzureRM.Compute                     {Remove-AzureRmAvailabilitySet, Get-AzureRmAvailabilitySet...
Manifest   2.5.0      AzureRM.DataFactories               {Remove-AzureRmDataFactory, Get-AzureRmDataFactoryRun, Get...
Manifest   2.5.0      AzureRM.DataLakeAnalytics           {Get-AzureRmDataLakeAnalyticsDataSource, New-AzureRmDataLa...
Manifest   3.3.0      AzureRM.DataLakeStore               {Get-AzureRmDataLakeStoreTrustedIdProvider, Remove-AzureRm...
Manifest   2.5.0      AzureRM.DevTestLabs                 {Get-AzureRmDtlAllowedVMSizesPolicy, Get-AzureRmDtlAutoShu...
Manifest   2.5.0      AzureRM.Dns                         {Get-AzureRmDnsRecordSet, New-AzureRmDnsRecordConfig, Remo...
Manifest   0.0.2      AzureRM.EventHub                    {New-AzureRmEventHubKey, Get-AzureRmEventHubNamespace, Get...
Manifest   2.5.0      AzureRM.HDInsight                   {Get-AzureRmHDInsightJob, New-AzureRmHDInsightSqoopJobDefi...
Manifest   2.5.0      AzureRM.Insights                    {Get-AzureRmUsage, Get-AzureRmMetricDefinition, Get-AzureR...
Manifest   1.1.0      AzureRM.IotHub                      {Add-AzureRmIotHubKey, Get-AzureRmIotHubEventHubConsumerGr...
Manifest   2.5.0      AzureRM.KeyVault                    {Add-AzureKeyVaultCertificate, Set-AzureKeyVaultCertificat...
Manifest   2.5.0      AzureRM.LogicApp                    {Get-AzureRmIntegrationAccountAgreement, Get-AzureRmIntegr...
Manifest   0.11.3     AzureRM.MachineLearning             {Move-AzureRmMlCommitmentAssociation, Get-AzureRmMlCommitm...
Manifest   0.3.3      AzureRM.Media                       {Sync-AzureRmMediaServiceStorageKeys, Set-AzureRmMediaServ...
Manifest   3.4.0      AzureRM.Network                     {Add-AzureRmApplicationGatewayAuthenticationCertificate, G...
Manifest   2.5.0      AzureRM.NotificationHubs            {Get-AzureRmNotificationHub, Get-AzureRmNotificationHubAut...
Manifest   2.5.0      AzureRM.OperationalInsights         {New-AzureRmOperationalInsightsAzureActivityLogDataSource,...
Manifest   2.5.0      AzureRM.PowerBIEmbedded             {Remove-AzureRmPowerBIWorkspaceCollection, Get-AzureRmPowe...
Manifest   2.5.0      AzureRM.Profile                     {Disable-AzureRmDataCollection, Enable-AzureRmDataCollecti...
Manifest   2.5.0      AzureRM.RecoveryServices            {Get-AzureRmRecoveryServicesBackupProperties, Get-AzureRmR...
Manifest   2.5.0      AzureRM.RecoveryServices.Backup     {Backup-AzureRmRecoveryServicesBackupItem, Get-AzureRmReco...
Manifest   2.5.0      AzureRM.RedisCache                  {Remove-AzureRmRedisCachePatchSchedule, New-AzureRmRedisCa...
Manifest   3.5.0      AzureRM.Resources                   {Get-AzureRmProviderOperation, Remove-AzureRmRoleAssignmen...
Manifest   0.11.3     AzureRM.Scheduler                   {Disable-AzureRmSchedulerJobCollection, Enable-AzureRmSche...
Manifest   2.5.0      AzureRM.ServerManagement            {Invoke-AzureRmServerManagementPowerShellCommand, Get-Azur...
Manifest   0.0        AzureRM.ServiceBus
Manifest   3.4.0      AzureRM.SiteRecovery                {Get-AzureRmSiteRecoveryFabric, New-AzureRmSiteRecoveryFab...
Manifest   2.5.0      AzureRM.Sql                         {Get-AzureRmSqlDatabaseTransparentDataEncryption, Get-Azur...
Manifest   2.5.0      AzureRM.Storage                     {Get-AzureRmStorageAccount, Get-AzureRmStorageAccountKey, ...
Manifest   2.5.0      AzureRM.StreamAnalytics             {Get-AzureRmStreamAnalyticsFunction, Get-AzureRmStreamAnal...
Manifest   2.5.0      AzureRM.Tags                        {Remove-AzureRmTag, Get-AzureRmTag, New-AzureRmTag}
Manifest   2.5.0      AzureRM.TrafficManager              {Disable-AzureRmTrafficManagerEndpoint, Enable-AzureRmTraf...
Manifest   2.5.0      AzureRM.UsageAggregates             Get-UsageAggregates
Manifest   2.5.0      AzureRM.Websites                    {Get-AzureRmAppServicePlan, Set-AzureRmAppServicePlan, New...
 
 
    Directory: C:\Program Files (x86)\Microsoft SDKs\Azure\PowerShell\ServiceManagement
 
 
ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Manifest   3.4.0      Azure                               {Get-AzureAutomationCertificate, Get-AzureAutomationConnec...
 
 
    Directory: C:\Program Files (x86)\Microsoft SDKs\Azure\PowerShell\Storage
 
 
ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Manifest   2.5.0      Azure.Storage                       {Get-AzureStorageTable, New-AzureStorageTableSASToken, New...

Commands to Start With

# To log in to Azure Resource Manager
Login-AzureRmAccount
 
# To view all subscriptions for your account
Get-AzureRmSubscription
 
# View your current Azure PowerShell session context
# This session state is only applicable to the current session and will not affect other sessions
Get-AzureRmContext
 
# To select the default storage context for your current session
Set-AzureRmCurrentStorageAccount -ResourceGroupName "your resource group" -StorageAccountName "your storage account name"
 
# View your current Azure PowerShell session context
# Note: the CurrentStorageAccount is now set in your session context
Get-AzureRmContext
 
# To list all of the blobs in all of your containers in all of your accounts
Get-AzureRmStorageAccount | Get-AzureStorageContainer | Get-AzureStorageBlob

Examples

Get-AzureRmSubscription

PS C:\> get-azurermsubscription -subscriptionname "Visual studio enterprise"
 
 
SubscriptionName : Visual Studio Enterprise
SubscriptionId   : xxxxxxxx-yyyy-zzzz-wwww-aaaaaaaaaaaa
TenantId         : wwwwwwww-2222-dddd-4444-jjjjjjjjjjjjjj
State            : Enabled

Get-AzureRmVM

PS C:\> get-azurermvm
WARNING: Breaking change notice: In upcoming release, top level properties, DataDiskNames and NetworkInterfaceIDs, will be removed from VM object because they are also in
StorageProfile and NetworkProfile, respectively.
 
ResourceGroupName         Name   Location      VmSize  OsType          NIC ProvisioningState
-----------------         ----   --------      ------  ------          --- -----------------
AZURERESOURCEGROUP01 AzureDC01 westeurope Standard_A1 Windows azuredc01772         Succeeded
 
PS C:\> get-azurermvm -name azuredc01
 
cmdlet Get-AzureRmVM at command pipeline position 1
Supply values for the following parameters:
ResourceGroupName: azureresourcegroup01
WARNING: Breaking change notice: In upcoming release, top level properties, DataDiskNames and NetworkInterfaceIDs, will be removed from VM object because they are also in
StorageProfile and NetworkProfile, respectively.
 
 
ResourceGroupName   : azureresourcegroup01
Id                  : /subscriptions/be1b3ee6-4a03-4ba4-8ac0-75d5a451a364/resourceGroups/azureresourcegroup01/providers/Microsoft.Compute/virtualMachines/AzureDC01
VmId                : 8e334dc5-96c9-4a82-adc8-772a12976550
Name                : AzureDC01
Type                : Microsoft.Compute/virtualMachines
Location            : westeurope
Tags                : {}
HardwareProfile     : {VmSize}
NetworkProfile      : {NetworkInterfaces}
OSProfile           : {ComputerName, AdminUsername, WindowsConfiguration, Secrets}
ProvisioningState   : Succeeded
StorageProfile      : {ImageReference, OsDisk, DataDisks}
DataDiskNames       : {AzureDC01-20170201-155949}
NetworkInterfaceIDs : {/subscriptions/be1b3ee6-4a03-4ba4-8ac0-75d5a451a364/resourceGroups/AzureResourceGroup01/providers/Microsoft.Network/networkInterfaces/azuredc01772}
2025/06/01 11:59
start.txt · Last modified: by 127.0.0.1