├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── LICENSE ├── README.md ├── SECURITY.md ├── terraform └── terraform-tutorial │ ├── app │ └── index.php │ ├── autoscale.tf │ ├── compute.tf │ ├── main.tf │ ├── network.tf │ ├── outputs.tf │ ├── user-data.sh │ └── variables.tf └── vmss-flex-n-tier-demo ├── README.md ├── compute ├── vmssautoscale.bicep └── vmssflex.bicep ├── main.bicep └── network ├── appgw.bicep ├── basenetwork.bicep └── slb.bicep /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: vmss-flex-cassandra-demo 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | branches: [main] 10 | paths: 11 | - 'vmss-flex-n-tier-demo/**' 12 | pull_request: 13 | branches: [main] 14 | 15 | # Allows you to run this workflow manually from the Actions tab 16 | workflow_dispatch: 17 | 18 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 19 | jobs: 20 | # This workflow contains a single job called "build" 21 | build: 22 | # The type of runner that the job will run on 23 | runs-on: ubuntu-latest 24 | env: 25 | AZURE_RG: 'vmss-flex-cassandra-demo' 26 | AZURE_LOCATION: 'southcentralus' 27 | # Steps represent a sequence of tasks that will be executed as part of the job 28 | steps: 29 | # Runs a single command using the runners shell 30 | - name: Run a one-line script 31 | run: echo Hello, world! 32 | 33 | # Runs a set of commands using the runners shell 34 | - name: Run a multi-line script 35 | run: | 36 | echo Add other actions to build, 37 | echo test, and deploy your project. 38 | 39 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 40 | - uses: actions/checkout@v2 41 | 42 | # Login to Azure using service principle 43 | - name: Azure Login 44 | uses: azure/login@v1 45 | with: 46 | creds: ${{ secrets.AZURE_CREDENTIALS }} 47 | 48 | - name: Deploy Bicep File to Azure via Azure CLI Action 49 | uses: Azure/cli@1.0.4 50 | with: 51 | # Specify the script here 52 | inlineScript: | 53 | az deployment sub create -n $AZURE_RG -l $AZURE_LOCATION -f $GITHUB_WORKSPACE/vmss-flex-n-tier-demo/main.bicep 54 | # Azure CLI version to be used to execute the script. If not provided, latest version is used 55 | # azcliversion: # optional, default is latest 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | .DS_Store 91 | 92 | # Local .terraform directories 93 | **/.terraform/* 94 | 95 | # .tfstate files 96 | *.tfstate 97 | *.tfstate.* 98 | 99 | # Crash log files 100 | crash.log 101 | crash.*.log 102 | 103 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 104 | # password, private keys, and other secrets. These should not be part of version 105 | # control as they are data points which are potentially sensitive and subject 106 | # to change depending on the environment. 107 | *.tfvars 108 | *.tfvars.json 109 | 110 | # Ignore override files as they are usually used to override resources locally and so 111 | # are not checked in 112 | override.tf 113 | override.tf.json 114 | *_override.tf 115 | *_override.tf.json 116 | 117 | # Include override files you do wish to add to version control using negated pattern 118 | # !example_override.tf 119 | 120 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 121 | # example: *tfplan* 122 | 123 | # Ignore CLI configuration files 124 | .terraformrc 125 | terraform.rc -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Azure VM Scale Sets 2 | This repo is for managing Azure virtual machine scale sets, in particular preview programs, samples and issues. 3 | 4 | > **Note** 5 | > Please refer to the [official VMSS documentation](https://docs.microsoft.com/en-us/azure/virtual-machine-scale-sets/overview) for product documentation, feedback, etc. 6 | 7 | -------------------------------------------------------------------------------- /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), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 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/opensource/security/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/opensource/security/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/opensource/security/pgpkey). 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://aka.ms/opensource/security/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/opensource/security/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/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /terraform/terraform-tutorial/app/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Terramino 6 | 7 | 48 | 49 | '2021-02-01', 'format'=>'text'); 52 | $vm_name_url = "http://169.254.169.254/metadata/instance/compute/name" . '?' . http_build_query($params); 53 | $zone_url = "http://169.254.169.254/metadata/instance/compute/zone" . '?' . http_build_query($params); 54 | $resourceid_url = "http://169.254.169.254/metadata/instance/compute/resourceId" . '?' . http_build_query($params);// set url 55 | curl_setopt($ch, CURLOPT_URL, $vm_name_url); 56 | 57 | // set header 58 | curl_setopt($ch, CURLOPT_HTTPHEADER, array('Metadata:true')); 59 | 60 | //return the transfer as a string 61 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 62 | 63 | // $output contains the output string 64 | $vm_name = curl_exec($ch); 65 | 66 | curl_setopt($ch, CURLOPT_URL, $zone_url); 67 | $zone = curl_exec($ch); 68 | 69 | curl_setopt($ch, CURLOPT_URL, $resourceid_url); 70 | $resource_id = curl_exec($ch); 71 | 72 | // close curl resource to free up system resources 73 | curl_close($ch); 74 | 75 | ?> 76 | 77 | 78 |
79 |
80 |

Terramino

81 |

VM Name:

82 |

Instance ID:

83 |

Availability Zones:

84 |

Use left and right arrow keys to move blocks.
Use up arrow key to flip block.

85 |
86 |
87 | 88 |
89 |
90 | 352 | 353 | 354 | -------------------------------------------------------------------------------- /terraform/terraform-tutorial/autoscale.tf: -------------------------------------------------------------------------------- 1 | # create autoscale resource that will decrease the number of instances if the azurerm_orchestrated_scale set cpu usaae is below 10% for 2 minutes 2 | resource "azurerm_monitor_autoscale_setting" "autoscale" { 3 | name = "autoscale" 4 | location = azurerm_resource_group.rg.location 5 | resource_group_name = azurerm_resource_group.rg.name 6 | target_resource_id = azurerm_orchestrated_virtual_machine_scale_set.vmss_terraform_tutorial.id 7 | enabled = true 8 | profile { 9 | name = "autoscale" 10 | capacity { 11 | default = 3 12 | minimum = 1 13 | maximum = 10 14 | } 15 | rule { 16 | metric_trigger { 17 | metric_name = "Percentage CPU" 18 | metric_resource_id = azurerm_orchestrated_virtual_machine_scale_set.vmss_terraform_tutorial.id 19 | operator = "LessThan" 20 | statistic = "Average" 21 | time_aggregation = "Average" 22 | time_window = "PT2M" 23 | time_grain = "PT1M" 24 | threshold = 10 25 | } 26 | scale_action { 27 | direction = "Decrease" 28 | type = "ChangeCount" 29 | value = "1" 30 | cooldown = "PT1M" 31 | } 32 | } 33 | rule { 34 | metric_trigger { 35 | metric_name = "Percentage CPU" 36 | metric_resource_id = azurerm_orchestrated_virtual_machine_scale_set.vmss_terraform_tutorial.id 37 | operator = "GreaterThan" 38 | statistic = "Average" 39 | time_aggregation = "Average" 40 | time_window = "PT2M" 41 | time_grain = "PT1M" 42 | threshold = 90 43 | } 44 | scale_action { 45 | direction = "Increase" 46 | type = "ChangeCount" 47 | value = "1" 48 | cooldown = "PT1M" 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /terraform/terraform-tutorial/compute.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_orchestrated_virtual_machine_scale_set" "vmss_terraform_tutorial" { 2 | name = "vmss-terraform" 3 | resource_group_name = azurerm_resource_group.rg.name 4 | location = azurerm_resource_group.rg.location 5 | sku_name = "Standard_D2s_v4" 6 | instances = var.num_instances 7 | platform_fault_domain_count = 1 # For zonal deployments, this must be set to 1 8 | zones = ["1"] # Zones required to lookup zone in the startup script 9 | # tags = var.tags 10 | 11 | user_data_base64 = base64encode(file("user-data.sh")) 12 | os_profile { 13 | 14 | linux_configuration { 15 | disable_password_authentication = true 16 | admin_username = "azureuser" 17 | admin_ssh_key { 18 | username = "azureuser" 19 | public_key = file("~/.ssh/id_rsa.pub") 20 | } 21 | } 22 | } 23 | 24 | source_image_reference { 25 | publisher = "Canonical" 26 | offer = "0001-com-ubuntu-server-jammy" 27 | sku = "22_04-LTS-gen2" 28 | version = "latest" 29 | } 30 | os_disk { 31 | storage_account_type = "Premium_LRS" 32 | caching = "ReadWrite" 33 | } 34 | 35 | network_interface { 36 | name = "nic" 37 | primary = true 38 | enable_accelerated_networking = false 39 | 40 | ip_configuration { 41 | name = "ipconfig" 42 | primary = true 43 | subnet_id = azurerm_subnet.subnet.id 44 | load_balancer_backend_address_pool_ids = [azurerm_lb_backend_address_pool.bepool.id] 45 | # Enable a public ip address on each VM instance to enable direct access to each instance 46 | # public_ip_address { 47 | # name = "vmsspip" 48 | # sku_name = "Standard_Regional" 49 | # version = "IPv4" 50 | # } 51 | } 52 | } 53 | ## Enable automatic instance repair, so that if a VM instance fails, it will be replaced 54 | # automatic_instance_repair { 55 | # enabled = true 56 | # grace_period = "PT30M" 57 | # } 58 | 59 | # Enable application health extension to report instance health Azure. This is required for automatic instance repair to work. 60 | # extension { 61 | # name = "${var.worker_group_name}-health" 62 | # publisher = "Microsoft.ManagedServices" 63 | # type = "ApplicationHealthLinux" 64 | # type_handler_version = "1.0" 65 | # settings = jsonencode({ 66 | # "protocol" = "http" 67 | # "port" = 80 68 | # "requestPath" = "/" 69 | # }) 70 | # } 71 | 72 | boot_diagnostics { 73 | storage_account_uri = "" 74 | } 75 | 76 | # Ignore changes to the instances property, so that the VMSS is not recreated when the number of instances is changed 77 | lifecycle { 78 | ignore_changes = [ 79 | instances 80 | ] 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /terraform/terraform-tutorial/main.tf: -------------------------------------------------------------------------------- 1 | provider "azurerm" { 2 | features {} 3 | } 4 | 5 | data "azurerm_client_config" "current" {} 6 | 7 | # A resource group is like a folder for related resources. You can delete the resource group to delete all resources in it. 8 | resource "azurerm_resource_group" "rg" { 9 | name = "terraform-tutorial" 10 | location = "westus3" 11 | } 12 | 13 | -------------------------------------------------------------------------------- /terraform/terraform-tutorial/network.tf: -------------------------------------------------------------------------------- 1 | 2 | # Create an virtual network and subnet 3 | resource "azurerm_virtual_network" "test" { 4 | name = "terraformvnet" 5 | address_space = ["10.0.0.0/16"] 6 | location = azurerm_resource_group.rg.location 7 | resource_group_name = azurerm_resource_group.rg.name 8 | } 9 | 10 | resource "azurerm_subnet" "subnet" { 11 | name = "subnet" 12 | resource_group_name = azurerm_resource_group.rg.name 13 | virtual_network_name = azurerm_virtual_network.test.name 14 | address_prefixes = ["10.0.0.0/20"] 15 | } 16 | 17 | # network security group for the subnet with a rule to allow http, https and ssh traffic 18 | resource "azurerm_network_security_group" "myNSG" { 19 | name = "myNSG" 20 | location = azurerm_resource_group.rg.location 21 | resource_group_name = azurerm_resource_group.rg.name 22 | 23 | security_rule { 24 | name = "allow-http" 25 | priority = 100 26 | direction = "Inbound" 27 | access = "Allow" 28 | protocol = "Tcp" 29 | source_port_range = "*" 30 | destination_port_range = "80" 31 | source_address_prefix = "*" 32 | destination_address_prefix = "*" 33 | } 34 | 35 | security_rule { 36 | name = "allow-https" 37 | priority = 101 38 | direction = "Inbound" 39 | access = "Allow" 40 | protocol = "Tcp" 41 | source_port_range = "*" 42 | destination_port_range = "443" 43 | source_address_prefix = "*" 44 | destination_address_prefix = "*" 45 | } 46 | #ssh security rule 47 | security_rule { 48 | name = "allow-ssh" 49 | priority = 102 50 | direction = "Inbound" 51 | access = "Allow" 52 | protocol = "Tcp" 53 | source_port_range = "*" 54 | destination_port_range = "22" 55 | source_address_prefix = "*" 56 | destination_address_prefix = "*" 57 | } 58 | } 59 | 60 | resource "azurerm_subnet_network_security_group_association" "myNSG" { 61 | subnet_id = azurerm_subnet.subnet.id 62 | network_security_group_id = azurerm_network_security_group.myNSG.id 63 | } 64 | 65 | # A public IP address for the load balancer 66 | resource "azurerm_public_ip" "example" { 67 | name = "lb-publicIP" 68 | location = azurerm_resource_group.rg.location 69 | resource_group_name = azurerm_resource_group.rg.name 70 | allocation_method = "Static" 71 | sku = "Standard" 72 | zones = ["1","2","3"] 73 | domain_name_label = "${azurerm_resource_group.rg.name}-dns" 74 | } 75 | 76 | # A load balancer with a frontend IP configuration and a backend address pool 77 | resource "azurerm_lb" "example" { 78 | name = "myLB" 79 | location = azurerm_resource_group.rg.location 80 | resource_group_name = azurerm_resource_group.rg.name 81 | sku = "Standard" 82 | frontend_ip_configuration { 83 | name = "myPublicIP" 84 | public_ip_address_id = azurerm_public_ip.example.id 85 | } 86 | } 87 | 88 | resource "azurerm_lb_backend_address_pool" "bepool" { 89 | name = "myBackendAddressPool" 90 | loadbalancer_id = azurerm_lb.example.id 91 | } 92 | 93 | #set up load balancer rule from azurerm_lb.example frontend ip to azurerm_lb_backend_address_pool.bepool backend ip port 80 to port 80 94 | resource "azurerm_lb_rule" "example" { 95 | name = "http" 96 | loadbalancer_id = azurerm_lb.example.id 97 | protocol = "Tcp" 98 | frontend_port = 80 99 | backend_port = 80 100 | frontend_ip_configuration_name = "myPublicIP" 101 | backend_address_pool_ids = [azurerm_lb_backend_address_pool.bepool.id] 102 | probe_id = azurerm_lb_probe.example.id 103 | } 104 | 105 | #set up load balancer probe to check if the backend is up 106 | resource "azurerm_lb_probe" "example" { 107 | name = "http-probe" 108 | loadbalancer_id = azurerm_lb.example.id 109 | protocol = "Http" 110 | port = 80 111 | request_path = "/" 112 | } 113 | 114 | #add lb nat rules to allow ssh access to the backend instances 115 | resource "azurerm_lb_nat_rule" "ssh" { 116 | name = "ssh" 117 | resource_group_name = azurerm_resource_group.rg.name 118 | loadbalancer_id = azurerm_lb.example.id 119 | protocol = "Tcp" 120 | frontend_port_start = var.nat_rule_frontend_port_start 121 | frontend_port_end = 50119 122 | backend_port = 22 123 | frontend_ip_configuration_name = "myPublicIP" 124 | backend_address_pool_id = azurerm_lb_backend_address_pool.bepool.id 125 | } 126 | 127 | resource "azurerm_public_ip" "natgwpip" { 128 | name = "natgw-publicIP" 129 | location = azurerm_resource_group.rg.location 130 | resource_group_name = azurerm_resource_group.rg.name 131 | allocation_method = "Static" 132 | sku = "Standard" 133 | zones = ["1"] 134 | } 135 | 136 | #add nat gateway to enable outbound traffic from the backend instances 137 | resource "azurerm_nat_gateway" "example" { 138 | name = "nat-Gateway" 139 | location = azurerm_resource_group.rg.location 140 | resource_group_name = azurerm_resource_group.rg.name 141 | sku_name = "Standard" 142 | idle_timeout_in_minutes = 10 143 | zones = ["1"] 144 | } 145 | 146 | resource "azurerm_subnet_nat_gateway_association" "example" { 147 | subnet_id = azurerm_subnet.subnet.id 148 | nat_gateway_id = azurerm_nat_gateway.example.id 149 | } 150 | 151 | # add nat gateway public ip association 152 | resource "azurerm_nat_gateway_public_ip_association" "example" { 153 | public_ip_address_id = azurerm_public_ip.natgwpip.id 154 | nat_gateway_id = azurerm_nat_gateway.example.id 155 | } 156 | -------------------------------------------------------------------------------- /terraform/terraform-tutorial/outputs.tf: -------------------------------------------------------------------------------- 1 | output "lb_endpoint" { 2 | value = "http://${azurerm_public_ip.example.fqdn}" 3 | } 4 | 5 | output "application_endpoint" { 6 | value = "http://${azurerm_public_ip.example.fqdn}/index.php" 7 | } 8 | 9 | output "ssh_endpoint" { 10 | value = "ssh azureuser@${azurerm_public_ip.example.ip_address} -p ${var.nat_rule_frontend_port_start}" 11 | } 12 | 13 | output "vmss_azure_portal_url" { 14 | value = "https://portal.azure.com/#@${data.azurerm_client_config.current.tenant_id}/resource${azurerm_orchestrated_virtual_machine_scale_set.vmss_terraform_tutorial.id}/overview" 15 | } -------------------------------------------------------------------------------- /terraform/terraform-tutorial/user-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | apt-get update -y 3 | apt-get install -y apache2 php php-curl libapache2-mod-php php-mysql jq 4 | ufw allow 'Apache Full' 5 | 6 | usermod -a -G azureuser azureuser 7 | 8 | mkdir -p /var/www/html 9 | chown -R azureuser:azureuser /var/www 10 | chmod 2775 /var/www 11 | find /var/www -type d -exec chmod 2775 {} \; 12 | find /var/www -type f -exec chmod 0664 {} \; 13 | cd /var/www/html 14 | curl -s -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | jq > index.html 15 | sed -i '1i
' index.html
16 | sed -i '$a
' index.html 17 | curl https://raw.githubusercontent.com/Azure/vm-scale-sets/master/terraform/terraform-tutorial/app/index.php -O -------------------------------------------------------------------------------- /terraform/terraform-tutorial/variables.tf: -------------------------------------------------------------------------------- 1 | 2 | variable "num_instances" { 3 | description = "Number of instances for the worker" 4 | type = number 5 | default = 3 6 | } 7 | 8 | 9 | variable "nat_rule_frontend_port_start" { 10 | description = "The Load Balancer port through which you can ssh into the first VM instance" 11 | type = number 12 | default = 50000 13 | } -------------------------------------------------------------------------------- /vmss-flex-n-tier-demo/README.md: -------------------------------------------------------------------------------- 1 | # vmss-flex-cassandra-demo 2 | 3 | This is a reimplementation of the [n-tier-cassandra reference architecture](https://github.com/mspnp/reference-architectures/tree/master/virtual-machines/n-tier-linux) with Virtual Machine Scale Sets with Flexible Orchestration 4 | -------------------------------------------------------------------------------- /vmss-flex-n-tier-demo/compute/vmssautoscale.bicep: -------------------------------------------------------------------------------- 1 | param vmssId string 2 | param autoscalename string = 'autoscaler' 3 | param autoscaleenabled bool = false 4 | param autoScaleMin string = '1' 5 | param autoScaleMax string = '10' 6 | param autoScaleDefault string = '5' 7 | param durationInMinutes int = 10 8 | param scaleOutCPUPercentageThreshold int = 75 9 | param scaleOutInterval string = '1' 10 | param scaleInCPUPercentageThreshold int = 25 11 | param scaleInInterval string = '1' 12 | 13 | 14 | var oneMin = 'PT1M' 15 | 16 | resource autoscale 'Microsoft.insights/autoscalesettings@2015-04-01' = { 17 | name: autoscalename 18 | location: resourceGroup().location 19 | properties: { 20 | targetResourceUri: vmssId 21 | enabled: autoscaleenabled 22 | profiles:[ 23 | { 24 | name: 'Profile1' 25 | capacity: { 26 | minimum: autoScaleMin 27 | maximum: autoScaleMax 28 | default: autoScaleDefault 29 | } 30 | rules: [ 31 | { 32 | metricTrigger:{ 33 | metricName: 'Percentage CPU' 34 | metricNamespace: '' 35 | metricResourceUri: vmssId 36 | statistic: 'Average' 37 | timeGrain: oneMin 38 | timeWindow: 'PT${durationInMinutes}M' 39 | timeAggregation: 'Average' 40 | operator: 'GreaterThan' 41 | threshold: scaleOutCPUPercentageThreshold 42 | } 43 | scaleAction: { 44 | direction:'Increase' 45 | type: 'ChangeCount' 46 | value: scaleOutInterval 47 | cooldown: oneMin 48 | } 49 | } 50 | { 51 | metricTrigger:{ 52 | metricName: 'Percentage CPU' 53 | metricNamespace: '' 54 | metricResourceUri: vmssId 55 | statistic: 'Average' 56 | timeGrain: oneMin 57 | timeWindow: 'PT5M' 58 | timeAggregation: 'Average' 59 | operator: 'LessThan' 60 | threshold: scaleInCPUPercentageThreshold 61 | } 62 | scaleAction: { 63 | direction:'Decrease' 64 | type: 'ChangeCount' 65 | value: scaleInInterval 66 | cooldown: oneMin 67 | } 68 | } 69 | ] 70 | } 71 | ] 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /vmss-flex-n-tier-demo/compute/vmssflex.bicep: -------------------------------------------------------------------------------- 1 | param vmssname string = 'myVmssFlex' 2 | param region string = resourceGroup().location 3 | param zones array = [] 4 | 5 | param vmSize string = 'Standard_DS1_v2' 6 | @allowed([ 7 | 1 8 | 2 9 | 3 10 | 5 11 | ]) 12 | param platformFaultDomainCount int = 1 13 | @maxValue(500) 14 | param vmCount int = 3 15 | 16 | param subnetId string 17 | param lbBackendPoolArray array = [] 18 | param appGwBackendPoolArray array = [] 19 | 20 | @allowed([ 21 | 'Regular' 22 | 'Spot' 23 | ]) 24 | param priority string = 'Regular' 25 | @allowed([ 26 | 'Deallocate' 27 | 'Delete' 28 | '' 29 | ]) 30 | param spotEvictionPolicy string = '' 31 | param spotBillingProfile object = { 32 | 33 | } 34 | 35 | param adminUsername string = 'azureuser' 36 | @allowed([ 37 | 'password' 38 | 'sshPublicKey' 39 | ]) 40 | param authenticationType string = 'password' 41 | param adminPasswordOrKey string = guid(resourceGroup().id, deployment().name) 42 | 43 | var networkApiVersion = '2020-11-01' 44 | // This launches a simple node app that returns the contents of IMDS. It also exposes /health for application health. https://github.com/fitzgeraldsteele/azure-sig-aib-vmss-demo/blob/master/cloud-init/cloud-init.sample.yml 45 | var imdsCustomDataBase64 = 'I2Nsb3VkLWNvbmZpZwpwYWNrYWdlX3VwZ3JhZGU6IHRydWUKcGFja2FnZXM6CiAgLSBuZ2lueAogIC0gbm9kZWpzCiAgLSBucG0Kd3JpdGVfZmlsZXM6CiAgLSBvd25lcjogd3d3LWRhdGE6d3d3LWRhdGEKICAgIHBhdGg6IC9ldGMvbmdpbngvc2l0ZXMtYXZhaWxhYmxlL2RlZmF1bHQKICAgIGNvbnRlbnQ6IHwKICAgICAgc2VydmVyIHsKICAgICAgICBsaXN0ZW4gODA7CiAgICAgICAgbG9jYXRpb24gLyB7CiAgICAgICAgICBwcm94eV9wYXNzIGh0dHA6Ly9sb2NhbGhvc3Q6MzAwMDsKICAgICAgICAgIHByb3h5X2h0dHBfdmVyc2lvbiAxLjE7CiAgICAgICAgICBwcm94eV9zZXRfaGVhZGVyIFVwZ3JhZGUgJGh0dHBfdXBncmFkZTsKICAgICAgICAgIHByb3h5X3NldF9oZWFkZXIgQ29ubmVjdGlvbiBrZWVwLWFsaXZlOwogICAgICAgICAgcHJveHlfc2V0X2hlYWRlciBIb3N0ICRob3N0OwogICAgICAgICAgcHJveHlfY2FjaGVfYnlwYXNzICRodHRwX3VwZ3JhZGU7CiAgICAgICAgfQogICAgICB9CiAgLSBvd25lcjogYXp1cmV1c2VyOmF6dXJldXNlcgogICAgcGF0aDogL2hvbWUvYXp1cmV1c2VyL215YXBwL2luZGV4LmpzCiAgICBjb250ZW50OiB8CiAgICAgIHZhciBleHByZXNzID0gcmVxdWlyZSgnZXhwcmVzcycpCiAgICAgIHZhciBhcHAgPSBleHByZXNzKCkKICAgICAgdmFyIG9zID0gcmVxdWlyZSgnb3MnKTsKICAgICAgYXBwLmdldCgnLycsIGZ1bmN0aW9uIChyZXEsIHJlcykgewogICAgICAgIGNvbnN0IHJlcXVlc3QgPSByZXF1aXJlKCdyZXF1ZXN0Jyk7CiAgICAgICAgY29uc3Qgb3B0aW9ucyA9IHsKICAgICAgICAgIHVybDogJ2h0dHA6Ly8xNjkuMjU0LjE2OS4yNTQvbWV0YWRhdGEvaW5zdGFuY2U/YXBpLXZlcnNpb249MjAxOS0wMy0xMScsCiAgICAgICAgICBoZWFkZXJzOiB7CiAgICAgICAgICAgICdNZXRhZGF0YSc6ICd0cnVlJwogICAgICAgICAgfQogICAgICAgIH07CiAgICAgICAgcmVxdWVzdChvcHRpb25zLCBmdW5jdGlvbiAoZXJyb3IsIHJlc3BvbnNlLCBib2R5KSB7CiAgICAgICAgICBpZiAoIWVycm9yICYmIHJlc3BvbnNlLnN0YXR1c0NvZGUgPT0gMjAwKSB7CiAgICAgICAgICAgIHZhciBtZXRhZGF0YU9iaiA9IEpTT04ucGFyc2UoYm9keSkKICAgICAgICAgICAgbGV0IGVqcyA9IHJlcXVpcmUoJ2VqcycpLAogICAgICAgICAgICAgIGh0bWwgPSBlanMucmVuZGVyKAogICAgICAgICAgICAgICAgJzxoMj48JT0gbWV0YWRhdGFPYmouY29tcHV0ZS5uYW1lICU+PC9oMj4gXAogICAgICAgICAgICAgICAgICA8cD5OYW1lOiA8JT0gbWV0YWRhdGFPYmouY29tcHV0ZS5uYW1lICU+PC9wPiBcCiAgICAgICAgICAgICAgICAgIDxwPlByaXZhdGUgSVAgQWRkcmVzczogPCU9IG1ldGFkYXRhT2JqLm5ldHdvcmsuaW50ZXJmYWNlWzBdLmlwdjQuaXBBZGRyZXNzWzBdLnByaXZhdGVJcEFkZHJlc3MgJT48L3A+IFwKICAgICAgICAgICAgICAgICAgPHA+UmVzb3VyY2UgaWQ6IDwlPSBtZXRhZGF0YU9iai5jb21wdXRlLnJlc291cmNlSWQgJT48L3A+IFwKICAgICAgICAgICAgICAgICAgPHA+U2NhbGUgc2V0IG5hbWU6IDwlPSBtZXRhZGF0YU9iai5jb21wdXRlLnZtU2NhbGVTZXROYW1lICU+PC9wPjwvcD4gXAogICAgICAgICAgICAgICAgICA8cD48cHJlPjwlPSAgSlNPTi5zdHJpbmdpZnkobWV0YWRhdGFPYmosIG51bGwsIDIpICU+PC9wcmU+PC9wPicsCiAgICAgICAgICAgICAgICAgIHsgbWV0YWRhdGFPYmo6IG1ldGFkYXRhT2JqIH0KICAgICAgICAgICAgICApCiAgICAgICAgICAgIHJlcy5zZW5kKGh0bWwpCiAgICAgICAgICB9CiAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgY29uc29sZS5sb2coIkVycm9yICIgKyByZXNwb25zZS5zdGF0dXNDb2RlKQogICAgICAgICAgfQogICAgICAgIH0pCiAgICAgIH0pCiAgICAgIGFwcC5nZXQoJy9tZXRhJywgZnVuY3Rpb24gKHJlcSwgcmVzKSB7CiAgICAgICAgY29uc3QgcmVxdWVzdCA9IHJlcXVpcmUoJ3JlcXVlc3QnKTsKICAgICAgICBjb25zdCBvcHRpb25zID0gewogICAgICAgICAgdXJsOiAnaHR0cDovLzE2OS4yNTQuMTY5LjI1NC9tZXRhZGF0YS9pbnN0YW5jZT9hcGktdmVyc2lvbj0yMDE5LTAzLTExJywKICAgICAgICAgIGhlYWRlcnM6IHsKICAgICAgICAgICAgJ01ldGFkYXRhJzogJ3RydWUnCiAgICAgICAgICB9CiAgICAgICAgfTsKICAgICAgICByZXF1ZXN0KG9wdGlvbnMsIGZ1bmN0aW9uIChlcnJvciwgcmVzcG9uc2UsIGJvZHkpIHsKICAgICAgICAgIGlmICghZXJyb3IgJiYgcmVzcG9uc2Uuc3RhdHVzQ29kZSA9PSAyMDApIHsKICAgICAgICAgICAgcmVzLnNldCgnQ29udGVudC1UeXBlJywgJ2FwcGxpY2F0aW9uL2pzb24nKTsKICAgICAgICAgICAgcmVzLnNlbmQoYm9keSkKICAgICAgICAgIH0KICAgICAgICAgIGVsc2UgewogICAgICAgICAgICBjb25zb2xlLmxvZygiRXJyb3IgIiArIHJlc3BvbnNlLnN0YXR1c0NvZGUpCiAgICAgICAgICB9CiAgICAgICAgfSkKICAgICAgfSkKICAgICAgYXBwLmdldCgnL2hlYWx0aCcsIGZ1bmN0aW9uIChyZXEsIHJlcykgewogICAgICAgIHJlcy5zZW5kKCdQT05HIScpCiAgICAgIH0pCiAgICAgIGFwcC5saXN0ZW4oMzAwMCwgZnVuY3Rpb24gKCkgewogICAgICAgIGNvbnNvbGUubG9nKCdIZWxsbyB3b3JsZCBhcHAgbGlzdGVuaW5nIG9uIHBvcnQgMzAwMCEnKQogICAgICB9KQpydW5jbWQ6CiAgLSBzZXJ2aWNlIG5naW54IHJlc3RhcnQKICAtIGNkICIvaG9tZS9henVyZXVzZXIvbXlhcHAiCiAgLSBucG0gaW5pdAogIC0gbnBtIGluc3RhbGwgZXhwcmVzcyAteQogIC0gbnBtIGluc3RhbGwgbm9kZW1vbiAteQogIC0gbnBtIGluc3RhbGwgZWpzIC15CiAgLSBub2RlanMgaW5kZXguanM=' 46 | var linuxConfiguration = { 47 | disablePasswordAuthentication: true 48 | provisionVMAgent: true 49 | ssh: { 50 | publicKeys: [ 51 | { 52 | path: '/home/${adminUsername}/.ssh/authorized_keys' 53 | keyData: '${adminPasswordOrKey}' 54 | } 55 | ] 56 | } 57 | } 58 | 59 | resource diagStorageAccount 'Microsoft.Storage/storageAccounts@2021-01-01' = { 60 | name: 'diagstorage${uniqueString(resourceGroup().id)}' 61 | location: region 62 | kind: 'Storage' 63 | sku: { 64 | name: 'Standard_LRS' 65 | } 66 | } 67 | 68 | 69 | resource vmssflex 'Microsoft.Compute/virtualMachineScaleSets@2021-03-01' = { 70 | name: '${vmssname}' 71 | location: '${region}' 72 | zones: zones 73 | sku: { 74 | name: vmSize 75 | tier: 'Standard' 76 | capacity: vmCount 77 | } 78 | properties: { 79 | orchestrationMode: 'Flexible' 80 | singlePlacementGroup: false 81 | platformFaultDomainCount: platformFaultDomainCount 82 | 83 | virtualMachineProfile: { 84 | osProfile: { 85 | computerNamePrefix: 'myVm' 86 | customData: imdsCustomDataBase64 87 | adminUsername: adminUsername 88 | adminPassword: adminPasswordOrKey 89 | linuxConfiguration: any(authenticationType == 'password' ? null : linuxConfiguration) // TODO: workaround for https://github.com/Azure/bicep/issues/449 90 | // windowsConfiguration: { 91 | // timeZone: 'Pacific Standard Time' 92 | // } 93 | //licenseType: 'Windows_Server' 94 | } 95 | networkProfile: { 96 | networkApiVersion: networkApiVersion 97 | networkInterfaceConfigurations: [ 98 | { 99 | name: '${vmssname}NicConfig01' 100 | properties: { 101 | primary: true 102 | enableAcceleratedNetworking: false 103 | ipConfigurations: [ 104 | { 105 | name: '${vmssname}IpConfig' 106 | properties: { 107 | // publicIPAddressConfiguration: { 108 | // name: '${vmssname}PipConfig' 109 | // properties:{ 110 | // publicIPAddressVersion: 'IPv4' 111 | // idleTimeoutInMinutes: 5 112 | // } 113 | // } 114 | privateIPAddressVersion: 'IPv4' 115 | subnet: { 116 | id: subnetId 117 | } 118 | loadBalancerBackendAddressPools: lbBackendPoolArray 119 | applicationGatewayBackendAddressPools: appGwBackendPoolArray 120 | } 121 | } 122 | ] 123 | } 124 | } 125 | ] 126 | } 127 | diagnosticsProfile: { 128 | bootDiagnostics: { 129 | enabled: true 130 | storageUri: diagStorageAccount.properties.primaryEndpoints.blob 131 | } 132 | } 133 | extensionProfile: { 134 | extensions: [ 135 | { 136 | name: 'AppHealthExtension' 137 | properties: { 138 | publisher: 'Microsoft.ManagedServices' 139 | type: 'ApplicationHealthLinux' 140 | autoUpgradeMinorVersion: true 141 | typeHandlerVersion: '1.0' 142 | settings: { 143 | protocol: 'http' 144 | port: 80 145 | requestPath: '/health' 146 | } 147 | } 148 | } 149 | ] 150 | } 151 | storageProfile: { 152 | osDisk: { 153 | osType: 'Linux' 154 | //osType: 'Windows' 155 | createOption: 'FromImage' 156 | //deleteOption: 'Delete' // deleteOption will be set to delete by default 157 | caching: 'ReadWrite' 158 | diskSizeGB: 256 159 | managedDisk: { 160 | storageAccountType: 'Premium_LRS' 161 | } 162 | } 163 | imageReference: { 164 | publisher: 'Canonical' 165 | offer: 'UbuntuServer' 166 | sku: '18.04-LTS' 167 | version: 'latest' 168 | } 169 | // imageReference: { 170 | // publisher: 'MicrosoftWindowsServer' 171 | // offer: 'WindowsServer' 172 | // sku: '2019-Datacenter' 173 | // version: 'latest' 174 | // } 175 | } 176 | // Use spot instances for testing 177 | priority: priority 178 | evictionPolicy: spotEvictionPolicy 179 | billingProfile: spotBillingProfile 180 | // Enable Terminate notification 181 | scheduledEventsProfile: { 182 | terminateNotificationProfile: { 183 | notBeforeTimeout: 'PT5M' 184 | enable: true 185 | } 186 | } 187 | } 188 | automaticRepairsPolicy: { 189 | enabled: true 190 | gracePeriod: 'PT30M' 191 | } 192 | // Ultra SSD not yet supported on VMSS Flex level 193 | // This can be assigned at the individual VM level 194 | // additionalCapabilities: { 195 | // ultraSSDEnabled: false 196 | // } 197 | 198 | // Managed identity. Only UserAssigned identity can be passed in to VMSS Profile 199 | // UserAssigned identity must be created before it can be assigned here 200 | // SystemAssigned identity can be assigned at the individual VM level 201 | // identity: { 202 | // type: 'UserAssigned' 203 | // } 204 | } 205 | } 206 | 207 | output vmssid string = vmssflex.id 208 | -------------------------------------------------------------------------------- /vmss-flex-n-tier-demo/main.bicep: -------------------------------------------------------------------------------- 1 | // Deploy VMSS Flex into a subscription. 2 | // This will create and deploy resources at subscription scope into Resource Group rgName 3 | // bicep template. Bicep reference: https://aka.ms/bicep 4 | // az deployment sub create -n myflexdeploy -l westus2 -f main.bicep --parameters rgName=MyResourceGroup 5 | targetScope= 'subscription' 6 | 7 | param rgName string = 'vmss-flex-cassandra-demo' 8 | param region string = 'southcentralus' 9 | param appGwName string = 'webAppGateway' 10 | param lbName string = 'biz-lb' 11 | 12 | param dataVmssName string = 'data-vmss' 13 | param vmCount int = 3 14 | @allowed([ 15 | 1 16 | 2 17 | 3 18 | 5 19 | ]) 20 | param platformFaultDomainCount int = 1 21 | 22 | 23 | param sshKeyName string = 'caroldanvers-westus' 24 | param rgSshKeys string = 'mySSHKeys' 25 | 26 | var appGwBackendPools = [] 27 | var slbBackendPools = [] 28 | var vnetName= 'ra-ntier-vnet' 29 | 30 | resource deployrg 'Microsoft.Resources/resourceGroups@2020-06-01' = { 31 | name: rgName 32 | location: region 33 | } 34 | module basenetwork './network/basenetwork.bicep' = { 35 | name: 'basenetwork' 36 | scope: resourceGroup(deployrg.name) 37 | params: { 38 | virtualNetworkName: vnetName 39 | } 40 | } 41 | 42 | module appgw './network/appgw.bicep' = { 43 | name: 'appgw' 44 | scope: resourceGroup(deployrg.name) 45 | params: { 46 | appGwName: appGwName 47 | appGwSubnetId: basenetwork.outputs.vnetSubnetArray[4].id 48 | } 49 | } 50 | 51 | module slb './network/slb.bicep' = { 52 | name: 'slb' 53 | scope: resourceGroup(deployrg.name) 54 | params: { 55 | slbName: lbName 56 | } 57 | } 58 | 59 | module vmssFlexDataTier './compute/vmssflex.bicep' = { 60 | name: 'data-vmss' 61 | scope: resourceGroup(deployrg.name) 62 | params: { 63 | vmssname: 'data-vmss' 64 | vmCount: 6 65 | vmSize: 'Standard_D16s_v4' 66 | platformFaultDomainCount: 3 67 | subnetId: basenetwork.outputs.vnetSubnetArray[2].id 68 | // appGwBackendPoolArray: appgw.outputs.appGwBackendPoolArray 69 | // lbBackendPoolArray: slbBackendPools 70 | } 71 | } 72 | 73 | module vmssFlexBizTier './compute/vmssflex.bicep' = { 74 | name: 'biz-vmss' 75 | scope: resourceGroup(deployrg.name) 76 | params: { 77 | vmssname: 'biz-vmss' 78 | vmCount: 3 79 | vmSize: 'Standard_D2s_v4' 80 | platformFaultDomainCount: 1 81 | subnetId: basenetwork.outputs.vnetSubnetArray[1].id 82 | lbBackendPoolArray: slb.outputs.slbBackendPoolArray 83 | } 84 | } 85 | 86 | module vmssFlexWebTier './compute/vmssflex.bicep' = { 87 | name: 'web-vmss' 88 | scope: resourceGroup(deployrg.name) 89 | params: { 90 | vmssname: 'web-vmss' 91 | vmCount: 3 92 | vmSize: 'Standard_D2s_v4' 93 | platformFaultDomainCount: 1 94 | priority: 'Spot' 95 | spotEvictionPolicy: 'Delete' 96 | spotBillingProfile: { 97 | maxPrice: -1 98 | } 99 | subnetId: basenetwork.outputs.vnetSubnetArray[0].id 100 | appGwBackendPoolArray: appgw.outputs.appGwBackendPoolArray 101 | } 102 | } 103 | 104 | // // Enable autoscaling on the VMSS Flex 105 | module autoscaler './compute/vmssautoscale.bicep' = { 106 | name: 'autoscale' 107 | scope: resourceGroup(deployrg.name) 108 | params:{ 109 | vmssId: vmssFlexWebTier.outputs.vmssid 110 | } 111 | } 112 | 113 | 114 | 115 | 116 | 117 | 118 | // module sshkey './compute/ssh.bicep' = { 119 | // name: 'sshkey' 120 | // scope: resourceGroup('${rgSshKeys}') 121 | // params: { 122 | // sshKeyName: sshKeyName 123 | // } 124 | // } 125 | 126 | // module vmss './compute/vmssflex.bicep' = { 127 | // name: 'vmss-bicep' 128 | // scope: resourceGroup(deployrg.name) 129 | // params: { 130 | // vmssname: vmssName 131 | // vmCount: vmCount 132 | // platformFaultDomainCount: platformFaultDomainCount 133 | // zones: zones 134 | // subnetId: basenetwork.outputs.vnetSubnetArray[0].id 135 | // // appGwBackendPoolArray: appgw.outputs.appGwBackendPoolArray 136 | // // lbBackendPoolArray: slbBackendPools 137 | // } 138 | // } 139 | // // Enable autoscaling on the VMSS Flex 140 | // module autoscaler './compute/vmssautoscale.bicep' = { 141 | // name: 'autoscale' 142 | // scope: resourceGroup(deployrg.name) 143 | // params:{ 144 | // vmssId: vmss.outputs.vmssid 145 | // } 146 | //} 147 | 148 | //If you want to add a standalone VM to the scale set 149 | // module vm './flexvm-datadiskloop.bicep' = { 150 | // name: 'vm-flex' 151 | // scope: resourceGroup(deployrg.name) 152 | // params: { 153 | // vmName: vmName 154 | // vmssName: vmssName 155 | // vmSize: 'Standard_DS1_v2' 156 | // zone: myzone 157 | // subnetId: basenetwork.outputs.vnetSubnetArray[0].id 158 | // appGwBackendPoolArray: appGwBackendPools 159 | // lbBackendPoolArray: slbBackendPools 160 | // adminUsername: 'fisteele' 161 | // authenticationType: 'sshPublicKey' 162 | // adminPasswordOrKey: sshkey.outputs.publickey 163 | // // authenticationType: 'password' 164 | // // adminPasswordOrKey: guid(resourceGroup().id, deployment().name)d 165 | // } 166 | // } 167 | -------------------------------------------------------------------------------- /vmss-flex-n-tier-demo/network/appgw.bicep: -------------------------------------------------------------------------------- 1 | param appGwName string = 'myAppGw' 2 | param location string = resourceGroup().location 3 | param appGwSubnetId string 4 | 5 | var appGwSku = 'Standard_v2' 6 | 7 | var appGwPIPName = '${appGwName}-PIP' 8 | var appGwMinCapacty = 0 9 | var appGwMaxCapacity = 10 10 | 11 | resource appGwPIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = { 12 | name: appGwPIPName 13 | location: location 14 | sku:{ 15 | tier: 'Regional' 16 | name: 'Standard' 17 | } 18 | properties: { 19 | publicIPAddressVersion:'IPv4' 20 | publicIPAllocationMethod: 'Static' 21 | idleTimeoutInMinutes: 4 22 | } 23 | zones: [ 24 | '1' 25 | '2' 26 | '3' 27 | ] 28 | } 29 | resource appgw 'Microsoft.Network/applicationGateways@2020-08-01' = { 30 | name: appGwName 31 | location: location 32 | 33 | properties: { 34 | sku:{ 35 | name: appGwSku 36 | tier: appGwSku 37 | } 38 | gatewayIPConfigurations: [ 39 | { 40 | name: '${appGwName}IpConfig' 41 | properties:{ 42 | subnet:{ 43 | id: appGwSubnetId 44 | } 45 | } 46 | } 47 | ] 48 | frontendIPConfigurations: [ 49 | { 50 | name: '${appGwName}FrontendIp' 51 | properties: { 52 | privateIPAllocationMethod: 'Dynamic' 53 | publicIPAddress: { 54 | id: appGwPIP.id 55 | } 56 | } 57 | } 58 | ] 59 | frontendPorts: [ 60 | { 61 | name: 'port_80' 62 | properties: { 63 | port: 80 64 | } 65 | } 66 | ] 67 | backendAddressPools:[ 68 | { 69 | name: 'bepool01' 70 | properties:{ 71 | backendAddresses:[] 72 | 73 | } 74 | } 75 | 76 | ] 77 | backendHttpSettingsCollection: [ 78 | { 79 | name: 'httpsettings01' 80 | properties: { 81 | port: 80 82 | protocol: 'Http' 83 | cookieBasedAffinity: 'Disabled' 84 | requestTimeout: 20 85 | } 86 | } 87 | ] 88 | httpListeners:[ 89 | { 90 | name: '${appGwName}httplistener01' 91 | properties:{ 92 | frontendIPConfiguration:{ 93 | id: '${resourceId('Microsoft.Network/applicationGateways', appGwName)}/frontendIPConfigurations/${appGwName}FrontendIp' 94 | } 95 | frontendPort: { 96 | id: '${resourceId('Microsoft.Network/applicationGateways', appGwName)}/frontendPorts/port_80' 97 | } 98 | protocol: 'Http' 99 | } 100 | } 101 | 102 | ] 103 | requestRoutingRules: [ 104 | { 105 | name: 'routingRules01' 106 | properties: { 107 | ruleType: 'Basic' 108 | httpListener:{ 109 | id: '${resourceId('Microsoft.Network/applicationGateways', appGwName)}/httpListeners/${appGwName}httplistener01' 110 | } 111 | backendAddressPool: { 112 | id: '${resourceId('Microsoft.Network/applicationGateways', appGwName)}/backendAddressPools/bepool01' 113 | } 114 | backendHttpSettings: { 115 | id: '${resourceId('Microsoft.Network/applicationGateways', appGwName)}/backendHttpSettingsCollection/httpsettings01' 116 | } 117 | } 118 | } 119 | 120 | ] 121 | enableHttp2: false 122 | autoscaleConfiguration: { 123 | minCapacity: appGwMinCapacty 124 | maxCapacity: appGwMaxCapacity 125 | } 126 | } 127 | zones: [ 128 | '1' 129 | '2' 130 | '3' 131 | ] 132 | } 133 | output appGwId string = appgw.id 134 | output appGwBackendPoolArray array = appgw.properties.backendAddressPools 135 | output appGwPublicIPAddress string = appGwPIP.properties.ipAddress 136 | 137 | -------------------------------------------------------------------------------- /vmss-flex-n-tier-demo/network/basenetwork.bicep: -------------------------------------------------------------------------------- 1 | // Location for all resources. 2 | param location string = resourceGroup().location 3 | 4 | // Name of the VNET. 5 | param virtualNetworkName string = 'vNet' 6 | 7 | var addressPrefix = '10.0.0.0/16' 8 | var subnetList = [ 9 | { 10 | name: 'web' 11 | addr: '10.0.1.0/24' 12 | nsgId: nsg.id 13 | } 14 | { 15 | name: 'biz' 16 | addr: '10.0.2.0/24' 17 | nsgId: nsg.id 18 | } 19 | { 20 | name: 'data' 21 | addr: '10.0.3.0/24' 22 | nsgId: nsg.id 23 | } 24 | { 25 | name: 'mgmt' 26 | addr: '10.0.0.128/25' 27 | nsgId: nsg.id 28 | } 29 | { 30 | name: 'appgateway' 31 | addr: '10.0.4.0/25' 32 | nsgId: appgwnsg.id 33 | } 34 | ] 35 | 36 | var nsgName = '${virtualNetworkName}NSG' 37 | 38 | resource nsg 'Microsoft.Network/networkSecurityGroups@2020-06-01' = { 39 | name: nsgName 40 | location: location 41 | properties: { 42 | securityRules: [ 43 | { 44 | name: 'SSH' 45 | properties: { 46 | priority: 1000 47 | protocol: 'Tcp' 48 | access: 'Allow' 49 | direction: 'Inbound' 50 | sourceAddressPrefix: '*' 51 | sourcePortRange: '*' 52 | destinationAddressPrefix: '*' 53 | destinationPortRange: '22' 54 | } 55 | } 56 | { 57 | name: 'RDP' 58 | properties: { 59 | priority: 1050 60 | protocol: 'Tcp' 61 | access: 'Allow' 62 | direction: 'Inbound' 63 | sourceAddressPrefix: '*' 64 | sourcePortRange: '*' 65 | destinationAddressPrefix: '*' 66 | destinationPortRange: '3389' 67 | } 68 | } 69 | { 70 | name: 'HTTP' 71 | properties: { 72 | priority: 1100 73 | protocol: 'Tcp' 74 | access: 'Allow' 75 | direction: 'Inbound' 76 | sourceAddressPrefix: '*' 77 | sourcePortRange: '*' 78 | destinationAddressPrefix: '*' 79 | destinationPortRange: '80' 80 | } 81 | } 82 | { 83 | name: 'HTTPS' 84 | properties: { 85 | priority: 1200 86 | protocol: 'Tcp' 87 | access: 'Allow' 88 | direction: 'Inbound' 89 | sourceAddressPrefix: '*' 90 | sourcePortRange: '*' 91 | destinationAddressPrefix: '*' 92 | destinationPortRange: '443' 93 | } 94 | } 95 | ] 96 | } 97 | } 98 | 99 | resource appgwnsg 'Microsoft.Network/networkSecurityGroups@2020-06-01' = { 100 | name: 'appgwnsg' 101 | location: location 102 | properties: { 103 | securityRules: [ 104 | { 105 | name: 'allow-appgw-v2' 106 | properties: { 107 | priority: 1000 108 | protocol: '*' 109 | access: 'Allow' 110 | direction: 'Inbound' 111 | sourceAddressPrefix: '*' 112 | sourcePortRange: '*' 113 | destinationAddressPrefix: '*' 114 | destinationPortRange: '65200-65535' 115 | } 116 | } 117 | ] 118 | } 119 | } 120 | 121 | resource vnet 'Microsoft.Network/virtualNetworks@2020-06-01' = { 122 | name: virtualNetworkName 123 | location: location 124 | properties: { 125 | addressSpace: { 126 | addressPrefixes: [ 127 | addressPrefix 128 | ] 129 | } 130 | subnets:[for subnet in subnetList: { 131 | name: subnet.name 132 | properties: { 133 | addressPrefix: subnet.addr 134 | privateEndpointNetworkPolicies: 'Enabled' 135 | privateLinkServiceNetworkPolicies: 'Enabled' 136 | serviceEndpoints:[ 137 | { 138 | service: 'Microsoft.KeyVault' 139 | } 140 | ] 141 | networkSecurityGroup:{ 142 | id: subnet.nsgId 143 | } 144 | } 145 | }] 146 | } 147 | } 148 | 149 | output vnetId string = vnet.id 150 | output vnetSubnetArray array = vnet.properties.subnets 151 | 152 | -------------------------------------------------------------------------------- /vmss-flex-n-tier-demo/network/slb.bicep: -------------------------------------------------------------------------------- 1 | //LB name 2 | param slbName string = 'myLoadBalancer' 3 | 4 | 5 | var location = resourceGroup().location 6 | var slbPIPName = '${slbName}-PIP' 7 | 8 | resource slbPIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = { 9 | name: slbPIPName 10 | location: location 11 | sku:{ 12 | tier: 'Regional' 13 | name: 'Standard' 14 | } 15 | properties: { 16 | publicIPAddressVersion:'IPv4' 17 | publicIPAllocationMethod: 'Static' 18 | idleTimeoutInMinutes: 4 19 | } 20 | zones: [ 21 | '1' 22 | '2' 23 | '3' 24 | ] 25 | } 26 | 27 | resource slb 'Microsoft.Network/loadBalancers@2020-07-01' = { 28 | name: slbName 29 | location: location 30 | tags: {} 31 | sku: { 32 | name: 'Standard' 33 | tier: 'Regional' 34 | } 35 | properties: { 36 | frontendIPConfigurations: [ 37 | { 38 | properties: { 39 | privateIPAllocationMethod: 'Dynamic' 40 | privateIPAddressVersion: 'IPv4' 41 | publicIPAddress: { 42 | id: slbPIP.id 43 | } 44 | } 45 | name: '${slbName}FrontEndConfig' 46 | } 47 | ] 48 | backendAddressPools: [ 49 | { 50 | properties: {} 51 | name: 'bepool01' 52 | } 53 | { 54 | properties: {} 55 | name: 'bepool02' 56 | } 57 | ] 58 | loadBalancingRules: [ 59 | { 60 | properties: { 61 | frontendIPConfiguration: { 62 | id: '${resourceId('Microsoft.Network/loadBalancers', slbName)}/frontendIPConfigurations/${slbName}FrontEndConfig' 63 | } 64 | backendAddressPool: { 65 | id: '${resourceId('Microsoft.Network/loadBalancers', slbName)}/backendAddressPools/bepool01' 66 | } 67 | probe: { 68 | id: '${resourceId('Microsoft.Network/loadBalancers', slbName)}/probes/${slbName}-probe01' 69 | } 70 | protocol: 'Tcp' 71 | loadDistribution: 'Default' 72 | frontendPort: 80 73 | backendPort: 80 74 | idleTimeoutInMinutes: 4 75 | enableFloatingIP: false 76 | enableTcpReset: false 77 | disableOutboundSnat: false 78 | } 79 | name: 'string' 80 | } 81 | ] 82 | probes: [ 83 | { 84 | properties: { 85 | protocol: 'Tcp' 86 | port: 80 87 | intervalInSeconds: 5 88 | numberOfProbes: 2 89 | } 90 | name: '${slbName}-probe01' 91 | } 92 | ] 93 | } 94 | } 95 | output slbId string = slb.id 96 | output slbBackendPoolArray array = slb.properties.backendAddressPools 97 | output slbPublicIPAddress string = slbPIP.properties.ipAddress 98 | output slbProbe array = slb.properties.probes 99 | --------------------------------------------------------------------------------