├── session-delivery-resources ├── Copilot_demo │ └── Directors_Cut.mp4 ├── demo │ ├── reset.sh │ ├── cleanup.sh │ ├── demo.sh │ ├── demo-magic.sh │ ├── setup.sh │ └── WALKTHROUGH.md └── README.md ├── src ├── infra │ └── terraform │ │ ├── locals.tf │ │ ├── loganalytics.tf │ │ ├── prometheus-config │ │ ├── grafana.tf │ │ ├── main.tf │ │ ├── servicebus.tf │ │ ├── outputs.tf │ │ ├── variables.tf │ │ ├── openai.tf │ │ ├── kubernetes.tf │ │ ├── cosmosdb.tf │ │ ├── .terraform.lock.hcl │ │ └── prometheus.tf └── manifests │ ├── kustomize │ ├── overlays │ │ └── dev │ │ │ └── kustomization.yaml │ └── base │ │ ├── kustomization.yaml │ │ ├── virtual-worker.yaml │ │ ├── virtual-customer.yaml │ │ ├── product-service.yaml │ │ ├── order-service.yaml │ │ ├── makeline-service.yaml │ │ ├── store-front.yaml │ │ └── store-admin.yaml │ └── sample.yaml ├── Makefile ├── CODE_OF_CONDUCT.md ├── .github └── workflows │ └── auto_add_to_project.yml ├── lab └── README.md ├── .gitignore ├── LICENSE ├── SUPPORT.md ├── SECURITY.md └── README.md /session-delivery-resources/Copilot_demo/Directors_Cut.mp4: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/infra/terraform/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | random_name = "${random_pet.example.id}${random_integer.example.result}" 3 | } 4 | -------------------------------------------------------------------------------- /src/manifests/kustomize/overlays/dev/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../base 5 | namespace: pets 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # run a script 2 | setup: 3 | @./session-delivery-resources/demo/setup.sh; 4 | 5 | run: 6 | @cd ./session-delivery-resources/demo && ./demo.sh; 7 | 8 | reset: 9 | @./session-delivery-resources/demo/reset.sh; 10 | 11 | cleanup: 12 | @./session-delivery-resources/demo/cleanup.sh; -------------------------------------------------------------------------------- /src/manifests/kustomize/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - makeline-service.yaml 5 | - order-service.yaml 6 | - product-service.yaml 7 | - store-admin.yaml 8 | - store-front.yaml 9 | - virtual-customer.yaml 10 | - virtual-worker.yaml 11 | -------------------------------------------------------------------------------- /src/infra/terraform/loganalytics.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_log_analytics_workspace" "example" { 2 | resource_group_name = azurerm_resource_group.example.name 3 | location = azurerm_resource_group.example.location 4 | name = "log-${local.random_name}" 5 | sku = "PerGB2018" 6 | retention_in_days = 30 7 | } 8 | -------------------------------------------------------------------------------- /session-delivery-resources/demo/reset.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Deleting ArgoCD app..." 4 | argocd app delete pets --yes 5 | 6 | echo "Resetting the forked demo repository..." 7 | cd src/infra/terraform/ai-tour-aks-demo 8 | git fetch upstream main 9 | git reset --hard upstream/main 10 | git push origin main --force 11 | 12 | echo "Removing host entries..." 13 | sudo sed -i '' '/admin.aks.rocks/d' /etc/hosts 14 | 15 | echo "Reset complete!" -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /src/infra/terraform/prometheus-config: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 30s 3 | scrape_configs: 4 | - job_name: workload 5 | scheme: http 6 | kubernetes_sd_configs: 7 | - role: endpoints 8 | relabel_configs: 9 | - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] 10 | action: keep 11 | regex: true 12 | - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] 13 | action: replace 14 | target_label: __metrics_path__ 15 | regex: (.+) 16 | - source_labels: 17 | [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port] 18 | action: replace 19 | regex: ([^:]+)(?::\d+)?;(\d+) 20 | replacement: $1:$2 21 | target_label: __address__ -------------------------------------------------------------------------------- /src/manifests/sample.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: mynginx 6 | name: mynginx 7 | spec: 8 | replicas: 3 9 | selector: 10 | matchLabels: 11 | app: mynginx 12 | strategy: {} 13 | template: 14 | metadata: 15 | labels: 16 | app: mynginx 17 | spec: 18 | containers: 19 | - image: nginx 20 | name: nginx 21 | resources: {} 22 | affinity: 23 | podAntiAffinity: 24 | requiredDuringSchedulingIgnoredDuringExecution: 25 | - labelSelector: 26 | matchExpressions: 27 | - key: app 28 | operator: In 29 | values: 30 | - mynginx 31 | topologyKey: "kubernetes.io/hostname" -------------------------------------------------------------------------------- /session-delivery-resources/demo/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export ARM_SUBSCRIPTION_ID=$(az account show --query id -o tsv) 4 | 5 | echo "Destroying Terraform created resources..." 6 | cd src/infra/terraform 7 | terraform destroy -var="owner=$(whoami)" --auto-approve 8 | rm terraform.tfstate* 9 | 10 | echo "Resetting the forked demo repository..." 11 | cd ./ai-tour-aks-demo 12 | git fetch upstream main 13 | git reset --hard upstream/main 14 | git push origin main --force 15 | 16 | echo "Deleting the forked demo repository..." 17 | cd .. 18 | rm -rf ai-tour-aks-demo 19 | gh auth login -h github.com -s delete_repo 20 | gh repo delete $(gh api user --jq .login)/ai-tour-aks-demo --yes 21 | 22 | echo "Removing host entries..." 23 | sudo sed -i '' '/admin.aks.rocks/d' /etc/hosts 24 | 25 | echo "Cleanup complete!" -------------------------------------------------------------------------------- /.github/workflows/auto_add_to_project.yml: -------------------------------------------------------------------------------- 1 | name: Add new issues to AI Tour GH Project 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | add-to-project: 10 | name: Add issue to project 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/add-to-project@v1.0.2 14 | with: 15 | project-url: ${{ secrets.GH_PROJECT_URL }} 16 | github-token: ${{ secrets.ADD_TO_PROJECT }} 17 | label_issues: 18 | runs-on: ubuntu-latest 19 | permissions: 20 | issues: write 21 | steps: 22 | - run: gh issue edit "$NUMBER" --add-label "$LABELS" 23 | env: 24 | GH_TOKEN: ${{ secrets.ADD_TO_PROJECT }} 25 | GH_REPO: ${{ github.repository }} 26 | NUMBER: ${{ github.event.issue.number }} 27 | LABELS: Infrastructure 28 | -------------------------------------------------------------------------------- /src/manifests/kustomize/base/virtual-worker.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: virtual-worker 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: virtual-worker 10 | template: 11 | metadata: 12 | labels: 13 | app: virtual-worker 14 | spec: 15 | nodeSelector: 16 | "kubernetes.io/os": linux 17 | containers: 18 | - name: virtual-worker 19 | image: ghcr.io/pauldotyu/aks-store-demo/virtual-worker:1.5.0 20 | env: 21 | - name: MAKELINE_SERVICE_URL 22 | value: http://makeline-service:3001 23 | - name: ORDERS_PER_HOUR 24 | value: "300" 25 | resources: 26 | requests: 27 | cpu: 1m 28 | memory: 1Mi 29 | limits: 30 | cpu: 1m 31 | memory: 5Mi -------------------------------------------------------------------------------- /src/manifests/kustomize/base/virtual-customer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: virtual-customer 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: virtual-customer 10 | template: 11 | metadata: 12 | labels: 13 | app: virtual-customer 14 | spec: 15 | nodeSelector: 16 | "kubernetes.io/os": linux 17 | containers: 18 | - name: virtual-customer 19 | image: ghcr.io/pauldotyu/aks-store-demo/virtual-customer:1.5.0 20 | env: 21 | - name: ORDER_SERVICE_URL 22 | value: http://order-service:3000/ 23 | - name: ORDERS_PER_HOUR 24 | value: "500" 25 | resources: 26 | requests: 27 | cpu: 1m 28 | memory: 1Mi 29 | limits: 30 | cpu: 1m 31 | memory: 5Mi -------------------------------------------------------------------------------- /lab/README.md: -------------------------------------------------------------------------------- 1 | # Session folder 2 | 3 | This folder contains the public facing files for the lab `LAB NAME HERE`. 4 | 5 | ## Deployment/Getting Started/Steps 6 | 7 | TODO: Add instructions below on how to follow/interect with your content as intended. An assets folder was created for your convienced if any additional media is needed. 8 | 9 | ## Discussions 10 | Use the Discussion page on this repository to ask any questions or continue the learning conversation! 11 | 12 | ## Additional Resources and Continued Learning 13 | | Resources | Links | Description | 14 | |-------------------|----------------------------------|-------------------| 15 | | Future Learning 1 | [Link 1](https://www.google.com/) | Learn more about X | 16 | | Future Learning 2 | [Link 2](https://www.google.com/) | Learn more about Y | 17 | 18 | ## Source code 19 | 20 | The source code for this session is included in the CODE folder in the root directory. 21 | -------------------------------------------------------------------------------- /src/infra/terraform/grafana.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_dashboard_grafana" "example" { 2 | resource_group_name = azurerm_resource_group.example.name 3 | location = azurerm_resource_group.example.location 4 | name = "graf-${local.random_name}" 5 | grafana_major_version = 10 6 | 7 | identity { 8 | type = "SystemAssigned" 9 | } 10 | 11 | azure_monitor_workspace_integrations { 12 | resource_id = azurerm_monitor_workspace.example.id 13 | } 14 | } 15 | 16 | resource "azurerm_role_assignment" "grafana1" { 17 | scope = azurerm_dashboard_grafana.example.id 18 | role_definition_name = "Grafana Admin" 19 | principal_id = data.azurerm_client_config.current.object_id 20 | } 21 | 22 | resource "azurerm_role_assignment" "grafana2" { 23 | scope = azurerm_resource_group.example.id 24 | role_definition_name = "Monitoring Data Reader" 25 | principal_id = azurerm_dashboard_grafana.example.identity[0].principal_id 26 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Ignore transient lock info files created by terraform apply 27 | .terraform.tfstate.lock.info 28 | 29 | # Include override files you do wish to add to version control using negated pattern 30 | # !example_override.tf 31 | 32 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 33 | # example: *tfplan* 34 | 35 | # Ignore CLI configuration files 36 | .terraformrc 37 | terraform.rc -------------------------------------------------------------------------------- /src/infra/terraform/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azapi = { 4 | source = "Azure/azapi" 5 | version = "=2.2.0" 6 | } 7 | 8 | azurerm = { 9 | source = "hashicorp/azurerm" 10 | version = "=4.15.0" 11 | } 12 | 13 | local = { 14 | source = "hashicorp/local" 15 | version = "=2.5.2" 16 | } 17 | } 18 | } 19 | 20 | provider "azurerm" { 21 | features { 22 | resource_group { 23 | prevent_deletion_if_contains_resources = false 24 | } 25 | 26 | cognitive_account { 27 | purge_soft_delete_on_destroy = true 28 | } 29 | } 30 | } 31 | 32 | data "azurerm_subscription" "current" {} 33 | data "azurerm_client_config" "current" {} 34 | 35 | resource "random_integer" "example" { 36 | min = 10 37 | max = 99 38 | } 39 | 40 | resource "random_pet" "example" { 41 | length = 2 42 | separator = "" 43 | keepers = { 44 | location = var.location 45 | } 46 | } 47 | 48 | resource "azurerm_resource_group" "example" { 49 | name = "rg-${local.random_name}" 50 | location = var.location 51 | tags = { 52 | owner = var.owner 53 | session = "BRK470" 54 | } 55 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # TODO: The maintainer of this repo has not yet edited this file 2 | 3 | **REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? 4 | 5 | - **No CSS support:** Fill out this template with information about how to file issues and get help. 6 | - **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps. 7 | - **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide. 8 | 9 | *Then remove this first heading from this SUPPORT.MD file before publishing your repo.* 10 | 11 | # Support 12 | 13 | ## How to file issues and get help 14 | 15 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 16 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 17 | feature request as a new Issue. 18 | 19 | For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE 20 | FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER 21 | CHANNEL. WHERE WILL YOU HELP PEOPLE?**. 22 | 23 | ## Microsoft Support Policy 24 | 25 | Support for this **PROJECT or PRODUCT** is limited to the resources listed above. 26 | -------------------------------------------------------------------------------- /src/infra/terraform/servicebus.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_servicebus_namespace" "example" { 2 | name = "sb-${local.random_name}" 3 | location = azurerm_resource_group.example.location 4 | resource_group_name = azurerm_resource_group.example.name 5 | sku = "Standard" 6 | local_auth_enabled = false 7 | } 8 | 9 | resource "azurerm_servicebus_queue" "example" { 10 | name = "orders" 11 | namespace_id = azurerm_servicebus_namespace.example.id 12 | } 13 | 14 | resource "azurerm_user_assigned_identity" "sb" { 15 | resource_group_name = azurerm_resource_group.example.name 16 | location = var.location 17 | name = "sb-${local.random_name}-identity" 18 | } 19 | 20 | resource "azurerm_federated_identity_credential" "sb" { 21 | resource_group_name = azurerm_resource_group.example.name 22 | parent_id = azurerm_user_assigned_identity.db.id 23 | name = "sb-${local.random_name}" 24 | issuer = azapi_resource.aks.output.properties.oidcIssuerProfile.issuerURL 25 | audience = ["api://AzureADTokenExchange"] 26 | subject = "system:serviceaccount:${var.k8s_namespace}:order-service-account" 27 | } 28 | 29 | resource "azurerm_role_assignment" "sb_data_owner" { 30 | scope = azurerm_servicebus_namespace.example.id 31 | role_definition_name = "Azure Service Bus Data Owner" 32 | principal_id = azurerm_user_assigned_identity.sb.principal_id 33 | } -------------------------------------------------------------------------------- /src/infra/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "rg_name" { 2 | value = azurerm_resource_group.example.name 3 | } 4 | 5 | output "aks_name" { 6 | value = azapi_resource.aks.name 7 | } 8 | 9 | output "oai_gpt_endpoint" { 10 | value = azurerm_cognitive_account.example.endpoint 11 | } 12 | 13 | output "oai_gpt_model_name" { 14 | value = var.gpt_model_name 15 | } 16 | 17 | output "oai_dalle_endpoint" { 18 | value = azurerm_cognitive_account.example.endpoint 19 | } 20 | 21 | output "oai_dalle_model_name" { 22 | value = var.dalle_model_name 23 | } 24 | 25 | output "oai_dalle_api_version" { 26 | value = var.dalle_openai_api_version 27 | } 28 | 29 | output "oai_identity_client_id" { 30 | value = azurerm_user_assigned_identity.oai.client_id 31 | } 32 | 33 | output "amg_name" { 34 | value = azurerm_dashboard_grafana.example.name 35 | } 36 | 37 | output "sb_hostname" { 38 | value = "${azurerm_servicebus_namespace.example.name}.servicebus.windows.net" 39 | } 40 | 41 | output "sb_queue_name" { 42 | value = azurerm_servicebus_queue.example.name 43 | } 44 | 45 | output "sb_identity_client_id" { 46 | value = azurerm_user_assigned_identity.sb.client_id 47 | } 48 | 49 | output "db_endpoint" { 50 | value = azurerm_cosmosdb_account.example.endpoint 51 | } 52 | 53 | output "db_database_name" { 54 | value = azurerm_cosmosdb_sql_database.example.name 55 | } 56 | 57 | output "db_container_name" { 58 | value = azurerm_cosmosdb_sql_container.example.name 59 | } 60 | 61 | output "db_identity_client_id" { 62 | value = azurerm_user_assigned_identity.db.client_id 63 | } -------------------------------------------------------------------------------- /src/manifests/kustomize/base/product-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: product-service 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: product-service 10 | template: 11 | metadata: 12 | labels: 13 | app: product-service 14 | spec: 15 | nodeSelector: 16 | "kubernetes.io/os": linux 17 | containers: 18 | - name: product-service 19 | image: ghcr.io/pauldotyu/aks-store-demo/product-service:1.5.0 20 | ports: 21 | - containerPort: 3002 22 | resources: 23 | requests: 24 | cpu: 1m 25 | memory: 1Mi 26 | limits: 27 | cpu: 1m 28 | memory: 6Mi 29 | startupProbe: 30 | httpGet: 31 | path: /health 32 | port: 3002 33 | failureThreshold: 3 34 | initialDelaySeconds: 5 35 | periodSeconds: 10 36 | readinessProbe: 37 | httpGet: 38 | path: /health 39 | port: 3002 40 | failureThreshold: 3 41 | initialDelaySeconds: 3 42 | periodSeconds: 15 43 | livenessProbe: 44 | httpGet: 45 | path: /health 46 | port: 3002 47 | failureThreshold: 5 48 | initialDelaySeconds: 3 49 | periodSeconds: 20 50 | affinity: 51 | podAntiAffinity: 52 | requiredDuringSchedulingIgnoredDuringExecution: 53 | - labelSelector: 54 | matchExpressions: 55 | - key: app 56 | operator: In 57 | values: 58 | - product-service 59 | topologyKey: "kubernetes.io/hostname" 60 | --- 61 | apiVersion: v1 62 | kind: Service 63 | metadata: 64 | name: product-service 65 | spec: 66 | ports: 67 | - name: http 68 | port: 3002 69 | targetPort: 3002 70 | selector: 71 | app: product-service 72 | --- 73 | apiVersion: policy/v1 74 | kind: PodDisruptionBudget 75 | metadata: 76 | name: product-service 77 | spec: 78 | minAvailable: 1 79 | selector: 80 | matchLabels: 81 | app: product-service 82 | unhealthyPodEvictionPolicy: AlwaysAllow -------------------------------------------------------------------------------- /src/manifests/kustomize/base/order-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: order-service 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: order-service 10 | template: 11 | metadata: 12 | labels: 13 | app: order-service 14 | azure.workload.identity/use: "true" 15 | spec: 16 | serviceAccountName: order-service-account 17 | nodeSelector: 18 | "kubernetes.io/os": linux 19 | containers: 20 | - name: order-service 21 | image: ghcr.io/pauldotyu/aks-store-demo/order-service:1.5.0 22 | ports: 23 | - containerPort: 3000 24 | envFrom: 25 | - configMapRef: 26 | name: order-service-configs 27 | resources: 28 | requests: 29 | cpu: 1m 30 | memory: 50Mi 31 | limits: 32 | cpu: "1" 33 | memory: 128Mi 34 | startupProbe: 35 | httpGet: 36 | path: /health 37 | port: 3000 38 | failureThreshold: 3 39 | initialDelaySeconds: 20 40 | periodSeconds: 10 41 | readinessProbe: 42 | httpGet: 43 | path: /health 44 | port: 3000 45 | failureThreshold: 3 46 | initialDelaySeconds: 3 47 | periodSeconds: 15 48 | livenessProbe: 49 | httpGet: 50 | path: /health 51 | port: 3000 52 | failureThreshold: 5 53 | initialDelaySeconds: 3 54 | periodSeconds: 20 55 | affinity: 56 | podAntiAffinity: 57 | requiredDuringSchedulingIgnoredDuringExecution: 58 | - labelSelector: 59 | matchExpressions: 60 | - key: app 61 | operator: In 62 | values: 63 | - order-service 64 | topologyKey: "kubernetes.io/hostname" 65 | --- 66 | apiVersion: v1 67 | kind: Service 68 | metadata: 69 | name: order-service 70 | spec: 71 | ports: 72 | - name: http 73 | port: 3000 74 | targetPort: 3000 75 | selector: 76 | app: order-service 77 | --- 78 | apiVersion: policy/v1 79 | kind: PodDisruptionBudget 80 | metadata: 81 | name: order-service 82 | spec: 83 | minAvailable: 1 84 | selector: 85 | matchLabels: 86 | app: order-service 87 | unhealthyPodEvictionPolicy: AlwaysAllow -------------------------------------------------------------------------------- /src/manifests/kustomize/base/makeline-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: makeline-service 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: makeline-service 10 | template: 11 | metadata: 12 | labels: 13 | app: makeline-service 14 | azure.workload.identity/use: "true" 15 | spec: 16 | serviceAccountName: makeline-service-account 17 | nodeSelector: 18 | "kubernetes.io/os": linux 19 | containers: 20 | - name: makeline-service 21 | image: ghcr.io/pauldotyu/aks-store-demo/makeline-service:1.5.0 22 | ports: 23 | - containerPort: 3001 24 | envFrom: 25 | - configMapRef: 26 | name: makeline-service-configs 27 | resources: 28 | requests: 29 | cpu: 1m 30 | memory: 6Mi 31 | limits: 32 | cpu: 5m 33 | memory: 20Mi 34 | startupProbe: 35 | httpGet: 36 | path: /health 37 | port: 3001 38 | failureThreshold: 3 39 | initialDelaySeconds: 5 40 | periodSeconds: 10 41 | readinessProbe: 42 | httpGet: 43 | path: /health 44 | port: 3001 45 | failureThreshold: 3 46 | initialDelaySeconds: 3 47 | periodSeconds: 15 48 | livenessProbe: 49 | httpGet: 50 | path: /health 51 | port: 3001 52 | failureThreshold: 5 53 | initialDelaySeconds: 3 54 | periodSeconds: 20 55 | affinity: 56 | podAntiAffinity: 57 | requiredDuringSchedulingIgnoredDuringExecution: 58 | - labelSelector: 59 | matchExpressions: 60 | - key: app 61 | operator: In 62 | values: 63 | - makeline-service 64 | topologyKey: "kubernetes.io/hostname" 65 | --- 66 | apiVersion: v1 67 | kind: Service 68 | metadata: 69 | name: makeline-service 70 | spec: 71 | ports: 72 | - name: http 73 | port: 3001 74 | targetPort: 3001 75 | selector: 76 | app: makeline-service 77 | --- 78 | apiVersion: policy/v1 79 | kind: PodDisruptionBudget 80 | metadata: 81 | name: makeline-service 82 | spec: 83 | minAvailable: 1 84 | selector: 85 | matchLabels: 86 | app: makeline-service 87 | unhealthyPodEvictionPolicy: AlwaysAllow -------------------------------------------------------------------------------- /src/infra/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "owner" { 2 | description = "value to identify the owner of the resources" 3 | type = string 4 | } 5 | 6 | variable "location" { 7 | description = "value of azure region" 8 | type = string 9 | default = "swedencentral" 10 | } 11 | 12 | # reference for model availability: https://learn.microsoft.com/azure/ai-services/openai/concepts/models#standard-deployment-model-availability 13 | variable "ai_location" { 14 | description = "value of azure region for deploying azure open ai service" 15 | type = string 16 | default = "swedencentral" 17 | } 18 | 19 | variable "gpt_model_name" { 20 | description = "value of azure open ai gpt model name" 21 | type = string 22 | default = "gpt-4o" 23 | } 24 | 25 | variable "gpt_model_version" { 26 | description = "value of azure open ai gpt model version" 27 | type = string 28 | default = "2024-08-06" 29 | } 30 | 31 | 32 | variable "gpt_model_sku_name" { 33 | description = "value of azure open ai gpt model sku name" 34 | type = string 35 | default = "GlobalStandard" 36 | } 37 | 38 | variable "gpt_model_capacity" { 39 | description = "value of azure open ai gpt model capacity" 40 | type = number 41 | default = 8 42 | } 43 | 44 | variable "dalle_model_name" { 45 | description = "value of azure open ai dall-e model name" 46 | type = string 47 | default = "dall-e-3" 48 | } 49 | 50 | variable "dalle_model_version" { 51 | description = "value of azure open ai dall-e model version" 52 | type = string 53 | default = "3.0" 54 | } 55 | 56 | variable "dalle_model_sku_name" { 57 | description = "value of azure open ai dall-e model sku name" 58 | type = string 59 | default = "Standard" 60 | } 61 | 62 | variable "dalle_model_capacity" { 63 | description = "value of azure open ai dall-e model capacity" 64 | type = number 65 | default = 1 66 | } 67 | 68 | variable "dalle_openai_api_version" { 69 | description = "value of azure open ai dall-e api version" 70 | type = string 71 | default = "2024-02-15-preview" 72 | } 73 | 74 | variable "k8s_version" { 75 | description = "value of kubernetes version" 76 | type = string 77 | default = "1.30.0" 78 | } 79 | 80 | variable "k8s_namespace" { 81 | description = "value of kubernetes namespace" 82 | type = string 83 | default = "pets" 84 | } 85 | -------------------------------------------------------------------------------- /src/infra/terraform/openai.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_cognitive_account" "example" { 2 | resource_group_name = azurerm_resource_group.example.name 3 | location = var.ai_location 4 | name = "oai-${local.random_name}" 5 | custom_subdomain_name = "oai-${local.random_name}" 6 | kind = "OpenAI" 7 | sku_name = "S0" 8 | local_auth_enabled = false 9 | } 10 | 11 | resource "azurerm_cognitive_deployment" "gpt" { 12 | cognitive_account_id = azurerm_cognitive_account.example.id 13 | name = var.gpt_model_name 14 | 15 | model { 16 | format = "OpenAI" 17 | name = var.gpt_model_name 18 | version = var.gpt_model_version 19 | } 20 | 21 | sku { 22 | name = var.gpt_model_sku_name 23 | capacity = var.gpt_model_capacity 24 | } 25 | } 26 | 27 | resource "azurerm_cognitive_deployment" "dalle" { 28 | cognitive_account_id = azurerm_cognitive_account.example.id 29 | name = var.dalle_model_name 30 | 31 | model { 32 | format = "OpenAI" 33 | name = var.dalle_model_name 34 | version = var.dalle_model_version 35 | } 36 | 37 | sku { 38 | name = var.dalle_model_sku_name 39 | capacity = var.dalle_model_capacity 40 | } 41 | } 42 | 43 | resource "azurerm_user_assigned_identity" "oai" { 44 | resource_group_name = azurerm_resource_group.example.name 45 | location = var.ai_location 46 | name = "oai-${local.random_name}-identity" 47 | } 48 | 49 | resource "azurerm_federated_identity_credential" "oai" { 50 | resource_group_name = azurerm_resource_group.example.name 51 | parent_id = azurerm_user_assigned_identity.oai.id 52 | name = "oai-${local.random_name}" 53 | issuer = azapi_resource.aks.output.properties.oidcIssuerProfile.issuerURL 54 | audience = ["api://AzureADTokenExchange"] 55 | subject = "system:serviceaccount:${var.k8s_namespace}:ai-service-account" 56 | } 57 | 58 | resource "azurerm_role_assignment" "oai_rbac_mi" { 59 | scope = azurerm_cognitive_account.example.id 60 | role_definition_name = "Cognitive Services OpenAI User" 61 | principal_id = azurerm_user_assigned_identity.oai.principal_id 62 | } 63 | 64 | resource "azurerm_role_assignment" "oai_rbac_me" { 65 | scope = azurerm_cognitive_account.example.id 66 | role_definition_name = "Cognitive Services OpenAI User" 67 | principal_id = data.azurerm_client_config.current.object_id 68 | } -------------------------------------------------------------------------------- /src/infra/terraform/kubernetes.tf: -------------------------------------------------------------------------------- 1 | resource "azapi_resource" "aks" { 2 | type = "Microsoft.ContainerService/managedClusters@2024-03-02-preview" 3 | parent_id = azurerm_resource_group.example.id 4 | location = azurerm_resource_group.example.location 5 | name = "aks-${local.random_name}" 6 | schema_validation_enabled = false 7 | 8 | body = { 9 | identity = { 10 | type = "SystemAssigned" 11 | }, 12 | properties = { 13 | agentPoolProfiles = [ 14 | { 15 | name = "systempool" 16 | count = 2 17 | vmSize = "Standard_D4pds_v6" 18 | osType = "Linux" 19 | mode = "System" 20 | } 21 | ] 22 | addonProfiles = { 23 | omsagent = { 24 | enabled = true 25 | config = { 26 | logAnalyticsWorkspaceResourceID = azurerm_log_analytics_workspace.example.id 27 | useAADAuth = "true" 28 | } 29 | } 30 | } 31 | azureMonitorProfile = { 32 | metrics = { 33 | enabled = true, 34 | kubeStateMetrics = { 35 | metricLabelsAllowlist = "", 36 | metricAnnotationsAllowList = "" 37 | } 38 | }, 39 | containerInsights = { 40 | enabled = true, 41 | logAnalyticsWorkspaceResourceId = azurerm_log_analytics_workspace.example.id 42 | } 43 | } 44 | serviceMeshProfile = { 45 | mode = "Istio" 46 | istio = { 47 | components = { 48 | ingressGateways = [ 49 | { 50 | enabled = true 51 | mode = "External" 52 | }, 53 | { 54 | enabled = true 55 | mode = "Internal" 56 | } 57 | ] 58 | } 59 | revisions = [ 60 | "asm-1-22" 61 | ] 62 | } 63 | } 64 | } 65 | sku = { 66 | name = "Automatic" 67 | tier = "Standard" 68 | } 69 | } 70 | 71 | response_export_values = [ 72 | "properties.identityProfile.kubeletidentity.objectId", 73 | "properties.oidcIssuerProfile.issuerURL", 74 | "properties.nodeResourceGroup" 75 | ] 76 | } 77 | 78 | resource "azurerm_role_assignment" "aks2" { 79 | principal_id = data.azurerm_client_config.current.object_id 80 | role_definition_name = "Azure Kubernetes Service RBAC Cluster Admin" 81 | scope = azapi_resource.aks.id 82 | } -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /session-delivery-resources/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## How To Use 3 | 4 | Welcome! 5 | 6 | We're glad you are here and look forward to your delivery of this amazing content. As an experienced presenter, we know you know HOW to present so this guide will focus on WHAT you need to present. It will provide you a full run-through of the presentation created by the presentation design team. 7 | 8 | Along with the video of the presentation, this document will link to all the assets you need to successfully present including PowerPoint slides and demo instructions & 9 | code. 10 | 11 | 1. Clone this repo 12 | 1. Read presentation in its entirety. 13 | 1. Watch the video demos that are embedded in the presentation 14 | 1. Read the [demo instructions](./demo/WALKTHROUGH.md) and do the demo at least once manually before presenting and delete the resources after to avoid charges and quota limits. 15 | 1. Open a terminal 16 | 1. Log into your Azure tenant/subscription using Azure CLI 17 | 1. Run the `make setup` command which will invoke the [`setup.sh`](./demo/setup.sh) script to setup a demo environment on the day of the presentation 18 | 1. Run the `make demo` command which will invoke the [`demo.sh`](./demo/demo.sh) script to run the demo on the day of the presentation 19 | 1. Ask questions of the Lead Presenter 20 | 1. When complete, run the `make cleanup` command which will invoke the [`clean.sh`](./demo/cleanup.sh) script to clean up the demo environment and delete all resources created during the demo 21 | 1. To restart the demo, run the `make reset` command which will invoke the [`reset.sh`](./demo/reset.sh) script to delete the application and reset the forked repo to its original state 22 | 23 | ## File Summary 24 | 25 | | Resources | Links | Description | 26 | |------------------------|-------------------------------------------|--------------------------------------------------------------------| 27 | | PowerPoint English | [Presentation](https://aka.ms/AAryjht) | Slides | 28 | | PowerPoint Spanish | [ES Presentation](https://aka.ms/AAs7u29) | ES Slides | 29 | | PowerPoint Portuguese | [PT Presentation](https://aka.ms/AAs7ets) | PT Slides | 30 | | Demo Instructions | [Walkthrough](./demo/WALKTHROUGH.md) | Step-by-step instructions for manually setting up and running demo | 31 | | Demo Setup Script | [Script](./demo/setup.sh) | Script to setup demo environment | 32 | | Demo Script | [Script](./demo/demo.md) | Script to run the demo | 33 | -------------------------------------------------------------------------------- /src/manifests/kustomize/base/store-front.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: store-front 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: store-front 10 | template: 11 | metadata: 12 | labels: 13 | app: store-front 14 | spec: 15 | nodeSelector: 16 | "kubernetes.io/os": linux 17 | containers: 18 | - name: store-front 19 | image: ghcr.io/pauldotyu/aks-store-demo/store-front:1.5.0 20 | ports: 21 | - containerPort: 8080 22 | name: store-front 23 | env: 24 | - name: VUE_APP_ORDER_SERVICE_URL 25 | value: "http://order-service:3000/" 26 | - name: VUE_APP_PRODUCT_SERVICE_URL 27 | value: "http://product-service:3002/" 28 | resources: 29 | requests: 30 | cpu: 1m 31 | memory: 200Mi 32 | limits: 33 | cpu: 1000m 34 | memory: 512Mi 35 | startupProbe: 36 | httpGet: 37 | path: /health 38 | port: 8080 39 | failureThreshold: 3 40 | initialDelaySeconds: 10 41 | periodSeconds: 10 42 | readinessProbe: 43 | httpGet: 44 | path: /health 45 | port: 8080 46 | failureThreshold: 3 47 | initialDelaySeconds: 3 48 | periodSeconds: 15 49 | livenessProbe: 50 | httpGet: 51 | path: /health 52 | port: 8080 53 | failureThreshold: 5 54 | initialDelaySeconds: 3 55 | periodSeconds: 20 56 | affinity: 57 | podAntiAffinity: 58 | requiredDuringSchedulingIgnoredDuringExecution: 59 | - labelSelector: 60 | matchExpressions: 61 | - key: app 62 | operator: In 63 | values: 64 | - store-front 65 | topologyKey: "kubernetes.io/hostname" 66 | --- 67 | apiVersion: v1 68 | kind: Service 69 | metadata: 70 | name: store-front 71 | spec: 72 | ports: 73 | - port: 80 74 | targetPort: 8080 75 | selector: 76 | app: store-front 77 | --- 78 | apiVersion: gateway.networking.k8s.io/v1 79 | kind: HTTPRoute 80 | metadata: 81 | name: store-front 82 | spec: 83 | parentRefs: 84 | - name: gateway-external 85 | namespace: aks-istio-ingress 86 | hostnames: ["store.aks.rocks"] 87 | rules: 88 | - matches: 89 | - path: 90 | type: PathPrefix 91 | value: / 92 | backendRefs: 93 | - name: store-front 94 | port: 80 95 | --- 96 | apiVersion: policy/v1 97 | kind: PodDisruptionBudget 98 | metadata: 99 | name: store-front 100 | spec: 101 | minAvailable: 1 102 | selector: 103 | matchLabels: 104 | app: store-front 105 | unhealthyPodEvictionPolicy: AlwaysAllow -------------------------------------------------------------------------------- /src/manifests/kustomize/base/store-admin.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: store-admin 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: store-admin 10 | template: 11 | metadata: 12 | labels: 13 | app: store-admin 14 | spec: 15 | nodeSelector: 16 | "kubernetes.io/os": linux 17 | containers: 18 | - name: store-admin 19 | image: ghcr.io/pauldotyu/aks-store-demo/store-admin:1.5.0 20 | ports: 21 | - containerPort: 8081 22 | name: store-admin 23 | env: 24 | - name: VUE_APP_PRODUCT_SERVICE_URL 25 | value: "http://product-service:3002/" 26 | - name: VUE_APP_MAKELINE_SERVICE_URL 27 | value: "http://makeline-service:3001/" 28 | resources: 29 | requests: 30 | cpu: 1m 31 | memory: 200Mi 32 | limits: 33 | cpu: 1000m 34 | memory: 512Mi 35 | startupProbe: 36 | httpGet: 37 | path: /health 38 | port: 8081 39 | failureThreshold: 3 40 | initialDelaySeconds: 10 41 | periodSeconds: 10 42 | readinessProbe: 43 | httpGet: 44 | path: /health 45 | port: 8081 46 | failureThreshold: 3 47 | initialDelaySeconds: 3 48 | periodSeconds: 15 49 | livenessProbe: 50 | httpGet: 51 | path: /health 52 | port: 8081 53 | failureThreshold: 5 54 | initialDelaySeconds: 3 55 | periodSeconds: 20 56 | affinity: 57 | podAntiAffinity: 58 | requiredDuringSchedulingIgnoredDuringExecution: 59 | - labelSelector: 60 | matchExpressions: 61 | - key: app 62 | operator: In 63 | values: 64 | - store-admin 65 | topologyKey: "kubernetes.io/hostname" 66 | --- 67 | apiVersion: v1 68 | kind: Service 69 | metadata: 70 | name: store-admin 71 | spec: 72 | ports: 73 | - port: 80 74 | targetPort: 8081 75 | selector: 76 | app: store-admin 77 | --- 78 | apiVersion: gateway.networking.k8s.io/v1 79 | kind: HTTPRoute 80 | metadata: 81 | name: store-admin 82 | spec: 83 | parentRefs: 84 | - name: gateway-external 85 | namespace: aks-istio-ingress 86 | hostnames: ["admin.aks.rocks"] 87 | rules: 88 | - matches: 89 | - path: 90 | type: PathPrefix 91 | value: / 92 | backendRefs: 93 | - name: store-admin 94 | port: 80 95 | --- 96 | apiVersion: policy/v1 97 | kind: PodDisruptionBudget 98 | metadata: 99 | name: store-admin 100 | spec: 101 | minAvailable: 1 102 | selector: 103 | matchLabels: 104 | app: store-admin 105 | unhealthyPodEvictionPolicy: AlwaysAllow -------------------------------------------------------------------------------- /session-delivery-resources/demo/demo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # import the magic file shout out to @paxtonhare ✨ 3 | # make sure you have pv installed for the pe and pei functions to work! 4 | which pv 5 | if [ $? -ne 0 ]; then 6 | echo "pv is not installed. Please install it using 'brew install pv' or 'sudo apt-get install pv'" 7 | exit 1 8 | fi 9 | 10 | . demo-magic.sh 11 | DEMO_PROMPT="${GREEN}➜ ${CYAN}\W ${COLOR_RESET}" 12 | clear 13 | 14 | cd ../../src/infra/terraform/ai-tour-aks-demo 15 | 16 | TYPE_SPEED=40 17 | 18 | p "# Check the pets namespace" 19 | pei "kubectl get all -n pets" 20 | 21 | p "# Deploy the demo application" 22 | TYPE_SPEED=100 23 | pei "argocd app create pets --sync-policy auto --repo \$(gh repo view --json url | jq .url -r) --revision HEAD --path src/manifests/kustomize/overlays/dev --dest-namespace pets --dest-server https://kubernetes.default.svc" 24 | TYPE_SPEED=40 25 | 26 | p "# Check the app sync status" 27 | pei "argocd app list" 28 | 29 | p "# Get the ArgoCD server password" 30 | pei "argocd admin initial-password" 31 | 32 | p "# Port-forward to the ArgoCD dashboard" 33 | pei "kubectl port-forward svc/argocd-server -n argocd 8080:443" 34 | pei "clear" 35 | 36 | p "# Get the public IP of the ingress gateway" 37 | pei "INGRESS_PUBLIC_IP=\$(kubectl get svc -n aks-istio-ingress aks-istio-ingressgateway-external -o jsonpath='{.status.loadBalancer.ingress[0].ip}')" 38 | 39 | p "# Test the app using curl" 40 | pei "curl -IL \"http://\${INGRESS_PUBLIC_IP}\" -H \"Host: admin.aks.rocks\"" 41 | 42 | p "# Test the app in the browser" 43 | pei "echo \"\${INGRESS_PUBLIC_IP} admin.aks.rocks store.aks.rocks\" | sudo tee -a /etc/hosts" 44 | pei "echo http://admin.aks.rocks" 45 | 46 | p "# Add AI to the application" 47 | pei "git fetch upstream feat/ai-rollout" 48 | pei "git merge upstream/feat/ai-rollout" 49 | 50 | p "# Push the changes to remote" 51 | pei "git push" 52 | 53 | p "# Force app sync" 54 | pei "argocd app sync pets --force" 55 | 56 | p "# Check the changes" 57 | pei "less src/manifests/kustomize/base/ai-service.yaml" 58 | 59 | p "# Watch the rollout" 60 | pei "kubectl argo rollouts get rollout ai-service -n pets -w" 61 | 62 | p "# Check the httproute weight distribution" 63 | pei "kubectl describe httproute ai-service -n pets | grep -B 3 Weight:" 64 | 65 | p "# Test the ai feature in the browser" 66 | pei "echo http://admin.aks.rocks" 67 | 68 | p "# Deploy a new version of the AI service" 69 | pei "kubectl argo rollouts set image ai-service -n pets ai-service=ghcr.io/pauldotyu/aks-store-demo/ai-service:latest" 70 | 71 | p "# Watch the rollout of the new version" 72 | pei "kubectl argo rollouts get rollout ai-service -n pets -w" 73 | 74 | p "# Check the httproute" 75 | pei "kubectl describe httproute ai-service -n pets | grep -B 3 Weight:" 76 | 77 | p "# Promote the new version of the AI service" 78 | pei "kubectl argo rollouts promote ai-service -n pets" 79 | 80 | p "# Check the httproute" 81 | pei "kubectl describe httproute ai-service -n pets | grep -B 3 Weight:" 82 | 83 | p "# Final promotion" 84 | pei "kubectl argo rollouts promote ai-service -n pets" 85 | 86 | p "# Watch the final rollout" 87 | pei "kubectl argo rollouts get rollout ai-service -n pets -w" 88 | 89 | p "# Check the final httproute" 90 | pei "kubectl describe httproute ai-service -n pets | grep -B 3 Weight:" 91 | 92 | p "# Test the new ai feature in the browser" 93 | pei "echo http://admin.aks.rocks" 94 | 95 | p "exit" 96 | clear -------------------------------------------------------------------------------- /src/infra/terraform/cosmosdb.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_cosmosdb_account" "example" { 2 | name = "db-${local.random_name}" 3 | location = azurerm_resource_group.example.location 4 | resource_group_name = azurerm_resource_group.example.name 5 | offer_type = "Standard" 6 | kind = "GlobalDocumentDB" 7 | access_key_metadata_writes_enabled = false 8 | minimal_tls_version = "Tls12" 9 | automatic_failover_enabled = true 10 | 11 | consistency_policy { 12 | consistency_level = "BoundedStaleness" 13 | max_interval_in_seconds = 300 14 | max_staleness_prefix = 100000 15 | } 16 | 17 | geo_location { 18 | location = azurerm_resource_group.example.location 19 | failover_priority = 0 # primary location 20 | } 21 | } 22 | 23 | resource "azurerm_cosmosdb_sql_database" "example" { 24 | name = "orderdb" 25 | resource_group_name = azurerm_cosmosdb_account.example.resource_group_name 26 | account_name = azurerm_cosmosdb_account.example.name 27 | throughput = 400 28 | } 29 | 30 | resource "azurerm_cosmosdb_sql_container" "example" { 31 | name = "orders" 32 | resource_group_name = azurerm_cosmosdb_account.example.resource_group_name 33 | account_name = azurerm_cosmosdb_account.example.name 34 | database_name = azurerm_cosmosdb_sql_database.example.name 35 | partition_key_paths = ["/storeId"] 36 | partition_key_version = 1 37 | throughput = 400 38 | } 39 | 40 | resource "azurerm_user_assigned_identity" "db" { 41 | resource_group_name = azurerm_resource_group.example.name 42 | location = var.location 43 | name = "db-${local.random_name}-identity" 44 | } 45 | 46 | resource "azurerm_federated_identity_credential" "db" { 47 | resource_group_name = azurerm_resource_group.example.name 48 | parent_id = azurerm_user_assigned_identity.db.id 49 | name = "db-${local.random_name}" 50 | issuer = azapi_resource.aks.output.properties.oidcIssuerProfile.issuerURL 51 | audience = ["api://AzureADTokenExchange"] 52 | subject = "system:serviceaccount:${var.k8s_namespace}:makeline-service-account" 53 | } 54 | 55 | resource "azurerm_cosmosdb_sql_role_definition" "db" { 56 | resource_group_name = azurerm_resource_group.example.name 57 | account_name = azurerm_cosmosdb_account.example.name 58 | name = "CosmosDBDataContributor - ${azurerm_user_assigned_identity.db.name}" 59 | type = "CustomRole" 60 | assignable_scopes = [azurerm_cosmosdb_account.example.id] 61 | 62 | permissions { 63 | data_actions = [ 64 | "Microsoft.DocumentDB/databaseAccounts/readMetadata", 65 | "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*", 66 | "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*", 67 | ] 68 | } 69 | } 70 | 71 | resource "azurerm_cosmosdb_sql_role_assignment" "db_sql_data_contributor" { 72 | resource_group_name = azurerm_resource_group.example.name 73 | role_definition_id = azurerm_cosmosdb_sql_role_definition.db.id 74 | scope = azurerm_cosmosdb_account.example.id 75 | account_name = azurerm_cosmosdb_account.example.name 76 | principal_id = azurerm_user_assigned_identity.db.principal_id 77 | } 78 | 79 | resource "azurerm_role_assignment" "db_sb_data_owner" { 80 | scope = azurerm_servicebus_namespace.example.id 81 | role_definition_name = "Azure Service Bus Data Owner" 82 | principal_id = azurerm_user_assigned_identity.db.principal_id 83 | } -------------------------------------------------------------------------------- /src/infra/terraform/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/azure/azapi" { 5 | version = "2.2.0" 6 | constraints = "2.2.0" 7 | hashes = [ 8 | "h1:Us5LvK2ju2qo3MQlXVtDDKCt5SMFRDIHUL8ubVdCEUg=", 9 | "h1:yckm1jqUMUGSeS57a3uR0gG/V7scwvjpkRVXnQIUAo4=", 10 | "zh:062be5d8272cac297a88c2057449f449ea6906c4121ba3dfdeb5cecb3ff91178", 11 | "zh:1fd9abec3ffcbf8d0244408334e9bfc8f49ada50978cd73ee0ed5f8560987267", 12 | "zh:48e84b0302af99d7e7f4248a724088fb1c34aeee78c9ca63ec5a9464ec5054a0", 13 | "zh:4e7302883fd9dd83bfbbcd72ebd55f83d8b16ccc6d12d1573d578058e604d5cf", 14 | "zh:5b6e181e32cbf62f5d2ce34f9d6d9ffe17192e24943450bbe335e1baf0494e66", 15 | "zh:62d525d426c6d5f10109ab04a9abc231b204ea413238f5690f69b420a8b8583a", 16 | "zh:90aab23497ec9c7af44ad9ea1a1d6063dc3331334915e1c549527a73c2c6948d", 17 | "zh:91ecf30a01df5e832191e0c55c87f8403a1f584796fd70f9c9c913d35c2e2a37", 18 | "zh:bc3a5db5e4b9695a69dff47cf1e7184eaf5564d3dc50f231cbcbf535dd140d19", 19 | "zh:cb566bec2676511bf4722e24d0dfc9bf58aff78af38b8e0864970f20d263118f", 20 | "zh:d4fa0c1462b389cee313e1c152e00f5dfc175a1be3615d3b23b526a8581e39a5", 21 | "zh:f8136b0f41045a1e5a6dedc6b6fb055faee3d825f84a3192312e3ac5d057ff72", 22 | ] 23 | } 24 | 25 | provider "registry.terraform.io/hashicorp/azurerm" { 26 | version = "4.15.0" 27 | constraints = "4.15.0" 28 | hashes = [ 29 | "h1:eKF94ScY2csUTysr7T4vMWvHb3A9hf0gUBzKXG0nrLA=", 30 | "h1:l4IeiC5B5iXJq0yQCl6M6ktov6DDPXKm4ziitiMWvtk=", 31 | "zh:1e3f9f2e94d6ee07c74e137b22ad6199081ae0c2dde1f986bd09fa516a0a6d4b", 32 | "zh:3e9dda2795c76f143d335fb2f78a600845ba9bdaf87d400a1a3131b533d641ef", 33 | "zh:617cf5db531ba0f1648613f31c43481b6736bdec4b08a8980217a0e1d40173b6", 34 | "zh:9d561393fc9e09ade829a97e0e5f58c8efd010f0a6cd513d41a0d3baf5e66d98", 35 | "zh:a1932e5614a920cca5372733686bcd8b8d80f7c9dac7b570dd49916982853004", 36 | "zh:b7e6eeb2a4998dcf74a2f005da23b99fd1cf89e45c77424a288a27f7c1b5c562", 37 | "zh:baec97748db05ce4b24a6d91f8bad2f3db7414f68c41d78782c410a47b4a460a", 38 | "zh:ce1eb226304a65a29c1e70af4c05d359168ef70ad297e37de54d7070619a50c1", 39 | "zh:d04e582146310375c9bb7decfbed3acef537d70be77201de3ca235cfbc6f64f6", 40 | "zh:df4098cd6b961d1cf0ea858f2964afadb264c8ee3ccf59215f620c421573ffa0", 41 | "zh:ec1837c5a6b4b6c77c6587a98cc0978dbfeb6be0fb7f1c9618f1e66213d81e6b", 42 | "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", 43 | ] 44 | } 45 | 46 | provider "registry.terraform.io/hashicorp/local" { 47 | version = "2.5.2" 48 | constraints = "2.5.2" 49 | hashes = [ 50 | "h1:IyFbOIO6mhikFNL/2h1iZJ6kyN3U00jgkpCLUCThAfE=", 51 | "h1:JlMZD6nYqJ8sSrFfEAH0Vk/SL8WLZRmFaMUF9PJK5wM=", 52 | "zh:136299545178ce281c56f36965bf91c35407c11897f7082b3b983d86cb79b511", 53 | "zh:3b4486858aa9cb8163378722b642c57c529b6c64bfbfc9461d940a84cd66ebea", 54 | "zh:4855ee628ead847741aa4f4fc9bed50cfdbf197f2912775dd9fe7bc43fa077c0", 55 | "zh:4b8cd2583d1edcac4011caafe8afb7a95e8110a607a1d5fb87d921178074a69b", 56 | "zh:52084ddaff8c8cd3f9e7bcb7ce4dc1eab00602912c96da43c29b4762dc376038", 57 | "zh:71562d330d3f92d79b2952ffdda0dad167e952e46200c767dd30c6af8d7c0ed3", 58 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 59 | "zh:805f81ade06ff68fa8b908d31892eaed5c180ae031c77ad35f82cb7a74b97cf4", 60 | "zh:8b6b3ebeaaa8e38dd04e56996abe80db9be6f4c1df75ac3cccc77642899bd464", 61 | "zh:ad07750576b99248037b897de71113cc19b1a8d0bc235eb99173cc83d0de3b1b", 62 | "zh:b9f1c3bfadb74068f5c205292badb0661e17ac05eb23bfe8bd809691e4583d0e", 63 | "zh:cc4cbcd67414fefb111c1bf7ab0bc4beb8c0b553d01719ad17de9a047adff4d1", 64 | ] 65 | } 66 | 67 | provider "registry.terraform.io/hashicorp/random" { 68 | version = "3.6.3" 69 | hashes = [ 70 | "h1:Fnaec9vA8sZ8BXVlN3Xn9Jz3zghSETIKg7ch8oXhxno=", 71 | "h1:zG9uFP8l9u+yGZZvi5Te7PV62j50azpgwPunq2vTm1E=", 72 | "zh:04ceb65210251339f07cd4611885d242cd4d0c7306e86dda9785396807c00451", 73 | "zh:448f56199f3e99ff75d5c0afacae867ee795e4dfda6cb5f8e3b2a72ec3583dd8", 74 | "zh:4b4c11ccfba7319e901df2dac836b1ae8f12185e37249e8d870ee10bb87a13fe", 75 | "zh:4fa45c44c0de582c2edb8a2e054f55124520c16a39b2dfc0355929063b6395b1", 76 | "zh:588508280501a06259e023b0695f6a18149a3816d259655c424d068982cbdd36", 77 | "zh:737c4d99a87d2a4d1ac0a54a73d2cb62974ccb2edbd234f333abd079a32ebc9e", 78 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 79 | "zh:a357ab512e5ebc6d1fda1382503109766e21bbfdfaa9ccda43d313c122069b30", 80 | "zh:c51bfb15e7d52cc1a2eaec2a903ac2aff15d162c172b1b4c17675190e8147615", 81 | "zh:e0951ee6fa9df90433728b96381fb867e3db98f66f735e0c3e24f8f16903f0ad", 82 | "zh:e3cdcb4e73740621dabd82ee6a37d6cfce7fee2a03d8074df65086760f5cf556", 83 | "zh:eff58323099f1bd9a0bec7cb04f717e7f1b2774c7d612bf7581797e1622613a0", 84 | ] 85 | } 86 | -------------------------------------------------------------------------------- /session-delivery-resources/demo/demo-magic.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################### 4 | # 5 | # demo-magic.sh 6 | # 7 | # Copyright (c) 2015-2022 Paxton Hare 8 | # 9 | # This script lets you script demos in bash. It runs through your demo script 10 | # when you press ENTER. It simulates typing and runs commands. 11 | # 12 | ############################################################################### 13 | 14 | # the speed to simulate typing the text 15 | TYPE_SPEED=20 16 | 17 | # no wait after "p" or "pe" 18 | NO_WAIT=false 19 | 20 | # if > 0, will pause for this amount of seconds before automatically proceeding with any p or pe 21 | PROMPT_TIMEOUT=0 22 | 23 | # don't show command number unless user specifies it 24 | SHOW_CMD_NUMS=false 25 | 26 | 27 | # handy color vars for pretty prompts 28 | BLACK="\033[0;30m" 29 | BLUE="\033[0;34m" 30 | GREEN="\033[0;32m" 31 | GREY="\033[0;90m" 32 | CYAN="\033[0;36m" 33 | RED="\033[0;31m" 34 | PURPLE="\033[0;35m" 35 | BROWN="\033[0;33m" 36 | WHITE="\033[0;37m" 37 | BOLD="\033[1m" 38 | COLOR_RESET="\033[0m" 39 | 40 | C_NUM=0 41 | 42 | # prompt and command color which can be overriden 43 | DEMO_PROMPT="$ " 44 | DEMO_CMD_COLOR=$BOLD 45 | DEMO_COMMENT_COLOR=$GREY 46 | 47 | ## 48 | # prints the script usage 49 | ## 50 | function usage() { 51 | echo -e "" 52 | echo -e "Usage: $0 [options]" 53 | echo -e "" 54 | echo -e " Where options is one or more of:" 55 | echo -e " -h Prints Help text" 56 | echo -e " -d Debug mode. Disables simulated typing" 57 | echo -e " -n No wait" 58 | echo -e " -w Waits max the given amount of seconds before " 59 | echo -e " proceeding with demo (e.g. '-w5')" 60 | echo -e "" 61 | } 62 | 63 | ## 64 | # wait for user to press ENTER 65 | # if $PROMPT_TIMEOUT > 0 this will be used as the max time for proceeding automatically 66 | ## 67 | function wait() { 68 | if [[ "$PROMPT_TIMEOUT" == "0" ]]; then 69 | read -rs 70 | else 71 | read -rst "$PROMPT_TIMEOUT" 72 | fi 73 | } 74 | 75 | ## 76 | # print command only. Useful for when you want to pretend to run a command 77 | # 78 | # takes 1 param - the string command to print 79 | # 80 | # usage: p "ls -l" 81 | # 82 | ## 83 | function p() { 84 | if [[ ${1:0:1} == "#" ]]; then 85 | cmd=$DEMO_COMMENT_COLOR$1$COLOR_RESET 86 | else 87 | cmd=$DEMO_CMD_COLOR$1$COLOR_RESET 88 | fi 89 | 90 | # render the prompt 91 | x=$(PS1="$DEMO_PROMPT" "$BASH" --norc -i &1 | sed -n '${s/^\(.*\)exit$/\1/p;}') 92 | 93 | # show command number is selected 94 | if $SHOW_CMD_NUMS; then 95 | printf "[$((++C_NUM))] $x" 96 | else 97 | printf "$x" 98 | fi 99 | 100 | # wait for the user to press a key before typing the command 101 | if [ $NO_WAIT = false ]; then 102 | wait 103 | fi 104 | 105 | if [[ -z $TYPE_SPEED ]]; then 106 | echo -en "$cmd" 107 | else 108 | echo -en "$cmd" | pv -qL $[$TYPE_SPEED+(-2 + RANDOM%5)]; 109 | fi 110 | 111 | # wait for the user to press a key before moving on 112 | if [ $NO_WAIT = false ]; then 113 | wait 114 | fi 115 | echo "" 116 | } 117 | 118 | ## 119 | # Prints and executes a command 120 | # 121 | # takes 1 parameter - the string command to run 122 | # 123 | # usage: pe "ls -l" 124 | # 125 | ## 126 | function pe() { 127 | # print the command 128 | p "$@" 129 | run_cmd "$@" 130 | } 131 | 132 | ## 133 | # print and executes a command immediately 134 | # 135 | # takes 1 parameter - the string command to run 136 | # 137 | # usage: pei "ls -l" 138 | # 139 | ## 140 | function pei { 141 | NO_WAIT=true pe "$@" 142 | } 143 | 144 | ## 145 | # Enters script into interactive mode 146 | # 147 | # and allows newly typed commands to be executed within the script 148 | # 149 | # usage : cmd 150 | # 151 | ## 152 | function cmd() { 153 | # render the prompt 154 | x=$(PS1="$DEMO_PROMPT" "$BASH" --norc -i &1 | sed -n '${s/^\(.*\)exit$/\1/p;}') 155 | printf "$x\033[0m" 156 | read command 157 | run_cmd "${command}" 158 | } 159 | 160 | function run_cmd() { 161 | function handle_cancel() { 162 | printf "" 163 | } 164 | 165 | trap handle_cancel SIGINT 166 | stty -echoctl 167 | eval $@ 168 | stty echoctl 169 | trap - SIGINT 170 | } 171 | 172 | 173 | function check_pv() { 174 | command -v pv >/dev/null 2>&1 || { 175 | 176 | echo "" 177 | echo -e "${RED}##############################################################" 178 | echo "# HOLD IT!! I require pv for simulated typing but it's " >&2 179 | echo "# not installed. Aborting." >&2; 180 | echo -e "${RED}##############################################################" 181 | echo "" 182 | echo -e "${COLOR_RESET}Disable simulated typing: " 183 | echo "" 184 | echo -e " unset TYPE_SPEED" 185 | echo "" 186 | echo "Installing pv:" 187 | echo "" 188 | echo " Mac: $ brew install pv" 189 | echo "" 190 | echo " Other: https://www.ivarch.com/programs/pv.shtml" 191 | echo "" 192 | exit 1; 193 | } 194 | } 195 | 196 | # 197 | # handle some default params 198 | # -h for help 199 | # -d for disabling simulated typing 200 | # 201 | while getopts ":dhncw:" opt; do 202 | case $opt in 203 | h) 204 | usage 205 | exit 1 206 | ;; 207 | d) 208 | unset TYPE_SPEED 209 | ;; 210 | n) 211 | NO_WAIT=true 212 | ;; 213 | c) 214 | SHOW_CMD_NUMS=true 215 | ;; 216 | w) 217 | PROMPT_TIMEOUT=$OPTARG 218 | ;; 219 | esac 220 | done 221 | 222 | ## 223 | # Do not check for pv. This trusts the user to not set TYPE_SPEED later in the 224 | # demo in which case an error will occur if pv is not installed. 225 | ## 226 | if [[ -n "$TYPE_SPEED" ]]; then 227 | check_pv 228 | fi 229 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Leveraging cloud native infra for your intelligent apps 2 | 3 | ## Session Description 4 | 5 | Deploying AI-enabled applications involves an application or microservice interacting with a LLM inferencing endpoint. Microservices architectures and a cloud native approach is ideal for hosting your intelligent apps. This session demonstrates how you can use Kubernetes and cloud-native tools to reduce operational overhead in building and running intelligent apps. 6 | 7 | ![image](https://github.com/user-attachments/assets/78a9c7ef-e16b-4b29-a61a-9514776339b7) 8 | 9 | 10 | ## Learning Outcomes 11 | 12 | Key takeaways for this session are: 13 | 14 | - AI is revolutionizing the industry 15 | - Taking a cloud native approach makes adopting AI easier 16 | - Use AI to help you operate your cloud native infrastructure and apps 17 | 18 | ## Technology Used 19 | 20 | Azure services used in this session include: 21 | 22 | - AKS Automatic 23 | - Azure OpenAI 24 | - Azure Service Bus 25 | - Azure CosmosDB 26 | - Azure Managed Grafana 27 | - Azure Managed Prometheus 28 | - Azure Log Analytics 29 | - Copilot in Azure 30 | 31 | Cloud Native tools used in this session include: 32 | 33 | - Istio Service Mesh 34 | - ArgoCD 35 | - Argo Rollouts 36 | - Gateway API 37 | 38 | ## Additional Resources and Continued Learning 39 | 40 | If you would like to link the user to further learning, please enter that here. 41 | 42 | | Resources | Links | Description | 43 | |:-------------------|:----------------------------------|:-------------------| 44 | | Azure Kubernetes Service (AKS) | [Docs](https://aka.ms/aks/automatic) | Learn more about AKS Automatic | 45 | | Azure OpenAI Service | [Docs](https://learn.microsoft.com/azure/ai-services/openai/) | Learn more about Azure OpenAI Services | 46 | | Azure Service Bus | [Docs](https://learn.microsoft.com/azure/service-bus/) | Learn more about Azure Service Bus | 47 | | Azure CosmosDB | [Docs](https://learn.microsoft.com/azure/cosmos-db/) | Learn more about Azure CosmosDB | 48 | | Azure Managed Grafana | [Docs](https://learn.microsoft.com/azure/managed-grafana/) | Learn more about Azure Managed Grafana | 49 | | Azure Managed Prometheus | [Docs](https://learn.microsoft.com/azure/azure-monitor/essentials/prometheus-metrics-overview) | Learn more about Azure Managed Prometheus | 50 | | Azure Monitor for Kubernetes | [Docs](https://learn.microsoft.com/azure/azure-monitor/containers/container-insights-overview) | Learn more about Azure Log Analytics | 51 | | Istio Service Mesh | [Docs](https://learn.microsoft.com/azure/aks/istio-about) | Learn more about Istio-based Service Mesh add-on for AKS | 52 | | ArgoCD | [Docs](https://argoproj.github.io/cd/) | Learn more about ArgoCD | 53 | | Argo Rollouts | [Docs](https://argoproj.github.io/rollouts/) | Learn more about Argo Rollouts | 54 | | Gateway API | [Docs](https://gateway-api.sigs.k8s.io/) | Learn more about Kubernetes Gateway API | 55 | 56 | 57 | 58 | ## Content Owners 59 | 60 | 61 | 62 | 69 | 76 | 77 | 78 |
63 | Paul Yu
64 |
65 | Paul Yu 66 |

67 | 📢 68 |
70 | Vinicius Apolinario
71 |
72 | Vinicius Apolinario 73 |

74 | 📢 75 |
79 | 80 | 81 | 82 | 83 | ## Responsible AI 84 | Microsoft is committed to helping our customers use our AI products responsibly, sharing our learnings, and building trust-based partnerships through tools like Transparency Notes and Impact Assessments. Many of these resources can be found at https://aka.ms/RAI. Microsoft’s approach to responsible AI is grounded in our AI principles of fairness, reliability and safety, privacy and security, inclusiveness, transparency, and accountability. 85 | 86 | Large-scale natural language, image, and speech models - like the ones used in this sample - can potentially behave in ways that are unfair, unreliable, or offensive, in turn causing harms. Please consult the Azure OpenAI service Transparency note to be informed about risks and limitations. The recommended approach to mitigating these risks is to include a safety system in your architecture that can detect and prevent harmful behavior. Azure AI Content Safety provides an independent layer of protection, able to detect harmful user-generated and AI-generated content in applications and services. Azure AI Content Safety includes text and image APIs that allow you to detect material that is harmful. We also have an interactive Content Safety Studio that allows you to view, explore and try out sample code for detecting harmful content across different modalities. The following quickstart documentation guides you through making requests to the service. 87 | 88 | Another aspect to take into account is the overall application performance. With multi-modal and multi-models applications, we consider performance to mean that the system performs as you and your users expect, including not generating harmful outputs. It's important to assess the performance of your overall application using generation quality and risk and safety metrics. 89 | 90 | You can evaluate your AI application in your development environment using the prompt flow SDK. Given either a test dataset or a target, your generative AI application generations are quantitatively measured with built-in evaluators or custom evaluators of your choice. To get started with the prompt flow sdk to evaluate your system, you can follow the quickstart guide. Once you execute an evaluation run, you can visualize the results in Azure AI Studio. Empowering responsible AI practices | Microsoft AI Explore how Microsoft is committed to advancing AI in a way that is driven by ethical principles that put people first. 91 | -------------------------------------------------------------------------------- /session-delivery-resources/demo/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export ARM_SUBSCRIPTION_ID=$(az account show --query id -o tsv) 4 | 5 | echo "Registering features and providers..." 6 | az provider register -n Microsoft.Monitor 7 | az provider register -n Microsoft.ContainerService 8 | az provider register -n Microsoft.Dashboard 9 | az provider register -n Microsoft.AlertsManagement 10 | az feature register --namespace Microsoft.ContainerService --name AutomaticSKUPreview 11 | 12 | # wait for feature registration 13 | while [[ $(az feature show --namespace "Microsoft.ContainerService" --name "AutomaticSKUPreview" --query "properties.state" -o tsv) != "Registered" ]]; do 14 | echo "Waiting for AutomaticSKUPreview feature registration..." 15 | sleep 3 16 | done 17 | 18 | # propagate the feature registrations 19 | az provider register -n Microsoft.ContainerService 20 | 21 | while [[ $(az provider show --namespace "Microsoft.Dashboard" --query "registrationState" -o tsv) != "Registered" ]]; do 22 | echo "Waiting for Microsoft.Dashboard provider registration..." 23 | sleep 3 24 | done 25 | 26 | while [[ $(az provider show --namespace "Microsoft.AlertsManagement" --query "registrationState" -o tsv) != "Registered" ]]; do 27 | echo "Waiting for Microsoft.AlertsManagement provider registration..." 28 | sleep 3 29 | done 30 | 31 | echo "Installing extensions..." 32 | az extension add --name aks-preview 33 | az extension add --name amg 34 | 35 | echo "Applying Terraform and exporting output variables..." 36 | cd src/infra/terraform 37 | terraform init 38 | terraform apply -var="owner=$(whoami)" --auto-approve 39 | 40 | # check return code from previous command 41 | if [ $? -ne 0 ]; then 42 | echo "Terraform apply failed. Exiting..." 43 | exit 1 44 | fi 45 | 46 | export RG_NAME=$(terraform output -raw rg_name) 47 | export AKS_NAME=$(terraform output -raw aks_name) 48 | export OAI_GPT_ENDPOINT=$(terraform output -raw oai_gpt_endpoint) 49 | export OAI_GPT_DEPLOYMENT_NAME=$(terraform output -raw oai_gpt_model_name) 50 | export OAI_DALLE_ENDPOINT=$(terraform output -raw oai_dalle_endpoint) 51 | export OAI_DALLE_DEPLOYMENT_NAME=$(terraform output -raw oai_dalle_model_name) 52 | export OAI_DALLE_API_VERSION=$(terraform output -raw oai_dalle_api_version) 53 | export OAI_IDENTITY_CLIENT_ID=$(terraform output -raw oai_identity_client_id) 54 | export AMG_NAME=$(terraform output -raw amg_name) 55 | export DB_CONTAINER_NAME=$(terraform output -raw db_container_name) 56 | export DB_DATABASE_NAME=$(terraform output -raw db_database_name) 57 | export DB_ENDPOINT=$(terraform output -raw db_endpoint) 58 | export DB_IDENTITY_CLIENT_ID=$(terraform output -raw db_identity_client_id) 59 | export SB_HOSTNAME=$(terraform output -raw sb_hostname) 60 | export SB_QUEUE_NAME=$(terraform output -raw sb_queue_name) 61 | export SB_IDENTITY_CLIENT_ID=$(terraform output -raw sb_identity_client_id) 62 | 63 | echo "Downloading kubeconfig..." 64 | az aks get-credentials --name $AKS_NAME --resource-group $RG_NAME 65 | 66 | which kubelogin 67 | if [ $? -ne 0 ]; then 68 | echo "Installing kubelogin..." 69 | sudo az aks install-cli 70 | fi 71 | 72 | echo "Deploying a sample app to kickstart the user nodepool nodes..." 73 | kubectl apply -f https://raw.githubusercontent.com/microsoft/aitour-cloud-native-apps-with-azure-ai-and-aks/refs/heads/main/src/manifests/sample.yaml 74 | 75 | echo "Deploying prometheus scrape configs..." 76 | kubectl create configmap -n kube-system ama-metrics-prometheus-config --from-file prometheus-config 77 | 78 | echo "Installing the gateway api..." 79 | kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml 80 | 81 | echo "Deploying internal and external gateways..." 82 | kubectl apply -f - < [!IMPORTANT] 10 | > The commands listed below should be run in a POSIX compliant shell such as bash or zsh. 11 | 12 | - [Azure CLI](https://docs.microsoft.com/cli/azure/install-azure-cli?view=azure-cli-latest) 13 | - [GitHub CLI](https://cli.github.com/) 14 | - [Terraform](https://www.terraform.io/downloads.html) 15 | - [kubectl](https://kubernetes.io/docs/tasks/tools/) 16 | - [Helm](https://helm.sh/docs/intro/install/) 17 | - [ArgoCD CLI](https://argo-cd.readthedocs.io/en/stable/cli_installation/#linux-and-wsl) 18 | - [Argo Rollouts Kubectl Plugin](https://argo-rollouts.readthedocs.io/en/stable/installation/#kubectl-plugin-installation) 19 | - [jq](https://jqlang.github.io/jq/download/) 20 | 21 | ## Getting started 22 | 23 | Start by logging into GitHub CLI. 24 | 25 | ```bash 26 | gh auth login -h github.com -s delete_repo 27 | ``` 28 | 29 | Clone this repository to your local machine. 30 | 31 | ```bash 32 | gh repo clone microsoft/aitour-cloud-native-apps-with-azure-ai-and-aks 33 | cd aitour-cloud-native-apps-with-azure-ai-and-aks 34 | ``` 35 | 36 | Login to the Azure CLI using the `az login` command. 37 | 38 | ```bash 39 | az login --tenant 40 | export ARM_SUBSCRIPTION_ID=$(az account show --query id -o tsv) 41 | ``` 42 | 43 | Before running the `terraform apply` command, be sure you have the required preview features enabled in your subscription for provisioning an AKS Automatic cluster. 44 | 45 | ```bash 46 | az provider register -n Microsoft.ContainerService 47 | az provider register -n Microsoft.Dashboard 48 | az provider register -n Microsoft.AlertsManagement 49 | az feature register --namespace Microsoft.ContainerService --name AutomaticSKUPreview 50 | ``` 51 | 52 | > [!WARNING] 53 | > Wait until all the providers and features are registered before proceeding. 54 | 55 | Once all preview features are registered, run the following command to propagate the feature registrations for the AKS Automatic cluster. 56 | 57 | ```bash 58 | az provider register -n Microsoft.ContainerService 59 | ``` 60 | 61 | You should also have the following Azure CLI extensions installed. 62 | 63 | ```bash 64 | az extension add --name aks-preview 65 | az extension add --name amg 66 | ``` 67 | 68 | ## Provision with Terraform 69 | 70 | Assuming you are in the root of the cloned repository, navigate to the `src/infra/terraform` directory. 71 | 72 | ```bash 73 | cd src/infra/terraform 74 | ``` 75 | 76 | The following commands will deploy the infrastructure with Terraform. 77 | 78 | ```bash 79 | terraform init 80 | terraform apply -var="owner=$(whoami)" 81 | ``` 82 | 83 | After the deployment is complete, export output variables which will be used in the next steps. 84 | 85 | ```bash 86 | export RG_NAME=$(terraform output -raw rg_name) 87 | export AKS_NAME=$(terraform output -raw aks_name) 88 | export OAI_GPT_ENDPOINT=$(terraform output -raw oai_gpt_endpoint) 89 | export OAI_GPT_DEPLOYMENT_NAME=$(terraform output -raw oai_gpt_model_name) 90 | export OAI_DALLE_ENDPOINT=$(terraform output -raw oai_dalle_endpoint) 91 | export OAI_DALLE_DEPLOYMENT_NAME=$(terraform output -raw oai_dalle_model_name) 92 | export OAI_DALLE_API_VERSION=$(terraform output -raw oai_dalle_api_version) 93 | export OAI_IDENTITY_CLIENT_ID=$(terraform output -raw oai_identity_client_id) 94 | export AMG_NAME=$(terraform output -raw amg_name) 95 | export DB_CONTAINER_NAME=$(terraform output -raw db_container_name) 96 | export DB_DATABASE_NAME=$(terraform output -raw db_database_name) 97 | export DB_ENDPOINT=$(terraform output -raw db_endpoint) 98 | export DB_IDENTITY_CLIENT_ID=$(terraform output -raw db_identity_client_id) 99 | export SB_HOSTNAME=$(terraform output -raw sb_hostname) 100 | export SB_QUEUE_NAME=$(terraform output -raw sb_queue_name) 101 | export SB_IDENTITY_CLIENT_ID=$(terraform output -raw sb_identity_client_id) 102 | ``` 103 | 104 | Download the kubeconfig file to connect to the AKS cluster. 105 | 106 | ```bash 107 | az aks get-credentials --name $AKS_NAME --resource-group $RG_NAME 108 | ``` 109 | 110 | ## Enable Azure Managed Prometheus metrics scraping 111 | 112 | Configure Azure Managed Prometheus to scrape metrics from any Pod across all Namespaces that have Prometheus annotations. This will enable the Istio service mesh metrics scraping. 113 | 114 | ```bash 115 | kubectl create configmap -n kube-system ama-metrics-prometheus-config --from-file prometheus-config 116 | ``` 117 | 118 | ## Install GatewayAPI CRDs 119 | 120 | Deploy GatewayAPI for the application. 121 | 122 | > [!NOTE] 123 | > The [Kubernetes Gateway API](https://github.com/kubernetes-sigs/gateway-api) project is still under active development and its CRDs are not installed by default in Kubernetes. You will need to install them manually. Keep an eye on the project's [releases](https://github.com/kubernetes-sigs/gateway-api/releases) page for the latest version of the CRDs. 124 | 125 | ```bash 126 | kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml 127 | ``` 128 | 129 | ## Install Istio Gateways 130 | 131 | After the GatewayAPI CRDs are installed, deploy internal and external Gateways that will utilize the AKS Istio Ingress Gateways that were provisioned by the Terraform deployment. 132 | 133 | > [!NOTE] 134 | > Additional information on this approach can be found in [this](https://azure.github.io/AKS/2024/08/06/istio-with-gateway-api) blog post. 135 | 136 | ```bash 137 | kubectl apply -f - < [!TIP] 198 | > AKS Automatic clusters leverages Karpenter (Node Autoprovision) for node autoscaling, so it can take a minute or two for the new node to be provisioned. 199 | 200 | Install the Argo Rollouts Helm chart 201 | 202 | ```bash 203 | helm upgrade argo-rollouts argo/argo-rollouts \ 204 | --install \ 205 | --namespace argo-rollouts \ 206 | --create-namespace \ 207 | --version 2.38.2 \ 208 | --set 'controller.podAnnotations.karpenter\.sh/do-not-disrupt=true' \ 209 | --set 'dashboard.podAnnotations.karpenter\.sh/do-not-disrupt=true' 210 | ``` 211 | 212 | This demo will use Argo Rollouts to manage the progressive delivery of the AI service. To do this, we will need to enable Argo Rollouts to use the Gateway API plugin. 213 | 214 | To enable Argo Rollouts to use the Gateway API, you will need to install the TrafficRouter plugin. This can be done by creating a ConfigMap in the `argo-rollouts` namespace that points to the plugin binary. Latest versions of the plugin can be found [here](https://github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/releases). 215 | 216 | Install the TrafficRouter plugin. 217 | 218 | ```bash 219 | kubectl apply -f - < [!CAUTION] 382 | > Make sure to run the following command and select your forked repository as the default. Otherwise, you may run into issues with merging and pushing changes as you work through the demo. 383 | 384 | ```bash 385 | gh repo set-default 386 | ``` 387 | 388 | > [!TIP] 389 | > If you run into the following error message "failed to fork: HTTP 403: Name already exists on this account" when trying to fork the repository, it means you have already forked the repository. In this case, you should simply clone it again to your local machine, pull the latest, and force push to your forked repository to ensure you have the latest changes. 390 | 391 | Here is an example of how to do this. 392 | 393 | ```bash 394 | gh repo clone $(gh api user --jq .login)/ai-tour-aks-demo 395 | cd ai-tour-aks-demo 396 | gh repo set-default $(gh api user --jq .login)/ai-tour-aks-demo 397 | git remote add upstream https://github.com/microsoft/aitour-cloud-native-apps-with-azure-ai-and-aks.git 398 | git fetch upstream main 399 | git reset --hard upstream/main 400 | git push origin main --force 401 | ``` 402 | 403 | Update the current context to the ArgoCD namespace. 404 | 405 | ```bash 406 | kubectl config set-context --current --namespace=argocd 407 | ``` 408 | 409 | Connect to the ArgoCD server. 410 | 411 | ```bash 412 | argocd login --core 413 | ``` 414 | 415 | Run the following command to add your forked repository to ArgoCD. 416 | 417 | > [!NOTE] 418 | > This is required for ArgoCD to be able read from private repositories. In this case, we are using a public repository so this step is not necessary but it is good practice to add the repository to ArgoCD. 419 | 420 | ```bash 421 | argocd repo add $(gh repo view --json url | jq .url -r) \ 422 | --username $(gh api user --jq .login) \ 423 | --password $(gh auth token) 424 | ``` 425 | 426 | > [!IMPORTANT] 427 | > The demo environment is now set up and ready to demo to your audience. You can either walk through the rest of the steps below or run the `run-demo.sh` script in the `train-the-trainer/demo` directory. When running the demo script, you need to ensure you have `pv` installed on your machine. To install `pv`, run `brew install pv` on macOS or `sudo apt-get install pv` on Ubuntu. 428 | 429 | ## Demo app deployment with ArgoCD 430 | 431 | Deploy the demo application. 432 | 433 | > [!WARNING] 434 | > Make sure you have not run the `gh repo set-default` command as mentioned above, you should do that now before running the next set of commands. 435 | 436 | ```bash 437 | argocd app create pets \ 438 | --sync-policy auto \ 439 | --repo $(gh repo view --json url | jq .url -r) \ 440 | --revision HEAD \ 441 | --path src/manifests/kustomize/overlays/dev \ 442 | --dest-namespace pets \ 443 | --dest-server https://kubernetes.default.svc 444 | ``` 445 | 446 | Check the status of the application and wait for the **STATUS** to be **Synced** and **HEALTH** to be **Healthy**. 447 | 448 | ```bash 449 | argocd app list 450 | ``` 451 | 452 | Optionally, you can use the ArgoCD Release Server UI to watch the application deployment. 453 | 454 | Run the following command then click on the link below to open the ArgoCD UI. 455 | 456 | ```bash 457 | # get the admin password 458 | argocd admin initial-password 459 | 460 | # port forward to the ArgoCD server 461 | kubectl port-forward svc/argocd-server -n argocd 8080:443 462 | ``` 463 | 464 | Browse to https://localhost:8080 and log in with the admin password (username is **admin**). 465 | 466 | > [!NOTE] 467 | > This can take a few minutes to deploy the application due to Node Autoprovisioning. 468 | 469 | Once you see the application has deployed completely, press "Ctrl+C" to exit the dashboard, then run the following command to get the public IP address of the application and issue a curl command to test the application. You should see a **200 OK** response. 470 | 471 | ```bash 472 | INGRESS_PUBLIC_IP=$(kubectl get svc -n aks-istio-ingress aks-istio-ingressgateway-external -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 473 | curl -IL "http://${INGRESS_PUBLIC_IP}" -H "Host: store.aks.rocks" 474 | ``` 475 | 476 | Add a hosts file entry on your local machine to browse to the application using a friendly URL. 477 | 478 | ```bash 479 | echo "${INGRESS_PUBLIC_IP} admin.aks.rocks store.aks.rocks" | sudo tee -a /etc/hosts 480 | ``` 481 | 482 | Now you can browse to both the frontend and backend applications using the following URLs. 483 | 484 | - [http://store.aks.rocks](http://store.aks.rocks) 485 | - [http://admin.aks.rocks](http://admin.aks.rocks) 486 | 487 | > [!NOTE] 488 | > This is where you show the admin site and how AI is not enabled yet. 489 | 490 | ## Sprinkle in some AI magic ✨ 491 | 492 | Merge the **ai** branch to deploy the rollout for the **ai-service**. 493 | 494 | ```bash 495 | git fetch upstream feat/ai-rollout 496 | git merge upstream/feat/ai-rollout 497 | ``` 498 | 499 | Open the `src/manifests/kustomize/base/ai-service.yaml` manifest and note the canary steps in the manifest. The first step sets the weight to 50% for the canary service. The second step pauses the rollout. The third step sets the weight to 100% for the canary service. The fourth step pauses the rollout and waits for a final promotion. 500 | 501 | ```bash 502 | less src/manifests/kustomize/base/ai-service.yaml 503 | ``` 504 | 505 | Press **q** to exit the **less** command. 506 | 507 | Push the commit to the remote repository. 508 | 509 | ```bash 510 | git push 511 | ``` 512 | 513 | Force an ArgoCD sync to deploy the ai-service rollout. 514 | 515 | ```bash 516 | argocd app sync pets --force 517 | ``` 518 | 519 | When the app is fully synced, watch the rollout and wait for **Status** to show **✔ Healthy**. 520 | 521 | ```bash 522 | kubectl argo rollouts get rollout ai-service -n pets -w 523 | ``` 524 | 525 | When the rollout is healthy, hit **CTRL+C** to exit the watch. 526 | 527 | Run the following command to check on the AI service's HTTPRoute. You should see that it has been updated to 100/0 traffic split between the stable and canary with the stable service receiving all traffic. 528 | 529 | ```bash 530 | kubectl describe httproute ai-service -n pets | grep -B 3 Weight: 531 | ``` 532 | 533 | Using a web browser, navigate to the store admin site and create a new product. 534 | 535 | > [!NOTE] 536 | > The AI service is now being used to generate the product descriptions ✨ 537 | 538 | Next, let's update the rollout to set the **ai-service** image to the **latest** version. 539 | 540 | ```bash 541 | kubectl argo rollouts set image ai-service -n pets ai-service=ghcr.io/pauldotyu/aks-store-demo/ai-service:latest 542 | ``` 543 | 544 | Watch the rollout again this time in the argo rollouts dashboard and wait for **Status** to show **॥ Paused**. 545 | 546 | ```bash 547 | kubectl argo rollouts dashboard 548 | ``` 549 | 550 | When the rollout is paused, hit **CTRL+C** to exit the watch then check the weights of the HTTPRoute. You should see that it has been updated to 50/50 traffic split between the stable and canary. 551 | 552 | ```bash 553 | kubectl describe httproute ai-service -n pets | grep -B 3 Weight: 554 | ``` 555 | 556 | Promote the rollout from the Argo Rollouts dashboard to shift all traffic to the canary. 557 | 558 | ```bash 559 | kubectl argo rollouts dashboard 560 | ``` 561 | 562 | Watch the rollout and wait for **Status** to show **॥ Paused**. When the rollout is paused, hit **CTRL+C** to exit the dashboard then check the weights of the HTTPRoute again. 563 | 564 | You should see that it has been updated to 0/100 traffic split with the canary service receiving all traffic. 565 | 566 | ```bash 567 | kubectl describe httproute ai-service -n pets | grep -B 3 Weight: 568 | ``` 569 | 570 | All that is left is to promote the canary to the stable version. We can do this using the Argo Rollouts CLI. 571 | 572 | ```bash 573 | kubectl argo rollouts promote ai-service -n pets 574 | ``` 575 | 576 | Watch the rollout one last time to see the stable version is now running the latest version and after a few minutes the rollout will scale down the **revision:1** ReplicaSet pods. 577 | 578 | ```bash 579 | kubectl argo rollouts get rollout ai-service -n pets -w 580 | ``` 581 | 582 | When you see the **Status** show **✔ Healthy** and **revision:1** ReplicaSet status as **ScaledDown**, hit **CTRL+C** to exit the watch. 583 | 584 | Take a look at the HTTPRoute weights one last time to see that the stable service is now receiving all traffic. 585 | 586 | ```bash 587 | kubectl describe httproute ai-service -n pets | grep -B 3 Weight: 588 | ``` 589 | 590 | Go back to the store admin site and edit the product you created earlier. 591 | 592 | > [!NOTE] 593 | > The AI service is now being used to generate the product images too ✨✨ 594 | 595 | ## BONUS: Import Istio dashboard into Azure Managed Grafana 596 | 597 | Import the Istio dashboard into the Azure Managed Grafana instance. 598 | 599 | ```bash 600 | az grafana dashboard import \ 601 | --name $AMG_NAME \ 602 | --resource-group $RG_NAME \ 603 | --folder 'Azure Managed Prometheus' \ 604 | --definition 7630 605 | ``` 606 | 607 | Navigate to your Azure Managed Grafana in the Azure portal, click on the endpoint link, then log in. In the Grafana portal, click **Dashboards** in the left navigation, then click the **Azure Managed Prometheus** folder. You should see the Istio dashboard along with other Kubernetes dashboards. Click on a few and explore the metrics. 608 | 609 | ## Troubleshooting 610 | 611 | Some common issues you may run into. 612 | 613 | ### Unable to browse to the site? 614 | 615 | Take a look at the **PROGRAMMED** status of the Gateway. 616 | 617 | ```bash 618 | kubectl get gtw -n aks-istio-ingress gateway-external 619 | ``` 620 | 621 | If the value for **PROGRAMMED** is not **True**, then take a look at the status conditions for the Gateway. 622 | 623 | ```bash 624 | kubectl describe gtw -n aks-istio-ingress gateway-external 625 | ``` 626 | 627 | If you see something like the following, then check to see if the managed ingress gateway is properly deployed. 628 | 629 | ```text 630 | Message: Failed to assign to any requested addresses: hostname "aks-istio-ingressgateway-external.aks-istio-ingress.svc.cluster.local" not found 631 | ``` 632 | 633 | Run the command below. If you don't see the managed ingress gateway, then you may need to deploy it manually. 634 | 635 | ```bash 636 | kubectl get svc -n aks-istio-ingress 637 | ``` 638 | 639 | Also check the logs for the Istio Ingress Gateway. 640 | 641 | ```bash 642 | ISTIO_INGRESS_POD_1=$(kubectl get po -n aks-istio-ingress -l app=aks-istio-ingressgateway-external -o jsonpath='{.items[0].metadata.name}') 643 | kubectl logs -n aks-istio-ingress $ISTIO_INGRESS_POD_1 644 | 645 | ISTIO_INGRESS_POD_2=$(kubectl get po -n aks-istio-ingress -l app=aks-istio-ingressgateway-external -o jsonpath='{.items[1].metadata.name}') 646 | kubectl logs -n aks-istio-ingress $ISTIO_INGRESS_POD_2 647 | ``` 648 | 649 | ### Unable to see your ConfigMap from Azure App Configuration? 650 | 651 | Common things to check include. 652 | 653 | 1. Ensure RBAC is properly configured for the managed identity that the AKS extension created. 654 | 1. Ensure the federated credential is properly configured for the managed identity that the AKS extension created. 655 | 1. Ensure the Azure App Configuration provider for Kubernetes pod has Azure tenant environment variables set. 656 | 1. Ensure the ServiceAccount has the clientId annotation set. 657 | 658 | Typically you will see an error in the logs of the Azure App Configuration provider for Kubernetes pod if there is an issue. 659 | 660 | ```bash 661 | kubectl logs -n azappconfig-system -l app.kubernetes.io/name=appconfig-provider 662 | ``` 663 | 664 | ## Cleanup 665 | 666 | Reset your demo repo to the upstream main branch. 667 | 668 | ```bash 669 | git fetch upstream main 670 | git reset --hard upstream/main 671 | git push origin main --force 672 | ``` 673 | 674 | Delete the demo repo from your local machine. 675 | 676 | ```bash 677 | cd .. 678 | rm -rf ai-tour-aks-demo 679 | ``` 680 | 681 | Delete your fork of the demo repository. 682 | 683 | ```bash 684 | gh repo delete $(gh api user --jq .login)/ai-tour-aks-demo --yes 685 | ``` 686 | 687 | Run the following command to destroy the infrastructure. 688 | 689 | ```bash 690 | terraform destroy -var="owner=$(whoami)" 691 | rm terraform.tfstate* 692 | ``` 693 | 694 | Delete the hosts file line entry on your local machine. 695 | 696 | ```bash 697 | sudo sed -i '' '/admin.aks.rocks/d' /etc/hosts 698 | ``` 699 | 700 | ## Feedback 701 | 702 | Please provide any feedback on this sample as a GitHub issue. 703 | --------------------------------------------------------------------------------