Istioaddresses 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.
In addition, this sample shows how to deploy anAzure Kubernetes Servicecluster with the following features:
API Server VNET Integrationallows you to enable network communication between the API server and the cluster nodes without requiring a private link or tunnel. AKS clusters with API Server VNET integration provide a series of advantages, for example, they can have public network access or private cluster mode enabled or disabled without redeploying the cluster. For more information, seeCreate an Azure Kubernetes Service cluster with API Server VNet Integration.
Azure NAT Gatewayto manage outbound connections initiated by AKS-hosted workloads.
Event-driven Autoscaling (KEDA) add-onis a single-purpose and lightweight component that strives to make application autoscaling simple and is a CNCF Incubation project.
Dapr extension for Azure Kubernetes Service (AKS)allows you to installDapr, a portable, event-driven runtime that simplifies building resilient, stateless, and stateful applications that run on the cloud and edge and embrace the diversity of languages and developer frameworks. With its sidecar architecture, Dapr helps you tackle the challenges that come with building microservices and keeps your code platform agnostic.
Vertical Pod Autoscalingallows you to automatically sets resource requests and limits on containers per workload based on past usage. VPA makes certain pods are scheduled onto nodes that have the required CPU and memory resources. For more information, seeKubernetes Vertical Pod Autoscaling.
Image Cleanerto clean up stale images on your Azure Kubernetes Service cluster.
Open Service Mesh add-onis a lightweight, extensible, cloud native service mesh that allows you to uniformly manage, secure, and get out-of-the-box observability features for highly dynamic microservice environments. Bicep modules allow to install the Open Service Mesh add-on as an alternative to the Istio Service Mesh add-on.
NOTE you can't install both the Open Service Mesh add-on and Istio Service Mesh add-on on the same AKS cluster.
Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies:Azure Web Application Firewall (WAF)onAzure Application Gatewayprovides centralized protection for your web applications. WAF defends your web services against common exploits and vulnerabilities. It keeps your service highly available for your users and helps you meet compliance requirements. In this sample, the installation of the Application Gateway and WAF Policy is disabled, but you can turn it on in the parameters file. The WAF policy deployed by this sample consists of three types of security rules:
Custom rulesare used to block incoming requests based on the content of the payload, querystring, HTTP request method, IP address of the caller, and more. This sample add a couple of customer rules to block calls coming from a given IP range or calls that contain the wordblockmein the querystring.
OWASPCore rule setsprovide an easy way to deploy protection against a common set of security threats like SQL injection or cross-site scripting.
Bot protection rule setcan be used to take custom actions on requests from known bot categories.
systemnode pool in a dedicated subnet. The default node pool hosts only critical system pods and services. The worker nodes have node taint which prevents application pods from beings scheduled on this node pool.
usernode pool hosting user workloads and artifacts in a dedicated subnet.
SystemSubnet: a subnet used for the agent nodes of thesystemnode pool.
UserSubnet: a subnet used for the agent nodes of theusernode pool.
PodSubnet: a subnet used to allocate private IP addresses to pods dynamically.
ApiServerSubnet: API Server VNET Integration projects the API server endpoint directly into this delegated subnet in the virtual network where the AKS cluster is deployed.
AzureBastionSubnet: a subnet for the Azure Bastion Host.
VmSubnet: a subnet for a jump-box virtual machine used to connect to the (private) AKS cluster and for the private endpoints.
AppGatewaySubnet: a subnet hosting the Application Gateway.
Microsoft.Compute/virtualMachines: Bicep modules can optionally create a jump-box virtual machine to manage the private AKS cluster.
Microsoft.Network/bastionHosts: a separate Azure Bastion is deployed in the AKS cluster virtual network to provide SSH connectivity to both agent nodes and virtual machines.
Microsoft.Network/natGateways: a bring-your-own (BYO)Azure NAT Gatewayto manage outbound connections initiated by AKS-hosted workloads. The NAT Gateway is associated to theSystemSubnet,UserSubnet, andPodSubnetsubnets. TheoutboundTypeproperty of the cluster is set touserAssignedNatGatewayto specify that a BYO NAT Gateway is used for outbound connections. NOTE: you can update theoutboundTypeafter cluster creation and this will deploy or remove resources as required to put the cluster into the new egress configuration. For more information, seeUpdating outboundType after cluster creation.
Microsoft.Storage/storageAccounts: this storage account is used to store the boot diagnostics logs of both the service provider and service consumer virtual machines. Boot Diagnostics is a debugging feature that allows you to view console output and screenshots to diagnose virtual machine status.
Microsoft.ContainerRegistry/registries: an Azure Container Registry (ACR) to build, store, and manage container images and artifacts in a private registry for all container deployments.
NOTE You can find thearchitecture.vsdxfile used for the diagram under thevisiofolder.
What is Bicep?
Bicepis 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 termservice meshdescribes 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?
Istiois 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 likecorednswhen Istio is enabled.
Microsoft provides managed lifecycle (upgrades) for Istio components when triggered by user.
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 theAzureServiceMeshPreviewfeature 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 reflectsRegistered, refresh the registration of theMicrosoft.ContainerServiceresource 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 thebicepfolder using thedeploy.shBash script in the same folder. Specify a value for the following parameters in thedeploy.shscript andmain.parameters.jsonparameters 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.sshPublicKeyis the recommended value. Allowed values:sshPublicKeyandpassword.
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.
// 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
@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 sample makes use of aDeployment Scriptto run theinstall-istio-demo.shBash script that installs thebookinfoweb 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. Theproductpagemicroservice calls the details and reviews microservices to populate the page.
details. Thedetailsmicroservice contains book information.
reviews. Thereviewsmicroservice contains book reviews. It also calls the ratings microservice.
ratings. Theratingsmicroservice 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
Creates a namespace for thebookinfosample application.
Annotates the namespace to inject Istio sidecar containers into the pods of the workload. For more information, seeInstalling the Sidecarin the Istio documentation.
Deploys thebookinfosample application using YAML manifests.
Deploys theGatewayandVirtual Serviceobjects to expose the sample application via the Istio Ingress Gateway.
In Istio,Gatewaysare 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 withdestination 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.
Theinstall-istio-demo.shBash script returns the following outputs to the deployment script:
Namespace hosting the bookinfo sample. You can change the defaultbookinfonamespace by assigning a different value to thenamespaceparameter of themain.bicepmodule.
Public IP address used by the Istio ingress gateway.
Port used by the Istio ingress gateway.
URL of the Istio ingress gateway.
URL of theproductpageof thebookinfosample application.
Test the application
If the deployment succeeds, thebookinfoapplication will be publicly exposed via the external Istio ingress gateway. You can retrieve the URL of theproductpageof thebookinfosample application from the outputs of the deployment script, as shown in the following picture:
You can use open the URL of theproductpageof thebookinfosample application using a web browser, as shown in the following picture.
You can also use thetest.shBash script under thebicepfolder to automatically retrieve the the URL of theproductpageof thebookinfosample 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>
"}},"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":"
Istioaddresses 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.
In addition, this sample shows how to deploy anAzure Kubernetes Servicecluster with the following features:
\n
\n
\n
API Server VNET Integrationallows you to enable network communication between the API server and the cluster nodes without requiring a private link or tunnel. AKS clusters with API Server VNET integration provide a series of advantages, for example, they can have public network access or private cluster mode enabled or disabled without redeploying the cluster. For more information, seeCreate an Azure Kubernetes Service cluster with API Server VNet Integration.
\n
Azure NAT Gatewayto manage outbound connections initiated by AKS-hosted workloads.
\n
Event-driven Autoscaling (KEDA) add-onis a single-purpose and lightweight component that strives to make application autoscaling simple and is a CNCF Incubation project.
\n
Dapr extension for Azure Kubernetes Service (AKS)allows you to installDapr, a portable, event-driven runtime that simplifies building resilient, stateless, and stateful applications that run on the cloud and edge and embrace the diversity of languages and developer frameworks. With its sidecar architecture, Dapr helps you tackle the challenges that come with building microservices and keeps your code platform agnostic.
Vertical Pod Autoscalingallows you to automatically sets resource requests and limits on containers per workload based on past usage. VPA makes certain pods are scheduled onto nodes that have the required CPU and memory resources. For more information, seeKubernetes Vertical Pod Autoscaling.
Image Cleanerto clean up stale images on your Azure Kubernetes Service cluster.
\n
Open Service Mesh add-onis a lightweight, extensible, cloud native service mesh that allows you to uniformly manage, secure, and get out-of-the-box observability features for highly dynamic microservice environments. Bicep modules allow to install the Open Service Mesh add-on as an alternative to the Istio Service Mesh add-on.
\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.
Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies:Azure Web Application Firewall (WAF)onAzure Application Gatewayprovides centralized protection for your web applications. WAF defends your web services against common exploits and vulnerabilities. It keeps your service highly available for your users and helps you meet compliance requirements. In this sample, the installation of the Application Gateway and WAF Policy is disabled, but you can turn it on in the parameters file. The WAF policy deployed by this sample consists of three types of security rules:\n
\n
Custom rulesare used to block incoming requests based on the content of the payload, querystring, HTTP request method, IP address of the caller, and more. This sample add a couple of customer rules to block calls coming from a given IP range or calls that contain the wordblockmein the querystring.
\n
OWASPCore rule setsprovide an easy way to deploy protection against a common set of security threats like SQL injection or cross-site scripting.
\n
Bot protection rule setcan be used to take custom actions on requests from known bot categories.
systemnode pool in a dedicated subnet. The default node pool hosts only critical system pods and services. The worker nodes have node taint which prevents application pods from beings scheduled on this node pool.
\n
usernode pool hosting user workloads and artifacts in a dedicated subnet.
SystemSubnet: a subnet used for the agent nodes of thesystemnode pool.
\n
UserSubnet: a subnet used for the agent nodes of theusernode pool.
\n
PodSubnet: a subnet used to allocate private IP addresses to pods dynamically.
\n
ApiServerSubnet: API Server VNET Integration projects the API server endpoint directly into this delegated subnet in the virtual network where the AKS cluster is deployed.
\n
AzureBastionSubnet: a subnet for the Azure Bastion Host.
\n
VmSubnet: a subnet for a jump-box virtual machine used to connect to the (private) AKS cluster and for the private endpoints.
\n
AppGatewaySubnet: a subnet hosting the Application Gateway.
Microsoft.Compute/virtualMachines: Bicep modules can optionally create a jump-box virtual machine to manage the private AKS cluster.
\n
Microsoft.Network/bastionHosts: a separate Azure Bastion is deployed in the AKS cluster virtual network to provide SSH connectivity to both agent nodes and virtual machines.
\n
Microsoft.Network/natGateways: a bring-your-own (BYO)Azure NAT Gatewayto manage outbound connections initiated by AKS-hosted workloads. The NAT Gateway is associated to theSystemSubnet,UserSubnet, andPodSubnetsubnets. TheoutboundTypeproperty of the cluster is set touserAssignedNatGatewayto specify that a BYO NAT Gateway is used for outbound connections. NOTE: you can update theoutboundTypeafter cluster creation and this will deploy or remove resources as required to put the cluster into the new egress configuration. For more information, seeUpdating outboundType after cluster creation.
\n
Microsoft.Storage/storageAccounts: this storage account is used to store the boot diagnostics logs of both the service provider and service consumer virtual machines. Boot Diagnostics is a debugging feature that allows you to view console output and screenshots to diagnose virtual machine status.
\n
Microsoft.ContainerRegistry/registries: an Azure Container Registry (ACR) to build, store, and manage container images and artifacts in a private registry for all container deployments.
NOTE You can find thearchitecture.vsdxfile used for the diagram under thevisiofolder.
\n
\n
\n
What is Bicep?
\n
Bicepis 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 termservice meshdescribes 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
Istiois 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
Secure service-to-service communication in a cluster with TLS encryption, strong identity-based authentication and authorization.
\n
Automatic load balancing for HTTP, gRPC, WebSocket, and TCP traffic.
\n
Fine-grained control of traffic behavior with rich routing rules, retries, failovers, and fault injection.
\n
A pluggable policy layer and configuration API supporting access controls, rate limits and quotas.
\n
Automatic metrics, logs, and traces for all traffic within a cluster, including cluster ingress and egress.
\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
Istio versions are tested and verified to be compatible with supported versions of Azure Kubernetes Service.
\n
Microsoft handles scaling and configuration of Istio control plane
\n
Microsoft adjusts scaling of AKS components likecorednswhen Istio is enabled.
\n
Microsoft provides managed lifecycle (upgrades) for Istio components when triggered by user.
The add-on doesn't work on AKS clusters that have Istio installed on them already outside the add-on installation.
\n
Managed lifecycle of mesh on how Istio versions are installed and later made available for upgrades.
\n
Istio doesn't support Windows Server containers.
\n
Customization of mesh based on the following custom resources is blocked for now -EnvoyFilter, ProxyConfig, WorkloadEntry, WorkloadGroup, Telemetry, IstioOperator, WasmPlugin
\n
\n
\n
Register the AzureServiceMeshPreview feature flag
\n
Register theAzureServiceMeshPreviewfeature 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 reflectsRegistered, refresh the registration of theMicrosoft.ContainerServiceresource 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 thebicepfolder using thedeploy.shBash script in the same folder. Specify a value for the following parameters in thedeploy.shscript andmain.parameters.jsonparameters file before deploying the Bicep modules.
\n
\n
\n
prefix: specifies a prefix for all the Azure resources.
\n
authenticationType: specifies the type of authentication when accessing the Virtual Machine.sshPublicKeyis the recommended value. Allowed values:sshPublicKeyandpassword.
\n
vmAdminUsername: specifies the name of the administrator account of the virtual machine.
\n
vmAdminPasswordOrKey: specifies the SSH Key or password for the virtual machine.
\n
aksClusterSshPublicKey: specifies the SSH Key or password for AKS cluster agent nodes.
\n
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.
\n
keyVaultObjectIds: Specifies the object ID of the service principals to configure in Key Vault access policies.
// 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
@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'
The sample makes use of aDeployment Scriptto run theinstall-istio-demo.shBash script that installs thebookinfoweb 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
productpage. Theproductpagemicroservice calls the details and reviews microservices to populate the page.
\n
details. Thedetailsmicroservice contains book information.
\n
reviews. Thereviewsmicroservice contains book reviews. It also calls the ratings microservice.
\n
ratings. Theratingsmicroservice contains book ranking information that accompanies a book review.
\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
Creates a namespace for thebookinfosample application.
\n
Annotates the namespace to inject Istio sidecar containers into the pods of the workload. For more information, seeInstalling the Sidecarin the Istio documentation.
\n
Deploys thebookinfosample application using YAML manifests.
\n
Deploys theGatewayandVirtual Serviceobjects to expose the sample application via the Istio Ingress Gateway.\n
\n
In Istio,Gatewaysare 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.
\n
Virtual services, along withdestination 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.
\n
\n
\n
\n
Theinstall-istio-demo.shBash script returns the following outputs to the deployment script:
\n
\n
\n
Namespace hosting the bookinfo sample. You can change the defaultbookinfonamespace by assigning a different value to thenamespaceparameter of themain.bicepmodule.
\n
Public IP address used by the Istio ingress gateway.
\n
Port used by the Istio ingress gateway.
\n
URL of the Istio ingress gateway.
\n
URL of theproductpageof thebookinfosample application.
\n
\n
\n
Test the application
\n
If the deployment succeeds, thebookinfoapplication will be publicly exposed via the external Istio ingress gateway. You can retrieve the URL of theproductpageof thebookinfosample 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 theproductpageof thebookinfosample application using a web browser, as shown in the following picture.
\n
\n
\n
\n
You can also use thetest.shBash script under thebicepfolder to automatically retrieve the the URL of theproductpageof thebookinfosample 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>
Istioaddresses 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.
In addition, this sample shows how to deploy anAzure Kubernetes Servicecluster with the following features:
\n
\n
\n
API Server VNET Integrationallows you to enable network communication between the API server and the cluster nodes without requiring a private link or tunnel. AKS clusters with API Server VNET integration provide a series of advantages, for example, they can have public network access or private cluster mode enabled or disabled without redeploying the cluster. For more information, seeCreate an Azure Kubernetes Service cluster with API Server VNet Integration.
\n
Azure NAT Gatewayto manage outbound connections initiated by AKS-hosted workloads.
\n
Event-driven Autoscaling (KEDA) add-onis a single-purpose and lightweight component that strives to make application autoscaling simple and is a CNCF Incubation project.
\n
Dapr extension for Azure Kubernetes Service (AKS)allows you to installDapr, a portable, event-driven runtime that simplifies building resilient, stateless, and stateful applications that run on the cloud and edge and embrace the diversity of languages and developer frameworks. With its sidecar architecture, Dapr helps you tackle the challenges that come with building microservices and keeps your code platform agnostic.
Vertical Pod Autoscalingallows you to automatically sets resource requests and limits on containers per workload based on past usage. VPA makes certain pods are scheduled onto nodes that have the required CPU and memory resources. For more information, seeKubernetes Vertical Pod Autoscaling.
Image Cleanerto clean up stale images on your Azure Kubernetes Service cluster.
\n
Open Service Mesh add-onis a lightweight, extensible, cloud native service mesh that allows you to uniformly manage, secure, and get out-of-the-box observability features for highly dynamic microservice environments. Bicep modules allow to install the Open Service Mesh add-on as an alternative to the Istio Service Mesh add-on.
\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.
Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies:Azure Web Application Firewall (WAF)onAzure Application Gatewayprovides centralized protection for your web applications. WAF defends your web services against common exploits and vulnerabilities. It keeps your service highly available for your users and helps you meet compliance requirements. In this sample, the installation of the Application Gateway and WAF Policy is disabled, but you can turn it on in the parameters file. The WAF policy deployed by this sample consists of three types of security rules:\n
\n
Custom rulesare used to block incoming requests based on the content of the payload, querystring, HTTP request method, IP address of the caller, and more. This sample add a couple of customer rules to block calls coming from a given IP range or calls that contain the wordblockmein the querystring.
\n
OWASPCore rule setsprovide an easy way to deploy protection against a common set of security threats like SQL injection or cross-site scripting.
\n
Bot protection rule setcan be used to take custom actions on requests from known bot categories.
systemnode pool in a dedicated subnet. The default node pool hosts only critical system pods and services. The worker nodes have node taint which prevents application pods from beings scheduled on this node pool.
\n
usernode pool hosting user workloads and artifacts in a dedicated subnet.
SystemSubnet: a subnet used for the agent nodes of thesystemnode pool.
\n
UserSubnet: a subnet used for the agent nodes of theusernode pool.
\n
PodSubnet: a subnet used to allocate private IP addresses to pods dynamically.
\n
ApiServerSubnet: API Server VNET Integration projects the API server endpoint directly into this delegated subnet in the virtual network where the AKS cluster is deployed.
\n
AzureBastionSubnet: a subnet for the Azure Bastion Host.
\n
VmSubnet: a subnet for a jump-box virtual machine used to connect to the (private) AKS cluster and for the private endpoints.
\n
AppGatewaySubnet: a subnet hosting the Application Gateway.
Microsoft.Compute/virtualMachines: Bicep modules can optionally create a jump-box virtual machine to manage the private AKS cluster.
\n
Microsoft.Network/bastionHosts: a separate Azure Bastion is deployed in the AKS cluster virtual network to provide SSH connectivity to both agent nodes and virtual machines.
\n
Microsoft.Network/natGateways: a bring-your-own (BYO)Azure NAT Gatewayto manage outbound connections initiated by AKS-hosted workloads. The NAT Gateway is associated to theSystemSubnet,UserSubnet, andPodSubnetsubnets. TheoutboundTypeproperty of the cluster is set touserAssignedNatGatewayto specify that a BYO NAT Gateway is used for outbound connections. NOTE: you can update theoutboundTypeafter cluster creation and this will deploy or remove resources as required to put the cluster into the new egress configuration. For more information, seeUpdating outboundType after cluster creation.
\n
Microsoft.Storage/storageAccounts: this storage account is used to store the boot diagnostics logs of both the service provider and service consumer virtual machines. Boot Diagnostics is a debugging feature that allows you to view console output and screenshots to diagnose virtual machine status.
\n
Microsoft.ContainerRegistry/registries: an Azure Container Registry (ACR) to build, store, and manage container images and artifacts in a private registry for all container deployments.
NOTE You can find thearchitecture.vsdxfile used for the diagram under thevisiofolder.
\n
\n
\n
What is Bicep?
\n
Bicepis 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 termservice meshdescribes 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
Istiois 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
Secure service-to-service communication in a cluster with TLS encryption, strong identity-based authentication and authorization.
\n
Automatic load balancing for HTTP, gRPC, WebSocket, and TCP traffic.
\n
Fine-grained control of traffic behavior with rich routing rules, retries, failovers, and fault injection.
\n
A pluggable policy layer and configuration API supporting access controls, rate limits and quotas.
\n
Automatic metrics, logs, and traces for all traffic within a cluster, including cluster ingress and egress.
\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
Istio versions are tested and verified to be compatible with supported versions of Azure Kubernetes Service.
\n
Microsoft handles scaling and configuration of Istio control plane
\n
Microsoft adjusts scaling of AKS components likecorednswhen Istio is enabled.
\n
Microsoft provides managed lifecycle (upgrades) for Istio components when triggered by user.
The add-on doesn't work on AKS clusters that have Istio installed on them already outside the add-on installation.
\n
Managed lifecycle of mesh on how Istio versions are installed and later made available for upgrades.
\n
Istio doesn't support Windows Server containers.
\n
Customization of mesh based on the following custom resources is blocked for now -EnvoyFilter, ProxyConfig, WorkloadEntry, WorkloadGroup, Telemetry, IstioOperator, WasmPlugin
\n
\n
\n
Register the AzureServiceMeshPreview feature flag
\n
Register theAzureServiceMeshPreviewfeature flag by using the az feature register command:
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 reflectsRegistered, refresh the registration of theMicrosoft.ContainerServiceresource provider by using the az provider register command:
You can deploy the Bicep modules in thebicepfolder using thedeploy.shBash script in the same folder. Specify a value for the following parameters in thedeploy.shscript andmain.parameters.jsonparameters file before deploying the Bicep modules.
\n
\n
\n
prefix: specifies a prefix for all the Azure resources.
\n
authenticationType: specifies the type of authentication when accessing the Virtual Machine.sshPublicKeyis the recommended value. Allowed values:sshPublicKeyandpassword.
\n
vmAdminUsername: specifies the name of the administrator account of the virtual machine.
\n
vmAdminPasswordOrKey: specifies the SSH Key or password for the virtual machine.
\n
aksClusterSshPublicKey: specifies the SSH Key or password for AKS cluster agent nodes.
\n
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.
\n
keyVaultObjectIds: Specifies the object ID of the service principals to configure in Key Vault access policies.
\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@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
The sample makes use of aDeployment Scriptto run theinstall-istio-demo.shBash script that installs thebookinfoweb 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
productpage. Theproductpagemicroservice calls the details and reviews microservices to populate the page.
\n
details. Thedetailsmicroservice contains book information.
\n
reviews. Thereviewsmicroservice contains book reviews. It also calls the ratings microservice.
\n
ratings. Theratingsmicroservice contains book ranking information that accompanies a book review.
\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
Creates a namespace for thebookinfosample application.
\n
Annotates the namespace to inject Istio sidecar containers into the pods of the workload. For more information, seeInstalling the Sidecarin the Istio documentation.
\n
Deploys thebookinfosample application using YAML manifests.
\n
Deploys theGatewayandVirtual Serviceobjects to expose the sample application via the Istio Ingress Gateway.\n
\n
In Istio,Gatewaysare 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.
\n
Virtual services, along withdestination 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.
\n
\n
\n
\n
Theinstall-istio-demo.shBash script returns the following outputs to the deployment script:
\n
\n
\n
Namespace hosting the bookinfo sample. You can change the defaultbookinfonamespace by assigning a different value to thenamespaceparameter of themain.bicepmodule.
\n
Public IP address used by the Istio ingress gateway.
\n
Port used by the Istio ingress gateway.
\n
URL of the Istio ingress gateway.
\n
URL of theproductpageof thebookinfosample application.
\n
\n
\n
Test the application
\n
If the deployment succeeds, thebookinfoapplication will be publicly exposed via the external Istio ingress gateway. You can retrieve the URL of theproductpageof thebookinfosample 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 theproductpageof thebookinfosample application using a web browser, as shown in the following picture.
\n
\n
\n
\n
You can also use thetest.shBash script under thebicepfolder to automatically retrieve the the URL of theproductpageof thebookinfosample 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
","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"}]}