├── 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 |  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 |
63 | ![]() 65 | Paul Yu 66 | 67 | 📢 68 | |
69 |
70 | ![]() 72 | Vinicius Apolinario 73 | 74 | 📢 75 | |
76 |