Blog Post

FastTrack for Azure
29 MIN READ

How to install an AKS cluster with the Istio service mesh add-on via Bicep

paolosalvatori's avatar
Apr 21, 2023

Istio addresses the challenges developers and operators face with a distributed or microservices architecture. The Istio-based service mesh add-on provides an officially supported and tested Azure Kubernetes Service (AKS) integration.

This article shows how to create an Azure Kubernetes Service(AKS) cluster with the Istio Service Mesh add-on via Bicep and deploy the bookinfo sample application via a Deployment Script.

For more information, see the following articles:

 

 

Code

You can find the code for this sample in this Azure Sample.

 

Prerequisites

 

You can run az --version to verify above versions. To install the aks-preview extension, run the following command:

 

az extension add --name aks-preview

 

Run the following command to update to the latest version of the extension released:

 

az extension update --name aks-preview

 

Architecture

This sample provides a set of Bicep modules to deploy an Azure Kubernetes Service(AKS) cluster with the Istio Service Mesh add-on via Bicep. A Deployment Script is used to deploy the bookinfo sample application. The following diagram shows the architecture and network topology deployed by the sample:

 

 

Bicep modules are parametric, so you can choose any network plugin:

 

The Bicep modules also allow installing the following extensions and add-ons for Azure Kubernetes Service(AKS):

In addition, this sample shows how to deploy an Azure Kubernetes Service cluster with the following features:

 

 

NOTE
you can't install both the Open Service Mesh add-on and Istio Service Mesh add-on on the same AKS cluster.

 

In a production environment, we strongly recommend deploying a private AKS cluster with Uptime SLA. For more information, see private AKS cluster with a Public DNS address. Alternatively, you can deploy a public AKS cluster and secure access to the API server using authorized IP address ranges.

 

Bicep modules deploy the following Azure resources:

 

 

NOTE
You can find the architecture.vsdx file used for the diagram under the visio folder.

 

What is Bicep?

Bicep is a domain-specific language (DSL) that uses a declarative syntax to deploy Azure resources. It provides concise syntax, reliable type safety, and support for code reuse. Bicep offers the best authoring experience for your infrastructure-as-code solutions in Azure.

 

What is a Service Mesh?

Modern applications are typically architected as distributed collections of microservices, with each collection of microservices performing some discrete business function. A service mesh is a dedicated infrastructure layer that you can add to your applications. It allows you to transparently add capabilities like observability, traffic management, and security, without adding them to your own code. The term service mesh describes both the type of software you use to implement this pattern, and the security or network domain that is created when you use that software.

As the deployment of distributed services, such as in a Kubernetes-based system, grows in size and complexity, it can become harder to understand and manage. You may need to implement capabilities such as discovery, load balancing, failure recovery, metrics, and monitoring. A service mesh can also address more complex operational requirements like A/B testing, canary deployments, rate limiting, access control, encryption, and end-to-end authentication.

Service-to-service communication is what makes a distributed application possible. Routing this communication, both within and across application clusters, becomes increasingly complex as the number of services grow. Istio helps reduce this complexity while easing the strain on development teams.

 

What is Istio?

Istio is an open-source service mesh that layers transparently onto existing distributed applications. Istio’s powerful features provide a uniform and more efficient way to secure, connect, and monitor services. Istio enables load balancing, service-to-service authentication, and monitoring – with few or no service code changes. Its powerful control plane brings vital features, including:

 

  • Secure service-to-service communication in a cluster with TLS encryption, strong identity-based authentication and authorization.
  • Automatic load balancing for HTTP, gRPC, WebSocket, and TCP traffic.
  • Fine-grained control of traffic behavior with rich routing rules, retries, failovers, and fault injection.
  • A pluggable policy layer and configuration API supporting access controls, rate limits and quotas.
  • Automatic metrics, logs, and traces for all traffic within a cluster, including cluster ingress and egress.

 

How is the add-on different from open-source Istio?

This service mesh add-on uses and builds on top of open-source Istio. The add-on flavor provides the following extra benefits:

 

  • Istio versions are tested and verified to be compatible with supported versions of Azure Kubernetes Service.
  • Microsoft handles scaling and configuration of Istio control plane
  • Microsoft adjusts scaling of AKS components like coredns when Istio is enabled.
  • Microsoft provides managed lifecycle (upgrades) for Istio components when triggered by user.
  • Verified external and internal ingress set-up.
  • Verified to work with Azure Monitor managed service for Prometheus and Azure Managed Grafana.
  • Official Azure support provided for the add-on.

 

Limitations

Istio-based service mesh add-on for AKS has the following limitations:

  • The add-on currently doesn't work on AKS clusters using Azure CNI Powered by Cilium.
  • The add-on doesn't work on AKS clusters that are using Open Service Mesh addon for AKS.
  • The add-on doesn't work on AKS clusters that have Istio installed on them already outside the add-on installation.
  • Managed lifecycle of mesh on how Istio versions are installed and later made available for upgrades.
  • Istio doesn't support Windows Server containers.
  • Customization of mesh based on the following custom resources is blocked for now - EnvoyFilter, ProxyConfig, WorkloadEntry, WorkloadGroup, Telemetry, IstioOperator, WasmPlugin

 

Register the AzureServiceMeshPreview feature flag

Register the AzureServiceMeshPreview feature flag by using the az feature register command:

 

az feature register --namespace "Microsoft.ContainerService" --name "AzureServiceMeshPreview"

 

It takes a few minutes for the feature to register. Verify the registration status by using the az feature show command:

 

az feature show --namespace "Microsoft.ContainerService" --name "AzureServiceMeshPreview"

 

When the status reflects Registered, refresh the registration of the Microsoft.ContainerService resource provider by using the az provider register command:

 

az provider register --namespace Microsoft.ContainerService

 

Deploy the Bicep modules

You can deploy the Bicep modules in the bicep folder using the deploy.sh Bash script in the same folder. Specify a value for the following parameters in the deploy.sh script and main.parameters.json parameters file before deploying the Bicep modules.

 

  • prefix: specifies a prefix for all the Azure resources.
  • authenticationType: specifies the type of authentication when accessing the Virtual Machine. sshPublicKey is the recommended value. Allowed values: sshPublicKey and password.
  • vmAdminUsername: specifies the name of the administrator account of the virtual machine.
  • vmAdminPasswordOrKey: specifies the SSH Key or password for the virtual machine.
  • aksClusterSshPublicKey: specifies the SSH Key or password for AKS cluster agent nodes.
  • aadProfileAdminGroupObjectIDs: when deploying an AKS cluster with Azure AD and Azure RBAC integration, this array parameter contains the list of Azure AD group object IDs that will have the admin role of the cluster.
  • keyVaultObjectIds: Specifies the object ID of the service principals to configure in Key Vault access policies.

 

We suggest reading sensitive configuration data such as passwords or SSH keys from a pre-existing Azure Key Vault resource. For more information, see Use Azure Key Vault to pass secure parameter value during Bicep deployment.

 

AKS Cluster Bicep module

The following table contains the Bicep code used to deploy the Azure Kubernetes Service(AKS) cluster.

 

// Parameters
@description('Specifies the name of the AKS cluster.')
param name string = 'aks-${uniqueString(resourceGroup().id)}'

@description('Specifies whether to enable API server VNET integration for the cluster or not.')
param enableVnetIntegration bool = true

@description('Specifies the name of the existing virtual network.')
param virtualNetworkName string

@description('Specifies the name of the subnet hosting the worker nodes of the default system agent pool of the AKS cluster.')
param systemAgentPoolSubnetName string = 'SystemSubnet'

@description('Specifies the name of the subnet hosting the worker nodes of the user agent pool of the AKS cluster.')
param userAgentPoolSubnetName string = 'UserSubnet'

@description('Specifies the name of the subnet hosting the pods running in the AKS cluster.')
param podSubnetName string = 'PodSubnet'

@description('Specifies the name of the subnet delegated to the API server when configuring the AKS cluster to use API server VNET integration.')
param apiServerSubnetName string = 'ApiServerSubnet'

@description('Specifies the name of the AKS user-defined managed identity.')
param managedIdentityName string

@description('Specifies the DNS prefix specified when creating the managed cluster.')
param dnsPrefix string = name

@description('Specifies the network plugin used for building Kubernetes network. - azure or kubenet.')
@allowed([
  'azure'
  'kubenet'
])
param networkPlugin string = 'azure'

@description('Specifies the Network plugin mode used for building the Kubernetes network.')
@allowed([
  ''
  'Overlay'
])

param networkPluginMode string = ''

@description('Specifies the network policy used for building Kubernetes network. - calico or azure')
@allowed([
  'azure'
  'calico'
])
param networkPolicy string = 'azure'

@description('Specifies the CIDR notation IP range from which to assign pod IPs when kubenet is used.')
param podCidr string = '192.168.0.0/16'

@description('A CIDR notation IP range from which to assign service cluster IPs. It must not overlap with any Subnet IP ranges.')
param serviceCidr string = '172.16.0.0/16'

@description('Specifies the IP address assigned to the Kubernetes DNS service. It must be within the Kubernetes service address range specified in serviceCidr.')
param dnsServiceIP string = '172.16.0.10'

@description('Specifies the CIDR notation IP range assigned to the Docker bridge network. It must not overlap with any Subnet IP ranges or the Kubernetes service address range.')
param dockerBridgeCidr string = '172.17.0.1/16'

@description('Specifies the sku of the load balancer used by the virtual machine scale sets used by nodepools.')
@allowed([
  'basic'
  'standard'
])
param loadBalancerSku string = 'standard'

@description('Specifies outbound (egress) routing method. - loadBalancer or userDefinedRouting.')
@allowed([
  'loadBalancer'
  'managedNATGateway'
  'userAssignedNATGateway'
  'userDefinedRouting'
])
param outboundType string = 'loadBalancer'

@description('Specifies the tier of a managed cluster SKU: Paid or Free')
@allowed([
  'Standard'
  'Free'
])
param skuTier string = 'Standard'

@description('Specifies the version of Kubernetes specified when creating the managed cluster.')
param kubernetesVersion string = '1.18.8'

@description('Specifies the administrator username of Linux virtual machines.')
param adminUsername string = 'azureuser'

@description('Specifies the SSH RSA public key string for the Linux nodes.')
param sshPublicKey string

@description('Specifies the tenant id of the Azure Active Directory used by the AKS cluster for authentication.')
param aadProfileTenantId string = subscription().tenantId

@description('Specifies the AAD group object IDs that will have admin role of the cluster.')
param aadProfileAdminGroupObjectIDs array = []

@description('Specifies the upgrade channel for auto upgrade. Allowed values include rapid, stable, patch, node-image, none.')
@allowed([
  'rapid'
  'stable'
  'patch'
  'node-image'
  'none'
])
param upgradeChannel string = 'stable'

@description('Specifies whether to create the cluster as a private cluster or not.')
param enablePrivateCluster bool = true

@description('Specifies the Private DNS Zone mode for private cluster. When the value is equal to None, a Public DNS Zone is used in place of a Private DNS Zone')
param privateDNSZone string = 'none'

@description('Specifies whether to create additional public FQDN for private cluster or not.')
param enablePrivateClusterPublicFQDN bool = true

@description('Specifies whether to enable managed AAD integration.')
param aadProfileManaged bool = true

@description('Specifies whether to  to enable Azure RBAC for Kubernetes authorization.')
param aadProfileEnableAzureRBAC bool = true

@description('Specifies the unique name of of the system node pool profile in the context of the subscription and resource group.')
param systemAgentPoolName string = 'nodepool1'

@description('Specifies the vm size of nodes in the system node pool.')
param systemAgentPoolVmSize string = 'Standard_DS5_v2'

@description('Specifies the OS Disk Size in GB to be used to specify the disk size for every machine in the system agent pool. If you specify 0, it will apply the default osDisk size according to the vmSize specified.')
param systemAgentPoolOsDiskSizeGB int = 100

@description('Specifies the OS disk type to be used for machines in a given agent pool. Allowed values are \'Ephemeral\' and \'Managed\'. If unspecified, defaults to \'Ephemeral\' when the VM supports ephemeral OS and has a cache disk larger than the requested OSDiskSizeGB. Otherwise, defaults to \'Managed\'. May not be changed after creation. - Managed or Ephemeral')
@allowed([
  'Ephemeral'
  'Managed'
])
param systemAgentPoolOsDiskType string = 'Ephemeral'

@description('Specifies the number of agents (VMs) to host docker containers in the system node pool. Allowed values must be in the range of 1 to 100 (inclusive). The default value is 1.')
param systemAgentPoolAgentCount int = 3

@description('Specifies the OS type for the vms in the system node pool. Choose from Linux and Windows. Default to Linux.')
@allowed([
  'Linux'
  'Windows'
])
param systemAgentPoolOsType string = 'Linux'

@description('Specifies the maximum number of pods that can run on a node in the system node pool. The maximum number of pods per node in an AKS cluster is 250. The default maximum number of pods per node varies between kubenet and Azure CNI networking, and the method of cluster deployment.')
param systemAgentPoolMaxPods int = 30

@description('Specifies the maximum number of nodes for auto-scaling for the system node pool.')
param systemAgentPoolMaxCount int = 5

@description('Specifies the minimum number of nodes for auto-scaling for the system node pool.')
param systemAgentPoolMinCount int = 3

@description('Specifies whether to enable auto-scaling for the system node pool.')
param systemAgentPoolEnableAutoScaling bool = true

@description('Specifies the virtual machine scale set priority in the system node pool: Spot or Regular.')
@allowed([
  'Spot'
  'Regular'
])
param systemAgentPoolScaleSetPriority string = 'Regular'

@description('Specifies the ScaleSetEvictionPolicy to be used to specify eviction policy for spot virtual machine scale set. Default to Delete. Allowed values are Delete or Deallocate.')
@allowed([
  'Delete'
  'Deallocate'
])
param systemAgentPoolScaleSetEvictionPolicy string = 'Delete'

@description('Specifies the Agent pool node labels to be persisted across all nodes in the system node pool.')
param systemAgentPoolNodeLabels object = {}

@description('Specifies the taints added to new nodes during node pool create and scale. For example, key=value:NoSchedule.')
param systemAgentPoolNodeTaints array = []

@description('Determines the placement of emptyDir volumes, container runtime data root, and Kubelet ephemeral storage.')
@allowed([
  'OS'
  'Temporary'
])
param systemAgentPoolKubeletDiskType string = 'OS'

@description('Specifies the type for the system node pool: VirtualMachineScaleSets or AvailabilitySet')
@allowed([
  'VirtualMachineScaleSets'
  'AvailabilitySet'
])
param systemAgentPoolType string = 'VirtualMachineScaleSets'

@description('Specifies the availability zones for the agent nodes in the system node pool. Requirese the use of VirtualMachineScaleSets as node pool type.')
param systemAgentPoolAvailabilityZones array = [
  '1'
  '2'
  '3'
]

@description('Specifies the unique name of of the user node pool profile in the context of the subscription and resource group.')
param userAgentPoolName string = 'nodepool1'

@description('Specifies the vm size of nodes in the user node pool.')
param userAgentPoolVmSize string = 'Standard_DS5_v2'

@description('Specifies the OS Disk Size in GB to be used to specify the disk size for every machine in the system agent pool. If you specify 0, it will apply the default osDisk size according to the vmSize specified..')
param userAgentPoolOsDiskSizeGB int = 100

@description('Specifies the OS disk type to be used for machines in a given agent pool. Allowed values are \'Ephemeral\' and \'Managed\'. If unspecified, defaults to \'Ephemeral\' when the VM supports ephemeral OS and has a cache disk larger than the requested OSDiskSizeGB. Otherwise, defaults to \'Managed\'. May not be changed after creation. - Managed or Ephemeral')
@allowed([
  'Ephemeral'
  'Managed'
])
param userAgentPoolOsDiskType string = 'Ephemeral'

@description('Specifies the number of agents (VMs) to host docker containers in the user node pool. Allowed values must be in the range of 1 to 100 (inclusive). The default value is 1.')
param userAgentPoolAgentCount int = 3

@description('Specifies the OS type for the vms in the user node pool. Choose from Linux and Windows. Default to Linux.')
@allowed([
  'Linux'
  'Windows'
])
param userAgentPoolOsType string = 'Linux'

@description('Specifies the maximum number of pods that can run on a node in the user node pool. The maximum number of pods per node in an AKS cluster is 250. The default maximum number of pods per node varies between kubenet and Azure CNI networking, and the method of cluster deployment.')
param userAgentPoolMaxPods int = 30

@description('Specifies the maximum number of nodes for auto-scaling for the user node pool.')
param userAgentPoolMaxCount int = 5

@description('Specifies the minimum number of nodes for auto-scaling for the user node pool.')
param userAgentPoolMinCount int = 3

@description('Specifies whether to enable auto-scaling for the user node pool.')
param userAgentPoolEnableAutoScaling bool = true

@description('Specifies the virtual machine scale set priority in the user node pool: Spot or Regular.')
@allowed([
  'Spot'
  'Regular'
])
param userAgentPoolScaleSetPriority string = 'Regular'

@description('Specifies the ScaleSetEvictionPolicy to be used to specify eviction policy for spot virtual machine scale set. Default to Delete. Allowed values are Delete or Deallocate.')
@allowed([
  'Delete'
  'Deallocate'
])
param userAgentPoolScaleSetEvictionPolicy string = 'Delete'

@description('Specifies the Agent pool node labels to be persisted across all nodes in the user node pool.')
param userAgentPoolNodeLabels object = {}

@description('Specifies the taints added to new nodes during node pool create and scale. For example, key=value:NoSchedule.')
@allowed([
  'OS'
  'Temporary'
])
param userAgentPoolNodeTaints array = []

@description('Determines the placement of emptyDir volumes, container runtime data root, and Kubelet ephemeral storage.')
param userAgentPoolKubeletDiskType string = 'OS'

@description('Specifies the type for the user node pool: VirtualMachineScaleSets or AvailabilitySet')
@allowed([
  'VirtualMachineScaleSets'
  'AvailabilitySet'
])
param userAgentPoolType string = 'VirtualMachineScaleSets'

@description('Specifies the availability zones for the agent nodes in the user node pool. Requirese the use of VirtualMachineScaleSets as node pool type.')
param userAgentPoolAvailabilityZones array = [
  '1'
  '2'
  '3'
]

@description('Specifies whether the httpApplicationRouting add-on is enabled or not.')
param httpApplicationRoutingEnabled bool = false

@description('Specifies whether the Open Service Mesh add-on is enabled or not.')
param openServiceMeshEnabled bool = false

@description('Specifies whether the Istio Service Mesh add-on is enabled or not.')
param istioServiceMeshEnabled bool = false

@description('Specifies whether the Istio Ingress Gateway is enabled or not.')
param istioIngressGatewayEnabled bool = false

@description('Specifies the type of the Istio Ingress Gateway.')
@allowed([
  'Internal'
  'External'
])
param istioIngressGatewayType string = 'External'

@description('Specifies whether the Kubernetes Event-Driven Autoscaler (KEDA) add-on is enabled or not.')
param kedaEnabled bool = false

@description('Specifies whether the Dapr extension is enabled or not.')
param daprEnabled bool = false

@description('Enable high availability (HA) mode for the Dapr control plane')
param daprHaEnabled bool = false

@description('Specifies whether the Flux V2 extension is enabled or not.')
param fluxGitOpsEnabled bool = false

@description('Specifies whether the Vertical Pod Autoscaler is enabled or not.')
param verticalPodAutoscalerEnabled bool = false

@description('Specifies whether the aciConnectorLinux add-on is enabled or not.')
param aciConnectorLinuxEnabled bool = false

@description('Specifies whether the azurepolicy add-on is enabled or not.')
param azurePolicyEnabled bool = true

@description('Specifies whether the Azure Key Vault Provider for Secrets Store CSI Driver addon is enabled or not.')
param azureKeyvaultSecretsProviderEnabled bool = true

@description('Specifies whether the kubeDashboard add-on is enabled or not.')
param kubeDashboardEnabled bool = false

@description('Specifies whether the pod identity addon is enabled..')
param podIdentityProfileEnabled bool = false

@description('Specifies whether the OIDC issuer is enabled.')
param oidcIssuerProfileEnabled bool = true

@description('Specifies the scan interval of the auto-scaler of the AKS cluster.')
param autoScalerProfileScanInterval string = '10s'

@description('Specifies the scale down delay after add of the auto-scaler of the AKS cluster.')
param autoScalerProfileScaleDownDelayAfterAdd string = '10m'

@description('Specifies the scale down delay after delete of the auto-scaler of the AKS cluster.')
param autoScalerProfileScaleDownDelayAfterDelete string = '20s'

@description('Specifies scale down delay after failure of the auto-scaler of the AKS cluster.')
param autoScalerProfileScaleDownDelayAfterFailure string = '3m'

@description('Specifies the scale down unneeded time of the auto-scaler of the AKS cluster.')
param autoScalerProfileScaleDownUnneededTime string = '10m'

@description('Specifies the scale down unready time of the auto-scaler of the AKS cluster.')
param autoScalerProfileScaleDownUnreadyTime string = '20m'

@description('Specifies the utilization threshold of the auto-scaler of the AKS cluster.')
param autoScalerProfileUtilizationThreshold string = '0.5'

@description('Specifies the max graceful termination time interval in seconds for the auto-scaler of the AKS cluster.')
param autoScalerProfileMaxGracefulTerminationSec string = '600'

@description('Specifies the resource id of the Log Analytics workspace.')
param workspaceId string

@description('Specifies the workspace data retention in days.')
param retentionInDays int = 60

@description('Specifies the location.')
param location string = resourceGroup().location

@description('Specifies the resource tags.')
param tags object

@description('Specifies whether to enable the Azure Blob CSI Driver. The default value is false.')
param blobCSIDriverEnabled bool = false

@description('Specifies whether to enable the Azure Disk CSI Driver. The default value is true.')
param diskCSIDriverEnabled bool = true

@description('Specifies whether to enable the Azure File CSI Driver. The default value is true.')
param fileCSIDriverEnabled bool = true

@description('Specifies whether to enable the Snapshot Controller. The default value is true.')
param snapshotControllerEnabled bool = true

@description('Specifies whether to enable Defender threat detection. The default value is false.')
param defenderSecurityMonitoringEnabled bool = false

@description('Specifies whether to enable ImageCleaner on AKS cluster. The default value is false.')
param imageCleanerEnabled bool = false

@description('Specifies whether ImageCleaner scanning interval in hours.')
param imageCleanerIntervalHours int = 24

@description('Specifies whether to enable Node Restriction. The default value is false.')
param nodeRestrictionEnabled bool = false

@description('Specifies whether to enable Workload Identity. The default value is false.')
param workloadIdentityEnabled bool = false

@description('Specifies whether creating the Application Gateway and enabling the Application Gateway Ingress Controller or not.')
param applicationGatewayEnabled bool = false

@description('Specifies the resource id of the Azure Application Gateway.')
param applicationGatewayId string

@description('Specifies the principal id of the managed identity of the Azure Application Gateway.')
param applicationGatewayManagedIdentityPrincipalId string

@description('Specifies the name of the Key Vault resource.')
param keyVaultName string

// Variables
var diagnosticSettingsName = 'diagnosticSettings'
var logCategories = [
  'kube-apiserver'
  'kube-audit'
  'kube-audit-admin'
  'kube-controller-manager'
  'kube-scheduler'
  'cluster-autoscaler'
  'cloud-controller-manager'
  'guard'
  'csi-azuredisk-controller'
  'csi-azurefile-controller'
  'csi-snapshot-controller'
]
var metricCategories = [
  'AllMetrics'
]
var logs = [for category in logCategories: {
  category: category
  enabled: true
  retentionPolicy: {
    enabled: true
    days: retentionInDays
  }
}]
var metrics = [for category in metricCategories: {
  category: category
  enabled: true
  retentionPolicy: {
    enabled: true
    days: retentionInDays
  }
}]

var managedIdentityOperatorRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')
var readerRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')
var contributorRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
var keyVaultSecretsUserRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')
var MonitoringMetricsPublisherRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')

// Resources
resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2021-09-30-preview' existing = {
  name: managedIdentityName
}

resource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-08-01' existing = {
  name: virtualNetworkName
}

resource systemAgentPoolSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-08-01' existing = {
  parent: virtualNetwork
  name: systemAgentPoolSubnetName
}

resource userAgentPoolSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-08-01' existing = {
  parent: virtualNetwork
  name: userAgentPoolSubnetName
}

resource podSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-08-01' existing = {
  parent: virtualNetwork
  name: podSubnetName
}

resource apiServerSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-08-01' existing = {
  parent: virtualNetwork
  name: apiServerSubnetName
}

resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-03-02-preview' = {
  name: name
  location: location
  tags: tags
  sku: {
    name: 'Base'
    tier: skuTier
  }
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: {
      '${managedIdentity.id}': {}
    }
  }
  properties: {
    kubernetesVersion: kubernetesVersion
    dnsPrefix: dnsPrefix
    agentPoolProfiles: [
      {
        name: toLower(systemAgentPoolName)
        count: systemAgentPoolAgentCount
        vmSize: systemAgentPoolVmSize
        osDiskSizeGB: systemAgentPoolOsDiskSizeGB
        osDiskType: systemAgentPoolOsDiskType
        vnetSubnetID: systemAgentPoolSubnet.id
        podSubnetID: networkPluginMode == 'Overlay' ? null : podSubnet.id
        maxPods: systemAgentPoolMaxPods
        osType: systemAgentPoolOsType
        maxCount: systemAgentPoolMaxCount
        minCount: systemAgentPoolMinCount
        scaleSetPriority: systemAgentPoolScaleSetPriority
        scaleSetEvictionPolicy: systemAgentPoolScaleSetEvictionPolicy
        enableAutoScaling: systemAgentPoolEnableAutoScaling
        mode: 'System'
        type: systemAgentPoolType
        availabilityZones: systemAgentPoolAvailabilityZones
        nodeLabels: systemAgentPoolNodeLabels
        nodeTaints: systemAgentPoolNodeTaints
        kubeletDiskType: systemAgentPoolKubeletDiskType
      }
      {
        name: toLower(userAgentPoolName)
        count: userAgentPoolAgentCount
        vmSize: userAgentPoolVmSize
        osDiskSizeGB: userAgentPoolOsDiskSizeGB
        osDiskType: userAgentPoolOsDiskType
        vnetSubnetID: userAgentPoolSubnet.id
        podSubnetID: networkPluginMode == 'Overlay' ? null : podSubnet.id
        maxPods: userAgentPoolMaxPods
        osType: userAgentPoolOsType
        maxCount: userAgentPoolMaxCount
        minCount: userAgentPoolMinCount
        scaleSetPriority: userAgentPoolScaleSetPriority
        scaleSetEvictionPolicy: userAgentPoolScaleSetEvictionPolicy
        enableAutoScaling: userAgentPoolEnableAutoScaling
        mode: 'User'
        type: userAgentPoolType
        availabilityZones: userAgentPoolAvailabilityZones
        nodeLabels: userAgentPoolNodeLabels
        nodeTaints: userAgentPoolNodeTaints
        kubeletDiskType: userAgentPoolKubeletDiskType
      }
    ]
    linuxProfile: {
      adminUsername: adminUsername
      ssh: {
        publicKeys: [
          {
            keyData: sshPublicKey
          }
        ]
      }
    }
    addonProfiles: {
      ingressApplicationGateway: applicationGatewayEnabled ? {
        config: {
          applicationGatewayId: applicationGatewayEnabled ? applicationGatewayId : null
        }
        enabled: true
      } : {
        enabled: false
      }
      httpApplicationRouting: {
        enabled: httpApplicationRoutingEnabled
      }
      openServiceMesh: {
        enabled: openServiceMeshEnabled
        config: {}
      }
      omsagent: {
        enabled: true
        config: {
          logAnalyticsWorkspaceResourceID: workspaceId
        }
      }
      aciConnectorLinux: {
        enabled: aciConnectorLinuxEnabled
      }
      azurepolicy: {
        enabled: azurePolicyEnabled
        config: {
          version: 'v2'
        }
      }
      kubeDashboard: {
        enabled: kubeDashboardEnabled
      }
      azureKeyvaultSecretsProvider: {
        config: {
          enableSecretRotation: 'false'
        }
        enabled: azureKeyvaultSecretsProviderEnabled
      }
    }
    podIdentityProfile: {
      enabled: podIdentityProfileEnabled
    }
    oidcIssuerProfile: {
      enabled: oidcIssuerProfileEnabled
    }
    enableRBAC: true
    networkProfile: {
      networkPlugin: networkPlugin
      networkPluginMode: networkPlugin == 'azure' ? networkPluginMode : ''
      networkPolicy: networkPolicy
      podCidr: networkPlugin == 'kubenet' || networkPluginMode == 'Overlay' ? podCidr : null
      serviceCidr: serviceCidr
      dnsServiceIP: dnsServiceIP
      dockerBridgeCidr: dockerBridgeCidr
      outboundType: outboundType
      loadBalancerSku: loadBalancerSku
      loadBalancerProfile: null
    }
    workloadAutoScalerProfile: {
      keda: {
        enabled: kedaEnabled
      }
      verticalPodAutoscaler: {
        controlledValues: 'RequestsAndLimits'
        enabled: verticalPodAutoscalerEnabled
        updateMode: 'Off'
      }
    }
    aadProfile: {
      clientAppID: null
      serverAppID: null
      serverAppSecret: null
      managed: aadProfileManaged
      enableAzureRBAC: aadProfileEnableAzureRBAC
      adminGroupObjectIDs: aadProfileAdminGroupObjectIDs
      tenantID: aadProfileTenantId
    }
    autoUpgradeProfile: {
      upgradeChannel: upgradeChannel
    }
    autoScalerProfile: {
      'scan-interval': autoScalerProfileScanInterval
      'scale-down-delay-after-add': autoScalerProfileScaleDownDelayAfterAdd
      'scale-down-delay-after-delete': autoScalerProfileScaleDownDelayAfterDelete
      'scale-down-delay-after-failure': autoScalerProfileScaleDownDelayAfterFailure
      'scale-down-unneeded-time': autoScalerProfileScaleDownUnneededTime
      'scale-down-unready-time': autoScalerProfileScaleDownUnreadyTime
      'scale-down-utilization-threshold': autoScalerProfileUtilizationThreshold
      'max-graceful-termination-sec': autoScalerProfileMaxGracefulTerminationSec
    }
    apiServerAccessProfile: {
      enablePrivateCluster: enablePrivateCluster
      enableVnetIntegration: enableVnetIntegration
      privateDNSZone: enablePrivateCluster ? privateDNSZone : null
      enablePrivateClusterPublicFQDN: enablePrivateClusterPublicFQDN
      subnetId: apiServerSubnet.id
    }
    securityProfile: {
      defender: {
        logAnalyticsWorkspaceResourceId: workspaceId
        securityMonitoring: {
          enabled: defenderSecurityMonitoringEnabled
        }
      }
      imageCleaner: {
        enabled: imageCleanerEnabled
        intervalHours: imageCleanerIntervalHours
      }
      nodeRestriction: {
        enabled: nodeRestrictionEnabled
      }
      workloadIdentity: {
        enabled: workloadIdentityEnabled
      }
    }
    serviceMeshProfile: istioServiceMeshEnabled ? {
      istio: {
        components: {
          ingressGateways: istioIngressGatewayEnabled ? [
            {
              enabled: true
              mode: istioIngressGatewayType
            }
          ] : null
        }
      }
      mode: 'Istio'
    } : null
    storageProfile: {
      blobCSIDriver: {
        enabled: blobCSIDriverEnabled
      }
      diskCSIDriver: {
        enabled: diskCSIDriverEnabled
      }
      fileCSIDriver: {
        enabled: fileCSIDriverEnabled
      }
      snapshotController: {
        enabled: snapshotControllerEnabled
      }
    }
  }
}

// AGIC's identity requires "Managed Identity Operator" permission over the user assigned identity of Application Gateway.
resource applicationGatewayAgicManagedIdentityOperatorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (applicationGatewayEnabled) {
  scope: resourceGroup()
  name: guid(aksCluster.id, applicationGatewayManagedIdentityPrincipalId, 'managedIdentityOperator')
  properties: {
    roleDefinitionId: managedIdentityOperatorRoleDefinitionId
    principalType: 'ServicePrincipal'
    principalId: applicationGatewayEnabled ? aksCluster.properties.addonProfiles.ingressApplicationGateway.identity.objectId : null
  }
}

resource applicationGatewayAgicContributorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (applicationGatewayEnabled) {
  scope: resourceGroup()
  name: guid(resourceGroup().id, 'ApplicationGateway', 'contributor')
  properties: {
    roleDefinitionId: contributorRoleDefinitionId
    principalType: 'ServicePrincipal'
    principalId: applicationGatewayEnabled ? aksCluster.properties.addonProfiles.ingressApplicationGateway.identity.objectId : null
  }
}

resource applicationGatewayAgicReaderRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (applicationGatewayEnabled) {
  scope: resourceGroup()
  name: guid(resourceGroup().id, 'ApplicationGateway', 'reader')
  properties: {
    roleDefinitionId: readerRoleDefinitionId
    principalType: 'ServicePrincipal'
    principalId: applicationGatewayEnabled ? aksCluster.properties.addonProfiles.ingressApplicationGateway.identity.objectId : null
  }
}

resource keyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = {
  name: keyVaultName
}

resource keyVaultSecretsUserApplicationGatewayIdentityRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (applicationGatewayEnabled) {
  scope: keyVault
  name: guid(keyVault.id, 'ApplicationGateway', 'keyVaultSecretsUser')
  properties: {
    roleDefinitionId: keyVaultSecretsUserRoleDefinitionId
    principalType: 'ServicePrincipal'
    principalId: applicationGatewayManagedIdentityPrincipalId
  }
}

resource keyVaultCSIdriverSecretsUserRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (azureKeyvaultSecretsProviderEnabled) {
  scope: keyVault
  name: guid(aksCluster.id, 'CSIDriver', keyVaultSecretsUserRoleDefinitionId)
  properties: {
    roleDefinitionId: keyVaultSecretsUserRoleDefinitionId
    principalType: 'ServicePrincipal'
    principalId: aksCluster.properties.addonProfiles.azureKeyvaultSecretsProvider.identity.objectId
  }
}

// This role assignment enables AKS->LA Fast Alerting experience
resource FastAlertingRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  scope: aksCluster
  name: guid(aksCluster.id, 'omsagent', MonitoringMetricsPublisherRoleDefinitionId)
  properties: {
    roleDefinitionId: MonitoringMetricsPublisherRoleDefinitionId
    principalId: aksCluster.properties.addonProfiles.omsagent.identity.objectId
    principalType: 'ServicePrincipal'
  }
}

// Dapr Extension
resource daprExtension 'Microsoft.KubernetesConfiguration/extensions@2022-04-02-preview' = if (daprEnabled) {
  name: 'dapr'
  scope: aksCluster
  properties: {
    extensionType: 'Microsoft.Dapr'
    autoUpgradeMinorVersion: true
    releaseTrain: 'Stable'
    configurationSettings: {
      'global.ha.enabled': '${daprHaEnabled}'
    }
    scope: {
      cluster: {
        releaseNamespace: 'dapr-system'
      }
    }
    configurationProtectedSettings: {}
  }
}

// Flux v2 Extension
resource fluxAddon 'Microsoft.KubernetesConfiguration/extensions@2022-04-02-preview' = if (fluxGitOpsEnabled) {
  name: 'flux'
  scope: aksCluster
  properties: {
    extensionType: 'microsoft.flux'
    autoUpgradeMinorVersion: true
    releaseTrain: 'Stable'
    scope: {
      cluster: {
        releaseNamespace: 'flux-system'
      }
    }
    configurationProtectedSettings: {}
  }
  dependsOn: [ daprExtension ] //Chaining dependencies because of: https://github.com/Azure/AKS-Construction/issues/385
}

// Diagnostic Settings
resource diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  name: diagnosticSettingsName
  scope: aksCluster
  properties: {
    workspaceId: workspaceId
    logs: logs
    metrics: metrics
  }
}

// Output
output id string = aksCluster.id
output name string = aksCluster.name

 

You can use the following parameters to control the installation of the Istio Service Mesh add-on:

 

@description('Specifies whether the Istio Service Mesh add-on is enabled or not.')
param istioServiceMeshEnabled bool = false

@description('Specifies whether the Istio Ingress Gateway is enabled or not.')
param istioIngressGatewayEnabled bool = false

@description('Specifies the type of the Istio Ingress Gateway.')
@allowed([
  'Internal'
  'External'
])
param istioIngressGatewayType string = 'External'

 

The version of the Microsoft.ContainerService/managedClusters service provider used to deploy the AKS cluster needs to be 2023-03-02-preview or higher. The serviceMeshProfile section of the resource definition controls the deployment of the Istio Service Mesh add-on.

 

resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-03-02-preview' = {
  name: name
  location: location
  tags: tags
  ...
  properties: {
  ...
    serviceMeshProfile: istioServiceMeshEnabled ? {
      istio: {
        components: {
          ingressGateways: istioIngressGatewayEnabled ? [
            {
              enabled: true
              mode: istioIngressGatewayType
            }
          ] : null
        }
      }
      mode: 'Istio'
    } : null
    ...
  }
  ...
}

 

The sample is configured to deploy the Istio Service Mesh add-on via an External Ingress Gateway, but you can use the istioIngressGatewayType to install Istio with an Internal Ingress Gateway. For more information, see Deploy external or internal ingresses for Istio service mesh add-on for Azure Kubernetes Service. The Istio Service Mesh add-on installs Istio runtime in the aks-istio-system namespace and Istio Ingress Gateway in aks-istio-ingress.

 

Deployment Script

The sample makes use of a Deployment Script to run the install-istio-demo.sh Bash script that installs the bookinfo web application via YAML templates, as shown in the following picture. The application displays information about a book, similar to a single catalog entry of an online bookstore. Displayed on the page is a description of the book, book details (ISBN, number of pages, and so on), and a few book reviews.

 

 

The Bookinfo application is broken into four separate microservices:

 

  • productpage. The productpage microservice calls the details and reviews microservices to populate the page.
  • details. The details microservice contains book information.
  • reviews. The reviews microservice contains book reviews. It also calls the ratings microservice.
  • ratings. The ratings microservice contains book ranking information that accompanies a book review.

 

#!/bin/bash

az aks install-cli --only-show-errors

# Get AKS credentials
az aks get-credentials \
  --admin \
  --name $clusterName \
  --resource-group $resourceGroupName \
  --subscription $subscriptionId \
  --only-show-errors

# Check if the cluster is private or not
private=$(az aks show --name $clusterName \
  --resource-group $resourceGroupName \
  --subscription $subscriptionId \
  --query apiServerAccessProfile.enablePrivateCluster \
  --output tsv)

if [[ $private == 'true' ]]; then
  # Log whether the cluster is public or private
  echo "$clusterName AKS cluster is public"

  # Create cluster issuer for the Application Gateway Ingress Controller (AGIC)
  if [[ $applicationGatewayEnabled == 'true' ]]; then
    command="cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-application-gateway
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: $email
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - http01:
        ingress:
          class: azure/application-gateway
          podTemplate:
            spec:
              nodeSelector:
                "kubernetes.io/os": linux
EOF"

    az aks command invoke \
      --name $clusterName \
      --resource-group $resourceGroupName \
      --subscription $subscriptionId \
      --command "$command"
  fi

  # Create a namespace for the sample bookinfo application
  command="kubectl create namespace $namespace"

  az aks command invoke \
    --name $clusterName \
    --resource-group $resourceGroupName \
    --subscription $subscriptionId \
    --command "$command"

  # To automatically install sidecar to any new pods, annotate your namespaces
  # The default istio-injection=enabled labeling doesn't work. Explicit versioning (istio.io/rev=asm-1-17) is required.
  command="kubectl label namespace $namespace istio.io/rev=asm-1-17"

  az aks command invoke \
    --name $clusterName \
    --resource-group $resourceGroupName \
    --subscription $subscriptionId \
    --command "$command"

  # Deploy the sample bookinfo application
  command="kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.17/samples/bookinfo/platform/kube/bookinfo.yaml -n $namespace"

  az aks command invoke \
    --name $clusterName \
    --resource-group $resourceGroupName \
    --subscription $subscriptionId \
    --command "$command"

  # The sample bookinfo application isn't accessible from outside the cluster by default after enabling the ingress gateway.
  # To make the application accessible from the internet, map the sample deployment's ingress to the Istio ingress gateway using the following manifest.
  # Create an ingress resource for the application
  command="cat <<EOF | kubectl apply -n $namespace -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: bookinfo-gateway-external
  namespace: $namespace
spec:
  selector:
    istio: aks-istio-ingressgateway-external
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: bookinfo-vs-external
  namespace: $namespace
spec:
  hosts:
  - "*"
  gateways:
  - bookinfo-gateway-external
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        prefix: /static
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    route:
    - destination:
        host: productpage
        port:
          number: 9080
EOF"

  az aks command invoke \
    --name $clusterName \
    --resource-group $resourceGroupName \
    --subscription $subscriptionId \
    --command "$command"

else
  # Log whether the cluster is public or private
  echo "$clusterName AKS cluster is public"

  # Create a namespace for the sample bookinfo application
  kubectl create namespace $namespace

  # To automatically install sidecar to any new pods, annotate your namespaces
  # The default istio-injection=enabled labeling doesn't work. Explicit versioning (istio.io/rev=asm-1-17) is required.
  kubectl label namespace $namespace istio.io/rev=asm-1-17

  # Deploy the sample bookinfo application
  kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.17/samples/bookinfo/platform/kube/bookinfo.yaml -n $namespace

  # The sample bookinfo application isn't accessible from outside the cluster by default after enabling the ingress gateway.
  # To make the application accessible from the internet, map the sample deployment's ingress to the Istio ingress gateway using the following manifest.
  # Create an ingress resource for the application
  cat <<EOF | kubectl apply -n $namespace -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: bookinfo-gateway-external
  namespace: $namespace
spec:
  selector:
    istio: aks-istio-ingressgateway-external
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: bookinfo-vs-external
  namespace: $namespace
spec:
  hosts:
  - "*"
  gateways:
  - bookinfo-gateway-external
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        prefix: /static
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    route:
    - destination:
        host: productpage
        port:
          number: 9080
EOF

fi

ingressHostExternal=$(kubectl -n aks-istio-ingress get service aks-istio-ingressgateway-external -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
ingressPortExternal=$(kubectl -n aks-istio-ingress get service aks-istio-ingressgateway-external -o jsonpath='{.spec.ports[?(@.name=="http2")].port}')
gatewayUrlExternal=$ingressHostExternal:$ingressPortExternal

# Create output as JSON file
echo '{}' |
  jq --arg x $namespace                                '.namespace=$x' |
  jq --arg x $ingressHostExternal                      '.ingressHostExternal=$x' |
  jq --arg x $ingressPortExternal                      '.ingressPortExternal=$x' |
  jq --arg x $gatewayUrlExternal                       '.gatewayUrlExternal=$x'  |
  jq --arg x "http://$gatewayUrlExternal/productpage"  '.bookInfoUrlExternal=$x'  >$AZ_SCRIPTS_OUTPUT_PATH

 

The install-istio-demo.sh Bash script can be used to automatically install the bookinfo sample application to a public AKS cluster or to private AKS cluster using the az aks command invoke. For more information, see Use command invoke to access a private Azure Kubernetes Service (AKS) cluster.

 

The script first performs the following steps:

 

  • Creates a namespace for the bookinfo sample application.
  • Annotates the namespace to inject Istio sidecar containers into the pods of the workload. For more information, see Installing the Sidecar in the Istio documentation.
  • Deploys the bookinfo sample application using YAML manifests.
  • Deploys the Gateway and Virtual Service objects to expose the sample application via the Istio Ingress Gateway.
    • In Istio, Gateways are used to manage inbound and outbound traffic for your mesh, letting you specify which traffic you want to enter or leave the mesh. Gateway configurations are applied to standalone Envoy proxies that are running at the edge of the mesh, rather than sidecar Envoy proxies running alongside your service workloads.
    • Virtual services, along with destination rules, are the key building blocks of Istio’s traffic routing functionality. A virtual service lets you configure how requests are routed to a service within an Istio service mesh, building on the basic connectivity and discovery provided by Istio and your platform. Each virtual service consists of a set of routing rules that are evaluated in order, letting Istio match each given request to the virtual service to a specific real destination within the mesh. Your mesh can require multiple virtual services or none depending on your use case.

The install-istio-demo.sh Bash script returns the following outputs to the deployment script:

 

  • Namespace hosting the bookinfo sample. You can change the default bookinfo namespace by assigning a different value to the namespace parameter of the main.bicep module.
  • Public IP address used by the Istio ingress gateway.
  • Port used by the Istio ingress gateway.
  • URL of the Istio ingress gateway.
  • URL of the productpage of the bookinfo sample application.

 

Test the application

If the deployment succeeds, the bookinfo application will be publicly exposed via the external Istio ingress gateway. You can retrieve the URL of the productpage of the bookinfo sample application from the outputs of the deployment script, as shown in the following picture:

 

 

You can use open the URL of the productpage of the bookinfo sample application using a web browser, as shown in the following picture.

 

You can also use the test.sh Bash script under the bicep folder to automatically retrieve the the URL of the productpage of the bookinfo sample application from the outputs of the deployment script and call the URL.

 

#!/bin/bash

# Variables
prefix="Pink"
resourceGroupName="${prefix}RG"
deploymentName="deploymentScript"

# Get the URL of the productpage of the bookinfo sample application
bookInfoUrlExternal=$(az deployment group show \
 --name $deploymentName \
 --resource-group $resourceGroupName \
 --query properties.outputs.bookInfoUrlExternal.value \
 --output tsv)

if [[ -n $bookInfoUrlExternal ]]; then
 echo "[$bookInfoUrlExternal] URL of the productpage of the bookinfo sample application successfully retrieved"
else
 echo "Failed to get the URL of the productpage of the bookinfo sample application"
 exit -1
fi

#  Call the URL of the productpage of the bookinfo sample application
echo "Calling the URL of the productpage of the bookinfo sample application"
curl -s $bookInfoUrlExternal | grep -o "<title>.*</title>"

 

If the call succeeds, you should see a result like the following:

 

$ ./test.sh
[http://20.82.231.13:80/productpage] URL of the productpage of the bookinfo sample application successfully retrieved
Calling the URL of the productpage of the bookinfo sample application
<title>Simple Bookstore App</title>

 

Review deployed resources

Use the Azure portal, Azure CLI, or Azure PowerShell to list the deployed resources in the resource group.

Azure CLI

 

az resource list --resource-group <resource-group-name>

 

PowerShell

 

Get-AzResource -ResourceGroupName <resource-group-name>

 

Clean up resources

Delete the resource group when you no longer need the resources you created. This will remove all the Azure resources.

Azure CLI

 

az group delete --name <resource-group-name>

 

PowerShell

 

Remove-AzResourceGroup -Name <resource-group-name>

 

 

Published Apr 21, 2023
Version 1.0
No CommentsBe the first to comment
"}},"componentScriptGroups({\"componentId\":\"custom.widget.Social_Sharing\"})":{"__typename":"ComponentScriptGroups","scriptGroups":{"__typename":"ComponentScriptGroupsDefinition","afterInteractive":{"__typename":"PageScriptGroupDefinition","group":"AFTER_INTERACTIVE","scriptIds":[]},"lazyOnLoad":{"__typename":"PageScriptGroupDefinition","group":"LAZY_ON_LOAD","scriptIds":[]}},"componentScripts":[]},"component({\"componentId\":\"custom.widget.MicrosoftFooter\"})":{"__typename":"Component","render({\"context\":{\"component\":{\"entities\":[],\"props\":{}},\"page\":{\"entities\":[\"board:FastTrackforAzureBlog\",\"message:3802069\"],\"name\":\"BlogMessagePage\",\"props\":{},\"url\":\"https://techcommunity.microsoft.com/blog/fasttrackforazureblog/how-to-install-an-aks-cluster-with-the-istio-service-mesh-add-on-via-bicep/3802069\"}}})":{"__typename":"ComponentRenderResult","html":""}},"componentScriptGroups({\"componentId\":\"custom.widget.MicrosoftFooter\"})":{"__typename":"ComponentScriptGroups","scriptGroups":{"__typename":"ComponentScriptGroupsDefinition","afterInteractive":{"__typename":"PageScriptGroupDefinition","group":"AFTER_INTERACTIVE","scriptIds":[]},"lazyOnLoad":{"__typename":"PageScriptGroupDefinition","group":"LAZY_ON_LOAD","scriptIds":[]}},"componentScripts":[]},"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"components/community/NavbarDropdownToggle\"]})":[{"__ref":"CachedAsset:text:en_US-components/community/NavbarDropdownToggle-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/common/QueryHandler\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/common/QueryHandler-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageCoverImage\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageCoverImage-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/nodes/NodeTitle\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/nodes/NodeTitle-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageTimeToRead\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageTimeToRead-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageSubject\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageSubject-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"components/users/UserLink\"]})":[{"__ref":"CachedAsset:text:en_US-components/users/UserLink-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/users/UserRank\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/users/UserRank-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageTime\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageTime-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageBody\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageBody-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageCustomFields\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageCustomFields-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageRevision\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageRevision-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageReplyButton\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageReplyButton-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageAuthorBio\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageAuthorBio-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/users/UserAvatar\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/users/UserAvatar-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/ranks/UserRankLabel\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/ranks/UserRankLabel-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"components/users/UserRegistrationDate\"]})":[{"__ref":"CachedAsset:text:en_US-components/users/UserRegistrationDate-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/nodes/NodeAvatar\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/nodes/NodeAvatar-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/nodes/NodeDescription\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/nodes/NodeDescription-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"components/tags/TagView/TagViewChip\"]})":[{"__ref":"CachedAsset:text:en_US-components/tags/TagView/TagViewChip-1737115705000"}],"cachedText({\"lastModified\":\"1737115705000\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/nodes/NodeIcon\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/nodes/NodeIcon-1737115705000"}]},"CachedAsset:pages-1741257668098":{"__typename":"CachedAsset","id":"pages-1741257668098","value":[{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"BlogViewAllPostsPage","type":"BLOG","urlPath":"/category/:categoryId/blog/:boardId/all-posts/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"CasePortalPage","type":"CASE_PORTAL","urlPath":"/caseportal","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"CreateGroupHubPage","type":"GROUP_HUB","urlPath":"/groups/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"CaseViewPage","type":"CASE_DETAILS","urlPath":"/case/:caseId/:caseNumber","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"InboxPage","type":"COMMUNITY","urlPath":"/inbox","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"HelpFAQPage","type":"COMMUNITY","urlPath":"/help","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"IdeaMessagePage","type":"IDEA_POST","urlPath":"/idea/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"IdeaViewAllIdeasPage","type":"IDEA","urlPath":"/category/:categoryId/ideas/:boardId/all-ideas/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"LoginPage","type":"USER","urlPath":"/signin","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"BlogPostPage","type":"BLOG","urlPath":"/category/:categoryId/blogs/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"UserBlogPermissions.Page","type":"COMMUNITY","urlPath":"/c/user-blog-permissions/page","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"ThemeEditorPage","type":"COMMUNITY","urlPath":"/designer/themes","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"TkbViewAllArticlesPage","type":"TKB","urlPath":"/category/:categoryId/kb/:boardId/all-articles/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1730142000000,"localOverride":null,"page":{"id":"AllEvents","type":"CUSTOM","urlPath":"/Events","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"OccasionEditPage","type":"EVENT","urlPath":"/event/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"OAuthAuthorizationAllowPage","type":"USER","urlPath":"/auth/authorize/allow","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"PageEditorPage","type":"COMMUNITY","urlPath":"/designer/pages","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"PostPage","type":"COMMUNITY","urlPath":"/category/:categoryId/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"ForumBoardPage","type":"FORUM","urlPath":"/category/:categoryId/discussions/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"TkbBoardPage","type":"TKB","urlPath":"/category/:categoryId/kb/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"EventPostPage","type":"EVENT","urlPath":"/category/:categoryId/events/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"UserBadgesPage","type":"COMMUNITY","urlPath":"/users/:login/:userId/badges","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"GroupHubMembershipAction","type":"GROUP_HUB","urlPath":"/membership/join/:nodeId/:membershipType","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"MaintenancePage","type":"COMMUNITY","urlPath":"/maintenance","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"IdeaReplyPage","type":"IDEA_REPLY","urlPath":"/idea/:boardId/:messageSubject/:messageId/comments/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"UserSettingsPage","type":"USER","urlPath":"/mysettings/:userSettingsTab","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"GroupHubsPage","type":"GROUP_HUB","urlPath":"/groups","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"ForumPostPage","type":"FORUM","urlPath":"/category/:categoryId/discussions/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"OccasionRsvpActionPage","type":"OCCASION","urlPath":"/event/:boardId/:messageSubject/:messageId/rsvp/:responseType","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"VerifyUserEmailPage","type":"USER","urlPath":"/verifyemail/:userId/:verifyEmailToken","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"AllOccasionsPage","type":"OCCASION","urlPath":"/category/:categoryId/events/:boardId/all-events/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"EventBoardPage","type":"EVENT","urlPath":"/category/:categoryId/events/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"TkbReplyPage","type":"TKB_REPLY","urlPath":"/kb/:boardId/:messageSubject/:messageId/comments/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"IdeaBoardPage","type":"IDEA","urlPath":"/category/:categoryId/ideas/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"CommunityGuideLinesPage","type":"COMMUNITY","urlPath":"/communityguidelines","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"CaseCreatePage","type":"SALESFORCE_CASE_CREATION","urlPath":"/caseportal/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"TkbEditPage","type":"TKB","urlPath":"/kb/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"ForgotPasswordPage","type":"USER","urlPath":"/forgotpassword","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"IdeaEditPage","type":"IDEA","urlPath":"/idea/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"TagPage","type":"COMMUNITY","urlPath":"/tag/:tagName","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"BlogBoardPage","type":"BLOG","urlPath":"/category/:categoryId/blog/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"OccasionMessagePage","type":"OCCASION_TOPIC","urlPath":"/event/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"ManageContentPage","type":"COMMUNITY","urlPath":"/managecontent","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"ClosedMembershipNodeNonMembersPage","type":"GROUP_HUB","urlPath":"/closedgroup/:groupHubId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"CommunityPage","type":"COMMUNITY","urlPath":"/","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"ForumMessagePage","type":"FORUM_TOPIC","urlPath":"/discussions/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"IdeaPostPage","type":"IDEA","urlPath":"/category/:categoryId/ideas/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1730142000000,"localOverride":null,"page":{"id":"CommunityHub.Page","type":"CUSTOM","urlPath":"/Directory","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"BlogMessagePage","type":"BLOG_ARTICLE","urlPath":"/blog/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"RegistrationPage","type":"USER","urlPath":"/register","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"EditGroupHubPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"ForumEditPage","type":"FORUM","urlPath":"/discussions/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"ResetPasswordPage","type":"USER","urlPath":"/resetpassword/:userId/:resetPasswordToken","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1730142000000,"localOverride":null,"page":{"id":"AllBlogs.Page","type":"CUSTOM","urlPath":"/blogs","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"TkbMessagePage","type":"TKB_ARTICLE","urlPath":"/kb/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"BlogEditPage","type":"BLOG","urlPath":"/blog/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"ManageUsersPage","type":"USER","urlPath":"/users/manage/:tab?/:manageUsersTab?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"ForumReplyPage","type":"FORUM_REPLY","urlPath":"/discussions/:boardId/:messageSubject/:messageId/replies/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"PrivacyPolicyPage","type":"COMMUNITY","urlPath":"/privacypolicy","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"NotificationPage","type":"COMMUNITY","urlPath":"/notifications","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"UserPage","type":"USER","urlPath":"/users/:login/:userId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"OccasionReplyPage","type":"OCCASION_REPLY","urlPath":"/event/:boardId/:messageSubject/:messageId/comments/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"ManageMembersPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId/manage/:tab?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"SearchResultsPage","type":"COMMUNITY","urlPath":"/search","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"BlogReplyPage","type":"BLOG_REPLY","urlPath":"/blog/:boardId/:messageSubject/:messageId/replies/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"GroupHubPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"TermsOfServicePage","type":"COMMUNITY","urlPath":"/termsofservice","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"CategoryPage","type":"CATEGORY","urlPath":"/category/:categoryId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"ForumViewAllTopicsPage","type":"FORUM","urlPath":"/category/:categoryId/discussions/:boardId/all-topics/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"TkbPostPage","type":"TKB","urlPath":"/category/:categoryId/kbs/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1741257668098,"localOverride":null,"page":{"id":"GroupHubPostPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"}],"localOverride":false},"CachedAsset:text:en_US-components/context/AppContext/AppContextProvider-0":{"__typename":"CachedAsset","id":"text:en_US-components/context/AppContext/AppContextProvider-0","value":{"noCommunity":"Cannot find community","noUser":"Cannot find current user","noNode":"Cannot find node with id {nodeId}","noMessage":"Cannot find message with id {messageId}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/common/Loading/LoadingDot-0":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/common/Loading/LoadingDot-0","value":{"title":"Loading..."},"localOverride":false},"User:user:-1":{"__typename":"User","id":"user:-1","uid":-1,"login":"Deleted","email":"","avatar":null,"rank":null,"kudosWeight":1,"registrationData":{"__typename":"RegistrationData","status":"ANONYMOUS","registrationTime":null,"confirmEmailStatus":false,"registrationAccessLevel":"VIEW","ssoRegistrationFields":[]},"ssoId":null,"profileSettings":{"__typename":"ProfileSettings","dateDisplayStyle":{"__typename":"InheritableStringSettingWithPossibleValues","key":"layout.friendly_dates_enabled","value":"false","localValue":"true","possibleValues":["true","false"]},"dateDisplayFormat":{"__typename":"InheritableStringSetting","key":"layout.format_pattern_date","value":"MMM dd yyyy","localValue":"MM-dd-yyyy"},"language":{"__typename":"InheritableStringSettingWithPossibleValues","key":"profile.language","value":"en-US","localValue":"en","possibleValues":["en-US"]}},"deleted":false},"Theme:customTheme1":{"__typename":"Theme","id":"customTheme1"},"Category:category:FastTrack":{"__typename":"Category","id":"category:FastTrack","entityType":"CATEGORY","displayId":"FastTrack","nodeType":"category","depth":3,"title":"Microsoft FastTrack","shortTitle":"Microsoft FastTrack","parent":{"__ref":"Category:category:products-services"}},"Category:category:top":{"__typename":"Category","id":"category:top","displayId":"top","nodeType":"category","depth":0,"title":"Top","entityType":"CATEGORY","shortTitle":"Top"},"Category:category:communities":{"__typename":"Category","id":"category:communities","displayId":"communities","nodeType":"category","depth":1,"parent":{"__ref":"Category:category:top"},"title":"Communities","entityType":"CATEGORY","shortTitle":"Communities"},"Category:category:products-services":{"__typename":"Category","id":"category:products-services","displayId":"products-services","nodeType":"category","depth":2,"parent":{"__ref":"Category:category:communities"},"title":"Products","entityType":"CATEGORY","shortTitle":"Products"},"Blog:board:FastTrackforAzureBlog":{"__typename":"Blog","id":"board:FastTrackforAzureBlog","entityType":"BLOG","displayId":"FastTrackforAzureBlog","nodeType":"board","depth":4,"conversationStyle":"BLOG","title":"FastTrack for Azure","description":"","avatar":null,"profileSettings":{"__typename":"ProfileSettings","language":null},"parent":{"__ref":"Category:category:FastTrack"},"ancestors":{"__typename":"CoreNodeConnection","edges":[{"__typename":"CoreNodeEdge","node":{"__ref":"Community:community:gxcuf89792"}},{"__typename":"CoreNodeEdge","node":{"__ref":"Category:category:communities"}},{"__typename":"CoreNodeEdge","node":{"__ref":"Category:category:products-services"}},{"__typename":"CoreNodeEdge","node":{"__ref":"Category:category:FastTrack"}}]},"userContext":{"__typename":"NodeUserContext","canAddAttachments":false,"canUpdateNode":false,"canPostMessages":false,"isSubscribed":false},"boardPolicies":{"__typename":"BoardPolicies","canPublishArticleOnCreate":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.forums.policy_can_publish_on_create_workflow_action.accessDenied","key":"error.lithium.policies.forums.policy_can_publish_on_create_workflow_action.accessDenied","args":[]}}},"shortTitle":"FastTrack for Azure","repliesProperties":{"__typename":"RepliesProperties","sortOrder":"REVERSE_PUBLISH_TIME","repliesFormat":"threaded"},"tagProperties":{"__typename":"TagNodeProperties","tagsEnabled":{"__typename":"PolicyResult","failureReason":null}},"requireTags":true,"tagType":"PRESET_ONLY"},"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/cmstNC05WEo0blc\"}":{"__typename":"AssociatedImage","url":"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/cmstNC05WEo0blc","height":512,"width":512,"mimeType":"image/png"},"Rank:rank:4":{"__typename":"Rank","id":"rank:4","position":6,"name":"Microsoft","color":"333333","icon":{"__ref":"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/cmstNC05WEo0blc\"}"},"rankStyle":"OUTLINE"},"User:user:988334":{"__typename":"User","id":"user:988334","uid":988334,"login":"paolosalvatori","deleted":false,"avatar":{"__typename":"UserAvatar","url":"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/dS05ODgzMzQtMzg1MjYyaTE4QTU5MkIyQUVCMkM0MDE"},"rank":{"__ref":"Rank:rank:4"},"email":"","messagesCount":67,"biography":null,"topicsCount":30,"kudosReceivedCount":159,"kudosGivenCount":29,"kudosWeight":1,"registrationData":{"__typename":"RegistrationData","status":null,"registrationTime":"2021-03-05T07:56:49.951-08:00","confirmEmailStatus":null},"followersCount":null,"solutionsCount":0},"BlogTopicMessage:message:3802069":{"__typename":"BlogTopicMessage","uid":3802069,"subject":"How to install an AKS cluster with the Istio service mesh add-on via Bicep","id":"message:3802069","revisionNum":1,"repliesCount":0,"author":{"__ref":"User:user:988334"},"depth":0,"hasGivenKudo":false,"board":{"__ref":"Blog:board:FastTrackforAzureBlog"},"conversation":{"__ref":"Conversation:conversation:3802069"},"messagePolicies":{"__typename":"MessagePolicies","canPublishArticleOnEdit":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.forums.policy_can_publish_on_edit_workflow_action.accessDenied","key":"error.lithium.policies.forums.policy_can_publish_on_edit_workflow_action.accessDenied","args":[]}},"canModerateSpamMessage":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.feature.moderation_spam.action.moderate_entity.allowed.accessDenied","key":"error.lithium.policies.feature.moderation_spam.action.moderate_entity.allowed.accessDenied","args":[]}}},"contentWorkflow":{"__typename":"ContentWorkflow","state":"PUBLISH","scheduledPublishTime":null,"scheduledTimezone":null,"userContext":{"__typename":"MessageWorkflowContext","canSubmitForReview":null,"canEdit":false,"canRecall":null,"canSubmitForPublication":null,"canReturnToAuthor":null,"canPublish":null,"canReturnToReview":null,"canSchedule":false},"shortScheduledTimezone":null},"readOnly":false,"editFrozen":false,"moderationData":{"__ref":"ModerationData:moderation_data:3802069"},"teaser":"

This article shows how to create an Azure Kubernetes Service(AKS) cluster with the Istio Service Mesh add-on via Bicep and deploy the bookinfo sample application via a Deployment Script.

\n

\n

\n

","body":"

Istio addresses the challenges developers and operators face with a distributed or microservices architecture. The Istio-based service mesh add-on provides an officially supported and tested Azure Kubernetes Service (AKS) integration.

\n

This article shows how to create an Azure Kubernetes Service(AKS) cluster with the Istio Service Mesh add-on via Bicep and deploy the bookinfo sample application via a Deployment Script.

\n

For more information, see the following articles:

\n

 

\n\n

 

\n

Code

\n

You can find the code for this sample in this Azure Sample.

\n

 

\n

Prerequisites

\n\n

 

\n

You can run az --version to verify above versions. To install the aks-preview extension, run the following command:

\n

 

\n
az extension add --name aks-preview
\n

 

\n

Run the following command to update to the latest version of the extension released:

\n

 

\n
az extension update --name aks-preview
\n

 

\n

Architecture

\n

This sample provides a set of Bicep modules to deploy an Azure Kubernetes Service(AKS) cluster with the Istio Service Mesh add-on via Bicep. A Deployment Script is used to deploy the bookinfo sample application. The following diagram shows the architecture and network topology deployed by the sample:

\n
 
\n

\n

\n

 

\n

Bicep modules are parametric, so you can choose any network plugin:

\n

 

\n\n

The Bicep modules also allow installing the following extensions and add-ons for Azure Kubernetes Service(AKS):

\n

In addition, this sample shows how to deploy an Azure Kubernetes Service cluster with the following features:

\n

 

\n\n

 

\n
\n

NOTE
you can't install both the Open Service Mesh add-on and Istio Service Mesh add-on on the same AKS cluster.

\n
\n

 

\n

In a production environment, we strongly recommend deploying a private AKS cluster with Uptime SLA. For more information, see private AKS cluster with a Public DNS address. Alternatively, you can deploy a public AKS cluster and secure access to the API server using authorized IP address ranges.

\n

 

\n

Bicep modules deploy the following Azure resources:

\n

 

\n\n

 

\n
\n

NOTE
You can find the architecture.vsdx file used for the diagram under the visio folder.

\n
\n

 

\n

What is Bicep?

\n

Bicep is a domain-specific language (DSL) that uses a declarative syntax to deploy Azure resources. It provides concise syntax, reliable type safety, and support for code reuse. Bicep offers the best authoring experience for your infrastructure-as-code solutions in Azure.

\n

 

\n

What is a Service Mesh?

\n

Modern applications are typically architected as distributed collections of microservices, with each collection of microservices performing some discrete business function. A service mesh is a dedicated infrastructure layer that you can add to your applications. It allows you to transparently add capabilities like observability, traffic management, and security, without adding them to your own code. The term service mesh describes both the type of software you use to implement this pattern, and the security or network domain that is created when you use that software.

\n

As the deployment of distributed services, such as in a Kubernetes-based system, grows in size and complexity, it can become harder to understand and manage. You may need to implement capabilities such as discovery, load balancing, failure recovery, metrics, and monitoring. A service mesh can also address more complex operational requirements like A/B testing, canary deployments, rate limiting, access control, encryption, and end-to-end authentication.

\n

Service-to-service communication is what makes a distributed application possible. Routing this communication, both within and across application clusters, becomes increasingly complex as the number of services grow. Istio helps reduce this complexity while easing the strain on development teams.

\n

 

\n

What is Istio?

\n

Istio is an open-source service mesh that layers transparently onto existing distributed applications. Istio’s powerful features provide a uniform and more efficient way to secure, connect, and monitor services. Istio enables load balancing, service-to-service authentication, and monitoring – with few or no service code changes. Its powerful control plane brings vital features, including:

\n

 

\n\n

 

\n

How is the add-on different from open-source Istio?

\n

This service mesh add-on uses and builds on top of open-source Istio. The add-on flavor provides the following extra benefits:

\n

 

\n\n

 

\n

Limitations

\n

Istio-based service mesh add-on for AKS has the following limitations:

\n\n

 

\n

Register the AzureServiceMeshPreview feature flag

\n

Register the AzureServiceMeshPreview feature flag by using the az feature register command:

\n

 

\n
az feature register --namespace \"Microsoft.ContainerService\" --name \"AzureServiceMeshPreview\"
\n

 

\n

It takes a few minutes for the feature to register. Verify the registration status by using the az feature show command:

\n

 

\n
az feature show --namespace \"Microsoft.ContainerService\" --name \"AzureServiceMeshPreview\"
\n

 

\n

When the status reflects Registered, refresh the registration of the Microsoft.ContainerService resource provider by using the az provider register command:

\n

 

\n
az provider register --namespace Microsoft.ContainerService
\n

 

\n

Deploy the Bicep modules

\n

You can deploy the Bicep modules in the bicep folder using the deploy.sh Bash script in the same folder. Specify a value for the following parameters in the deploy.sh script and main.parameters.json parameters file before deploying the Bicep modules.

\n

 

\n\n

 

\n

We suggest reading sensitive configuration data such as passwords or SSH keys from a pre-existing Azure Key Vault resource. For more information, see Use Azure Key Vault to pass secure parameter value during Bicep deployment.

\n

 

\n

AKS Cluster Bicep module

\n

The following table contains the Bicep code used to deploy the Azure Kubernetes Service(AKS) cluster.

\n

 

\n
// Parameters\n@description('Specifies the name of the AKS cluster.')\nparam name string = 'aks-${uniqueString(resourceGroup().id)}'\n\n@description('Specifies whether to enable API server VNET integration for the cluster or not.')\nparam enableVnetIntegration bool = true\n\n@description('Specifies the name of the existing virtual network.')\nparam virtualNetworkName string\n\n@description('Specifies the name of the subnet hosting the worker nodes of the default system agent pool of the AKS cluster.')\nparam systemAgentPoolSubnetName string = 'SystemSubnet'\n\n@description('Specifies the name of the subnet hosting the worker nodes of the user agent pool of the AKS cluster.')\nparam userAgentPoolSubnetName string = 'UserSubnet'\n\n@description('Specifies the name of the subnet hosting the pods running in the AKS cluster.')\nparam podSubnetName string = 'PodSubnet'\n\n@description('Specifies the name of the subnet delegated to the API server when configuring the AKS cluster to use API server VNET integration.')\nparam apiServerSubnetName string = 'ApiServerSubnet'\n\n@description('Specifies the name of the AKS user-defined managed identity.')\nparam managedIdentityName string\n\n@description('Specifies the DNS prefix specified when creating the managed cluster.')\nparam dnsPrefix string = name\n\n@description('Specifies the network plugin used for building Kubernetes network. - azure or kubenet.')\n@allowed([\n  'azure'\n  'kubenet'\n])\nparam networkPlugin string = 'azure'\n\n@description('Specifies the Network plugin mode used for building the Kubernetes network.')\n@allowed([\n  ''\n  'Overlay'\n])\n\nparam networkPluginMode string = ''\n\n@description('Specifies the network policy used for building Kubernetes network. - calico or azure')\n@allowed([\n  'azure'\n  'calico'\n])\nparam networkPolicy string = 'azure'\n\n@description('Specifies the CIDR notation IP range from which to assign pod IPs when kubenet is used.')\nparam podCidr string = '192.168.0.0/16'\n\n@description('A CIDR notation IP range from which to assign service cluster IPs. It must not overlap with any Subnet IP ranges.')\nparam serviceCidr string = '172.16.0.0/16'\n\n@description('Specifies the IP address assigned to the Kubernetes DNS service. It must be within the Kubernetes service address range specified in serviceCidr.')\nparam dnsServiceIP string = '172.16.0.10'\n\n@description('Specifies the CIDR notation IP range assigned to the Docker bridge network. It must not overlap with any Subnet IP ranges or the Kubernetes service address range.')\nparam dockerBridgeCidr string = '172.17.0.1/16'\n\n@description('Specifies the sku of the load balancer used by the virtual machine scale sets used by nodepools.')\n@allowed([\n  'basic'\n  'standard'\n])\nparam loadBalancerSku string = 'standard'\n\n@description('Specifies outbound (egress) routing method. - loadBalancer or userDefinedRouting.')\n@allowed([\n  'loadBalancer'\n  'managedNATGateway'\n  'userAssignedNATGateway'\n  'userDefinedRouting'\n])\nparam outboundType string = 'loadBalancer'\n\n@description('Specifies the tier of a managed cluster SKU: Paid or Free')\n@allowed([\n  'Standard'\n  'Free'\n])\nparam skuTier string = 'Standard'\n\n@description('Specifies the version of Kubernetes specified when creating the managed cluster.')\nparam kubernetesVersion string = '1.18.8'\n\n@description('Specifies the administrator username of Linux virtual machines.')\nparam adminUsername string = 'azureuser'\n\n@description('Specifies the SSH RSA public key string for the Linux nodes.')\nparam sshPublicKey string\n\n@description('Specifies the tenant id of the Azure Active Directory used by the AKS cluster for authentication.')\nparam aadProfileTenantId string = subscription().tenantId\n\n@description('Specifies the AAD group object IDs that will have admin role of the cluster.')\nparam aadProfileAdminGroupObjectIDs array = []\n\n@description('Specifies the upgrade channel for auto upgrade. Allowed values include rapid, stable, patch, node-image, none.')\n@allowed([\n  'rapid'\n  'stable'\n  'patch'\n  'node-image'\n  'none'\n])\nparam upgradeChannel string = 'stable'\n\n@description('Specifies whether to create the cluster as a private cluster or not.')\nparam enablePrivateCluster bool = true\n\n@description('Specifies the Private DNS Zone mode for private cluster. When the value is equal to None, a Public DNS Zone is used in place of a Private DNS Zone')\nparam privateDNSZone string = 'none'\n\n@description('Specifies whether to create additional public FQDN for private cluster or not.')\nparam enablePrivateClusterPublicFQDN bool = true\n\n@description('Specifies whether to enable managed AAD integration.')\nparam aadProfileManaged bool = true\n\n@description('Specifies whether to  to enable Azure RBAC for Kubernetes authorization.')\nparam aadProfileEnableAzureRBAC bool = true\n\n@description('Specifies the unique name of of the system node pool profile in the context of the subscription and resource group.')\nparam systemAgentPoolName string = 'nodepool1'\n\n@description('Specifies the vm size of nodes in the system node pool.')\nparam systemAgentPoolVmSize string = 'Standard_DS5_v2'\n\n@description('Specifies the OS Disk Size in GB to be used to specify the disk size for every machine in the system agent pool. If you specify 0, it will apply the default osDisk size according to the vmSize specified.')\nparam systemAgentPoolOsDiskSizeGB int = 100\n\n@description('Specifies the OS disk type to be used for machines in a given agent pool. Allowed values are \\'Ephemeral\\' and \\'Managed\\'. If unspecified, defaults to \\'Ephemeral\\' when the VM supports ephemeral OS and has a cache disk larger than the requested OSDiskSizeGB. Otherwise, defaults to \\'Managed\\'. May not be changed after creation. - Managed or Ephemeral')\n@allowed([\n  'Ephemeral'\n  'Managed'\n])\nparam systemAgentPoolOsDiskType string = 'Ephemeral'\n\n@description('Specifies the number of agents (VMs) to host docker containers in the system node pool. Allowed values must be in the range of 1 to 100 (inclusive). The default value is 1.')\nparam systemAgentPoolAgentCount int = 3\n\n@description('Specifies the OS type for the vms in the system node pool. Choose from Linux and Windows. Default to Linux.')\n@allowed([\n  'Linux'\n  'Windows'\n])\nparam systemAgentPoolOsType string = 'Linux'\n\n@description('Specifies the maximum number of pods that can run on a node in the system node pool. The maximum number of pods per node in an AKS cluster is 250. The default maximum number of pods per node varies between kubenet and Azure CNI networking, and the method of cluster deployment.')\nparam systemAgentPoolMaxPods int = 30\n\n@description('Specifies the maximum number of nodes for auto-scaling for the system node pool.')\nparam systemAgentPoolMaxCount int = 5\n\n@description('Specifies the minimum number of nodes for auto-scaling for the system node pool.')\nparam systemAgentPoolMinCount int = 3\n\n@description('Specifies whether to enable auto-scaling for the system node pool.')\nparam systemAgentPoolEnableAutoScaling bool = true\n\n@description('Specifies the virtual machine scale set priority in the system node pool: Spot or Regular.')\n@allowed([\n  'Spot'\n  'Regular'\n])\nparam systemAgentPoolScaleSetPriority string = 'Regular'\n\n@description('Specifies the ScaleSetEvictionPolicy to be used to specify eviction policy for spot virtual machine scale set. Default to Delete. Allowed values are Delete or Deallocate.')\n@allowed([\n  'Delete'\n  'Deallocate'\n])\nparam systemAgentPoolScaleSetEvictionPolicy string = 'Delete'\n\n@description('Specifies the Agent pool node labels to be persisted across all nodes in the system node pool.')\nparam systemAgentPoolNodeLabels object = {}\n\n@description('Specifies the taints added to new nodes during node pool create and scale. For example, key=value:NoSchedule.')\nparam systemAgentPoolNodeTaints array = []\n\n@description('Determines the placement of emptyDir volumes, container runtime data root, and Kubelet ephemeral storage.')\n@allowed([\n  'OS'\n  'Temporary'\n])\nparam systemAgentPoolKubeletDiskType string = 'OS'\n\n@description('Specifies the type for the system node pool: VirtualMachineScaleSets or AvailabilitySet')\n@allowed([\n  'VirtualMachineScaleSets'\n  'AvailabilitySet'\n])\nparam systemAgentPoolType string = 'VirtualMachineScaleSets'\n\n@description('Specifies the availability zones for the agent nodes in the system node pool. Requirese the use of VirtualMachineScaleSets as node pool type.')\nparam systemAgentPoolAvailabilityZones array = [\n  '1'\n  '2'\n  '3'\n]\n\n@description('Specifies the unique name of of the user node pool profile in the context of the subscription and resource group.')\nparam userAgentPoolName string = 'nodepool1'\n\n@description('Specifies the vm size of nodes in the user node pool.')\nparam userAgentPoolVmSize string = 'Standard_DS5_v2'\n\n@description('Specifies the OS Disk Size in GB to be used to specify the disk size for every machine in the system agent pool. If you specify 0, it will apply the default osDisk size according to the vmSize specified..')\nparam userAgentPoolOsDiskSizeGB int = 100\n\n@description('Specifies the OS disk type to be used for machines in a given agent pool. Allowed values are \\'Ephemeral\\' and \\'Managed\\'. If unspecified, defaults to \\'Ephemeral\\' when the VM supports ephemeral OS and has a cache disk larger than the requested OSDiskSizeGB. Otherwise, defaults to \\'Managed\\'. May not be changed after creation. - Managed or Ephemeral')\n@allowed([\n  'Ephemeral'\n  'Managed'\n])\nparam userAgentPoolOsDiskType string = 'Ephemeral'\n\n@description('Specifies the number of agents (VMs) to host docker containers in the user node pool. Allowed values must be in the range of 1 to 100 (inclusive). The default value is 1.')\nparam userAgentPoolAgentCount int = 3\n\n@description('Specifies the OS type for the vms in the user node pool. Choose from Linux and Windows. Default to Linux.')\n@allowed([\n  'Linux'\n  'Windows'\n])\nparam userAgentPoolOsType string = 'Linux'\n\n@description('Specifies the maximum number of pods that can run on a node in the user node pool. The maximum number of pods per node in an AKS cluster is 250. The default maximum number of pods per node varies between kubenet and Azure CNI networking, and the method of cluster deployment.')\nparam userAgentPoolMaxPods int = 30\n\n@description('Specifies the maximum number of nodes for auto-scaling for the user node pool.')\nparam userAgentPoolMaxCount int = 5\n\n@description('Specifies the minimum number of nodes for auto-scaling for the user node pool.')\nparam userAgentPoolMinCount int = 3\n\n@description('Specifies whether to enable auto-scaling for the user node pool.')\nparam userAgentPoolEnableAutoScaling bool = true\n\n@description('Specifies the virtual machine scale set priority in the user node pool: Spot or Regular.')\n@allowed([\n  'Spot'\n  'Regular'\n])\nparam userAgentPoolScaleSetPriority string = 'Regular'\n\n@description('Specifies the ScaleSetEvictionPolicy to be used to specify eviction policy for spot virtual machine scale set. Default to Delete. Allowed values are Delete or Deallocate.')\n@allowed([\n  'Delete'\n  'Deallocate'\n])\nparam userAgentPoolScaleSetEvictionPolicy string = 'Delete'\n\n@description('Specifies the Agent pool node labels to be persisted across all nodes in the user node pool.')\nparam userAgentPoolNodeLabels object = {}\n\n@description('Specifies the taints added to new nodes during node pool create and scale. For example, key=value:NoSchedule.')\n@allowed([\n  'OS'\n  'Temporary'\n])\nparam userAgentPoolNodeTaints array = []\n\n@description('Determines the placement of emptyDir volumes, container runtime data root, and Kubelet ephemeral storage.')\nparam userAgentPoolKubeletDiskType string = 'OS'\n\n@description('Specifies the type for the user node pool: VirtualMachineScaleSets or AvailabilitySet')\n@allowed([\n  'VirtualMachineScaleSets'\n  'AvailabilitySet'\n])\nparam userAgentPoolType string = 'VirtualMachineScaleSets'\n\n@description('Specifies the availability zones for the agent nodes in the user node pool. Requirese the use of VirtualMachineScaleSets as node pool type.')\nparam userAgentPoolAvailabilityZones array = [\n  '1'\n  '2'\n  '3'\n]\n\n@description('Specifies whether the httpApplicationRouting add-on is enabled or not.')\nparam httpApplicationRoutingEnabled bool = false\n\n@description('Specifies whether the Open Service Mesh add-on is enabled or not.')\nparam openServiceMeshEnabled bool = false\n\n@description('Specifies whether the Istio Service Mesh add-on is enabled or not.')\nparam istioServiceMeshEnabled bool = false\n\n@description('Specifies whether the Istio Ingress Gateway is enabled or not.')\nparam istioIngressGatewayEnabled bool = false\n\n@description('Specifies the type of the Istio Ingress Gateway.')\n@allowed([\n  'Internal'\n  'External'\n])\nparam istioIngressGatewayType string = 'External'\n\n@description('Specifies whether the Kubernetes Event-Driven Autoscaler (KEDA) add-on is enabled or not.')\nparam kedaEnabled bool = false\n\n@description('Specifies whether the Dapr extension is enabled or not.')\nparam daprEnabled bool = false\n\n@description('Enable high availability (HA) mode for the Dapr control plane')\nparam daprHaEnabled bool = false\n\n@description('Specifies whether the Flux V2 extension is enabled or not.')\nparam fluxGitOpsEnabled bool = false\n\n@description('Specifies whether the Vertical Pod Autoscaler is enabled or not.')\nparam verticalPodAutoscalerEnabled bool = false\n\n@description('Specifies whether the aciConnectorLinux add-on is enabled or not.')\nparam aciConnectorLinuxEnabled bool = false\n\n@description('Specifies whether the azurepolicy add-on is enabled or not.')\nparam azurePolicyEnabled bool = true\n\n@description('Specifies whether the Azure Key Vault Provider for Secrets Store CSI Driver addon is enabled or not.')\nparam azureKeyvaultSecretsProviderEnabled bool = true\n\n@description('Specifies whether the kubeDashboard add-on is enabled or not.')\nparam kubeDashboardEnabled bool = false\n\n@description('Specifies whether the pod identity addon is enabled..')\nparam podIdentityProfileEnabled bool = false\n\n@description('Specifies whether the OIDC issuer is enabled.')\nparam oidcIssuerProfileEnabled bool = true\n\n@description('Specifies the scan interval of the auto-scaler of the AKS cluster.')\nparam autoScalerProfileScanInterval string = '10s'\n\n@description('Specifies the scale down delay after add of the auto-scaler of the AKS cluster.')\nparam autoScalerProfileScaleDownDelayAfterAdd string = '10m'\n\n@description('Specifies the scale down delay after delete of the auto-scaler of the AKS cluster.')\nparam autoScalerProfileScaleDownDelayAfterDelete string = '20s'\n\n@description('Specifies scale down delay after failure of the auto-scaler of the AKS cluster.')\nparam autoScalerProfileScaleDownDelayAfterFailure string = '3m'\n\n@description('Specifies the scale down unneeded time of the auto-scaler of the AKS cluster.')\nparam autoScalerProfileScaleDownUnneededTime string = '10m'\n\n@description('Specifies the scale down unready time of the auto-scaler of the AKS cluster.')\nparam autoScalerProfileScaleDownUnreadyTime string = '20m'\n\n@description('Specifies the utilization threshold of the auto-scaler of the AKS cluster.')\nparam autoScalerProfileUtilizationThreshold string = '0.5'\n\n@description('Specifies the max graceful termination time interval in seconds for the auto-scaler of the AKS cluster.')\nparam autoScalerProfileMaxGracefulTerminationSec string = '600'\n\n@description('Specifies the resource id of the Log Analytics workspace.')\nparam workspaceId string\n\n@description('Specifies the workspace data retention in days.')\nparam retentionInDays int = 60\n\n@description('Specifies the location.')\nparam location string = resourceGroup().location\n\n@description('Specifies the resource tags.')\nparam tags object\n\n@description('Specifies whether to enable the Azure Blob CSI Driver. The default value is false.')\nparam blobCSIDriverEnabled bool = false\n\n@description('Specifies whether to enable the Azure Disk CSI Driver. The default value is true.')\nparam diskCSIDriverEnabled bool = true\n\n@description('Specifies whether to enable the Azure File CSI Driver. The default value is true.')\nparam fileCSIDriverEnabled bool = true\n\n@description('Specifies whether to enable the Snapshot Controller. The default value is true.')\nparam snapshotControllerEnabled bool = true\n\n@description('Specifies whether to enable Defender threat detection. The default value is false.')\nparam defenderSecurityMonitoringEnabled bool = false\n\n@description('Specifies whether to enable ImageCleaner on AKS cluster. The default value is false.')\nparam imageCleanerEnabled bool = false\n\n@description('Specifies whether ImageCleaner scanning interval in hours.')\nparam imageCleanerIntervalHours int = 24\n\n@description('Specifies whether to enable Node Restriction. The default value is false.')\nparam nodeRestrictionEnabled bool = false\n\n@description('Specifies whether to enable Workload Identity. The default value is false.')\nparam workloadIdentityEnabled bool = false\n\n@description('Specifies whether creating the Application Gateway and enabling the Application Gateway Ingress Controller or not.')\nparam applicationGatewayEnabled bool = false\n\n@description('Specifies the resource id of the Azure Application Gateway.')\nparam applicationGatewayId string\n\n@description('Specifies the principal id of the managed identity of the Azure Application Gateway.')\nparam applicationGatewayManagedIdentityPrincipalId string\n\n@description('Specifies the name of the Key Vault resource.')\nparam keyVaultName string\n\n// Variables\nvar diagnosticSettingsName = 'diagnosticSettings'\nvar logCategories = [\n  'kube-apiserver'\n  'kube-audit'\n  'kube-audit-admin'\n  'kube-controller-manager'\n  'kube-scheduler'\n  'cluster-autoscaler'\n  'cloud-controller-manager'\n  'guard'\n  'csi-azuredisk-controller'\n  'csi-azurefile-controller'\n  'csi-snapshot-controller'\n]\nvar metricCategories = [\n  'AllMetrics'\n]\nvar logs = [for category in logCategories: {\n  category: category\n  enabled: true\n  retentionPolicy: {\n    enabled: true\n    days: retentionInDays\n  }\n}]\nvar metrics = [for category in metricCategories: {\n  category: category\n  enabled: true\n  retentionPolicy: {\n    enabled: true\n    days: retentionInDays\n  }\n}]\n\nvar managedIdentityOperatorRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')\nvar readerRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')\nvar contributorRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')\nvar keyVaultSecretsUserRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')\nvar MonitoringMetricsPublisherRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')\n\n// Resources\nresource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2021-09-30-preview' existing = {\n  name: managedIdentityName\n}\n\nresource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-08-01' existing = {\n  name: virtualNetworkName\n}\n\nresource systemAgentPoolSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-08-01' existing = {\n  parent: virtualNetwork\n  name: systemAgentPoolSubnetName\n}\n\nresource userAgentPoolSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-08-01' existing = {\n  parent: virtualNetwork\n  name: userAgentPoolSubnetName\n}\n\nresource podSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-08-01' existing = {\n  parent: virtualNetwork\n  name: podSubnetName\n}\n\nresource apiServerSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-08-01' existing = {\n  parent: virtualNetwork\n  name: apiServerSubnetName\n}\n\nresource aksCluster 'Microsoft.ContainerService/managedClusters@2023-03-02-preview' = {\n  name: name\n  location: location\n  tags: tags\n  sku: {\n    name: 'Base'\n    tier: skuTier\n  }\n  identity: {\n    type: 'UserAssigned'\n    userAssignedIdentities: {\n      '${managedIdentity.id}': {}\n    }\n  }\n  properties: {\n    kubernetesVersion: kubernetesVersion\n    dnsPrefix: dnsPrefix\n    agentPoolProfiles: [\n      {\n        name: toLower(systemAgentPoolName)\n        count: systemAgentPoolAgentCount\n        vmSize: systemAgentPoolVmSize\n        osDiskSizeGB: systemAgentPoolOsDiskSizeGB\n        osDiskType: systemAgentPoolOsDiskType\n        vnetSubnetID: systemAgentPoolSubnet.id\n        podSubnetID: networkPluginMode == 'Overlay' ? null : podSubnet.id\n        maxPods: systemAgentPoolMaxPods\n        osType: systemAgentPoolOsType\n        maxCount: systemAgentPoolMaxCount\n        minCount: systemAgentPoolMinCount\n        scaleSetPriority: systemAgentPoolScaleSetPriority\n        scaleSetEvictionPolicy: systemAgentPoolScaleSetEvictionPolicy\n        enableAutoScaling: systemAgentPoolEnableAutoScaling\n        mode: 'System'\n        type: systemAgentPoolType\n        availabilityZones: systemAgentPoolAvailabilityZones\n        nodeLabels: systemAgentPoolNodeLabels\n        nodeTaints: systemAgentPoolNodeTaints\n        kubeletDiskType: systemAgentPoolKubeletDiskType\n      }\n      {\n        name: toLower(userAgentPoolName)\n        count: userAgentPoolAgentCount\n        vmSize: userAgentPoolVmSize\n        osDiskSizeGB: userAgentPoolOsDiskSizeGB\n        osDiskType: userAgentPoolOsDiskType\n        vnetSubnetID: userAgentPoolSubnet.id\n        podSubnetID: networkPluginMode == 'Overlay' ? null : podSubnet.id\n        maxPods: userAgentPoolMaxPods\n        osType: userAgentPoolOsType\n        maxCount: userAgentPoolMaxCount\n        minCount: userAgentPoolMinCount\n        scaleSetPriority: userAgentPoolScaleSetPriority\n        scaleSetEvictionPolicy: userAgentPoolScaleSetEvictionPolicy\n        enableAutoScaling: userAgentPoolEnableAutoScaling\n        mode: 'User'\n        type: userAgentPoolType\n        availabilityZones: userAgentPoolAvailabilityZones\n        nodeLabels: userAgentPoolNodeLabels\n        nodeTaints: userAgentPoolNodeTaints\n        kubeletDiskType: userAgentPoolKubeletDiskType\n      }\n    ]\n    linuxProfile: {\n      adminUsername: adminUsername\n      ssh: {\n        publicKeys: [\n          {\n            keyData: sshPublicKey\n          }\n        ]\n      }\n    }\n    addonProfiles: {\n      ingressApplicationGateway: applicationGatewayEnabled ? {\n        config: {\n          applicationGatewayId: applicationGatewayEnabled ? applicationGatewayId : null\n        }\n        enabled: true\n      } : {\n        enabled: false\n      }\n      httpApplicationRouting: {\n        enabled: httpApplicationRoutingEnabled\n      }\n      openServiceMesh: {\n        enabled: openServiceMeshEnabled\n        config: {}\n      }\n      omsagent: {\n        enabled: true\n        config: {\n          logAnalyticsWorkspaceResourceID: workspaceId\n        }\n      }\n      aciConnectorLinux: {\n        enabled: aciConnectorLinuxEnabled\n      }\n      azurepolicy: {\n        enabled: azurePolicyEnabled\n        config: {\n          version: 'v2'\n        }\n      }\n      kubeDashboard: {\n        enabled: kubeDashboardEnabled\n      }\n      azureKeyvaultSecretsProvider: {\n        config: {\n          enableSecretRotation: 'false'\n        }\n        enabled: azureKeyvaultSecretsProviderEnabled\n      }\n    }\n    podIdentityProfile: {\n      enabled: podIdentityProfileEnabled\n    }\n    oidcIssuerProfile: {\n      enabled: oidcIssuerProfileEnabled\n    }\n    enableRBAC: true\n    networkProfile: {\n      networkPlugin: networkPlugin\n      networkPluginMode: networkPlugin == 'azure' ? networkPluginMode : ''\n      networkPolicy: networkPolicy\n      podCidr: networkPlugin == 'kubenet' || networkPluginMode == 'Overlay' ? podCidr : null\n      serviceCidr: serviceCidr\n      dnsServiceIP: dnsServiceIP\n      dockerBridgeCidr: dockerBridgeCidr\n      outboundType: outboundType\n      loadBalancerSku: loadBalancerSku\n      loadBalancerProfile: null\n    }\n    workloadAutoScalerProfile: {\n      keda: {\n        enabled: kedaEnabled\n      }\n      verticalPodAutoscaler: {\n        controlledValues: 'RequestsAndLimits'\n        enabled: verticalPodAutoscalerEnabled\n        updateMode: 'Off'\n      }\n    }\n    aadProfile: {\n      clientAppID: null\n      serverAppID: null\n      serverAppSecret: null\n      managed: aadProfileManaged\n      enableAzureRBAC: aadProfileEnableAzureRBAC\n      adminGroupObjectIDs: aadProfileAdminGroupObjectIDs\n      tenantID: aadProfileTenantId\n    }\n    autoUpgradeProfile: {\n      upgradeChannel: upgradeChannel\n    }\n    autoScalerProfile: {\n      'scan-interval': autoScalerProfileScanInterval\n      'scale-down-delay-after-add': autoScalerProfileScaleDownDelayAfterAdd\n      'scale-down-delay-after-delete': autoScalerProfileScaleDownDelayAfterDelete\n      'scale-down-delay-after-failure': autoScalerProfileScaleDownDelayAfterFailure\n      'scale-down-unneeded-time': autoScalerProfileScaleDownUnneededTime\n      'scale-down-unready-time': autoScalerProfileScaleDownUnreadyTime\n      'scale-down-utilization-threshold': autoScalerProfileUtilizationThreshold\n      'max-graceful-termination-sec': autoScalerProfileMaxGracefulTerminationSec\n    }\n    apiServerAccessProfile: {\n      enablePrivateCluster: enablePrivateCluster\n      enableVnetIntegration: enableVnetIntegration\n      privateDNSZone: enablePrivateCluster ? privateDNSZone : null\n      enablePrivateClusterPublicFQDN: enablePrivateClusterPublicFQDN\n      subnetId: apiServerSubnet.id\n    }\n    securityProfile: {\n      defender: {\n        logAnalyticsWorkspaceResourceId: workspaceId\n        securityMonitoring: {\n          enabled: defenderSecurityMonitoringEnabled\n        }\n      }\n      imageCleaner: {\n        enabled: imageCleanerEnabled\n        intervalHours: imageCleanerIntervalHours\n      }\n      nodeRestriction: {\n        enabled: nodeRestrictionEnabled\n      }\n      workloadIdentity: {\n        enabled: workloadIdentityEnabled\n      }\n    }\n    serviceMeshProfile: istioServiceMeshEnabled ? {\n      istio: {\n        components: {\n          ingressGateways: istioIngressGatewayEnabled ? [\n            {\n              enabled: true\n              mode: istioIngressGatewayType\n            }\n          ] : null\n        }\n      }\n      mode: 'Istio'\n    } : null\n    storageProfile: {\n      blobCSIDriver: {\n        enabled: blobCSIDriverEnabled\n      }\n      diskCSIDriver: {\n        enabled: diskCSIDriverEnabled\n      }\n      fileCSIDriver: {\n        enabled: fileCSIDriverEnabled\n      }\n      snapshotController: {\n        enabled: snapshotControllerEnabled\n      }\n    }\n  }\n}\n\n// AGIC's identity requires \"Managed Identity Operator\" permission over the user assigned identity of Application Gateway.\nresource applicationGatewayAgicManagedIdentityOperatorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (applicationGatewayEnabled) {\n  scope: resourceGroup()\n  name: guid(aksCluster.id, applicationGatewayManagedIdentityPrincipalId, 'managedIdentityOperator')\n  properties: {\n    roleDefinitionId: managedIdentityOperatorRoleDefinitionId\n    principalType: 'ServicePrincipal'\n    principalId: applicationGatewayEnabled ? aksCluster.properties.addonProfiles.ingressApplicationGateway.identity.objectId : null\n  }\n}\n\nresource applicationGatewayAgicContributorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (applicationGatewayEnabled) {\n  scope: resourceGroup()\n  name: guid(resourceGroup().id, 'ApplicationGateway', 'contributor')\n  properties: {\n    roleDefinitionId: contributorRoleDefinitionId\n    principalType: 'ServicePrincipal'\n    principalId: applicationGatewayEnabled ? aksCluster.properties.addonProfiles.ingressApplicationGateway.identity.objectId : null\n  }\n}\n\nresource applicationGatewayAgicReaderRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (applicationGatewayEnabled) {\n  scope: resourceGroup()\n  name: guid(resourceGroup().id, 'ApplicationGateway', 'reader')\n  properties: {\n    roleDefinitionId: readerRoleDefinitionId\n    principalType: 'ServicePrincipal'\n    principalId: applicationGatewayEnabled ? aksCluster.properties.addonProfiles.ingressApplicationGateway.identity.objectId : null\n  }\n}\n\nresource keyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = {\n  name: keyVaultName\n}\n\nresource keyVaultSecretsUserApplicationGatewayIdentityRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (applicationGatewayEnabled) {\n  scope: keyVault\n  name: guid(keyVault.id, 'ApplicationGateway', 'keyVaultSecretsUser')\n  properties: {\n    roleDefinitionId: keyVaultSecretsUserRoleDefinitionId\n    principalType: 'ServicePrincipal'\n    principalId: applicationGatewayManagedIdentityPrincipalId\n  }\n}\n\nresource keyVaultCSIdriverSecretsUserRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (azureKeyvaultSecretsProviderEnabled) {\n  scope: keyVault\n  name: guid(aksCluster.id, 'CSIDriver', keyVaultSecretsUserRoleDefinitionId)\n  properties: {\n    roleDefinitionId: keyVaultSecretsUserRoleDefinitionId\n    principalType: 'ServicePrincipal'\n    principalId: aksCluster.properties.addonProfiles.azureKeyvaultSecretsProvider.identity.objectId\n  }\n}\n\n// This role assignment enables AKS->LA Fast Alerting experience\nresource FastAlertingRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {\n  scope: aksCluster\n  name: guid(aksCluster.id, 'omsagent', MonitoringMetricsPublisherRoleDefinitionId)\n  properties: {\n    roleDefinitionId: MonitoringMetricsPublisherRoleDefinitionId\n    principalId: aksCluster.properties.addonProfiles.omsagent.identity.objectId\n    principalType: 'ServicePrincipal'\n  }\n}\n\n// Dapr Extension\nresource daprExtension 'Microsoft.KubernetesConfiguration/extensions@2022-04-02-preview' = if (daprEnabled) {\n  name: 'dapr'\n  scope: aksCluster\n  properties: {\n    extensionType: 'Microsoft.Dapr'\n    autoUpgradeMinorVersion: true\n    releaseTrain: 'Stable'\n    configurationSettings: {\n      'global.ha.enabled': '${daprHaEnabled}'\n    }\n    scope: {\n      cluster: {\n        releaseNamespace: 'dapr-system'\n      }\n    }\n    configurationProtectedSettings: {}\n  }\n}\n\n// Flux v2 Extension\nresource fluxAddon 'Microsoft.KubernetesConfiguration/extensions@2022-04-02-preview' = if (fluxGitOpsEnabled) {\n  name: 'flux'\n  scope: aksCluster\n  properties: {\n    extensionType: 'microsoft.flux'\n    autoUpgradeMinorVersion: true\n    releaseTrain: 'Stable'\n    scope: {\n      cluster: {\n        releaseNamespace: 'flux-system'\n      }\n    }\n    configurationProtectedSettings: {}\n  }\n  dependsOn: [ daprExtension ] //Chaining dependencies because of: https://github.com/Azure/AKS-Construction/issues/385\n}\n\n// Diagnostic Settings\nresource diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {\n  name: diagnosticSettingsName\n  scope: aksCluster\n  properties: {\n    workspaceId: workspaceId\n    logs: logs\n    metrics: metrics\n  }\n}\n\n// Output\noutput id string = aksCluster.id\noutput name string = aksCluster.name
\n

 

\n

You can use the following parameters to control the installation of the Istio Service Mesh add-on:

\n

 

\n
@description('Specifies whether the Istio Service Mesh add-on is enabled or not.')\nparam istioServiceMeshEnabled bool = false\n\n@description('Specifies whether the Istio Ingress Gateway is enabled or not.')\nparam istioIngressGatewayEnabled bool = false\n\n@description('Specifies the type of the Istio Ingress Gateway.')\n@allowed([\n  'Internal'\n  'External'\n])\nparam istioIngressGatewayType string = 'External'
\n

 

\n

The version of the Microsoft.ContainerService/managedClusters service provider used to deploy the AKS cluster needs to be 2023-03-02-preview or higher. The serviceMeshProfile section of the resource definition controls the deployment of the Istio Service Mesh add-on.

\n

 

\n
resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-03-02-preview' = {\n  name: name\n  location: location\n  tags: tags\n  ...\n  properties: {\n  ...\n    serviceMeshProfile: istioServiceMeshEnabled ? {\n      istio: {\n        components: {\n          ingressGateways: istioIngressGatewayEnabled ? [\n            {\n              enabled: true\n              mode: istioIngressGatewayType\n            }\n          ] : null\n        }\n      }\n      mode: 'Istio'\n    } : null\n    ...\n  }\n  ...\n}
\n

 

\n

The sample is configured to deploy the Istio Service Mesh add-on via an External Ingress Gateway, but you can use the istioIngressGatewayType to install Istio with an Internal Ingress Gateway. For more information, see Deploy external or internal ingresses for Istio service mesh add-on for Azure Kubernetes Service. The Istio Service Mesh add-on installs Istio runtime in the aks-istio-system namespace and Istio Ingress Gateway in aks-istio-ingress.

\n

 

\n

Deployment Script

\n

The sample makes use of a Deployment Script to run the install-istio-demo.sh Bash script that installs the bookinfo web application via YAML templates, as shown in the following picture. The application displays information about a book, similar to a single catalog entry of an online bookstore. Displayed on the page is a description of the book, book details (ISBN, number of pages, and so on), and a few book reviews.

\n

 

\n

\n

\n

 

\n

The Bookinfo application is broken into four separate microservices:

\n

 

\n\n

 

\n
#!/bin/bash\n\naz aks install-cli --only-show-errors\n\n# Get AKS credentials\naz aks get-credentials \\\n  --admin \\\n  --name $clusterName \\\n  --resource-group $resourceGroupName \\\n  --subscription $subscriptionId \\\n  --only-show-errors\n\n# Check if the cluster is private or not\nprivate=$(az aks show --name $clusterName \\\n  --resource-group $resourceGroupName \\\n  --subscription $subscriptionId \\\n  --query apiServerAccessProfile.enablePrivateCluster \\\n  --output tsv)\n\nif [[ $private == 'true' ]]; then\n  # Log whether the cluster is public or private\n  echo \"$clusterName AKS cluster is public\"\n\n  # Create cluster issuer for the Application Gateway Ingress Controller (AGIC)\n  if [[ $applicationGatewayEnabled == 'true' ]]; then\n    command=\"cat <<EOF | kubectl apply -f -\napiVersion: cert-manager.io/v1\nkind: ClusterIssuer\nmetadata:\n  name: letsencrypt-application-gateway\nspec:\n  acme:\n    server: https://acme-v02.api.letsencrypt.org/directory\n    email: $email\n    privateKeySecretRef:\n      name: letsencrypt\n    solvers:\n    - http01:\n        ingress:\n          class: azure/application-gateway\n          podTemplate:\n            spec:\n              nodeSelector:\n                \"kubernetes.io/os\": linux\nEOF\"\n\n    az aks command invoke \\\n      --name $clusterName \\\n      --resource-group $resourceGroupName \\\n      --subscription $subscriptionId \\\n      --command \"$command\"\n  fi\n\n  # Create a namespace for the sample bookinfo application\n  command=\"kubectl create namespace $namespace\"\n\n  az aks command invoke \\\n    --name $clusterName \\\n    --resource-group $resourceGroupName \\\n    --subscription $subscriptionId \\\n    --command \"$command\"\n\n  # To automatically install sidecar to any new pods, annotate your namespaces\n  # The default istio-injection=enabled labeling doesn't work. Explicit versioning (istio.io/rev=asm-1-17) is required.\n  command=\"kubectl label namespace $namespace istio.io/rev=asm-1-17\"\n\n  az aks command invoke \\\n    --name $clusterName \\\n    --resource-group $resourceGroupName \\\n    --subscription $subscriptionId \\\n    --command \"$command\"\n\n  # Deploy the sample bookinfo application\n  command=\"kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.17/samples/bookinfo/platform/kube/bookinfo.yaml -n $namespace\"\n\n  az aks command invoke \\\n    --name $clusterName \\\n    --resource-group $resourceGroupName \\\n    --subscription $subscriptionId \\\n    --command \"$command\"\n\n  # The sample bookinfo application isn't accessible from outside the cluster by default after enabling the ingress gateway.\n  # To make the application accessible from the internet, map the sample deployment's ingress to the Istio ingress gateway using the following manifest.\n  # Create an ingress resource for the application\n  command=\"cat <<EOF | kubectl apply -n $namespace -f -\napiVersion: networking.istio.io/v1alpha3\nkind: Gateway\nmetadata:\n  name: bookinfo-gateway-external\n  namespace: $namespace\nspec:\n  selector:\n    istio: aks-istio-ingressgateway-external\n  servers:\n  - port:\n      number: 80\n      name: http\n      protocol: HTTP\n    hosts:\n    - \"*\"\n---\napiVersion: networking.istio.io/v1alpha3\nkind: VirtualService\nmetadata:\n  name: bookinfo-vs-external\n  namespace: $namespace\nspec:\n  hosts:\n  - \"*\"\n  gateways:\n  - bookinfo-gateway-external\n  http:\n  - match:\n    - uri:\n        exact: /productpage\n    - uri:\n        prefix: /static\n    - uri:\n        exact: /login\n    - uri:\n        exact: /logout\n    - uri:\n        prefix: /api/v1/products\n    route:\n    - destination:\n        host: productpage\n        port:\n          number: 9080\nEOF\"\n\n  az aks command invoke \\\n    --name $clusterName \\\n    --resource-group $resourceGroupName \\\n    --subscription $subscriptionId \\\n    --command \"$command\"\n\nelse\n  # Log whether the cluster is public or private\n  echo \"$clusterName AKS cluster is public\"\n\n  # Create a namespace for the sample bookinfo application\n  kubectl create namespace $namespace\n\n  # To automatically install sidecar to any new pods, annotate your namespaces\n  # The default istio-injection=enabled labeling doesn't work. Explicit versioning (istio.io/rev=asm-1-17) is required.\n  kubectl label namespace $namespace istio.io/rev=asm-1-17\n\n  # Deploy the sample bookinfo application\n  kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.17/samples/bookinfo/platform/kube/bookinfo.yaml -n $namespace\n\n  # The sample bookinfo application isn't accessible from outside the cluster by default after enabling the ingress gateway.\n  # To make the application accessible from the internet, map the sample deployment's ingress to the Istio ingress gateway using the following manifest.\n  # Create an ingress resource for the application\n  cat <<EOF | kubectl apply -n $namespace -f -\napiVersion: networking.istio.io/v1alpha3\nkind: Gateway\nmetadata:\n  name: bookinfo-gateway-external\n  namespace: $namespace\nspec:\n  selector:\n    istio: aks-istio-ingressgateway-external\n  servers:\n  - port:\n      number: 80\n      name: http\n      protocol: HTTP\n    hosts:\n    - \"*\"\n---\napiVersion: networking.istio.io/v1alpha3\nkind: VirtualService\nmetadata:\n  name: bookinfo-vs-external\n  namespace: $namespace\nspec:\n  hosts:\n  - \"*\"\n  gateways:\n  - bookinfo-gateway-external\n  http:\n  - match:\n    - uri:\n        exact: /productpage\n    - uri:\n        prefix: /static\n    - uri:\n        exact: /login\n    - uri:\n        exact: /logout\n    - uri:\n        prefix: /api/v1/products\n    route:\n    - destination:\n        host: productpage\n        port:\n          number: 9080\nEOF\n\nfi\n\ningressHostExternal=$(kubectl -n aks-istio-ingress get service aks-istio-ingressgateway-external -o jsonpath='{.status.loadBalancer.ingress[0].ip}')\ningressPortExternal=$(kubectl -n aks-istio-ingress get service aks-istio-ingressgateway-external -o jsonpath='{.spec.ports[?(@.name==\"http2\")].port}')\ngatewayUrlExternal=$ingressHostExternal:$ingressPortExternal\n\n# Create output as JSON file\necho '{}' |\n  jq --arg x $namespace                                '.namespace=$x' |\n  jq --arg x $ingressHostExternal                      '.ingressHostExternal=$x' |\n  jq --arg x $ingressPortExternal                      '.ingressPortExternal=$x' |\n  jq --arg x $gatewayUrlExternal                       '.gatewayUrlExternal=$x'  |\n  jq --arg x \"http://$gatewayUrlExternal/productpage\"  '.bookInfoUrlExternal=$x'  >$AZ_SCRIPTS_OUTPUT_PATH
\n

 

\n

The install-istio-demo.sh Bash script can be used to automatically install the bookinfo sample application to a public AKS cluster or to private AKS cluster using the az aks command invoke. For more information, see Use command invoke to access a private Azure Kubernetes Service (AKS) cluster.

\n

 

\n

The script first performs the following steps:

\n

 

\n\n

The install-istio-demo.sh Bash script returns the following outputs to the deployment script:

\n

 

\n\n

 

\n

Test the application

\n

If the deployment succeeds, the bookinfo application will be publicly exposed via the external Istio ingress gateway. You can retrieve the URL of the productpage of the bookinfo sample application from the outputs of the deployment script, as shown in the following picture:

\n
 
\n

\n

\n

 

\n

You can use open the URL of the productpage of the bookinfo sample application using a web browser, as shown in the following picture.

\n

 

\n

\n

\n

You can also use the test.sh Bash script under the bicep folder to automatically retrieve the the URL of the productpage of the bookinfo sample application from the outputs of the deployment script and call the URL.

\n

 

\n
#!/bin/bash\n\n# Variables\nprefix=\"Pink\"\nresourceGroupName=\"${prefix}RG\"\ndeploymentName=\"deploymentScript\"\n\n# Get the URL of the productpage of the bookinfo sample application\nbookInfoUrlExternal=$(az deployment group show \\\n --name $deploymentName \\\n --resource-group $resourceGroupName \\\n --query properties.outputs.bookInfoUrlExternal.value \\\n --output tsv)\n\nif [[ -n $bookInfoUrlExternal ]]; then\n echo \"[$bookInfoUrlExternal] URL of the productpage of the bookinfo sample application successfully retrieved\"\nelse\n echo \"Failed to get the URL of the productpage of the bookinfo sample application\"\n exit -1\nfi\n\n#  Call the URL of the productpage of the bookinfo sample application\necho \"Calling the URL of the productpage of the bookinfo sample application\"\ncurl -s $bookInfoUrlExternal | grep -o \"<title>.*</title>\"
\n

 

\n

If the call succeeds, you should see a result like the following:

\n

 

\n
$ ./test.sh\n[http://20.82.231.13:80/productpage] URL of the productpage of the bookinfo sample application successfully retrieved\nCalling the URL of the productpage of the bookinfo sample application\n<title>Simple Bookstore App</title>
\n

 

\n

Review deployed resources

\n

Use the Azure portal, Azure CLI, or Azure PowerShell to list the deployed resources in the resource group.

\n

Azure CLI

\n

 

\n
az resource list --resource-group <resource-group-name>
\n

 

\n

PowerShell

\n

 

\n
Get-AzResource -ResourceGroupName <resource-group-name>
\n

 

\n

Clean up resources

\n

Delete the resource group when you no longer need the resources you created. This will remove all the Azure resources.

\n

Azure CLI

\n

 

\n
az group delete --name <resource-group-name>
\n

 

\n

PowerShell

\n

 

\n
Remove-AzResourceGroup -Name <resource-group-name>
\n

 

\n

 

","body@stringLength":"92394","rawBody":"

Istio addresses the challenges developers and operators face with a distributed or microservices architecture. The Istio-based service mesh add-on provides an officially supported and tested Azure Kubernetes Service (AKS) integration.

\n

This article shows how to create an Azure Kubernetes Service(AKS) cluster with the Istio Service Mesh add-on via Bicep and deploy the bookinfo sample application via a Deployment Script.

\n

For more information, see the following articles:

\n

 

\n\n

 

\n

Code

\n

You can find the code for this sample in this Azure Sample.

\n

 

\n

Prerequisites

\n\n

 

\n

You can run az --version to verify above versions. To install the aks-preview extension, run the following command:

\n

 

\naz extension add --name aks-preview\n

 

\n

Run the following command to update to the latest version of the extension released:

\n

 

\naz extension update --name aks-preview\n

 

\n

Architecture

\n

This sample provides a set of Bicep modules to deploy an Azure Kubernetes Service(AKS) cluster with the Istio Service Mesh add-on via Bicep. A Deployment Script is used to deploy the bookinfo sample application. The following diagram shows the architecture and network topology deployed by the sample:

\n
 
\n

\n

\n

 

\n

Bicep modules are parametric, so you can choose any network plugin:

\n

 

\n\n

The Bicep modules also allow installing the following extensions and add-ons for Azure Kubernetes Service(AKS):

\n

In addition, this sample shows how to deploy an Azure Kubernetes Service cluster with the following features:

\n

 

\n\n

 

\n
\n

NOTE
you can't install both the Open Service Mesh add-on and Istio Service Mesh add-on on the same AKS cluster.

\n
\n

 

\n

In a production environment, we strongly recommend deploying a private AKS cluster with Uptime SLA. For more information, see private AKS cluster with a Public DNS address. Alternatively, you can deploy a public AKS cluster and secure access to the API server using authorized IP address ranges.

\n

 

\n

Bicep modules deploy the following Azure resources:

\n

 

\n\n

 

\n
\n

NOTE
You can find the architecture.vsdx file used for the diagram under the visio folder.

\n
\n

 

\n

What is Bicep?

\n

Bicep is a domain-specific language (DSL) that uses a declarative syntax to deploy Azure resources. It provides concise syntax, reliable type safety, and support for code reuse. Bicep offers the best authoring experience for your infrastructure-as-code solutions in Azure.

\n

 

\n

What is a Service Mesh?

\n

Modern applications are typically architected as distributed collections of microservices, with each collection of microservices performing some discrete business function. A service mesh is a dedicated infrastructure layer that you can add to your applications. It allows you to transparently add capabilities like observability, traffic management, and security, without adding them to your own code. The term service mesh describes both the type of software you use to implement this pattern, and the security or network domain that is created when you use that software.

\n

As the deployment of distributed services, such as in a Kubernetes-based system, grows in size and complexity, it can become harder to understand and manage. You may need to implement capabilities such as discovery, load balancing, failure recovery, metrics, and monitoring. A service mesh can also address more complex operational requirements like A/B testing, canary deployments, rate limiting, access control, encryption, and end-to-end authentication.

\n

Service-to-service communication is what makes a distributed application possible. Routing this communication, both within and across application clusters, becomes increasingly complex as the number of services grow. Istio helps reduce this complexity while easing the strain on development teams.

\n

 

\n

What is Istio?

\n

Istio is an open-source service mesh that layers transparently onto existing distributed applications. Istio’s powerful features provide a uniform and more efficient way to secure, connect, and monitor services. Istio enables load balancing, service-to-service authentication, and monitoring – with few or no service code changes. Its powerful control plane brings vital features, including:

\n

 

\n\n

 

\n

How is the add-on different from open-source Istio?

\n

This service mesh add-on uses and builds on top of open-source Istio. The add-on flavor provides the following extra benefits:

\n

 

\n\n

 

\n

Limitations

\n

Istio-based service mesh add-on for AKS has the following limitations:

\n\n

 

\n

Register the AzureServiceMeshPreview feature flag

\n

Register the AzureServiceMeshPreview feature flag by using the az feature register command:

\n

 

\naz feature register --namespace \"Microsoft.ContainerService\" --name \"AzureServiceMeshPreview\"\n

 

\n

It takes a few minutes for the feature to register. Verify the registration status by using the az feature show command:

\n

 

\naz feature show --namespace \"Microsoft.ContainerService\" --name \"AzureServiceMeshPreview\"\n

 

\n

When the status reflects Registered, refresh the registration of the Microsoft.ContainerService resource provider by using the az provider register command:

\n

 

\naz provider register --namespace Microsoft.ContainerService\n

 

\n

Deploy the Bicep modules

\n

You can deploy the Bicep modules in the bicep folder using the deploy.sh Bash script in the same folder. Specify a value for the following parameters in the deploy.sh script and main.parameters.json parameters file before deploying the Bicep modules.

\n

 

\n\n

 

\n

We suggest reading sensitive configuration data such as passwords or SSH keys from a pre-existing Azure Key Vault resource. For more information, see Use Azure Key Vault to pass secure parameter value during Bicep deployment.

\n

 

\n

AKS Cluster Bicep module

\n

The following table contains the Bicep code used to deploy the Azure Kubernetes Service(AKS) cluster.

\n

 

\n// Parameters\n@description('Specifies the name of the AKS cluster.')\nparam name string = 'aks-${uniqueString(resourceGroup().id)}'\n\n@description('Specifies whether to enable API server VNET integration for the cluster or not.')\nparam enableVnetIntegration bool = true\n\n@description('Specifies the name of the existing virtual network.')\nparam virtualNetworkName string\n\n@description('Specifies the name of the subnet hosting the worker nodes of the default system agent pool of the AKS cluster.')\nparam systemAgentPoolSubnetName string = 'SystemSubnet'\n\n@description('Specifies the name of the subnet hosting the worker nodes of the user agent pool of the AKS cluster.')\nparam userAgentPoolSubnetName string = 'UserSubnet'\n\n@description('Specifies the name of the subnet hosting the pods running in the AKS cluster.')\nparam podSubnetName string = 'PodSubnet'\n\n@description('Specifies the name of the subnet delegated to the API server when configuring the AKS cluster to use API server VNET integration.')\nparam apiServerSubnetName string = 'ApiServerSubnet'\n\n@description('Specifies the name of the AKS user-defined managed identity.')\nparam managedIdentityName string\n\n@description('Specifies the DNS prefix specified when creating the managed cluster.')\nparam dnsPrefix string = name\n\n@description('Specifies the network plugin used for building Kubernetes network. - azure or kubenet.')\n@allowed([\n 'azure'\n 'kubenet'\n])\nparam networkPlugin string = 'azure'\n\n@description('Specifies the Network plugin mode used for building the Kubernetes network.')\n@allowed([\n ''\n 'Overlay'\n])\n\nparam networkPluginMode string = ''\n\n@description('Specifies the network policy used for building Kubernetes network. - calico or azure')\n@allowed([\n 'azure'\n 'calico'\n])\nparam networkPolicy string = 'azure'\n\n@description('Specifies the CIDR notation IP range from which to assign pod IPs when kubenet is used.')\nparam podCidr string = '192.168.0.0/16'\n\n@description('A CIDR notation IP range from which to assign service cluster IPs. It must not overlap with any Subnet IP ranges.')\nparam serviceCidr string = '172.16.0.0/16'\n\n@description('Specifies the IP address assigned to the Kubernetes DNS service. It must be within the Kubernetes service address range specified in serviceCidr.')\nparam dnsServiceIP string = '172.16.0.10'\n\n@description('Specifies the CIDR notation IP range assigned to the Docker bridge network. It must not overlap with any Subnet IP ranges or the Kubernetes service address range.')\nparam dockerBridgeCidr string = '172.17.0.1/16'\n\n@description('Specifies the sku of the load balancer used by the virtual machine scale sets used by nodepools.')\n@allowed([\n 'basic'\n 'standard'\n])\nparam loadBalancerSku string = 'standard'\n\n@description('Specifies outbound (egress) routing method. - loadBalancer or userDefinedRouting.')\n@allowed([\n 'loadBalancer'\n 'managedNATGateway'\n 'userAssignedNATGateway'\n 'userDefinedRouting'\n])\nparam outboundType string = 'loadBalancer'\n\n@description('Specifies the tier of a managed cluster SKU: Paid or Free')\n@allowed([\n 'Standard'\n 'Free'\n])\nparam skuTier string = 'Standard'\n\n@description('Specifies the version of Kubernetes specified when creating the managed cluster.')\nparam kubernetesVersion string = '1.18.8'\n\n@description('Specifies the administrator username of Linux virtual machines.')\nparam adminUsername string = 'azureuser'\n\n@description('Specifies the SSH RSA public key string for the Linux nodes.')\nparam sshPublicKey string\n\n@description('Specifies the tenant id of the Azure Active Directory used by the AKS cluster for authentication.')\nparam aadProfileTenantId string = subscription().tenantId\n\n@description('Specifies the AAD group object IDs that will have admin role of the cluster.')\nparam aadProfileAdminGroupObjectIDs array = []\n\n@description('Specifies the upgrade channel for auto upgrade. Allowed values include rapid, stable, patch, node-image, none.')\n@allowed([\n 'rapid'\n 'stable'\n 'patch'\n 'node-image'\n 'none'\n])\nparam upgradeChannel string = 'stable'\n\n@description('Specifies whether to create the cluster as a private cluster or not.')\nparam enablePrivateCluster bool = true\n\n@description('Specifies the Private DNS Zone mode for private cluster. When the value is equal to None, a Public DNS Zone is used in place of a Private DNS Zone')\nparam privateDNSZone string = 'none'\n\n@description('Specifies whether to create additional public FQDN for private cluster or not.')\nparam enablePrivateClusterPublicFQDN bool = true\n\n@description('Specifies whether to enable managed AAD integration.')\nparam aadProfileManaged bool = true\n\n@description('Specifies whether to to enable Azure RBAC for Kubernetes authorization.')\nparam aadProfileEnableAzureRBAC bool = true\n\n@description('Specifies the unique name of of the system node pool profile in the context of the subscription and resource group.')\nparam systemAgentPoolName string = 'nodepool1'\n\n@description('Specifies the vm size of nodes in the system node pool.')\nparam systemAgentPoolVmSize string = 'Standard_DS5_v2'\n\n@description('Specifies the OS Disk Size in GB to be used to specify the disk size for every machine in the system agent pool. If you specify 0, it will apply the default osDisk size according to the vmSize specified.')\nparam systemAgentPoolOsDiskSizeGB int = 100\n\n@description('Specifies the OS disk type to be used for machines in a given agent pool. Allowed values are \\'Ephemeral\\' and \\'Managed\\'. If unspecified, defaults to \\'Ephemeral\\' when the VM supports ephemeral OS and has a cache disk larger than the requested OSDiskSizeGB. Otherwise, defaults to \\'Managed\\'. May not be changed after creation. - Managed or Ephemeral')\n@allowed([\n 'Ephemeral'\n 'Managed'\n])\nparam systemAgentPoolOsDiskType string = 'Ephemeral'\n\n@description('Specifies the number of agents (VMs) to host docker containers in the system node pool. Allowed values must be in the range of 1 to 100 (inclusive). The default value is 1.')\nparam systemAgentPoolAgentCount int = 3\n\n@description('Specifies the OS type for the vms in the system node pool. Choose from Linux and Windows. Default to Linux.')\n@allowed([\n 'Linux'\n 'Windows'\n])\nparam systemAgentPoolOsType string = 'Linux'\n\n@description('Specifies the maximum number of pods that can run on a node in the system node pool. The maximum number of pods per node in an AKS cluster is 250. The default maximum number of pods per node varies between kubenet and Azure CNI networking, and the method of cluster deployment.')\nparam systemAgentPoolMaxPods int = 30\n\n@description('Specifies the maximum number of nodes for auto-scaling for the system node pool.')\nparam systemAgentPoolMaxCount int = 5\n\n@description('Specifies the minimum number of nodes for auto-scaling for the system node pool.')\nparam systemAgentPoolMinCount int = 3\n\n@description('Specifies whether to enable auto-scaling for the system node pool.')\nparam systemAgentPoolEnableAutoScaling bool = true\n\n@description('Specifies the virtual machine scale set priority in the system node pool: Spot or Regular.')\n@allowed([\n 'Spot'\n 'Regular'\n])\nparam systemAgentPoolScaleSetPriority string = 'Regular'\n\n@description('Specifies the ScaleSetEvictionPolicy to be used to specify eviction policy for spot virtual machine scale set. Default to Delete. Allowed values are Delete or Deallocate.')\n@allowed([\n 'Delete'\n 'Deallocate'\n])\nparam systemAgentPoolScaleSetEvictionPolicy string = 'Delete'\n\n@description('Specifies the Agent pool node labels to be persisted across all nodes in the system node pool.')\nparam systemAgentPoolNodeLabels object = {}\n\n@description('Specifies the taints added to new nodes during node pool create and scale. For example, key=value:NoSchedule.')\nparam systemAgentPoolNodeTaints array = []\n\n@description('Determines the placement of emptyDir volumes, container runtime data root, and Kubelet ephemeral storage.')\n@allowed([\n 'OS'\n 'Temporary'\n])\nparam systemAgentPoolKubeletDiskType string = 'OS'\n\n@description('Specifies the type for the system node pool: VirtualMachineScaleSets or AvailabilitySet')\n@allowed([\n 'VirtualMachineScaleSets'\n 'AvailabilitySet'\n])\nparam systemAgentPoolType string = 'VirtualMachineScaleSets'\n\n@description('Specifies the availability zones for the agent nodes in the system node pool. Requirese the use of VirtualMachineScaleSets as node pool type.')\nparam systemAgentPoolAvailabilityZones array = [\n '1'\n '2'\n '3'\n]\n\n@description('Specifies the unique name of of the user node pool profile in the context of the subscription and resource group.')\nparam userAgentPoolName string = 'nodepool1'\n\n@description('Specifies the vm size of nodes in the user node pool.')\nparam userAgentPoolVmSize string = 'Standard_DS5_v2'\n\n@description('Specifies the OS Disk Size in GB to be used to specify the disk size for every machine in the system agent pool. If you specify 0, it will apply the default osDisk size according to the vmSize specified..')\nparam userAgentPoolOsDiskSizeGB int = 100\n\n@description('Specifies the OS disk type to be used for machines in a given agent pool. Allowed values are \\'Ephemeral\\' and \\'Managed\\'. If unspecified, defaults to \\'Ephemeral\\' when the VM supports ephemeral OS and has a cache disk larger than the requested OSDiskSizeGB. Otherwise, defaults to \\'Managed\\'. May not be changed after creation. - Managed or Ephemeral')\n@allowed([\n 'Ephemeral'\n 'Managed'\n])\nparam userAgentPoolOsDiskType string = 'Ephemeral'\n\n@description('Specifies the number of agents (VMs) to host docker containers in the user node pool. Allowed values must be in the range of 1 to 100 (inclusive). The default value is 1.')\nparam userAgentPoolAgentCount int = 3\n\n@description('Specifies the OS type for the vms in the user node pool. Choose from Linux and Windows. Default to Linux.')\n@allowed([\n 'Linux'\n 'Windows'\n])\nparam userAgentPoolOsType string = 'Linux'\n\n@description('Specifies the maximum number of pods that can run on a node in the user node pool. The maximum number of pods per node in an AKS cluster is 250. The default maximum number of pods per node varies between kubenet and Azure CNI networking, and the method of cluster deployment.')\nparam userAgentPoolMaxPods int = 30\n\n@description('Specifies the maximum number of nodes for auto-scaling for the user node pool.')\nparam userAgentPoolMaxCount int = 5\n\n@description('Specifies the minimum number of nodes for auto-scaling for the user node pool.')\nparam userAgentPoolMinCount int = 3\n\n@description('Specifies whether to enable auto-scaling for the user node pool.')\nparam userAgentPoolEnableAutoScaling bool = true\n\n@description('Specifies the virtual machine scale set priority in the user node pool: Spot or Regular.')\n@allowed([\n 'Spot'\n 'Regular'\n])\nparam userAgentPoolScaleSetPriority string = 'Regular'\n\n@description('Specifies the ScaleSetEvictionPolicy to be used to specify eviction policy for spot virtual machine scale set. Default to Delete. Allowed values are Delete or Deallocate.')\n@allowed([\n 'Delete'\n 'Deallocate'\n])\nparam userAgentPoolScaleSetEvictionPolicy string = 'Delete'\n\n@description('Specifies the Agent pool node labels to be persisted across all nodes in the user node pool.')\nparam userAgentPoolNodeLabels object = {}\n\n@description('Specifies the taints added to new nodes during node pool create and scale. For example, key=value:NoSchedule.')\n@allowed([\n 'OS'\n 'Temporary'\n])\nparam userAgentPoolNodeTaints array = []\n\n@description('Determines the placement of emptyDir volumes, container runtime data root, and Kubelet ephemeral storage.')\nparam userAgentPoolKubeletDiskType string = 'OS'\n\n@description('Specifies the type for the user node pool: VirtualMachineScaleSets or AvailabilitySet')\n@allowed([\n 'VirtualMachineScaleSets'\n 'AvailabilitySet'\n])\nparam userAgentPoolType string = 'VirtualMachineScaleSets'\n\n@description('Specifies the availability zones for the agent nodes in the user node pool. Requirese the use of VirtualMachineScaleSets as node pool type.')\nparam userAgentPoolAvailabilityZones array = [\n '1'\n '2'\n '3'\n]\n\n@description('Specifies whether the httpApplicationRouting add-on is enabled or not.')\nparam httpApplicationRoutingEnabled bool = false\n\n@description('Specifies whether the Open Service Mesh add-on is enabled or not.')\nparam openServiceMeshEnabled bool = false\n\n@description('Specifies whether the Istio Service Mesh add-on is enabled or not.')\nparam istioServiceMeshEnabled bool = false\n\n@description('Specifies whether the Istio Ingress Gateway is enabled or not.')\nparam istioIngressGatewayEnabled bool = false\n\n@description('Specifies the type of the Istio Ingress Gateway.')\n@allowed([\n 'Internal'\n 'External'\n])\nparam istioIngressGatewayType string = 'External'\n\n@description('Specifies whether the Kubernetes Event-Driven Autoscaler (KEDA) add-on is enabled or not.')\nparam kedaEnabled bool = false\n\n@description('Specifies whether the Dapr extension is enabled or not.')\nparam daprEnabled bool = false\n\n@description('Enable high availability (HA) mode for the Dapr control plane')\nparam daprHaEnabled bool = false\n\n@description('Specifies whether the Flux V2 extension is enabled or not.')\nparam fluxGitOpsEnabled bool = false\n\n@description('Specifies whether the Vertical Pod Autoscaler is enabled or not.')\nparam verticalPodAutoscalerEnabled bool = false\n\n@description('Specifies whether the aciConnectorLinux add-on is enabled or not.')\nparam aciConnectorLinuxEnabled bool = false\n\n@description('Specifies whether the azurepolicy add-on is enabled or not.')\nparam azurePolicyEnabled bool = true\n\n@description('Specifies whether the Azure Key Vault Provider for Secrets Store CSI Driver addon is enabled or not.')\nparam azureKeyvaultSecretsProviderEnabled bool = true\n\n@description('Specifies whether the kubeDashboard add-on is enabled or not.')\nparam kubeDashboardEnabled bool = false\n\n@description('Specifies whether the pod identity addon is enabled..')\nparam podIdentityProfileEnabled bool = false\n\n@description('Specifies whether the OIDC issuer is enabled.')\nparam oidcIssuerProfileEnabled bool = true\n\n@description('Specifies the scan interval of the auto-scaler of the AKS cluster.')\nparam autoScalerProfileScanInterval string = '10s'\n\n@description('Specifies the scale down delay after add of the auto-scaler of the AKS cluster.')\nparam autoScalerProfileScaleDownDelayAfterAdd string = '10m'\n\n@description('Specifies the scale down delay after delete of the auto-scaler of the AKS cluster.')\nparam autoScalerProfileScaleDownDelayAfterDelete string = '20s'\n\n@description('Specifies scale down delay after failure of the auto-scaler of the AKS cluster.')\nparam autoScalerProfileScaleDownDelayAfterFailure string = '3m'\n\n@description('Specifies the scale down unneeded time of the auto-scaler of the AKS cluster.')\nparam autoScalerProfileScaleDownUnneededTime string = '10m'\n\n@description('Specifies the scale down unready time of the auto-scaler of the AKS cluster.')\nparam autoScalerProfileScaleDownUnreadyTime string = '20m'\n\n@description('Specifies the utilization threshold of the auto-scaler of the AKS cluster.')\nparam autoScalerProfileUtilizationThreshold string = '0.5'\n\n@description('Specifies the max graceful termination time interval in seconds for the auto-scaler of the AKS cluster.')\nparam autoScalerProfileMaxGracefulTerminationSec string = '600'\n\n@description('Specifies the resource id of the Log Analytics workspace.')\nparam workspaceId string\n\n@description('Specifies the workspace data retention in days.')\nparam retentionInDays int = 60\n\n@description('Specifies the location.')\nparam location string = resourceGroup().location\n\n@description('Specifies the resource tags.')\nparam tags object\n\n@description('Specifies whether to enable the Azure Blob CSI Driver. The default value is false.')\nparam blobCSIDriverEnabled bool = false\n\n@description('Specifies whether to enable the Azure Disk CSI Driver. The default value is true.')\nparam diskCSIDriverEnabled bool = true\n\n@description('Specifies whether to enable the Azure File CSI Driver. The default value is true.')\nparam fileCSIDriverEnabled bool = true\n\n@description('Specifies whether to enable the Snapshot Controller. The default value is true.')\nparam snapshotControllerEnabled bool = true\n\n@description('Specifies whether to enable Defender threat detection. The default value is false.')\nparam defenderSecurityMonitoringEnabled bool = false\n\n@description('Specifies whether to enable ImageCleaner on AKS cluster. The default value is false.')\nparam imageCleanerEnabled bool = false\n\n@description('Specifies whether ImageCleaner scanning interval in hours.')\nparam imageCleanerIntervalHours int = 24\n\n@description('Specifies whether to enable Node Restriction. The default value is false.')\nparam nodeRestrictionEnabled bool = false\n\n@description('Specifies whether to enable Workload Identity. The default value is false.')\nparam workloadIdentityEnabled bool = false\n\n@description('Specifies whether creating the Application Gateway and enabling the Application Gateway Ingress Controller or not.')\nparam applicationGatewayEnabled bool = false\n\n@description('Specifies the resource id of the Azure Application Gateway.')\nparam applicationGatewayId string\n\n@description('Specifies the principal id of the managed identity of the Azure Application Gateway.')\nparam applicationGatewayManagedIdentityPrincipalId string\n\n@description('Specifies the name of the Key Vault resource.')\nparam keyVaultName string\n\n// Variables\nvar diagnosticSettingsName = 'diagnosticSettings'\nvar logCategories = [\n 'kube-apiserver'\n 'kube-audit'\n 'kube-audit-admin'\n 'kube-controller-manager'\n 'kube-scheduler'\n 'cluster-autoscaler'\n 'cloud-controller-manager'\n 'guard'\n 'csi-azuredisk-controller'\n 'csi-azurefile-controller'\n 'csi-snapshot-controller'\n]\nvar metricCategories = [\n 'AllMetrics'\n]\nvar logs = [for category in logCategories: {\n category: category\n enabled: true\n retentionPolicy: {\n enabled: true\n days: retentionInDays\n }\n}]\nvar metrics = [for category in metricCategories: {\n category: category\n enabled: true\n retentionPolicy: {\n enabled: true\n days: retentionInDays\n }\n}]\n\nvar managedIdentityOperatorRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')\nvar readerRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')\nvar contributorRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')\nvar keyVaultSecretsUserRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')\nvar MonitoringMetricsPublisherRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')\n\n// Resources\nresource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2021-09-30-preview' existing = {\n name: managedIdentityName\n}\n\nresource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-08-01' existing = {\n name: virtualNetworkName\n}\n\nresource systemAgentPoolSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-08-01' existing = {\n parent: virtualNetwork\n name: systemAgentPoolSubnetName\n}\n\nresource userAgentPoolSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-08-01' existing = {\n parent: virtualNetwork\n name: userAgentPoolSubnetName\n}\n\nresource podSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-08-01' existing = {\n parent: virtualNetwork\n name: podSubnetName\n}\n\nresource apiServerSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-08-01' existing = {\n parent: virtualNetwork\n name: apiServerSubnetName\n}\n\nresource aksCluster 'Microsoft.ContainerService/managedClusters@2023-03-02-preview' = {\n name: name\n location: location\n tags: tags\n sku: {\n name: 'Base'\n tier: skuTier\n }\n identity: {\n type: 'UserAssigned'\n userAssignedIdentities: {\n '${managedIdentity.id}': {}\n }\n }\n properties: {\n kubernetesVersion: kubernetesVersion\n dnsPrefix: dnsPrefix\n agentPoolProfiles: [\n {\n name: toLower(systemAgentPoolName)\n count: systemAgentPoolAgentCount\n vmSize: systemAgentPoolVmSize\n osDiskSizeGB: systemAgentPoolOsDiskSizeGB\n osDiskType: systemAgentPoolOsDiskType\n vnetSubnetID: systemAgentPoolSubnet.id\n podSubnetID: networkPluginMode == 'Overlay' ? null : podSubnet.id\n maxPods: systemAgentPoolMaxPods\n osType: systemAgentPoolOsType\n maxCount: systemAgentPoolMaxCount\n minCount: systemAgentPoolMinCount\n scaleSetPriority: systemAgentPoolScaleSetPriority\n scaleSetEvictionPolicy: systemAgentPoolScaleSetEvictionPolicy\n enableAutoScaling: systemAgentPoolEnableAutoScaling\n mode: 'System'\n type: systemAgentPoolType\n availabilityZones: systemAgentPoolAvailabilityZones\n nodeLabels: systemAgentPoolNodeLabels\n nodeTaints: systemAgentPoolNodeTaints\n kubeletDiskType: systemAgentPoolKubeletDiskType\n }\n {\n name: toLower(userAgentPoolName)\n count: userAgentPoolAgentCount\n vmSize: userAgentPoolVmSize\n osDiskSizeGB: userAgentPoolOsDiskSizeGB\n osDiskType: userAgentPoolOsDiskType\n vnetSubnetID: userAgentPoolSubnet.id\n podSubnetID: networkPluginMode == 'Overlay' ? null : podSubnet.id\n maxPods: userAgentPoolMaxPods\n osType: userAgentPoolOsType\n maxCount: userAgentPoolMaxCount\n minCount: userAgentPoolMinCount\n scaleSetPriority: userAgentPoolScaleSetPriority\n scaleSetEvictionPolicy: userAgentPoolScaleSetEvictionPolicy\n enableAutoScaling: userAgentPoolEnableAutoScaling\n mode: 'User'\n type: userAgentPoolType\n availabilityZones: userAgentPoolAvailabilityZones\n nodeLabels: userAgentPoolNodeLabels\n nodeTaints: userAgentPoolNodeTaints\n kubeletDiskType: userAgentPoolKubeletDiskType\n }\n ]\n linuxProfile: {\n adminUsername: adminUsername\n ssh: {\n publicKeys: [\n {\n keyData: sshPublicKey\n }\n ]\n }\n }\n addonProfiles: {\n ingressApplicationGateway: applicationGatewayEnabled ? {\n config: {\n applicationGatewayId: applicationGatewayEnabled ? applicationGatewayId : null\n }\n enabled: true\n } : {\n enabled: false\n }\n httpApplicationRouting: {\n enabled: httpApplicationRoutingEnabled\n }\n openServiceMesh: {\n enabled: openServiceMeshEnabled\n config: {}\n }\n omsagent: {\n enabled: true\n config: {\n logAnalyticsWorkspaceResourceID: workspaceId\n }\n }\n aciConnectorLinux: {\n enabled: aciConnectorLinuxEnabled\n }\n azurepolicy: {\n enabled: azurePolicyEnabled\n config: {\n version: 'v2'\n }\n }\n kubeDashboard: {\n enabled: kubeDashboardEnabled\n }\n azureKeyvaultSecretsProvider: {\n config: {\n enableSecretRotation: 'false'\n }\n enabled: azureKeyvaultSecretsProviderEnabled\n }\n }\n podIdentityProfile: {\n enabled: podIdentityProfileEnabled\n }\n oidcIssuerProfile: {\n enabled: oidcIssuerProfileEnabled\n }\n enableRBAC: true\n networkProfile: {\n networkPlugin: networkPlugin\n networkPluginMode: networkPlugin == 'azure' ? networkPluginMode : ''\n networkPolicy: networkPolicy\n podCidr: networkPlugin == 'kubenet' || networkPluginMode == 'Overlay' ? podCidr : null\n serviceCidr: serviceCidr\n dnsServiceIP: dnsServiceIP\n dockerBridgeCidr: dockerBridgeCidr\n outboundType: outboundType\n loadBalancerSku: loadBalancerSku\n loadBalancerProfile: null\n }\n workloadAutoScalerProfile: {\n keda: {\n enabled: kedaEnabled\n }\n verticalPodAutoscaler: {\n controlledValues: 'RequestsAndLimits'\n enabled: verticalPodAutoscalerEnabled\n updateMode: 'Off'\n }\n }\n aadProfile: {\n clientAppID: null\n serverAppID: null\n serverAppSecret: null\n managed: aadProfileManaged\n enableAzureRBAC: aadProfileEnableAzureRBAC\n adminGroupObjectIDs: aadProfileAdminGroupObjectIDs\n tenantID: aadProfileTenantId\n }\n autoUpgradeProfile: {\n upgradeChannel: upgradeChannel\n }\n autoScalerProfile: {\n 'scan-interval': autoScalerProfileScanInterval\n 'scale-down-delay-after-add': autoScalerProfileScaleDownDelayAfterAdd\n 'scale-down-delay-after-delete': autoScalerProfileScaleDownDelayAfterDelete\n 'scale-down-delay-after-failure': autoScalerProfileScaleDownDelayAfterFailure\n 'scale-down-unneeded-time': autoScalerProfileScaleDownUnneededTime\n 'scale-down-unready-time': autoScalerProfileScaleDownUnreadyTime\n 'scale-down-utilization-threshold': autoScalerProfileUtilizationThreshold\n 'max-graceful-termination-sec': autoScalerProfileMaxGracefulTerminationSec\n }\n apiServerAccessProfile: {\n enablePrivateCluster: enablePrivateCluster\n enableVnetIntegration: enableVnetIntegration\n privateDNSZone: enablePrivateCluster ? privateDNSZone : null\n enablePrivateClusterPublicFQDN: enablePrivateClusterPublicFQDN\n subnetId: apiServerSubnet.id\n }\n securityProfile: {\n defender: {\n logAnalyticsWorkspaceResourceId: workspaceId\n securityMonitoring: {\n enabled: defenderSecurityMonitoringEnabled\n }\n }\n imageCleaner: {\n enabled: imageCleanerEnabled\n intervalHours: imageCleanerIntervalHours\n }\n nodeRestriction: {\n enabled: nodeRestrictionEnabled\n }\n workloadIdentity: {\n enabled: workloadIdentityEnabled\n }\n }\n serviceMeshProfile: istioServiceMeshEnabled ? {\n istio: {\n components: {\n ingressGateways: istioIngressGatewayEnabled ? [\n {\n enabled: true\n mode: istioIngressGatewayType\n }\n ] : null\n }\n }\n mode: 'Istio'\n } : null\n storageProfile: {\n blobCSIDriver: {\n enabled: blobCSIDriverEnabled\n }\n diskCSIDriver: {\n enabled: diskCSIDriverEnabled\n }\n fileCSIDriver: {\n enabled: fileCSIDriverEnabled\n }\n snapshotController: {\n enabled: snapshotControllerEnabled\n }\n }\n }\n}\n\n// AGIC's identity requires \"Managed Identity Operator\" permission over the user assigned identity of Application Gateway.\nresource applicationGatewayAgicManagedIdentityOperatorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (applicationGatewayEnabled) {\n scope: resourceGroup()\n name: guid(aksCluster.id, applicationGatewayManagedIdentityPrincipalId, 'managedIdentityOperator')\n properties: {\n roleDefinitionId: managedIdentityOperatorRoleDefinitionId\n principalType: 'ServicePrincipal'\n principalId: applicationGatewayEnabled ? aksCluster.properties.addonProfiles.ingressApplicationGateway.identity.objectId : null\n }\n}\n\nresource applicationGatewayAgicContributorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (applicationGatewayEnabled) {\n scope: resourceGroup()\n name: guid(resourceGroup().id, 'ApplicationGateway', 'contributor')\n properties: {\n roleDefinitionId: contributorRoleDefinitionId\n principalType: 'ServicePrincipal'\n principalId: applicationGatewayEnabled ? aksCluster.properties.addonProfiles.ingressApplicationGateway.identity.objectId : null\n }\n}\n\nresource applicationGatewayAgicReaderRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (applicationGatewayEnabled) {\n scope: resourceGroup()\n name: guid(resourceGroup().id, 'ApplicationGateway', 'reader')\n properties: {\n roleDefinitionId: readerRoleDefinitionId\n principalType: 'ServicePrincipal'\n principalId: applicationGatewayEnabled ? aksCluster.properties.addonProfiles.ingressApplicationGateway.identity.objectId : null\n }\n}\n\nresource keyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = {\n name: keyVaultName\n}\n\nresource keyVaultSecretsUserApplicationGatewayIdentityRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (applicationGatewayEnabled) {\n scope: keyVault\n name: guid(keyVault.id, 'ApplicationGateway', 'keyVaultSecretsUser')\n properties: {\n roleDefinitionId: keyVaultSecretsUserRoleDefinitionId\n principalType: 'ServicePrincipal'\n principalId: applicationGatewayManagedIdentityPrincipalId\n }\n}\n\nresource keyVaultCSIdriverSecretsUserRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (azureKeyvaultSecretsProviderEnabled) {\n scope: keyVault\n name: guid(aksCluster.id, 'CSIDriver', keyVaultSecretsUserRoleDefinitionId)\n properties: {\n roleDefinitionId: keyVaultSecretsUserRoleDefinitionId\n principalType: 'ServicePrincipal'\n principalId: aksCluster.properties.addonProfiles.azureKeyvaultSecretsProvider.identity.objectId\n }\n}\n\n// This role assignment enables AKS->LA Fast Alerting experience\nresource FastAlertingRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {\n scope: aksCluster\n name: guid(aksCluster.id, 'omsagent', MonitoringMetricsPublisherRoleDefinitionId)\n properties: {\n roleDefinitionId: MonitoringMetricsPublisherRoleDefinitionId\n principalId: aksCluster.properties.addonProfiles.omsagent.identity.objectId\n principalType: 'ServicePrincipal'\n }\n}\n\n// Dapr Extension\nresource daprExtension 'Microsoft.KubernetesConfiguration/extensions@2022-04-02-preview' = if (daprEnabled) {\n name: 'dapr'\n scope: aksCluster\n properties: {\n extensionType: 'Microsoft.Dapr'\n autoUpgradeMinorVersion: true\n releaseTrain: 'Stable'\n configurationSettings: {\n 'global.ha.enabled': '${daprHaEnabled}'\n }\n scope: {\n cluster: {\n releaseNamespace: 'dapr-system'\n }\n }\n configurationProtectedSettings: {}\n }\n}\n\n// Flux v2 Extension\nresource fluxAddon 'Microsoft.KubernetesConfiguration/extensions@2022-04-02-preview' = if (fluxGitOpsEnabled) {\n name: 'flux'\n scope: aksCluster\n properties: {\n extensionType: 'microsoft.flux'\n autoUpgradeMinorVersion: true\n releaseTrain: 'Stable'\n scope: {\n cluster: {\n releaseNamespace: 'flux-system'\n }\n }\n configurationProtectedSettings: {}\n }\n dependsOn: [ daprExtension ] //Chaining dependencies because of: https://github.com/Azure/AKS-Construction/issues/385\n}\n\n// Diagnostic Settings\nresource diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {\n name: diagnosticSettingsName\n scope: aksCluster\n properties: {\n workspaceId: workspaceId\n logs: logs\n metrics: metrics\n }\n}\n\n// Output\noutput id string = aksCluster.id\noutput name string = aksCluster.name\n

 

\n

You can use the following parameters to control the installation of the Istio Service Mesh add-on:

\n

 

\n@description('Specifies whether the Istio Service Mesh add-on is enabled or not.')\nparam istioServiceMeshEnabled bool = false\n\n@description('Specifies whether the Istio Ingress Gateway is enabled or not.')\nparam istioIngressGatewayEnabled bool = false\n\n@description('Specifies the type of the Istio Ingress Gateway.')\n@allowed([\n 'Internal'\n 'External'\n])\nparam istioIngressGatewayType string = 'External'\n

 

\n

The version of the Microsoft.ContainerService/managedClusters service provider used to deploy the AKS cluster needs to be 2023-03-02-preview or higher. The serviceMeshProfile section of the resource definition controls the deployment of the Istio Service Mesh add-on.

\n

 

\nresource aksCluster 'Microsoft.ContainerService/managedClusters@2023-03-02-preview' = {\n name: name\n location: location\n tags: tags\n ...\n properties: {\n ...\n serviceMeshProfile: istioServiceMeshEnabled ? {\n istio: {\n components: {\n ingressGateways: istioIngressGatewayEnabled ? [\n {\n enabled: true\n mode: istioIngressGatewayType\n }\n ] : null\n }\n }\n mode: 'Istio'\n } : null\n ...\n }\n ...\n}\n

 

\n

The sample is configured to deploy the Istio Service Mesh add-on via an External Ingress Gateway, but you can use the istioIngressGatewayType to install Istio with an Internal Ingress Gateway. For more information, see Deploy external or internal ingresses for Istio service mesh add-on for Azure Kubernetes Service. The Istio Service Mesh add-on installs Istio runtime in the aks-istio-system namespace and Istio Ingress Gateway in aks-istio-ingress.

\n

 

\n

Deployment Script

\n

The sample makes use of a Deployment Script to run the install-istio-demo.sh Bash script that installs the bookinfo web application via YAML templates, as shown in the following picture. The application displays information about a book, similar to a single catalog entry of an online bookstore. Displayed on the page is a description of the book, book details (ISBN, number of pages, and so on), and a few book reviews.

\n

 

\n

\n

\n

 

\n

The Bookinfo application is broken into four separate microservices:

\n

 

\n\n

 

\n#!/bin/bash\n\naz aks install-cli --only-show-errors\n\n# Get AKS credentials\naz aks get-credentials \\\n --admin \\\n --name $clusterName \\\n --resource-group $resourceGroupName \\\n --subscription $subscriptionId \\\n --only-show-errors\n\n# Check if the cluster is private or not\nprivate=$(az aks show --name $clusterName \\\n --resource-group $resourceGroupName \\\n --subscription $subscriptionId \\\n --query apiServerAccessProfile.enablePrivateCluster \\\n --output tsv)\n\nif [[ $private == 'true' ]]; then\n # Log whether the cluster is public or private\n echo \"$clusterName AKS cluster is public\"\n\n # Create cluster issuer for the Application Gateway Ingress Controller (AGIC)\n if [[ $applicationGatewayEnabled == 'true' ]]; then\n command=\"cat <<EOF | kubectl apply -f -\napiVersion: cert-manager.io/v1\nkind: ClusterIssuer\nmetadata:\n name: letsencrypt-application-gateway\nspec:\n acme:\n server: https://acme-v02.api.letsencrypt.org/directory\n email: $email\n privateKeySecretRef:\n name: letsencrypt\n solvers:\n - http01:\n ingress:\n class: azure/application-gateway\n podTemplate:\n spec:\n nodeSelector:\n \"kubernetes.io/os\": linux\nEOF\"\n\n az aks command invoke \\\n --name $clusterName \\\n --resource-group $resourceGroupName \\\n --subscription $subscriptionId \\\n --command \"$command\"\n fi\n\n # Create a namespace for the sample bookinfo application\n command=\"kubectl create namespace $namespace\"\n\n az aks command invoke \\\n --name $clusterName \\\n --resource-group $resourceGroupName \\\n --subscription $subscriptionId \\\n --command \"$command\"\n\n # To automatically install sidecar to any new pods, annotate your namespaces\n # The default istio-injection=enabled labeling doesn't work. Explicit versioning (istio.io/rev=asm-1-17) is required.\n command=\"kubectl label namespace $namespace istio.io/rev=asm-1-17\"\n\n az aks command invoke \\\n --name $clusterName \\\n --resource-group $resourceGroupName \\\n --subscription $subscriptionId \\\n --command \"$command\"\n\n # Deploy the sample bookinfo application\n command=\"kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.17/samples/bookinfo/platform/kube/bookinfo.yaml -n $namespace\"\n\n az aks command invoke \\\n --name $clusterName \\\n --resource-group $resourceGroupName \\\n --subscription $subscriptionId \\\n --command \"$command\"\n\n # The sample bookinfo application isn't accessible from outside the cluster by default after enabling the ingress gateway.\n # To make the application accessible from the internet, map the sample deployment's ingress to the Istio ingress gateway using the following manifest.\n # Create an ingress resource for the application\n command=\"cat <<EOF | kubectl apply -n $namespace -f -\napiVersion: networking.istio.io/v1alpha3\nkind: Gateway\nmetadata:\n name: bookinfo-gateway-external\n namespace: $namespace\nspec:\n selector:\n istio: aks-istio-ingressgateway-external\n servers:\n - port:\n number: 80\n name: http\n protocol: HTTP\n hosts:\n - \"*\"\n---\napiVersion: networking.istio.io/v1alpha3\nkind: VirtualService\nmetadata:\n name: bookinfo-vs-external\n namespace: $namespace\nspec:\n hosts:\n - \"*\"\n gateways:\n - bookinfo-gateway-external\n http:\n - match:\n - uri:\n exact: /productpage\n - uri:\n prefix: /static\n - uri:\n exact: /login\n - uri:\n exact: /logout\n - uri:\n prefix: /api/v1/products\n route:\n - destination:\n host: productpage\n port:\n number: 9080\nEOF\"\n\n az aks command invoke \\\n --name $clusterName \\\n --resource-group $resourceGroupName \\\n --subscription $subscriptionId \\\n --command \"$command\"\n\nelse\n # Log whether the cluster is public or private\n echo \"$clusterName AKS cluster is public\"\n\n # Create a namespace for the sample bookinfo application\n kubectl create namespace $namespace\n\n # To automatically install sidecar to any new pods, annotate your namespaces\n # The default istio-injection=enabled labeling doesn't work. Explicit versioning (istio.io/rev=asm-1-17) is required.\n kubectl label namespace $namespace istio.io/rev=asm-1-17\n\n # Deploy the sample bookinfo application\n kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.17/samples/bookinfo/platform/kube/bookinfo.yaml -n $namespace\n\n # The sample bookinfo application isn't accessible from outside the cluster by default after enabling the ingress gateway.\n # To make the application accessible from the internet, map the sample deployment's ingress to the Istio ingress gateway using the following manifest.\n # Create an ingress resource for the application\n cat <<EOF | kubectl apply -n $namespace -f -\napiVersion: networking.istio.io/v1alpha3\nkind: Gateway\nmetadata:\n name: bookinfo-gateway-external\n namespace: $namespace\nspec:\n selector:\n istio: aks-istio-ingressgateway-external\n servers:\n - port:\n number: 80\n name: http\n protocol: HTTP\n hosts:\n - \"*\"\n---\napiVersion: networking.istio.io/v1alpha3\nkind: VirtualService\nmetadata:\n name: bookinfo-vs-external\n namespace: $namespace\nspec:\n hosts:\n - \"*\"\n gateways:\n - bookinfo-gateway-external\n http:\n - match:\n - uri:\n exact: /productpage\n - uri:\n prefix: /static\n - uri:\n exact: /login\n - uri:\n exact: /logout\n - uri:\n prefix: /api/v1/products\n route:\n - destination:\n host: productpage\n port:\n number: 9080\nEOF\n\nfi\n\ningressHostExternal=$(kubectl -n aks-istio-ingress get service aks-istio-ingressgateway-external -o jsonpath='{.status.loadBalancer.ingress[0].ip}')\ningressPortExternal=$(kubectl -n aks-istio-ingress get service aks-istio-ingressgateway-external -o jsonpath='{.spec.ports[?(@.name==\"http2\")].port}')\ngatewayUrlExternal=$ingressHostExternal:$ingressPortExternal\n\n# Create output as JSON file\necho '{}' |\n jq --arg x $namespace '.namespace=$x' |\n jq --arg x $ingressHostExternal '.ingressHostExternal=$x' |\n jq --arg x $ingressPortExternal '.ingressPortExternal=$x' |\n jq --arg x $gatewayUrlExternal '.gatewayUrlExternal=$x' |\n jq --arg x \"http://$gatewayUrlExternal/productpage\" '.bookInfoUrlExternal=$x' >$AZ_SCRIPTS_OUTPUT_PATH\n

 

\n

The install-istio-demo.sh Bash script can be used to automatically install the bookinfo sample application to a public AKS cluster or to private AKS cluster using the az aks command invoke. For more information, see Use command invoke to access a private Azure Kubernetes Service (AKS) cluster.

\n

 

\n

The script first performs the following steps:

\n

 

\n\n

The install-istio-demo.sh Bash script returns the following outputs to the deployment script:

\n

 

\n\n

 

\n

Test the application

\n

If the deployment succeeds, the bookinfo application will be publicly exposed via the external Istio ingress gateway. You can retrieve the URL of the productpage of the bookinfo sample application from the outputs of the deployment script, as shown in the following picture:

\n
 
\n

\n

\n

 

\n

You can use open the URL of the productpage of the bookinfo sample application using a web browser, as shown in the following picture.

\n

 

\n

\n

\n

You can also use the test.sh Bash script under the bicep folder to automatically retrieve the the URL of the productpage of the bookinfo sample application from the outputs of the deployment script and call the URL.

\n

 

\n#!/bin/bash\n\n# Variables\nprefix=\"Pink\"\nresourceGroupName=\"${prefix}RG\"\ndeploymentName=\"deploymentScript\"\n\n# Get the URL of the productpage of the bookinfo sample application\nbookInfoUrlExternal=$(az deployment group show \\\n --name $deploymentName \\\n --resource-group $resourceGroupName \\\n --query properties.outputs.bookInfoUrlExternal.value \\\n --output tsv)\n\nif [[ -n $bookInfoUrlExternal ]]; then\n echo \"[$bookInfoUrlExternal] URL of the productpage of the bookinfo sample application successfully retrieved\"\nelse\n echo \"Failed to get the URL of the productpage of the bookinfo sample application\"\n exit -1\nfi\n\n# Call the URL of the productpage of the bookinfo sample application\necho \"Calling the URL of the productpage of the bookinfo sample application\"\ncurl -s $bookInfoUrlExternal | grep -o \"<title>.*</title>\"\n

 

\n

If the call succeeds, you should see a result like the following:

\n

 

\n$ ./test.sh\n[http://20.82.231.13:80/productpage] URL of the productpage of the bookinfo sample application successfully retrieved\nCalling the URL of the productpage of the bookinfo sample application\n<title>Simple Bookstore App</title>\n

 

\n

Review deployed resources

\n

Use the Azure portal, Azure CLI, or Azure PowerShell to list the deployed resources in the resource group.

\n

Azure CLI

\n

 

\naz resource list --resource-group <resource-group-name>\n

 

\n

PowerShell

\n

 

\nGet-AzResource -ResourceGroupName <resource-group-name>\n

 

\n

Clean up resources

\n

Delete the resource group when you no longer need the resources you created. This will remove all the Azure resources.

\n

Azure CLI

\n

 

\naz group delete --name <resource-group-name>\n

 

\n

PowerShell

\n

 

\nRemove-AzResourceGroup -Name <resource-group-name>\n

 

\n

 

","kudosSumWeight":0,"postTime":"2023-04-21T02:25:30.436-07:00","images":{"__typename":"AssociatedImageConnection","edges":[{"__typename":"AssociatedImageEdge","cursor":"MjUuMXwyLjF8b3wyNXxfTlZffDE","node":{"__ref":"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0zODAyMDY5LTQ2MjkxNmk2QTA3NkJDRkE5QzY5NTE2?revision=1\"}"}},{"__typename":"AssociatedImageEdge","cursor":"MjUuMXwyLjF8b3wyNXxfTlZffDI","node":{"__ref":"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0zODAyMDY5LTQ2MjkxN2k5QTcyNDQ0OTQzMTM5QTQ3?revision=1\"}"}},{"__typename":"AssociatedImageEdge","cursor":"MjUuMXwyLjF8b3wyNXxfTlZffDM","node":{"__ref":"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0zODAyMDY5LTQ2MjkyNGkxQ0QyRkQ1REIzMTYxN0Iy?revision=1\"}"}},{"__typename":"AssociatedImageEdge","cursor":"MjUuMXwyLjF8b3wyNXxfTlZffDQ","node":{"__ref":"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0zODAyMDY5LTQ2MjkyNWkzRDAzREM5RjVENUU5M0FB?revision=1\"}"}},{"__typename":"AssociatedImageEdge","cursor":"MjUuMXwyLjF8b3wyNXxfTlZffDU","node":{"__ref":"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0zODAyMDY5LTQ2MjkyNmk5OTYwQzM1NDg2QzlEQTc4?revision=1\"}"}}],"totalCount":5,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}},"attachments":{"__typename":"AttachmentConnection","pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null},"edges":[]},"tags":{"__typename":"TagConnection","pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null},"edges":[{"__typename":"TagEdge","cursor":"MjUuMXwyLjF8b3wxMHxfTlZffDE","node":{"__typename":"Tag","id":"tag:App","text":"App","time":"2016-07-12T09:46:46.175-07:00","lastActivityTime":null,"messagesCount":null,"followersCount":null}},{"__typename":"TagEdge","cursor":"MjUuMXwyLjF8b3wxMHxfTlZffDI","node":{"__typename":"Tag","id":"tag:Cloud Native Apps","text":"Cloud Native Apps","time":"2021-01-08T08:35:18.760-08:00","lastActivityTime":null,"messagesCount":null,"followersCount":null}},{"__typename":"TagEdge","cursor":"MjUuMXwyLjF8b3wxMHxfTlZffDM","node":{"__typename":"Tag","id":"tag:Infra","text":"Infra","time":"2020-03-04T21:21:46.909-08:00","lastActivityTime":null,"messagesCount":null,"followersCount":null}},{"__typename":"TagEdge","cursor":"MjUuMXwyLjF8b3wxMHxfTlZffDQ","node":{"__typename":"Tag","id":"tag:ISV 1:Many","text":"ISV 1:Many","time":"2021-01-22T05:55:04.673-08:00","lastActivityTime":null,"messagesCount":null,"followersCount":null}}]},"timeToRead":29,"rawTeaser":"

This article shows how to create an Azure Kubernetes Service(AKS) cluster with the Istio Service Mesh add-on via Bicep and deploy the bookinfo sample application via a Deployment Script.

\n

\n

\n

","introduction":"","coverImage":null,"coverImageProperties":{"__typename":"CoverImageProperties","style":"STANDARD","titlePosition":"BOTTOM","altText":""},"currentRevision":{"__ref":"Revision:revision:3802069_1"},"latestVersion":{"__typename":"FriendlyVersion","major":"1","minor":"0"},"metrics":{"__typename":"MessageMetrics","views":11784},"visibilityScope":"PUBLIC","canonicalUrl":null,"seoTitle":null,"seoDescription":null,"placeholder":false,"originalMessageForPlaceholder":null,"contributors":{"__typename":"UserConnection","edges":[]},"nonCoAuthorContributors":{"__typename":"UserConnection","edges":[]},"coAuthors":{"__typename":"UserConnection","edges":[]},"blogMessagePolicies":{"__typename":"BlogMessagePolicies","canDoAuthoringActionsOnBlog":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.blog.action_can_do_authoring_action.accessDenied","key":"error.lithium.policies.blog.action_can_do_authoring_action.accessDenied","args":[]}}},"archivalData":null,"replies":{"__typename":"MessageConnection","edges":[],"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}},"customFields":[],"revisions({\"constraints\":{\"isPublished\":{\"eq\":true}},\"first\":1})":{"__typename":"RevisionConnection","totalCount":1}},"Conversation:conversation:3802069":{"__typename":"Conversation","id":"conversation:3802069","solved":false,"topic":{"__ref":"BlogTopicMessage:message:3802069"},"lastPostingActivityTime":"2023-04-21T02:25:30.436-07:00","lastPostTime":"2023-04-21T02:25:30.436-07:00","unreadReplyCount":0,"isSubscribed":false},"ModerationData:moderation_data:3802069":{"__typename":"ModerationData","id":"moderation_data:3802069","status":"APPROVED","rejectReason":null,"isReportedAbuse":false,"rejectUser":null,"rejectTime":null,"rejectActorType":null},"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0zODAyMDY5LTQ2MjkxNmk2QTA3NkJDRkE5QzY5NTE2?revision=1\"}":{"__typename":"AssociatedImage","url":"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0zODAyMDY5LTQ2MjkxNmk2QTA3NkJDRkE5QzY5NTE2?revision=1","title":"logo.png","associationType":"TEASER","width":1000,"height":720,"altText":null},"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0zODAyMDY5LTQ2MjkxN2k5QTcyNDQ0OTQzMTM5QTQ3?revision=1\"}":{"__typename":"AssociatedImage","url":"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0zODAyMDY5LTQ2MjkxN2k5QTcyNDQ0OTQzMTM5QTQ3?revision=1","title":"architecture.png","associationType":"BODY","width":800,"height":720,"altText":null},"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0zODAyMDY5LTQ2MjkyNGkxQ0QyRkQ1REIzMTYxN0Iy?revision=1\"}":{"__typename":"AssociatedImage","url":"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0zODAyMDY5LTQ2MjkyNGkxQ0QyRkQ1REIzMTYxN0Iy?revision=1","title":"sample.png","associationType":"BODY","width":743,"height":450,"altText":null},"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0zODAyMDY5LTQ2MjkyNWkzRDAzREM5RjVENUU5M0FB?revision=1\"}":{"__typename":"AssociatedImage","url":"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0zODAyMDY5LTQ2MjkyNWkzRDAzREM5RjVENUU5M0FB?revision=1","title":"outputs.png","associationType":"BODY","width":1107,"height":602,"altText":null},"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0zODAyMDY5LTQ2MjkyNmk5OTYwQzM1NDg2QzlEQTc4?revision=1\"}":{"__typename":"AssociatedImage","url":"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0zODAyMDY5LTQ2MjkyNmk5OTYwQzM1NDg2QzlEQTc4?revision=1","title":"bookinfo.png","associationType":"BODY","width":1108,"height":813,"altText":null},"Revision:revision:3802069_1":{"__typename":"Revision","id":"revision:3802069_1","lastEditTime":"2023-04-21T02:25:30.436-07:00"},"CachedAsset:theme:customTheme1-1741257667527":{"__typename":"CachedAsset","id":"theme:customTheme1-1741257667527","value":{"id":"customTheme1","animation":{"fast":"150ms","normal":"250ms","slow":"500ms","slowest":"750ms","function":"cubic-bezier(0.07, 0.91, 0.51, 1)","__typename":"AnimationThemeSettings"},"avatar":{"borderRadius":"50%","collections":["default"],"__typename":"AvatarThemeSettings"},"basics":{"browserIcon":{"imageAssetName":"favicon-1730836283320.png","imageLastModified":"1730836286415","__typename":"ThemeAsset"},"customerLogo":{"imageAssetName":"favicon-1730836271365.png","imageLastModified":"1730836274203","__typename":"ThemeAsset"},"maximumWidthOfPageContent":"1300px","oneColumnNarrowWidth":"800px","gridGutterWidthMd":"30px","gridGutterWidthXs":"10px","pageWidthStyle":"WIDTH_OF_BROWSER","__typename":"BasicsThemeSettings"},"buttons":{"borderRadiusSm":"3px","borderRadius":"3px","borderRadiusLg":"5px","paddingY":"5px","paddingYLg":"7px","paddingYHero":"var(--lia-bs-btn-padding-y-lg)","paddingX":"12px","paddingXLg":"16px","paddingXHero":"60px","fontStyle":"NORMAL","fontWeight":"700","textTransform":"NONE","disabledOpacity":0.5,"primaryTextColor":"var(--lia-bs-white)","primaryTextHoverColor":"var(--lia-bs-white)","primaryTextActiveColor":"var(--lia-bs-white)","primaryBgColor":"var(--lia-bs-primary)","primaryBgHoverColor":"hsl(var(--lia-bs-primary-h), var(--lia-bs-primary-s), calc(var(--lia-bs-primary-l) * 0.85))","primaryBgActiveColor":"hsl(var(--lia-bs-primary-h), var(--lia-bs-primary-s), calc(var(--lia-bs-primary-l) * 0.7))","primaryBorder":"1px solid transparent","primaryBorderHover":"1px solid transparent","primaryBorderActive":"1px solid transparent","primaryBorderFocus":"1px solid var(--lia-bs-white)","primaryBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","secondaryTextColor":"var(--lia-bs-gray-900)","secondaryTextHoverColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.95))","secondaryTextActiveColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.9))","secondaryBgColor":"var(--lia-bs-gray-200)","secondaryBgHoverColor":"hsl(var(--lia-bs-gray-200-h), var(--lia-bs-gray-200-s), calc(var(--lia-bs-gray-200-l) * 0.96))","secondaryBgActiveColor":"hsl(var(--lia-bs-gray-200-h), var(--lia-bs-gray-200-s), calc(var(--lia-bs-gray-200-l) * 0.92))","secondaryBorder":"1px solid transparent","secondaryBorderHover":"1px solid transparent","secondaryBorderActive":"1px solid transparent","secondaryBorderFocus":"1px solid transparent","secondaryBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","tertiaryTextColor":"var(--lia-bs-gray-900)","tertiaryTextHoverColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.95))","tertiaryTextActiveColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.9))","tertiaryBgColor":"transparent","tertiaryBgHoverColor":"transparent","tertiaryBgActiveColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.04)","tertiaryBorder":"1px solid transparent","tertiaryBorderHover":"1px solid hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","tertiaryBorderActive":"1px solid transparent","tertiaryBorderFocus":"1px solid transparent","tertiaryBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","destructiveTextColor":"var(--lia-bs-danger)","destructiveTextHoverColor":"hsl(var(--lia-bs-danger-h), var(--lia-bs-danger-s), calc(var(--lia-bs-danger-l) * 0.95))","destructiveTextActiveColor":"hsl(var(--lia-bs-danger-h), var(--lia-bs-danger-s), calc(var(--lia-bs-danger-l) * 0.9))","destructiveBgColor":"var(--lia-bs-gray-200)","destructiveBgHoverColor":"hsl(var(--lia-bs-gray-200-h), var(--lia-bs-gray-200-s), calc(var(--lia-bs-gray-200-l) * 0.96))","destructiveBgActiveColor":"hsl(var(--lia-bs-gray-200-h), var(--lia-bs-gray-200-s), calc(var(--lia-bs-gray-200-l) * 0.92))","destructiveBorder":"1px solid transparent","destructiveBorderHover":"1px solid transparent","destructiveBorderActive":"1px solid transparent","destructiveBorderFocus":"1px solid transparent","destructiveBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","__typename":"ButtonsThemeSettings"},"border":{"color":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","mainContent":"NONE","sideContent":"LIGHT","radiusSm":"3px","radius":"5px","radiusLg":"9px","radius50":"100vw","__typename":"BorderThemeSettings"},"boxShadow":{"xs":"0 0 0 1px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.08), 0 3px 0 -1px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.16)","sm":"0 2px 4px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.12)","md":"0 5px 15px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.3)","lg":"0 10px 30px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.3)","__typename":"BoxShadowThemeSettings"},"cards":{"bgColor":"var(--lia-panel-bg-color)","borderRadius":"var(--lia-panel-border-radius)","boxShadow":"var(--lia-box-shadow-xs)","__typename":"CardsThemeSettings"},"chip":{"maxWidth":"300px","height":"30px","__typename":"ChipThemeSettings"},"coreTypes":{"defaultMessageLinkColor":"var(--lia-bs-link-color)","defaultMessageLinkDecoration":"none","defaultMessageLinkFontStyle":"NORMAL","defaultMessageLinkFontWeight":"400","defaultMessageFontStyle":"NORMAL","defaultMessageFontWeight":"400","forumColor":"#4099E2","forumFontFamily":"var(--lia-bs-font-family-base)","forumFontWeight":"var(--lia-default-message-font-weight)","forumLineHeight":"var(--lia-bs-line-height-base)","forumFontStyle":"var(--lia-default-message-font-style)","forumMessageLinkColor":"var(--lia-default-message-link-color)","forumMessageLinkDecoration":"var(--lia-default-message-link-decoration)","forumMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","forumMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","forumSolvedColor":"#148563","blogColor":"#1CBAA0","blogFontFamily":"var(--lia-bs-font-family-base)","blogFontWeight":"var(--lia-default-message-font-weight)","blogLineHeight":"1.75","blogFontStyle":"var(--lia-default-message-font-style)","blogMessageLinkColor":"var(--lia-default-message-link-color)","blogMessageLinkDecoration":"var(--lia-default-message-link-decoration)","blogMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","blogMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","tkbColor":"#4C6B90","tkbFontFamily":"var(--lia-bs-font-family-base)","tkbFontWeight":"var(--lia-default-message-font-weight)","tkbLineHeight":"1.75","tkbFontStyle":"var(--lia-default-message-font-style)","tkbMessageLinkColor":"var(--lia-default-message-link-color)","tkbMessageLinkDecoration":"var(--lia-default-message-link-decoration)","tkbMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","tkbMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","qandaColor":"#4099E2","qandaFontFamily":"var(--lia-bs-font-family-base)","qandaFontWeight":"var(--lia-default-message-font-weight)","qandaLineHeight":"var(--lia-bs-line-height-base)","qandaFontStyle":"var(--lia-default-message-link-font-style)","qandaMessageLinkColor":"var(--lia-default-message-link-color)","qandaMessageLinkDecoration":"var(--lia-default-message-link-decoration)","qandaMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","qandaMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","qandaSolvedColor":"#3FA023","ideaColor":"#FF8000","ideaFontFamily":"var(--lia-bs-font-family-base)","ideaFontWeight":"var(--lia-default-message-font-weight)","ideaLineHeight":"var(--lia-bs-line-height-base)","ideaFontStyle":"var(--lia-default-message-font-style)","ideaMessageLinkColor":"var(--lia-default-message-link-color)","ideaMessageLinkDecoration":"var(--lia-default-message-link-decoration)","ideaMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","ideaMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","contestColor":"#FCC845","contestFontFamily":"var(--lia-bs-font-family-base)","contestFontWeight":"var(--lia-default-message-font-weight)","contestLineHeight":"var(--lia-bs-line-height-base)","contestFontStyle":"var(--lia-default-message-link-font-style)","contestMessageLinkColor":"var(--lia-default-message-link-color)","contestMessageLinkDecoration":"var(--lia-default-message-link-decoration)","contestMessageLinkFontStyle":"ITALIC","contestMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","occasionColor":"#D13A1F","occasionFontFamily":"var(--lia-bs-font-family-base)","occasionFontWeight":"var(--lia-default-message-font-weight)","occasionLineHeight":"var(--lia-bs-line-height-base)","occasionFontStyle":"var(--lia-default-message-font-style)","occasionMessageLinkColor":"var(--lia-default-message-link-color)","occasionMessageLinkDecoration":"var(--lia-default-message-link-decoration)","occasionMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","occasionMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","grouphubColor":"#333333","categoryColor":"#949494","communityColor":"#FFFFFF","productColor":"#949494","__typename":"CoreTypesThemeSettings"},"colors":{"black":"#000000","white":"#FFFFFF","gray100":"#F7F7F7","gray200":"#F7F7F7","gray300":"#E8E8E8","gray400":"#D9D9D9","gray500":"#CCCCCC","gray600":"#717171","gray700":"#707070","gray800":"#545454","gray900":"#333333","dark":"#545454","light":"#F7F7F7","primary":"#0069D4","secondary":"#333333","bodyText":"#333333","bodyBg":"#FFFFFF","info":"#409AE2","success":"#41C5AE","warning":"#FCC844","danger":"#BC341B","alertSystem":"#FF6600","textMuted":"#707070","highlight":"#FFFCAD","outline":"var(--lia-bs-primary)","custom":["#D3F5A4","#243A5E"],"__typename":"ColorsThemeSettings"},"divider":{"size":"3px","marginLeft":"4px","marginRight":"4px","borderRadius":"50%","bgColor":"var(--lia-bs-gray-600)","bgColorActive":"var(--lia-bs-gray-600)","__typename":"DividerThemeSettings"},"dropdown":{"fontSize":"var(--lia-bs-font-size-sm)","borderColor":"var(--lia-bs-border-color)","borderRadius":"var(--lia-bs-border-radius-sm)","dividerBg":"var(--lia-bs-gray-300)","itemPaddingY":"5px","itemPaddingX":"20px","headerColor":"var(--lia-bs-gray-700)","__typename":"DropdownThemeSettings"},"email":{"link":{"color":"#0069D4","hoverColor":"#0061c2","decoration":"none","hoverDecoration":"underline","__typename":"EmailLinkSettings"},"border":{"color":"#e4e4e4","__typename":"EmailBorderSettings"},"buttons":{"borderRadiusLg":"5px","paddingXLg":"16px","paddingYLg":"7px","fontWeight":"700","primaryTextColor":"#ffffff","primaryTextHoverColor":"#ffffff","primaryBgColor":"#0069D4","primaryBgHoverColor":"#005cb8","primaryBorder":"1px solid transparent","primaryBorderHover":"1px solid transparent","__typename":"EmailButtonsSettings"},"panel":{"borderRadius":"5px","borderColor":"#e4e4e4","__typename":"EmailPanelSettings"},"__typename":"EmailThemeSettings"},"emoji":{"skinToneDefault":"#ffcd43","skinToneLight":"#fae3c5","skinToneMediumLight":"#e2cfa5","skinToneMedium":"#daa478","skinToneMediumDark":"#a78058","skinToneDark":"#5e4d43","__typename":"EmojiThemeSettings"},"heading":{"color":"var(--lia-bs-body-color)","fontFamily":"Segoe UI","fontStyle":"NORMAL","fontWeight":"400","h1FontSize":"34px","h2FontSize":"32px","h3FontSize":"28px","h4FontSize":"24px","h5FontSize":"20px","h6FontSize":"16px","lineHeight":"1.3","subHeaderFontSize":"11px","subHeaderFontWeight":"500","h1LetterSpacing":"normal","h2LetterSpacing":"normal","h3LetterSpacing":"normal","h4LetterSpacing":"normal","h5LetterSpacing":"normal","h6LetterSpacing":"normal","subHeaderLetterSpacing":"2px","h1FontWeight":"var(--lia-bs-headings-font-weight)","h2FontWeight":"var(--lia-bs-headings-font-weight)","h3FontWeight":"var(--lia-bs-headings-font-weight)","h4FontWeight":"var(--lia-bs-headings-font-weight)","h5FontWeight":"var(--lia-bs-headings-font-weight)","h6FontWeight":"var(--lia-bs-headings-font-weight)","__typename":"HeadingThemeSettings"},"icons":{"size10":"10px","size12":"12px","size14":"14px","size16":"16px","size20":"20px","size24":"24px","size30":"30px","size40":"40px","size50":"50px","size60":"60px","size80":"80px","size120":"120px","size160":"160px","__typename":"IconsThemeSettings"},"imagePreview":{"bgColor":"var(--lia-bs-gray-900)","titleColor":"var(--lia-bs-white)","controlColor":"var(--lia-bs-white)","controlBgColor":"var(--lia-bs-gray-800)","__typename":"ImagePreviewThemeSettings"},"input":{"borderColor":"var(--lia-bs-gray-600)","disabledColor":"var(--lia-bs-gray-600)","focusBorderColor":"var(--lia-bs-primary)","labelMarginBottom":"10px","btnFontSize":"var(--lia-bs-font-size-sm)","focusBoxShadow":"0 0 0 3px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","checkLabelMarginBottom":"2px","checkboxBorderRadius":"3px","borderRadiusSm":"var(--lia-bs-border-radius-sm)","borderRadius":"var(--lia-bs-border-radius)","borderRadiusLg":"var(--lia-bs-border-radius-lg)","formTextMarginTop":"4px","textAreaBorderRadius":"var(--lia-bs-border-radius)","activeFillColor":"var(--lia-bs-primary)","__typename":"InputThemeSettings"},"loading":{"dotDarkColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.2)","dotLightColor":"hsla(var(--lia-bs-white-h), var(--lia-bs-white-s), var(--lia-bs-white-l), 0.5)","barDarkColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.06)","barLightColor":"hsla(var(--lia-bs-white-h), var(--lia-bs-white-s), var(--lia-bs-white-l), 0.4)","__typename":"LoadingThemeSettings"},"link":{"color":"var(--lia-bs-primary)","hoverColor":"hsl(var(--lia-bs-primary-h), var(--lia-bs-primary-s), calc(var(--lia-bs-primary-l) - 10%))","decoration":"none","hoverDecoration":"underline","__typename":"LinkThemeSettings"},"listGroup":{"itemPaddingY":"15px","itemPaddingX":"15px","borderColor":"var(--lia-bs-gray-300)","__typename":"ListGroupThemeSettings"},"modal":{"contentTextColor":"var(--lia-bs-body-color)","contentBg":"var(--lia-bs-white)","backgroundBg":"var(--lia-bs-black)","smSize":"440px","mdSize":"760px","lgSize":"1080px","backdropOpacity":0.3,"contentBoxShadowXs":"var(--lia-bs-box-shadow-sm)","contentBoxShadow":"var(--lia-bs-box-shadow)","headerFontWeight":"700","__typename":"ModalThemeSettings"},"navbar":{"position":"FIXED","background":{"attachment":null,"clip":null,"color":"var(--lia-bs-white)","imageAssetName":"","imageLastModified":"0","origin":null,"position":"CENTER_CENTER","repeat":"NO_REPEAT","size":"COVER","__typename":"BackgroundProps"},"backgroundOpacity":0.8,"paddingTop":"15px","paddingBottom":"15px","borderBottom":"1px solid var(--lia-bs-border-color)","boxShadow":"var(--lia-bs-box-shadow-sm)","brandMarginRight":"30px","brandMarginRightSm":"10px","brandLogoHeight":"30px","linkGap":"10px","linkJustifyContent":"flex-start","linkPaddingY":"5px","linkPaddingX":"10px","linkDropdownPaddingY":"9px","linkDropdownPaddingX":"var(--lia-nav-link-px)","linkColor":"var(--lia-bs-body-color)","linkHoverColor":"var(--lia-bs-primary)","linkFontSize":"var(--lia-bs-font-size-sm)","linkFontStyle":"NORMAL","linkFontWeight":"400","linkTextTransform":"NONE","linkLetterSpacing":"normal","linkBorderRadius":"var(--lia-bs-border-radius-sm)","linkBgColor":"transparent","linkBgHoverColor":"transparent","linkBorder":"none","linkBorderHover":"none","linkBoxShadow":"none","linkBoxShadowHover":"none","linkTextBorderBottom":"none","linkTextBorderBottomHover":"none","dropdownPaddingTop":"10px","dropdownPaddingBottom":"15px","dropdownPaddingX":"10px","dropdownMenuOffset":"2px","dropdownDividerMarginTop":"10px","dropdownDividerMarginBottom":"10px","dropdownBorderColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","controllerBgHoverColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.1)","controllerIconColor":"var(--lia-bs-body-color)","controllerIconHoverColor":"var(--lia-bs-body-color)","controllerTextColor":"var(--lia-nav-controller-icon-color)","controllerTextHoverColor":"var(--lia-nav-controller-icon-hover-color)","controllerHighlightColor":"hsla(30, 100%, 50%)","controllerHighlightTextColor":"var(--lia-yiq-light)","controllerBorderRadius":"var(--lia-border-radius-50)","hamburgerColor":"var(--lia-nav-controller-icon-color)","hamburgerHoverColor":"var(--lia-nav-controller-icon-color)","hamburgerBgColor":"transparent","hamburgerBgHoverColor":"transparent","hamburgerBorder":"none","hamburgerBorderHover":"none","collapseMenuMarginLeft":"20px","collapseMenuDividerBg":"var(--lia-nav-link-color)","collapseMenuDividerOpacity":0.16,"__typename":"NavbarThemeSettings"},"pager":{"textColor":"var(--lia-bs-link-color)","textFontWeight":"var(--lia-font-weight-md)","textFontSize":"var(--lia-bs-font-size-sm)","__typename":"PagerThemeSettings"},"panel":{"bgColor":"var(--lia-bs-white)","borderRadius":"var(--lia-bs-border-radius)","borderColor":"var(--lia-bs-border-color)","boxShadow":"none","__typename":"PanelThemeSettings"},"popover":{"arrowHeight":"8px","arrowWidth":"16px","maxWidth":"300px","minWidth":"100px","headerBg":"var(--lia-bs-white)","borderColor":"var(--lia-bs-border-color)","borderRadius":"var(--lia-bs-border-radius)","boxShadow":"0 0.5rem 1rem hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.15)","__typename":"PopoverThemeSettings"},"prism":{"color":"#000000","bgColor":"#f5f2f0","fontFamily":"var(--font-family-monospace)","fontSize":"var(--lia-bs-font-size-base)","fontWeightBold":"var(--lia-bs-font-weight-bold)","fontStyleItalic":"italic","tabSize":2,"highlightColor":"#b3d4fc","commentColor":"#62707e","punctuationColor":"#6f6f6f","namespaceOpacity":"0.7","propColor":"#990055","selectorColor":"#517a00","operatorColor":"#906736","operatorBgColor":"hsla(0, 0%, 100%, 0.5)","keywordColor":"#0076a9","functionColor":"#d3284b","variableColor":"#c14700","__typename":"PrismThemeSettings"},"rte":{"bgColor":"var(--lia-bs-white)","borderRadius":"var(--lia-panel-border-radius)","boxShadow":" var(--lia-panel-box-shadow)","customColor1":"#bfedd2","customColor2":"#fbeeb8","customColor3":"#f8cac6","customColor4":"#eccafa","customColor5":"#c2e0f4","customColor6":"#2dc26b","customColor7":"#f1c40f","customColor8":"#e03e2d","customColor9":"#b96ad9","customColor10":"#3598db","customColor11":"#169179","customColor12":"#e67e23","customColor13":"#ba372a","customColor14":"#843fa1","customColor15":"#236fa1","customColor16":"#ecf0f1","customColor17":"#ced4d9","customColor18":"#95a5a6","customColor19":"#7e8c8d","customColor20":"#34495e","customColor21":"#000000","customColor22":"#ffffff","defaultMessageHeaderMarginTop":"40px","defaultMessageHeaderMarginBottom":"20px","defaultMessageItemMarginTop":"0","defaultMessageItemMarginBottom":"10px","diffAddedColor":"hsla(170, 53%, 51%, 0.4)","diffChangedColor":"hsla(43, 97%, 63%, 0.4)","diffNoneColor":"hsla(0, 0%, 80%, 0.4)","diffRemovedColor":"hsla(9, 74%, 47%, 0.4)","specialMessageHeaderMarginTop":"40px","specialMessageHeaderMarginBottom":"20px","specialMessageItemMarginTop":"0","specialMessageItemMarginBottom":"10px","__typename":"RteThemeSettings"},"tags":{"bgColor":"var(--lia-bs-gray-200)","bgHoverColor":"var(--lia-bs-gray-400)","borderRadius":"var(--lia-bs-border-radius-sm)","color":"var(--lia-bs-body-color)","hoverColor":"var(--lia-bs-body-color)","fontWeight":"var(--lia-font-weight-md)","fontSize":"var(--lia-font-size-xxs)","textTransform":"UPPERCASE","letterSpacing":"0.5px","__typename":"TagsThemeSettings"},"toasts":{"borderRadius":"var(--lia-bs-border-radius)","paddingX":"12px","__typename":"ToastsThemeSettings"},"typography":{"fontFamilyBase":"Segoe UI","fontStyleBase":"NORMAL","fontWeightBase":"400","fontWeightLight":"300","fontWeightNormal":"400","fontWeightMd":"500","fontWeightBold":"700","letterSpacingSm":"normal","letterSpacingXs":"normal","lineHeightBase":"1.5","fontSizeBase":"16px","fontSizeXxs":"11px","fontSizeXs":"12px","fontSizeSm":"14px","fontSizeLg":"20px","fontSizeXl":"24px","smallFontSize":"14px","customFonts":[{"source":"SERVER","name":"Segoe UI","styles":[{"style":"NORMAL","weight":"400","__typename":"FontStyleData"},{"style":"NORMAL","weight":"300","__typename":"FontStyleData"},{"style":"NORMAL","weight":"600","__typename":"FontStyleData"},{"style":"NORMAL","weight":"700","__typename":"FontStyleData"},{"style":"ITALIC","weight":"400","__typename":"FontStyleData"}],"assetNames":["SegoeUI-normal-400.woff2","SegoeUI-normal-300.woff2","SegoeUI-normal-600.woff2","SegoeUI-normal-700.woff2","SegoeUI-italic-400.woff2"],"__typename":"CustomFont"},{"source":"SERVER","name":"MWF Fluent Icons","styles":[{"style":"NORMAL","weight":"400","__typename":"FontStyleData"}],"assetNames":["MWFFluentIcons-normal-400.woff2"],"__typename":"CustomFont"}],"__typename":"TypographyThemeSettings"},"unstyledListItem":{"marginBottomSm":"5px","marginBottomMd":"10px","marginBottomLg":"15px","marginBottomXl":"20px","marginBottomXxl":"25px","__typename":"UnstyledListItemThemeSettings"},"yiq":{"light":"#ffffff","dark":"#000000","__typename":"YiqThemeSettings"},"colorLightness":{"primaryDark":0.36,"primaryLight":0.74,"primaryLighter":0.89,"primaryLightest":0.95,"infoDark":0.39,"infoLight":0.72,"infoLighter":0.85,"infoLightest":0.93,"successDark":0.24,"successLight":0.62,"successLighter":0.8,"successLightest":0.91,"warningDark":0.39,"warningLight":0.68,"warningLighter":0.84,"warningLightest":0.93,"dangerDark":0.41,"dangerLight":0.72,"dangerLighter":0.89,"dangerLightest":0.95,"__typename":"ColorLightnessThemeSettings"},"localOverride":false,"__typename":"Theme"},"localOverride":false},"CachedAsset:text:en_US-components/common/EmailVerification-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/common/EmailVerification-1737115705000","value":{"email.verification.title":"Email Verification Required","email.verification.message.update.email":"To participate in the community, you must first verify your email address. The verification email was sent to {email}. To change your email, visit My Settings.","email.verification.message.resend.email":"To participate in the community, you must first verify your email address. The verification email was sent to {email}. Resend email."},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/common/Loading/LoadingDot-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/common/Loading/LoadingDot-1737115705000","value":{"title":"Loading..."},"localOverride":false},"CachedAsset:quilt:o365.prod:pages/blogs/BlogMessagePage:board:FastTrackforAzureBlog-1741257665717":{"__typename":"CachedAsset","id":"quilt:o365.prod:pages/blogs/BlogMessagePage:board:FastTrackforAzureBlog-1741257665717","value":{"id":"BlogMessagePage","container":{"id":"Common","headerProps":{"backgroundImageProps":null,"backgroundColor":null,"addComponents":null,"removeComponents":["community.widget.bannerWidget"],"componentOrder":null,"__typename":"QuiltContainerSectionProps"},"headerComponentProps":{"community.widget.breadcrumbWidget":{"disableLastCrumbForDesktop":false}},"footerProps":null,"footerComponentProps":null,"items":[{"id":"blog-article","layout":"ONE_COLUMN","bgColor":null,"showTitle":null,"showDescription":null,"textPosition":null,"textColor":null,"sectionEditLevel":"LOCKED","bgImage":null,"disableSpacing":null,"edgeToEdgeDisplay":null,"fullHeight":null,"showBorder":null,"__typename":"OneColumnQuiltSection","columnMap":{"main":[{"id":"blogs.widget.blogArticleWidget","className":"lia-blog-container","props":null,"__typename":"QuiltComponent"}],"__typename":"OneSectionColumns"}},{"id":"section-1729184836777","layout":"MAIN_SIDE","bgColor":"transparent","showTitle":false,"showDescription":false,"textPosition":"CENTER","textColor":"var(--lia-bs-body-color)","sectionEditLevel":null,"bgImage":null,"disableSpacing":null,"edgeToEdgeDisplay":null,"fullHeight":null,"showBorder":null,"__typename":"MainSideQuiltSection","columnMap":{"main":[],"side":[{"id":"custom.widget.Social_Sharing","className":null,"props":{"widgetVisibility":"signedInOrAnonymous","useTitle":true,"useBackground":true,"title":"Share","lazyLoad":false},"__typename":"QuiltComponent"}],"__typename":"MainSideSectionColumns"}}],"__typename":"QuiltContainer"},"__typename":"Quilt","localOverride":false},"localOverride":false},"CachedAsset:text:en_US-pages/blogs/BlogMessagePage-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-pages/blogs/BlogMessagePage-1737115705000","value":{"title":"{contextMessageSubject} | {communityTitle}","errorMissing":"This blog post cannot be found","name":"Blog Message Page","section.blog-article.title":"Blog Post","archivedMessageTitle":"This Content Has Been Archived","section.section-1729184836777.title":"","section.section-1729184836777.description":"","section.CncIde.title":"Blog Post","section.tifEmD.description":"","section.tifEmD.title":""},"localOverride":false},"CachedAsset:quiltWrapper:o365.prod:Common:1741257599614":{"__typename":"CachedAsset","id":"quiltWrapper:o365.prod:Common:1741257599614","value":{"id":"Common","header":{"backgroundImageProps":{"assetName":null,"backgroundSize":"COVER","backgroundRepeat":"NO_REPEAT","backgroundPosition":"CENTER_CENTER","lastModified":null,"__typename":"BackgroundImageProps"},"backgroundColor":"transparent","items":[{"id":"community.widget.navbarWidget","props":{"showUserName":true,"showRegisterLink":true,"useIconLanguagePicker":true,"useLabelLanguagePicker":true,"className":"QuiltComponent_lia-component-edit-mode__0nCcm","links":{"sideLinks":[],"mainLinks":[{"children":[],"linkType":"INTERNAL","id":"gxcuf89792","params":{},"routeName":"CommunityPage"},{"children":[],"linkType":"EXTERNAL","id":"external-link","url":"/Directory","target":"SELF"},{"children":[{"linkType":"INTERNAL","id":"microsoft365","params":{"categoryId":"microsoft365"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"microsoft-teams","params":{"categoryId":"MicrosoftTeams"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"windows","params":{"categoryId":"Windows"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"microsoft-securityand-compliance","params":{"categoryId":"microsoft-security"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"outlook","params":{"categoryId":"Outlook"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"planner","params":{"categoryId":"Planner"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"windows-server","params":{"categoryId":"Windows-Server"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"azure","params":{"categoryId":"Azure"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"exchange","params":{"categoryId":"Exchange"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"microsoft-endpoint-manager","params":{"categoryId":"microsoft-endpoint-manager"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"s-q-l-server","params":{"categoryId":"SQL-Server"},"routeName":"CategoryPage"},{"linkType":"EXTERNAL","id":"external-link-2","url":"/Directory","target":"SELF"}],"linkType":"EXTERNAL","id":"communities","url":"/","target":"BLANK"},{"children":[{"linkType":"INTERNAL","id":"education-sector","params":{"categoryId":"EducationSector"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"a-i","params":{"categoryId":"AI"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"i-t-ops-talk","params":{"categoryId":"ITOpsTalk"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"partner-community","params":{"categoryId":"PartnerCommunity"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"microsoft-mechanics","params":{"categoryId":"MicrosoftMechanics"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"healthcare-and-life-sciences","params":{"categoryId":"HealthcareAndLifeSciences"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"public-sector","params":{"categoryId":"PublicSector"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"io-t","params":{"categoryId":"IoT"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"driving-adoption","params":{"categoryId":"DrivingAdoption"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"s-m-b","params":{"categoryId":"SMB"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"startupsat-microsoft","params":{"categoryId":"StartupsatMicrosoft"},"routeName":"CategoryPage"},{"linkType":"EXTERNAL","id":"external-link-1","url":"/Directory","target":"SELF"}],"linkType":"EXTERNAL","id":"communities-1","url":"/","target":"SELF"},{"children":[],"linkType":"EXTERNAL","id":"external","url":"/Blogs","target":"SELF"},{"children":[],"linkType":"EXTERNAL","id":"external-1","url":"/Events","target":"SELF"},{"children":[{"linkType":"INTERNAL","id":"microsoft-learn-1","params":{"categoryId":"MicrosoftLearn"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"microsoft-learn-blog","params":{"boardId":"MicrosoftLearnBlog","categoryId":"MicrosoftLearn"},"routeName":"BlogBoardPage"},{"linkType":"EXTERNAL","id":"external-10","url":"https://learningroomdirectory.microsoft.com/","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-3","url":"https://docs.microsoft.com/learn/dynamics365/?WT.mc_id=techcom_header-webpage-m365","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-4","url":"https://docs.microsoft.com/learn/m365/?wt.mc_id=techcom_header-webpage-m365","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-5","url":"https://docs.microsoft.com/learn/topics/sci/?wt.mc_id=techcom_header-webpage-m365","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-6","url":"https://docs.microsoft.com/learn/powerplatform/?wt.mc_id=techcom_header-webpage-powerplatform","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-7","url":"https://docs.microsoft.com/learn/github/?wt.mc_id=techcom_header-webpage-github","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-8","url":"https://docs.microsoft.com/learn/teams/?wt.mc_id=techcom_header-webpage-teams","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-9","url":"https://docs.microsoft.com/learn/dotnet/?wt.mc_id=techcom_header-webpage-dotnet","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-2","url":"https://docs.microsoft.com/learn/azure/?WT.mc_id=techcom_header-webpage-m365","target":"BLANK"}],"linkType":"INTERNAL","id":"microsoft-learn","params":{"categoryId":"MicrosoftLearn"},"routeName":"CategoryPage"},{"children":[],"linkType":"INTERNAL","id":"community-info-center","params":{"categoryId":"Community-Info-Center"},"routeName":"CategoryPage"}]},"style":{"boxShadow":"var(--lia-bs-box-shadow-sm)","controllerHighlightColor":"hsla(30, 100%, 50%)","linkFontWeight":"400","dropdownDividerMarginBottom":"10px","hamburgerBorderHover":"none","linkBoxShadowHover":"none","linkFontSize":"14px","backgroundOpacity":0.8,"controllerBorderRadius":"var(--lia-border-radius-50)","hamburgerBgColor":"transparent","hamburgerColor":"var(--lia-nav-controller-icon-color)","linkTextBorderBottom":"none","brandLogoHeight":"30px","linkBgHoverColor":"transparent","linkLetterSpacing":"normal","collapseMenuDividerOpacity":0.16,"dropdownPaddingBottom":"15px","paddingBottom":"15px","dropdownMenuOffset":"2px","hamburgerBgHoverColor":"transparent","borderBottom":"1px solid var(--lia-bs-border-color)","hamburgerBorder":"none","dropdownPaddingX":"10px","brandMarginRightSm":"10px","linkBoxShadow":"none","collapseMenuDividerBg":"var(--lia-nav-link-color)","linkColor":"var(--lia-bs-body-color)","linkJustifyContent":"flex-start","dropdownPaddingTop":"10px","controllerHighlightTextColor":"var(--lia-yiq-dark)","controllerTextColor":"var(--lia-nav-controller-icon-color)","background":{"imageAssetName":"","color":"var(--lia-bs-white)","size":"COVER","repeat":"NO_REPEAT","position":"CENTER_CENTER","imageLastModified":""},"linkBorderRadius":"var(--lia-bs-border-radius-sm)","linkHoverColor":"var(--lia-bs-body-color)","position":"FIXED","linkBorder":"none","linkTextBorderBottomHover":"2px solid var(--lia-bs-body-color)","brandMarginRight":"30px","hamburgerHoverColor":"var(--lia-nav-controller-icon-color)","linkBorderHover":"none","collapseMenuMarginLeft":"20px","linkFontStyle":"NORMAL","controllerTextHoverColor":"var(--lia-nav-controller-icon-hover-color)","linkPaddingX":"10px","linkPaddingY":"5px","paddingTop":"15px","linkTextTransform":"NONE","dropdownBorderColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","controllerBgHoverColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.1)","linkBgColor":"transparent","linkDropdownPaddingX":"var(--lia-nav-link-px)","linkDropdownPaddingY":"9px","controllerIconColor":"var(--lia-bs-body-color)","dropdownDividerMarginTop":"10px","linkGap":"10px","controllerIconHoverColor":"var(--lia-bs-body-color)"},"showSearchIcon":false,"languagePickerStyle":"iconAndLabel"},"__typename":"QuiltComponent"},{"id":"community.widget.breadcrumbWidget","props":{"backgroundColor":"transparent","linkHighlightColor":"var(--lia-bs-primary)","visualEffects":{"showBottomBorder":true},"linkTextColor":"var(--lia-bs-gray-700)"},"__typename":"QuiltComponent"},{"id":"custom.widget.community_banner","props":{"widgetVisibility":"signedInOrAnonymous","useTitle":true,"usePageWidth":false,"useBackground":false,"title":"","lazyLoad":false},"__typename":"QuiltComponent"},{"id":"custom.widget.HeroBanner","props":{"widgetVisibility":"signedInOrAnonymous","usePageWidth":false,"useTitle":true,"cMax_items":3,"useBackground":false,"title":"","lazyLoad":false,"widgetChooser":"custom.widget.HeroBanner"},"__typename":"QuiltComponent"}],"__typename":"QuiltWrapperSection"},"footer":{"backgroundImageProps":{"assetName":null,"backgroundSize":"COVER","backgroundRepeat":"NO_REPEAT","backgroundPosition":"CENTER_CENTER","lastModified":null,"__typename":"BackgroundImageProps"},"backgroundColor":"transparent","items":[{"id":"custom.widget.MicrosoftFooter","props":{"widgetVisibility":"signedInOrAnonymous","useTitle":true,"useBackground":false,"title":"","lazyLoad":false},"__typename":"QuiltComponent"}],"__typename":"QuiltWrapperSection"},"__typename":"QuiltWrapper","localOverride":false},"localOverride":false},"CachedAsset:text:en_US-components/common/ActionFeedback-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/common/ActionFeedback-1737115705000","value":{"joinedGroupHub.title":"Welcome","joinedGroupHub.message":"You are now a member of this group and are subscribed to updates.","groupHubInviteNotFound.title":"Invitation Not Found","groupHubInviteNotFound.message":"Sorry, we could not find your invitation to the group. The owner may have canceled the invite.","groupHubNotFound.title":"Group Not Found","groupHubNotFound.message":"The grouphub you tried to join does not exist. It may have been deleted.","existingGroupHubMember.title":"Already Joined","existingGroupHubMember.message":"You are already a member of this group.","accountLocked.title":"Account Locked","accountLocked.message":"Your account has been locked due to multiple failed attempts. Try again in {lockoutTime} minutes.","editedGroupHub.title":"Changes Saved","editedGroupHub.message":"Your group has been updated.","leftGroupHub.title":"Goodbye","leftGroupHub.message":"You are no longer a member of this group and will not receive future updates.","deletedGroupHub.title":"Deleted","deletedGroupHub.message":"The group has been deleted.","groupHubCreated.title":"Group Created","groupHubCreated.message":"{groupHubName} is ready to use","accountClosed.title":"Account Closed","accountClosed.message":"The account has been closed and you will now be redirected to the homepage","resetTokenExpired.title":"Reset Password Link has Expired","resetTokenExpired.message":"Try resetting your password again","invalidUrl.title":"Invalid URL","invalidUrl.message":"The URL you're using is not recognized. Verify your URL and try again.","accountClosedForUser.title":"Account Closed","accountClosedForUser.message":"{userName}'s account is closed","inviteTokenInvalid.title":"Invitation Invalid","inviteTokenInvalid.message":"Your invitation to the community has been canceled or expired.","inviteTokenError.title":"Invitation Verification Failed","inviteTokenError.message":"The url you are utilizing is not recognized. Verify your URL and try again","pageNotFound.title":"Access Denied","pageNotFound.message":"You do not have access to this area of the community or it doesn't exist","eventAttending.title":"Responded as Attending","eventAttending.message":"You'll be notified when there's new activity and reminded as the event approaches","eventInterested.title":"Responded as Interested","eventInterested.message":"You'll be notified when there's new activity and reminded as the event approaches","eventNotFound.title":"Event Not Found","eventNotFound.message":"The event you tried to respond to does not exist.","redirectToRelatedPage.title":"Showing Related Content","redirectToRelatedPageForBaseUsers.title":"Showing Related Content","redirectToRelatedPageForBaseUsers.message":"The content you are trying to access is archived","redirectToRelatedPage.message":"The content you are trying to access is archived","relatedUrl.archivalLink.flyoutMessage":"The content you are trying to access is archived View Archived Content"},"localOverride":false},"CachedAsset:component:custom.widget.community_banner-en-1741257739230":{"__typename":"CachedAsset","id":"component:custom.widget.community_banner-en-1741257739230","value":{"component":{"id":"custom.widget.community_banner","template":{"id":"community_banner","markupLanguage":"HANDLEBARS","style":".community-banner {\n a.top-bar.btn {\n top: 0px;\n width: 100%;\n z-index: 999;\n text-align: center;\n left: 0px;\n background: #0068b8;\n color: white;\n padding: 10px 0px;\n display:block;\n box-shadow:none !important;\n border: none !important;\n border-radius: none !important;\n margin: 0px !important;\n font-size:14px;\n }\n}","texts":null,"defaults":{"config":{"applicablePages":[],"description":"community announcement text","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.community_banner","form":null,"config":null,"props":[],"__typename":"Component"}],"grouping":"CUSTOM","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":"community announcement text","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"form":null,"__typename":"Component","localOverride":false},"globalCss":{"css":".custom_widget_community_banner_community-banner_1a5zb_1 {\n a.custom_widget_community_banner_top-bar_1a5zb_2.custom_widget_community_banner_btn_1a5zb_2 {\n top: 0;\n width: 100%;\n z-index: 999;\n text-align: center;\n left: 0;\n background: #0068b8;\n color: white;\n padding: 0.625rem 0;\n display:block;\n box-shadow:none !important;\n border: none !important;\n border-radius: none !important;\n margin: 0 !important;\n font-size:0.875rem;\n }\n}","tokens":{"community-banner":"custom_widget_community_banner_community-banner_1a5zb_1","top-bar":"custom_widget_community_banner_top-bar_1a5zb_2","btn":"custom_widget_community_banner_btn_1a5zb_2"}},"form":null},"localOverride":false},"CachedAsset:component:custom.widget.HeroBanner-en-1741257739230":{"__typename":"CachedAsset","id":"component:custom.widget.HeroBanner-en-1741257739230","value":{"component":{"id":"custom.widget.HeroBanner","template":{"id":"HeroBanner","markupLanguage":"REACT","style":null,"texts":{"searchPlaceholderText":"Search this community","followActionText":"Follow","unfollowActionText":"Following","searchOnHoverText":"Please enter your search term(s) and then press return key to complete a search."},"defaults":{"config":{"applicablePages":[],"description":null,"fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[{"id":"max_items","dataType":"NUMBER","list":false,"defaultValue":"3","label":"Max Items","description":"The maximum number of items to display in the carousel","possibleValues":null,"control":"INPUT","__typename":"PropDefinition"}],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.HeroBanner","form":{"fields":[{"id":"widgetChooser","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"title","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"useTitle","validation":null,"noValidation":null,"dataType":"BOOLEAN","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"useBackground","validation":null,"noValidation":null,"dataType":"BOOLEAN","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"widgetVisibility","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"moreOptions","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"cMax_items","validation":null,"noValidation":null,"dataType":"NUMBER","list":false,"control":"INPUT","defaultValue":"3","label":"Max Items","description":"The maximum number of items to display in the carousel","possibleValues":null,"__typename":"FormField"}],"layout":{"rows":[{"id":"widgetChooserGroup","type":"fieldset","as":null,"items":[{"id":"widgetChooser","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"titleGroup","type":"fieldset","as":null,"items":[{"id":"title","className":null,"__typename":"FormFieldRef"},{"id":"useTitle","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"useBackground","type":"fieldset","as":null,"items":[{"id":"useBackground","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"widgetVisibility","type":"fieldset","as":null,"items":[{"id":"widgetVisibility","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"moreOptionsGroup","type":"fieldset","as":null,"items":[{"id":"moreOptions","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"componentPropsGroup","type":"fieldset","as":null,"items":[{"id":"cMax_items","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"}],"actionButtons":null,"className":"custom_widget_HeroBanner_form","formGroupFieldSeparator":"divider","__typename":"FormLayout"},"__typename":"Form"},"config":null,"props":[],"__typename":"Component"}],"grouping":"CUSTOM","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":null,"fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[{"id":"max_items","dataType":"NUMBER","list":false,"defaultValue":"3","label":"Max Items","description":"The maximum number of items to display in the carousel","possibleValues":null,"control":"INPUT","__typename":"PropDefinition"}],"__typename":"ComponentProperties"},"form":{"fields":[{"id":"widgetChooser","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"title","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"useTitle","validation":null,"noValidation":null,"dataType":"BOOLEAN","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"useBackground","validation":null,"noValidation":null,"dataType":"BOOLEAN","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"widgetVisibility","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"moreOptions","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"cMax_items","validation":null,"noValidation":null,"dataType":"NUMBER","list":false,"control":"INPUT","defaultValue":"3","label":"Max Items","description":"The maximum number of items to display in the carousel","possibleValues":null,"__typename":"FormField"}],"layout":{"rows":[{"id":"widgetChooserGroup","type":"fieldset","as":null,"items":[{"id":"widgetChooser","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"titleGroup","type":"fieldset","as":null,"items":[{"id":"title","className":null,"__typename":"FormFieldRef"},{"id":"useTitle","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"useBackground","type":"fieldset","as":null,"items":[{"id":"useBackground","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"widgetVisibility","type":"fieldset","as":null,"items":[{"id":"widgetVisibility","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"moreOptionsGroup","type":"fieldset","as":null,"items":[{"id":"moreOptions","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"componentPropsGroup","type":"fieldset","as":null,"items":[{"id":"cMax_items","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"}],"actionButtons":null,"className":"custom_widget_HeroBanner_form","formGroupFieldSeparator":"divider","__typename":"FormLayout"},"__typename":"Form"},"__typename":"Component","localOverride":false},"globalCss":null,"form":{"fields":[{"id":"widgetChooser","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"title","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"useTitle","validation":null,"noValidation":null,"dataType":"BOOLEAN","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"useBackground","validation":null,"noValidation":null,"dataType":"BOOLEAN","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"widgetVisibility","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"moreOptions","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"cMax_items","validation":null,"noValidation":null,"dataType":"NUMBER","list":false,"control":"INPUT","defaultValue":"3","label":"Max Items","description":"The maximum number of items to display in the carousel","possibleValues":null,"__typename":"FormField"}],"layout":{"rows":[{"id":"widgetChooserGroup","type":"fieldset","as":null,"items":[{"id":"widgetChooser","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"titleGroup","type":"fieldset","as":null,"items":[{"id":"title","className":null,"__typename":"FormFieldRef"},{"id":"useTitle","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"useBackground","type":"fieldset","as":null,"items":[{"id":"useBackground","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"widgetVisibility","type":"fieldset","as":null,"items":[{"id":"widgetVisibility","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"moreOptionsGroup","type":"fieldset","as":null,"items":[{"id":"moreOptions","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"componentPropsGroup","type":"fieldset","as":null,"items":[{"id":"cMax_items","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"}],"actionButtons":null,"className":"custom_widget_HeroBanner_form","formGroupFieldSeparator":"divider","__typename":"FormLayout"},"__typename":"Form"}},"localOverride":false},"CachedAsset:component:custom.widget.Social_Sharing-en-1741257739230":{"__typename":"CachedAsset","id":"component:custom.widget.Social_Sharing-en-1741257739230","value":{"component":{"id":"custom.widget.Social_Sharing","template":{"id":"Social_Sharing","markupLanguage":"HANDLEBARS","style":".social-share {\n .sharing-options {\n position: relative;\n margin: 0;\n padding: 0;\n line-height: 10px;\n display: flex;\n justify-content: left;\n gap: 5px;\n list-style-type: none;\n li {\n text-align: left;\n a {\n min-width: 30px;\n min-height: 30px;\n display: block;\n padding: 1px;\n .social-share-linkedin {\n img {\n background-color: rgb(0, 119, 181);\n }\n }\n .social-share-facebook {\n img {\n background-color: rgb(59, 89, 152);\n }\n }\n .social-share-x {\n img {\n background-color: rgb(0, 0, 0);\n }\n }\n .social-share-rss {\n img {\n background-color: rgb(0, 0, 0);\n }\n }\n .social-share-reddit {\n img {\n background-color: rgb(255, 69, 0);\n }\n }\n .social-share-email {\n img {\n background-color: rgb(132, 132, 132);\n }\n }\n }\n a {\n img {\n height: 2rem;\n }\n }\n }\n }\n}\n","texts":null,"defaults":{"config":{"applicablePages":[],"description":"Adds buttons to share to various social media websites","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.Social_Sharing","form":null,"config":null,"props":[],"__typename":"Component"}],"grouping":"CUSTOM","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":"Adds buttons to share to various social media websites","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"form":null,"__typename":"Component","localOverride":false},"globalCss":{"css":".custom_widget_Social_Sharing_social-share_c7xxz_1 {\n .custom_widget_Social_Sharing_sharing-options_c7xxz_2 {\n position: relative;\n margin: 0;\n padding: 0;\n line-height: 0.625rem;\n display: flex;\n justify-content: left;\n gap: 0.3125rem;\n list-style-type: none;\n li {\n text-align: left;\n a {\n min-width: 1.875rem;\n min-height: 1.875rem;\n display: block;\n padding: 0.0625rem;\n .custom_widget_Social_Sharing_social-share-linkedin_c7xxz_18 {\n img {\n background-color: rgb(0, 119, 181);\n }\n }\n .custom_widget_Social_Sharing_social-share-facebook_c7xxz_23 {\n img {\n background-color: rgb(59, 89, 152);\n }\n }\n .custom_widget_Social_Sharing_social-share-x_c7xxz_28 {\n img {\n background-color: rgb(0, 0, 0);\n }\n }\n .custom_widget_Social_Sharing_social-share-rss_c7xxz_33 {\n img {\n background-color: rgb(0, 0, 0);\n }\n }\n .custom_widget_Social_Sharing_social-share-reddit_c7xxz_38 {\n img {\n background-color: rgb(255, 69, 0);\n }\n }\n .custom_widget_Social_Sharing_social-share-email_c7xxz_43 {\n img {\n background-color: rgb(132, 132, 132);\n }\n }\n }\n a {\n img {\n height: 2rem;\n }\n }\n }\n }\n}\n","tokens":{"social-share":"custom_widget_Social_Sharing_social-share_c7xxz_1","sharing-options":"custom_widget_Social_Sharing_sharing-options_c7xxz_2","social-share-linkedin":"custom_widget_Social_Sharing_social-share-linkedin_c7xxz_18","social-share-facebook":"custom_widget_Social_Sharing_social-share-facebook_c7xxz_23","social-share-x":"custom_widget_Social_Sharing_social-share-x_c7xxz_28","social-share-rss":"custom_widget_Social_Sharing_social-share-rss_c7xxz_33","social-share-reddit":"custom_widget_Social_Sharing_social-share-reddit_c7xxz_38","social-share-email":"custom_widget_Social_Sharing_social-share-email_c7xxz_43"}},"form":null},"localOverride":false},"CachedAsset:component:custom.widget.MicrosoftFooter-en-1741257739230":{"__typename":"CachedAsset","id":"component:custom.widget.MicrosoftFooter-en-1741257739230","value":{"component":{"id":"custom.widget.MicrosoftFooter","template":{"id":"MicrosoftFooter","markupLanguage":"HANDLEBARS","style":".context-uhf {\n min-width: 280px;\n font-size: 15px;\n box-sizing: border-box;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%;\n & *,\n & *:before,\n & *:after {\n box-sizing: inherit;\n }\n a.c-uhff-link {\n color: #616161;\n word-break: break-word;\n text-decoration: none;\n }\n &a:link,\n &a:focus,\n &a:hover,\n &a:active,\n &a:visited {\n text-decoration: none;\n color: inherit;\n }\n & div {\n font-family: 'Segoe UI', SegoeUI, 'Helvetica Neue', Helvetica, Arial, sans-serif;\n }\n}\n.c-uhff {\n background: #f2f2f2;\n margin: -1.5625;\n width: auto;\n height: auto;\n}\n.c-uhff-nav {\n margin: 0 auto;\n max-width: calc(1600px + 10%);\n padding: 0 5%;\n box-sizing: inherit;\n &:before,\n &:after {\n content: ' ';\n display: table;\n clear: left;\n }\n @media only screen and (max-width: 1083px) {\n padding-left: 12px;\n }\n .c-heading-4 {\n color: #616161;\n word-break: break-word;\n font-size: 15px;\n line-height: 20px;\n padding: 36px 0 4px;\n font-weight: 600;\n }\n .c-uhff-nav-row {\n .c-uhff-nav-group {\n display: block;\n float: left;\n min-height: 1px;\n vertical-align: text-top;\n padding: 0 12px;\n width: 100%;\n zoom: 1;\n &:first-child {\n padding-left: 0;\n @media only screen and (max-width: 1083px) {\n padding-left: 12px;\n }\n }\n @media only screen and (min-width: 540px) and (max-width: 1082px) {\n width: 33.33333%;\n }\n @media only screen and (min-width: 1083px) {\n width: 16.6666666667%;\n }\n ul.c-list.f-bare {\n font-size: 11px;\n line-height: 16px;\n margin-top: 0;\n margin-bottom: 0;\n padding-left: 0;\n list-style-type: none;\n li {\n word-break: break-word;\n padding: 8px 0;\n margin: 0;\n }\n }\n }\n }\n}\n.c-uhff-base {\n background: #f2f2f2;\n margin: 0 auto;\n max-width: calc(1600px + 10%);\n padding: 30px 5% 16px;\n &:before,\n &:after {\n content: ' ';\n display: table;\n }\n &:after {\n clear: both;\n }\n a.c-uhff-ccpa {\n font-size: 11px;\n line-height: 16px;\n float: left;\n margin: 3px 0;\n }\n a.c-uhff-ccpa:hover {\n text-decoration: underline;\n }\n ul.c-list {\n font-size: 11px;\n line-height: 16px;\n float: right;\n margin: 3px 0;\n color: #616161;\n li {\n padding: 0 24px 4px 0;\n display: inline-block;\n }\n }\n .c-list.f-bare {\n padding-left: 0;\n list-style-type: none;\n }\n @media only screen and (max-width: 1083px) {\n display: flex;\n flex-wrap: wrap;\n padding: 30px 24px 16px;\n }\n}\n","texts":{"New tab":"What's New","New 1":"Surface Laptop Studio 2","New 2":"Surface Laptop Go 3","New 3":"Surface Pro 9","New 4":"Surface Laptop 5","New 5":"Surface Studio 2+","New 6":"Copilot in Windows","New 7":"Microsoft 365","New 8":"Windows 11 apps","Store tab":"Microsoft Store","Store 1":"Account Profile","Store 2":"Download Center","Store 3":"Microsoft Store Support","Store 4":"Returns","Store 5":"Order tracking","Store 6":"Certified Refurbished","Store 7":"Microsoft Store Promise","Store 8":"Flexible Payments","Education tab":"Education","Edu 1":"Microsoft in education","Edu 2":"Devices for education","Edu 3":"Microsoft Teams for Education","Edu 4":"Microsoft 365 Education","Edu 5":"How to buy for your school","Edu 6":"Educator Training and development","Edu 7":"Deals for students and parents","Edu 8":"Azure for students","Business tab":"Business","Bus 1":"Microsoft Cloud","Bus 2":"Microsoft Security","Bus 3":"Dynamics 365","Bus 4":"Microsoft 365","Bus 5":"Microsoft Power Platform","Bus 6":"Microsoft Teams","Bus 7":"Microsoft Industry","Bus 8":"Small Business","Developer tab":"Developer & IT","Dev 1":"Azure","Dev 2":"Developer Center","Dev 3":"Documentation","Dev 4":"Microsoft Learn","Dev 5":"Microsoft Tech Community","Dev 6":"Azure Marketplace","Dev 7":"AppSource","Dev 8":"Visual Studio","Company tab":"Company","Com 1":"Careers","Com 2":"About Microsoft","Com 3":"Company News","Com 4":"Privacy at Microsoft","Com 5":"Investors","Com 6":"Diversity and inclusion","Com 7":"Accessiblity","Com 8":"Sustainibility"},"defaults":{"config":{"applicablePages":[],"description":"The Microsoft Footer","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.MicrosoftFooter","form":null,"config":null,"props":[],"__typename":"Component"}],"grouping":"CUSTOM","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":"The Microsoft Footer","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"form":null,"__typename":"Component","localOverride":false},"globalCss":{"css":".custom_widget_MicrosoftFooter_context-uhf_f95yq_1 {\n min-width: 17.5rem;\n font-size: 0.9375rem;\n box-sizing: border-box;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%;\n & *,\n & *:before,\n & *:after {\n box-sizing: inherit;\n }\n a.custom_widget_MicrosoftFooter_c-uhff-link_f95yq_12 {\n color: #616161;\n word-break: break-word;\n text-decoration: none;\n }\n &a:link,\n &a:focus,\n &a:hover,\n &a:active,\n &a:visited {\n text-decoration: none;\n color: inherit;\n }\n & div {\n font-family: 'Segoe UI', SegoeUI, 'Helvetica Neue', Helvetica, Arial, sans-serif;\n }\n}\n.custom_widget_MicrosoftFooter_c-uhff_f95yq_12 {\n background: #f2f2f2;\n margin: -1.5625;\n width: auto;\n height: auto;\n}\n.custom_widget_MicrosoftFooter_c-uhff-nav_f95yq_35 {\n margin: 0 auto;\n max-width: calc(100rem + 10%);\n padding: 0 5%;\n box-sizing: inherit;\n &:before,\n &:after {\n content: ' ';\n display: table;\n clear: left;\n }\n @media only screen and (max-width: 1083px) {\n padding-left: 0.75rem;\n }\n .custom_widget_MicrosoftFooter_c-heading-4_f95yq_49 {\n color: #616161;\n word-break: break-word;\n font-size: 0.9375rem;\n line-height: 1.25rem;\n padding: 2.25rem 0 0.25rem;\n font-weight: 600;\n }\n .custom_widget_MicrosoftFooter_c-uhff-nav-row_f95yq_57 {\n .custom_widget_MicrosoftFooter_c-uhff-nav-group_f95yq_58 {\n display: block;\n float: left;\n min-height: 0.0625rem;\n vertical-align: text-top;\n padding: 0 0.75rem;\n width: 100%;\n zoom: 1;\n &:first-child {\n padding-left: 0;\n @media only screen and (max-width: 1083px) {\n padding-left: 0.75rem;\n }\n }\n @media only screen and (min-width: 540px) and (max-width: 1082px) {\n width: 33.33333%;\n }\n @media only screen and (min-width: 1083px) {\n width: 16.6666666667%;\n }\n ul.custom_widget_MicrosoftFooter_c-list_f95yq_78.custom_widget_MicrosoftFooter_f-bare_f95yq_78 {\n font-size: 0.6875rem;\n line-height: 1rem;\n margin-top: 0;\n margin-bottom: 0;\n padding-left: 0;\n list-style-type: none;\n li {\n word-break: break-word;\n padding: 0.5rem 0;\n margin: 0;\n }\n }\n }\n }\n}\n.custom_widget_MicrosoftFooter_c-uhff-base_f95yq_94 {\n background: #f2f2f2;\n margin: 0 auto;\n max-width: calc(100rem + 10%);\n padding: 1.875rem 5% 1rem;\n &:before,\n &:after {\n content: ' ';\n display: table;\n }\n &:after {\n clear: both;\n }\n a.custom_widget_MicrosoftFooter_c-uhff-ccpa_f95yq_107 {\n font-size: 0.6875rem;\n line-height: 1rem;\n float: left;\n margin: 0.1875rem 0;\n }\n a.custom_widget_MicrosoftFooter_c-uhff-ccpa_f95yq_107:hover {\n text-decoration: underline;\n }\n ul.custom_widget_MicrosoftFooter_c-list_f95yq_78 {\n font-size: 0.6875rem;\n line-height: 1rem;\n float: right;\n margin: 0.1875rem 0;\n color: #616161;\n li {\n padding: 0 1.5rem 0.25rem 0;\n display: inline-block;\n }\n }\n .custom_widget_MicrosoftFooter_c-list_f95yq_78.custom_widget_MicrosoftFooter_f-bare_f95yq_78 {\n padding-left: 0;\n list-style-type: none;\n }\n @media only screen and (max-width: 1083px) {\n display: flex;\n flex-wrap: wrap;\n padding: 1.875rem 1.5rem 1rem;\n }\n}\n","tokens":{"context-uhf":"custom_widget_MicrosoftFooter_context-uhf_f95yq_1","c-uhff-link":"custom_widget_MicrosoftFooter_c-uhff-link_f95yq_12","c-uhff":"custom_widget_MicrosoftFooter_c-uhff_f95yq_12","c-uhff-nav":"custom_widget_MicrosoftFooter_c-uhff-nav_f95yq_35","c-heading-4":"custom_widget_MicrosoftFooter_c-heading-4_f95yq_49","c-uhff-nav-row":"custom_widget_MicrosoftFooter_c-uhff-nav-row_f95yq_57","c-uhff-nav-group":"custom_widget_MicrosoftFooter_c-uhff-nav-group_f95yq_58","c-list":"custom_widget_MicrosoftFooter_c-list_f95yq_78","f-bare":"custom_widget_MicrosoftFooter_f-bare_f95yq_78","c-uhff-base":"custom_widget_MicrosoftFooter_c-uhff-base_f95yq_94","c-uhff-ccpa":"custom_widget_MicrosoftFooter_c-uhff-ccpa_f95yq_107"}},"form":null},"localOverride":false},"CachedAsset:text:en_US-components/community/Breadcrumb-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/community/Breadcrumb-1737115705000","value":{"navLabel":"Breadcrumbs","dropdown":"Additional parent page navigation"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageBanner-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageBanner-1737115705000","value":{"messageMarkedAsSpam":"This post has been marked as spam","messageMarkedAsSpam@board:TKB":"This article has been marked as spam","messageMarkedAsSpam@board:BLOG":"This post has been marked as spam","messageMarkedAsSpam@board:FORUM":"This discussion has been marked as spam","messageMarkedAsSpam@board:OCCASION":"This event has been marked as spam","messageMarkedAsSpam@board:IDEA":"This idea has been marked as spam","manageSpam":"Manage Spam","messageMarkedAsAbuse":"This post has been marked as abuse","messageMarkedAsAbuse@board:TKB":"This article has been marked as abuse","messageMarkedAsAbuse@board:BLOG":"This post has been marked as abuse","messageMarkedAsAbuse@board:FORUM":"This discussion has been marked as abuse","messageMarkedAsAbuse@board:OCCASION":"This event has been marked as abuse","messageMarkedAsAbuse@board:IDEA":"This idea has been marked as abuse","preModCommentAuthorText":"This comment will be published as soon as it is approved","preModCommentModeratorText":"This comment is awaiting moderation","messageMarkedAsOther":"This post has been rejected due to other reasons","messageMarkedAsOther@board:TKB":"This article has been rejected due to other reasons","messageMarkedAsOther@board:BLOG":"This post has been rejected due to other reasons","messageMarkedAsOther@board:FORUM":"This discussion has been rejected due to other reasons","messageMarkedAsOther@board:OCCASION":"This event has been rejected due to other reasons","messageMarkedAsOther@board:IDEA":"This idea has been rejected due to other reasons","messageArchived":"This post was archived on {date}","relatedUrl":"View Related Content","relatedContentText":"Showing related content","archivedContentLink":"View Archived Content"},"localOverride":false},"Category:category:Exchange":{"__typename":"Category","id":"category:Exchange","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:Planner":{"__typename":"Category","id":"category:Planner","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:Outlook":{"__typename":"Category","id":"category:Outlook","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:Community-Info-Center":{"__typename":"Category","id":"category:Community-Info-Center","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:EducationSector":{"__typename":"Category","id":"category:EducationSector","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:DrivingAdoption":{"__typename":"Category","id":"category:DrivingAdoption","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:Azure":{"__typename":"Category","id":"category:Azure","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:Windows-Server":{"__typename":"Category","id":"category:Windows-Server","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:SQL-Server":{"__typename":"Category","id":"category:SQL-Server","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:MicrosoftTeams":{"__typename":"Category","id":"category:MicrosoftTeams","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:PublicSector":{"__typename":"Category","id":"category:PublicSector","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:microsoft365":{"__typename":"Category","id":"category:microsoft365","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:IoT":{"__typename":"Category","id":"category:IoT","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:HealthcareAndLifeSciences":{"__typename":"Category","id":"category:HealthcareAndLifeSciences","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:SMB":{"__typename":"Category","id":"category:SMB","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:ITOpsTalk":{"__typename":"Category","id":"category:ITOpsTalk","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:microsoft-endpoint-manager":{"__typename":"Category","id":"category:microsoft-endpoint-manager","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:MicrosoftLearn":{"__typename":"Category","id":"category:MicrosoftLearn","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Blog:board:MicrosoftLearnBlog":{"__typename":"Blog","id":"board:MicrosoftLearnBlog","blogPolicies":{"__typename":"BlogPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:AI":{"__typename":"Category","id":"category:AI","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:MicrosoftMechanics":{"__typename":"Category","id":"category:MicrosoftMechanics","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:StartupsatMicrosoft":{"__typename":"Category","id":"category:StartupsatMicrosoft","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:PartnerCommunity":{"__typename":"Category","id":"category:PartnerCommunity","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:Windows":{"__typename":"Category","id":"category:Windows","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:microsoft-security":{"__typename":"Category","id":"category:microsoft-security","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"QueryVariables:TopicReplyList:message:3802069:1":{"__typename":"QueryVariables","id":"TopicReplyList:message:3802069:1","value":{"id":"message:3802069","first":10,"sorts":{"postTime":{"direction":"DESC"}},"repliesFirst":3,"repliesFirstDepthThree":1,"repliesSorts":{"postTime":{"direction":"DESC"}},"useAvatar":true,"useAuthorLogin":true,"useAuthorRank":true,"useBody":true,"useKudosCount":true,"useTimeToRead":false,"useMedia":false,"useReadOnlyIcon":false,"useRepliesCount":true,"useSearchSnippet":false,"useAcceptedSolutionButton":false,"useSolvedBadge":false,"useAttachments":false,"attachmentsFirst":5,"useTags":true,"useNodeAncestors":false,"useUserHoverCard":false,"useNodeHoverCard":false,"useModerationStatus":true,"usePreviewSubjectModal":false,"useMessageStatus":true}},"ROOT_MUTATION":{"__typename":"Mutation"},"CachedAsset:text:en_US-components/community/Navbar-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/community/Navbar-1737115705000","value":{"community":"Community Home","inbox":"Inbox","manageContent":"Manage Content","tos":"Terms of Service","forgotPassword":"Forgot Password","themeEditor":"Theme Editor","edit":"Edit Navigation Bar","skipContent":"Skip to content","gxcuf89792":"Tech Community","external-1":"Events","s-m-b":"Small and Medium Businesses","windows-server":"Windows Server","education-sector":"Education Sector","driving-adoption":"Driving Adoption","microsoft-learn":"Microsoft Learn","s-q-l-server":"SQL Server","partner-community":"Microsoft Partner Community","microsoft365":"Microsoft 365","external-9":".NET","external-8":"Teams","external-7":"Github","products-services":"Products","external-6":"Power Platform","communities-1":"Topics","external-5":"Microsoft Security","planner":"Planner","external-4":"Microsoft 365","external-3":"Dynamics 365","azure":"Azure","healthcare-and-life-sciences":"Healthcare and Life Sciences","external-2":"Azure","microsoft-mechanics":"Microsoft Mechanics","microsoft-learn-1":"Community","external-10":"Learning Room Directory","microsoft-learn-blog":"Blog","windows":"Windows","i-t-ops-talk":"ITOps Talk","external-link-1":"View All","microsoft-securityand-compliance":"Microsoft Security","public-sector":"Public Sector","community-info-center":"Lounge","external-link-2":"View All","microsoft-teams":"Microsoft Teams","external":"Blogs","microsoft-endpoint-manager":"Microsoft Intune and Configuration Manager","startupsat-microsoft":"Startups at Microsoft","exchange":"Exchange","a-i":"AI and Machine Learning","io-t":"Internet of Things (IoT)","outlook":"Outlook","external-link":"Community Hubs","communities":"Products"},"localOverride":false},"CachedAsset:text:en_US-components/community/NavbarHamburgerDropdown-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/community/NavbarHamburgerDropdown-1737115705000","value":{"hamburgerLabel":"Side Menu"},"localOverride":false},"CachedAsset:text:en_US-components/community/BrandLogo-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/community/BrandLogo-1737115705000","value":{"logoAlt":"Khoros","themeLogoAlt":"Brand Logo"},"localOverride":false},"CachedAsset:text:en_US-components/community/NavbarTextLinks-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/community/NavbarTextLinks-1737115705000","value":{"more":"More"},"localOverride":false},"CachedAsset:text:en_US-components/authentication/AuthenticationLink-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/authentication/AuthenticationLink-1737115705000","value":{"title.login":"Sign In","title.registration":"Register","title.forgotPassword":"Forgot Password","title.multiAuthLogin":"Sign In"},"localOverride":false},"CachedAsset:text:en_US-components/nodes/NodeLink-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/nodes/NodeLink-1737115705000","value":{"place":"Place {name}"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageView/MessageViewStandard-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageView/MessageViewStandard-1737115705000","value":{"anonymous":"Anonymous","author":"{messageAuthorLogin}","authorBy":"{messageAuthorLogin}","board":"{messageBoardTitle}","replyToUser":" to {parentAuthor}","showMoreReplies":"Show More","replyText":"Reply","repliesText":"Replies","markedAsSolved":"Marked as Solved","movedMessagePlaceholder.BLOG":"{count, plural, =0 {This comment has been} other {These comments have been} }","movedMessagePlaceholder.TKB":"{count, plural, =0 {This comment has been} other {These comments have been} }","movedMessagePlaceholder.FORUM":"{count, plural, =0 {This reply has been} other {These replies have been} }","movedMessagePlaceholder.IDEA":"{count, plural, =0 {This comment has been} other {These comments have been} }","movedMessagePlaceholder.OCCASION":"{count, plural, =0 {This comment has been} other {These comments have been} }","movedMessagePlaceholderUrlText":"moved.","messageStatus":"Status: ","statusChanged":"Status changed: {previousStatus} to {currentStatus}","statusAdded":"Status added: {status}","statusRemoved":"Status removed: {status}","labelExpand":"expand replies","labelCollapse":"collapse replies","unhelpfulReason.reason1":"Content is outdated","unhelpfulReason.reason2":"Article is missing information","unhelpfulReason.reason3":"Content is for a different Product","unhelpfulReason.reason4":"Doesn't match what I was searching for"},"localOverride":false},"CachedAsset:text:en_US-components/messages/ThreadedReplyList-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/ThreadedReplyList-1737115705000","value":{"title":"{count, plural, one{# Reply} other{# Replies}}","title@board:BLOG":"{count, plural, one{# Comment} other{# Comments}}","title@board:TKB":"{count, plural, one{# Comment} other{# Comments}}","title@board:IDEA":"{count, plural, one{# Comment} other{# Comments}}","title@board:OCCASION":"{count, plural, one{# Comment} other{# Comments}}","noRepliesTitle":"No Replies","noRepliesTitle@board:BLOG":"No Comments","noRepliesTitle@board:TKB":"No Comments","noRepliesTitle@board:IDEA":"No Comments","noRepliesTitle@board:OCCASION":"No Comments","noRepliesDescription":"Be the first to reply","noRepliesDescription@board:BLOG":"Be the first to comment","noRepliesDescription@board:TKB":"Be the first to comment","noRepliesDescription@board:IDEA":"Be the first to comment","noRepliesDescription@board:OCCASION":"Be the first to comment","messageReadOnlyAlert:BLOG":"Comments have been turned off for this post","messageReadOnlyAlert:TKB":"Comments have been turned off for this article","messageReadOnlyAlert:IDEA":"Comments have been turned off for this idea","messageReadOnlyAlert:FORUM":"Replies have been turned off for this discussion","messageReadOnlyAlert:OCCASION":"Comments have been turned off for this event"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageReplyCallToAction-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageReplyCallToAction-1737115705000","value":{"leaveReply":"Leave a reply...","leaveReply@board:BLOG@message:root":"Leave a comment...","leaveReply@board:TKB@message:root":"Leave a comment...","leaveReply@board:IDEA@message:root":"Leave a comment...","leaveReply@board:OCCASION@message:root":"Leave a comment...","repliesTurnedOff.FORUM":"Replies are turned off for this topic","repliesTurnedOff.BLOG":"Comments are turned off for this topic","repliesTurnedOff.TKB":"Comments are turned off for this topic","repliesTurnedOff.IDEA":"Comments are turned off for this topic","repliesTurnedOff.OCCASION":"Comments are turned off for this topic","infoText":"Stop poking me!"},"localOverride":false},"CachedAsset:text:en_US-components/community/NavbarDropdownToggle-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/community/NavbarDropdownToggle-1737115705000","value":{"ariaLabelClosed":"Press the down arrow to open the menu"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/common/QueryHandler-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/common/QueryHandler-1737115705000","value":{"title":"Query Handler"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageCoverImage-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageCoverImage-1737115705000","value":{"coverImageTitle":"Cover Image"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/nodes/NodeTitle-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/nodes/NodeTitle-1737115705000","value":{"nodeTitle":"{nodeTitle, select, community {Community} other {{nodeTitle}}} "},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageTimeToRead-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageTimeToRead-1737115705000","value":{"minReadText":"{min} MIN READ"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageSubject-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageSubject-1737115705000","value":{"noSubject":"(no subject)"},"localOverride":false},"CachedAsset:text:en_US-components/users/UserLink-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/users/UserLink-1737115705000","value":{"authorName":"View Profile: {author}","anonymous":"Anonymous"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/users/UserRank-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/users/UserRank-1737115705000","value":{"rankName":"{rankName}","userRank":"Author rank {rankName}"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageTime-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageTime-1737115705000","value":{"postTime":"Published: {time}","lastPublishTime":"Last Update: {time}","conversation.lastPostingActivityTime":"Last posting activity time: {time}","conversation.lastPostTime":"Last post time: {time}","moderationData.rejectTime":"Rejected time: {time}"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageBody-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageBody-1737115705000","value":{"showMessageBody":"Show More","mentionsErrorTitle":"{mentionsType, select, board {Board} user {User} message {Message} other {}} No Longer Available","mentionsErrorMessage":"The {mentionsType} you are trying to view has been removed from the community.","videoProcessing":"Video is being processed. Please try again in a few minutes.","bannerTitle":"Video provider requires cookies to play the video. Accept to continue or {url} it directly on the provider's site.","buttonTitle":"Accept","urlText":"watch"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageCustomFields-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageCustomFields-1737115705000","value":{"CustomField.default.label":"Value of {name}"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageRevision-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageRevision-1737115705000","value":{"lastUpdatedDatePublished":"{publishCount, plural, one{Published} other{Updated}} {date}","lastUpdatedDateDraft":"Created {date}","version":"Version {major}.{minor}"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageReplyButton-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageReplyButton-1737115705000","value":{"repliesCount":"{count}","title":"Reply","title@board:BLOG@message:root":"Comment","title@board:TKB@message:root":"Comment","title@board:IDEA@message:root":"Comment","title@board:OCCASION@message:root":"Comment"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageAuthorBio-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageAuthorBio-1737115705000","value":{"sendMessage":"Send Message","actionMessage":"Follow this blog board to get notified when there's new activity","coAuthor":"CO-PUBLISHER","contributor":"CONTRIBUTOR","userProfile":"View Profile","iconlink":"Go to {name} {type}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/users/UserAvatar-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/users/UserAvatar-1737115705000","value":{"altText":"{login}'s avatar","altTextGeneric":"User's avatar"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/ranks/UserRankLabel-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/ranks/UserRankLabel-1737115705000","value":{"altTitle":"Icon for {rankName} rank"},"localOverride":false},"CachedAsset:text:en_US-components/users/UserRegistrationDate-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/users/UserRegistrationDate-1737115705000","value":{"noPrefix":"{date}","withPrefix":"Joined {date}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/nodes/NodeAvatar-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/nodes/NodeAvatar-1737115705000","value":{"altTitle":"Node avatar for {nodeTitle}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/nodes/NodeDescription-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/nodes/NodeDescription-1737115705000","value":{"description":"{description}"},"localOverride":false},"CachedAsset:text:en_US-components/tags/TagView/TagViewChip-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-components/tags/TagView/TagViewChip-1737115705000","value":{"tagLabelName":"Tag name {tagName}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/nodes/NodeIcon-1737115705000":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/nodes/NodeIcon-1737115705000","value":{"contentType":"Content Type {style, select, FORUM {Forum} BLOG {Blog} TKB {Knowledge Base} IDEA {Ideas} OCCASION {Events} other {}} icon"},"localOverride":false}}}},"page":"/blogs/BlogMessagePage/BlogMessagePage","query":{"boardId":"fasttrackforazureblog","messageSubject":"how-to-install-an-aks-cluster-with-the-istio-service-mesh-add-on-via-bicep","messageId":"3802069"},"buildId":"rBSXYkarBGCCgv-Fy0Q8w","runtimeConfig":{"buildInformationVisible":false,"logLevelApp":"info","logLevelMetrics":"info","openTelemetryClientEnabled":false,"openTelemetryConfigName":"o365","openTelemetryServiceVersion":"25.1.0","openTelemetryUniverse":"prod","openTelemetryCollector":"http://localhost:4318","openTelemetryRouteChangeAllowedTime":"5000","apolloDevToolsEnabled":false,"inboxMuteWipFeatureEnabled":false},"isFallback":false,"isExperimentalCompile":false,"dynamicIds":["./components/community/Navbar/NavbarWidget.tsx","./components/community/Breadcrumb/BreadcrumbWidget.tsx","./components/customComponent/CustomComponent/CustomComponent.tsx","./components/blogs/BlogArticleWidget/BlogArticleWidget.tsx","./components/external/components/ExternalComponent.tsx","./components/messages/MessageView/MessageViewStandard/MessageViewStandard.tsx","./components/messages/ThreadedReplyList/ThreadedReplyList.tsx","../shared/client/components/common/List/UnwrappedList/UnwrappedList.tsx","./components/tags/TagView/TagView.tsx","./components/tags/TagView/TagViewChip/TagViewChip.tsx"],"appGip":true,"scriptLoader":[{"id":"analytics","src":"https://techcommunity.microsoft.com/t5/s/gxcuf89792/pagescripts/1729284608000/analytics.js?page.id=BlogMessagePage&entity.id=board%3Afasttrackforazureblog&entity.id=message%3A3802069","strategy":"afterInteractive"}]}