├── .github
└── funding.yml
├── hub-01-bicep
└── bicepconfig.json
├── images
├── dns.png
├── ipsec.png
├── sftp.png
├── waf-fw.png
├── ipsec-aa.png
├── ipsec-dr.png
├── elastic-san.png
├── frontdoor.png
├── ipsec-bgp.png
├── architecture.png
├── aoai-deployment.png
├── app-gateway-01.png
├── hl-architecture.png
├── routing-with-bgp.png
├── architecture-hub-01.png
├── architecture-hub-02.png
├── any-to-any-routing-avnm.png
├── routing-without-bgp-fw.png
├── any-to-any-routing-gateway.png
├── aoai-deployment-apim-view.png
├── architecture-on-premises-2.png
├── architecture-on-premises.png
├── cross-on-premise-routing.png
├── north-south-inspection-01.png
├── north-south-inspection-02.png
├── any-to-any-routing-firewall.png
├── hub-and-spoke-arc-drawing(s).vsdx
├── name-resolution-with-dns-resolver.png
├── aoai-deployment-apim-configuration.png
├── outbound-traffic-internet-firewall.png
├── cross-on-premise-routing.drawio
├── routing-with-bgp.drawio
├── waf-fw.drawio
├── routing-without-bgp-fw.drawio
├── dns.drawio
└── elastic-san.drawio
├── CODE_OF_CONDUCT.md
├── scenarios
├── web.md
├── dns.md
├── vnet-to-vnet.md
├── dnat-01-02.md
├── vnet-to-vnet-2.md
├── spoke-01-inet.md
├── ipsec.md
├── logs.md
├── ping-any-to-any-avnm.md
├── ipsec-bgp.md
├── solution-north-south-inspection.md
├── name-resolution-with-azure-firewall.md
├── network-watcher.md
├── ipsec-active-active.md
├── ping-any-to-any-gateway.md
├── ipsec-multiple-vpn-device.md
├── sftp.md
├── routing-with-bgp.md
├── ipsec-dual-redundancy.md
├── cross-on-premise-routing.md
├── routing-without-bgp-fw.md
├── outbound-traffic-to-internet-firewall.md
├── elastic-san.md
├── ping-any-to-any-firewall.md
├── p2s-vpn-certificate.md
├── frontdoor.md
├── name-resolution-with-dns-private-resolver.md
├── publish-waf-fw.md
├── p2s-vpn-certificate-always-on.md
├── app-gateway-01.md
└── apim-stv1.md
├── .gitignore
├── LICENSE
├── CONTRIBUTING
├── s2s-vpn-bicep
├── connect-on-prem.bicep
├── connect-gateways-bgp-to-be-verified.bicep
└── connect-on-prem.json
├── any-to-any-bicep
├── fw-policy.bicep
└── any-to-any.bicep
├── on-prem-bicep
├── on-prem.bicep
└── on-prem.json
└── on-prem-2-bicep
└── on-prem-2.bicep
/.github/funding.yml:
--------------------------------------------------------------------------------
1 | buy_me_a_coffee: nicolcoffee
2 |
--------------------------------------------------------------------------------
/hub-01-bicep/bicepconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "experimentalFeaturesEnabled": {
3 | }
4 | }
--------------------------------------------------------------------------------
/images/dns.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/dns.png
--------------------------------------------------------------------------------
/images/ipsec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/ipsec.png
--------------------------------------------------------------------------------
/images/sftp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/sftp.png
--------------------------------------------------------------------------------
/images/waf-fw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/waf-fw.png
--------------------------------------------------------------------------------
/images/ipsec-aa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/ipsec-aa.png
--------------------------------------------------------------------------------
/images/ipsec-dr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/ipsec-dr.png
--------------------------------------------------------------------------------
/images/elastic-san.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/elastic-san.png
--------------------------------------------------------------------------------
/images/frontdoor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/frontdoor.png
--------------------------------------------------------------------------------
/images/ipsec-bgp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/ipsec-bgp.png
--------------------------------------------------------------------------------
/images/architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/architecture.png
--------------------------------------------------------------------------------
/images/aoai-deployment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/aoai-deployment.png
--------------------------------------------------------------------------------
/images/app-gateway-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/app-gateway-01.png
--------------------------------------------------------------------------------
/images/hl-architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/hl-architecture.png
--------------------------------------------------------------------------------
/images/routing-with-bgp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/routing-with-bgp.png
--------------------------------------------------------------------------------
/images/architecture-hub-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/architecture-hub-01.png
--------------------------------------------------------------------------------
/images/architecture-hub-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/architecture-hub-02.png
--------------------------------------------------------------------------------
/images/any-to-any-routing-avnm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/any-to-any-routing-avnm.png
--------------------------------------------------------------------------------
/images/routing-without-bgp-fw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/routing-without-bgp-fw.png
--------------------------------------------------------------------------------
/images/any-to-any-routing-gateway.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/any-to-any-routing-gateway.png
--------------------------------------------------------------------------------
/images/aoai-deployment-apim-view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/aoai-deployment-apim-view.png
--------------------------------------------------------------------------------
/images/architecture-on-premises-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/architecture-on-premises-2.png
--------------------------------------------------------------------------------
/images/architecture-on-premises.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/architecture-on-premises.png
--------------------------------------------------------------------------------
/images/cross-on-premise-routing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/cross-on-premise-routing.png
--------------------------------------------------------------------------------
/images/north-south-inspection-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/north-south-inspection-01.png
--------------------------------------------------------------------------------
/images/north-south-inspection-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/north-south-inspection-02.png
--------------------------------------------------------------------------------
/images/any-to-any-routing-firewall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/any-to-any-routing-firewall.png
--------------------------------------------------------------------------------
/images/hub-and-spoke-arc-drawing(s).vsdx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/hub-and-spoke-arc-drawing(s).vsdx
--------------------------------------------------------------------------------
/images/name-resolution-with-dns-resolver.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/name-resolution-with-dns-resolver.png
--------------------------------------------------------------------------------
/images/aoai-deployment-apim-configuration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/aoai-deployment-apim-configuration.png
--------------------------------------------------------------------------------
/images/outbound-traffic-internet-firewall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/outbound-traffic-internet-firewall.png
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
2 |
3 | Resources:
4 |
5 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
6 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
7 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
--------------------------------------------------------------------------------
/scenarios/web.md:
--------------------------------------------------------------------------------
1 | # SOLUTION:
2 |
3 | # Install a web server on spoke-03-vm
4 |
5 | ```
6 | sudo apt-get update
7 | sudo apt-get upgrade
8 | sudo apt install nginx -y
9 |
10 | sudo rm /usr/share/nginx/html/index.html
11 | sudo git clone https://github.com/nicolgit/html-resume /usr/share/nginx/html
12 | ```
13 |
14 |
15 | to check web server status
16 |
17 | ```
18 | systemctl status nginx.service
19 | ```
20 |
21 | ## web test pages
22 |
23 | * http://**web-server-url**/
24 | * http://**web-server-url**/resume/resume.html
25 |
26 | or from the machine itself
27 |
28 | ```
29 | wget spoke-03-vm -O /dev/stdout
30 | ```
31 | cd
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Local .terraform directories
2 | **/.terraform/*
3 |
4 | # .tfstate files
5 | *.tfstate
6 | *.tfstate.*
7 |
8 | # temp visio files
9 | *.~vsdx
10 |
11 | # Crash log files
12 | crash.log
13 |
14 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most
15 | # .tfvars files are managed as part of configuration and so should be included in
16 | # version control.
17 | #
18 | # example.tfvars
19 |
20 | # Ignore override files as they are usually used to override resources locally and so
21 | # are not checked in
22 | override.tf
23 | override.tf.json
24 | *_override.tf
25 | *_override.tf.json
26 |
27 | # Include override files you do wish to add to version control using negated pattern
28 | #
29 | # !example_override.tf
30 |
31 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
32 | # example: *tfplan*
33 |
34 | *.bkp
35 | *.dtmp
36 | *.vscode
37 |
38 | /.vs
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 NicolD
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 |
--------------------------------------------------------------------------------
/scenarios/dns.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: configure a DNS on the cloud, so that all machines are reachable via FQDN
2 |
3 | ## Pre-requisites
4 |
5 | In order to apply this solution you have to deploy hub playground only.
6 |
7 | ## Solution
8 |
9 | From Azure Portal create a Private DNS zone named `cloudasset.interal`, with the following Network links:
10 |
11 | | Name | Network name |
12 | |---|---|
13 | |hub | hub-lab-net |
14 | |spoke01 | spoke-01 |
15 | |spoke02 | spoke-02 |
16 | |spoke03 | spoke-03 |
17 |
18 | Enable `auto-registration` on all network. The result is:
19 |
20 | | Link Name | Link status | Virtual Network | Auto-registration |
21 | |---|---|---|---|
22 | | hub | Completed | hub-lab-net | Enabled |
23 | | spoke01 | Completed | spoke-01 | Enabled |
24 | | spoke02 | Completed | spoke-02 | Enabled |
25 | | spoke03 | Completed | spoke-03 | Enabled |
26 |
27 | in order to have an additiona ALIAS for `spoke-01-vm` add the following record:
28 |
29 | Name: vm01.spoke01[.cloudasset.internal]
30 | Type: A
31 | IP: `10.13.1.4`
32 |
33 | ## Test solution
34 | Connect via RDP from hub-vm-01 machine to spoke-01 VM using the following Names:
35 | * spoke-01-vm.cloudasset.internal
36 | * vm01.spoke01.cloudasset.intenal
--------------------------------------------------------------------------------
/scenarios/vnet-to-vnet.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: connect on-prem and hub with a VNet-to-VNet connection
2 |
3 | ## Pre-requisites
4 | In order to apply this solution you have to deploy `hub` and `on-premise` playgrounds.
5 |
6 | ## Solution
7 | in order to make this connection, you have to create 2 connections one from on-prem to cloud and another from cloud to onprem
8 |
9 | ### Step 1: connection onprem-to-cloud
10 | Open `on-prem-gateway`, go to Connections and add the following object
11 | * Connection Name: onprem-to-cloud
12 | * Type: VNet-to-VNet
13 | * First virtual Network Gateway: `on-prem-gateway`
14 | * Second virtual Network Gateway: `lab-gateway`
15 | * Shared Key: `password.123`
16 | * IKE: IKEv2
17 |
18 | ### Step 2: connection cloud-to-onprem
19 | Open `lab-gateway`, go to Connections and add the following object
20 | * Connection Name: cloud-to-onprem
21 | * Type: VNet-to-VNet
22 | * First virtual Network Gateway: `lab-gateway`
23 | * Second virtual Network Gateway: `on-prem-gateway`
24 | * Shared Key: `password.123`
25 | * IKE: IKEv2
26 |
27 | after a couple of minutes you will have the following connections:
28 |
29 | | Name | Status | Connection Type | Peer |
30 | |---|---|---|---|
31 | |cloud-to-onprem | connected |VNet-toVNet| lab-gateway |
32 | |onprem-to-cloud | connected |VNet-toVNet| lab-gateway |
33 |
34 | ## Test solution
35 | Via bastion go to W10onprem (192.168.1.4) and from there open RDP to hub-vm-01 (10.12.1.4).
36 | Ddthe same in the opposite direction
--------------------------------------------------------------------------------
/scenarios/dnat-01-02.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: DNAT - expose on public IP, via RDP (port 3389) machines spoke-01-vm and spoke-02-vm
2 |
3 | ## Pre-requisites
4 |
5 | In order to apply this solution you have to deploy hub playground only.
6 |
7 | ## Solution
8 | When you install Azure Firewall, it has only one public IP. In order to access via Remote Virtual Desktop, on the same port 2 machines, as first step an additional public IP is required.
9 |
10 | So, from Azure Portal:
11 | * create a new public IP.
12 | * in `lab-firewall` select Public IP Configuration
13 | * add a public IP configuration
14 | * take note of the 2 public IPs
15 |
16 | | Name | Public IP |
17 | |--------|-----------|
18 | |IpConf | `x.x.x.x` |
19 | |IpConf2 | `y.y.y.y` |
20 |
21 | Create the following Firewall Policy: `hub-fw-policy`
22 |
23 | **DNAT rules**
24 |
25 | |priority | collection name | rule name | src | port | protocol | destination | translated address| translated port | action |
26 | |---|---|---|---|---|---|---|---|---|---|
27 | |1000 | coll01 |rdp-spk-01 | * | 3389 | TCP | `x.x.x.x` | 10.13.1.4 | 3389 | Dnat |
28 | |1000 | coll01 |rdp-spk-02 | * | 3389 | TCP | `y.y.y.y` | 10.13.2.4 | 3389 | Dnat |
29 |
30 | Associate the policy `hub-fw-policy` to `lab-firewall` via Firewall Manager
31 |
32 | ## Test Solution
33 |
34 | Test connection using RRP client from a machine on internet to `x.x.x.x` and `y.y.y.y`.
35 |
--------------------------------------------------------------------------------
/scenarios/vnet-to-vnet-2.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: connect on-prem-2 and hub with a VNet-to-VNet connection
2 |
3 | ## Pre-requisites
4 | In order to apply this solution you have to deploy `hub` and `on-premise-2` playgrounds.
5 |
6 | ## Solution
7 | In order to make this connection, you have to create 2 connections one from on-prem to cloud and another from cloud to onprem
8 |
9 | ### Step 1: connection onprem-to-cloud
10 | Open `on-prem-2-gateway`, go to Connections and add the following object
11 | * Connection Name: `onprem2-to-cloud`
12 | * Type: VNet-to-VNet
13 | * First virtual Network Gateway: `on-prem-2-gateway`
14 | * Second virtual Network Gateway: `lab-gateway`
15 | * Shared Key: `password.123`
16 | * IKE: IKEv2
17 |
18 | ### Step 2: connection cloud-to-onprem
19 | Open `lab-gateway`, go to Connections and add the following object
20 | * Connection Name: `cloud-to-onprem2`
21 | * Type: VNet-to-VNet
22 | * First virtual Network Gateway: `lab-gateway`
23 | * Second virtual Network Gateway: `on-prem-gateway`
24 | * Shared Key: `password.123`
25 | * IKE: IKEv2
26 |
27 | after a couple of minutes you will have the following connections:
28 |
29 | | Name | Status | Connection Type | Peer |
30 | |---|---|---|---|
31 | |cloud-to-onprem2 | connected |VNet-toVNet| lab-gateway |
32 | |onprem2-to-cloud | connected |VNet-toVNet| lab-gateway |
33 |
34 | ## Test solution
35 | Via bastion go to W10onprem (192.168.1.4) and from there open RDP to hub-vm-01 (10.12.1.4).
36 | Ddthe same in the opposite direction
--------------------------------------------------------------------------------
/CONTRIBUTING:
--------------------------------------------------------------------------------
1 | # Contributing to Transcriptase
2 | We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
3 |
4 | - Reporting a bug
5 | - Discussing the current state of the code
6 | - Submitting a fix
7 | - Proposing new features
8 | - Becoming a maintainer
9 |
10 | ## We Develop with Github
11 | We use github to host code, to track issues and feature requests, as well as accept pull requests.
12 |
13 | ## All Code Changes Happen Through Pull Requests
14 | Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://docs.github.com/en/get-started/quickstart/github-flow). We actively welcome your pull requests:
15 |
16 | 1. Fork the repo and create your branch from `master`.
17 | 2. update where needed.
18 | 3. Issue that pull request!
19 |
20 | ## Any contributions you make will be under the MIT Software License
21 | In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern.
22 |
23 | ## Report bugs using Github's issues
24 | We use GitHub issues to track public bugs. Report a bug by opening a new issue; it's that easy!
25 |
26 | ## Write bug reports with detail, background, and sample code
27 | **Great Bug Reports** tend to have:
28 |
29 | - A quick summary and/or background
30 | - Steps to reproduce
31 | - Be specific!
32 | - What you expected would happen
33 | - What actually happens
34 | - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
35 |
36 | ## License
37 | By contributing, you agree that your contributions will be licensed under its MIT License.
38 |
--------------------------------------------------------------------------------
/s2s-vpn-bicep/connect-on-prem.bicep:
--------------------------------------------------------------------------------
1 | @description('Select the on-premises location to connect with the hub')
2 | @allowed([ 'on-prem-gateway', 'on-prem-2-gateway' ])
3 | param gatewayOnPremName string = 'on-prem-gateway'
4 | param gatewayOnPremLocation string = 'francecentral'
5 | param gatewayOnPremResourceGroup string = 'hub-and-spoke-playground'
6 |
7 | param hubGatewayName string = 'lab-gateway'
8 | param hubGatewayResourceGroup string = 'hub-and-spoke-playground'
9 | param hubGatewayLocation string = 'westeurope'
10 |
11 | @secure()
12 | param psk string = 'pAsSwOrD.123'
13 | param enableBgp bool = false
14 |
15 | resource vnetGateway1 'Microsoft.Network/virtualNetworkGateways@2019-09-01' existing = {
16 | name: gatewayOnPremName
17 | scope: resourceGroup(gatewayOnPremResourceGroup)
18 | }
19 |
20 | resource vnetGateway2 'Microsoft.Network/virtualNetworkGateways@2019-09-01' existing = {
21 | name: hubGatewayName
22 | scope: resourceGroup(hubGatewayResourceGroup)
23 | }
24 |
25 | resource connection1 'Microsoft.Network/connections@2022-07-01' = {
26 | name: '${gatewayOnPremName}-to-cloud'
27 | location: gatewayOnPremLocation
28 | properties: {
29 | connectionType: 'Vnet2Vnet'
30 | virtualNetworkGateway1: {
31 | id: vnetGateway1.id
32 | properties: {}
33 | }
34 | virtualNetworkGateway2: {
35 | id: vnetGateway2.id
36 | properties: {}
37 | }
38 | sharedKey: psk
39 | connectionProtocol: 'IKEv2'
40 | enableBgp: enableBgp
41 | }
42 | }
43 |
44 | resource connection2 'Microsoft.Network/connections@2022-07-01' = {
45 | name: 'cloud-to-${gatewayOnPremName}'
46 | location: hubGatewayLocation
47 | properties: {
48 | connectionType: 'Vnet2Vnet'
49 | virtualNetworkGateway1: {
50 | id: vnetGateway2.id
51 | properties: {}
52 | }
53 | virtualNetworkGateway2: {
54 | id: vnetGateway1.id
55 | properties: {}
56 | }
57 | sharedKey: psk
58 | connectionProtocol: 'IKEv2'
59 | enableBgp: enableBgp
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/scenarios/spoke-01-inet.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: filter internet traffic and spokes connectivity
2 |
3 | ## Pre-requisites
4 |
5 | In order to apply this solution you have to deploy hub playground only.
6 |
7 | ## Solution
8 | This configuration allows `spoke-01` to:
9 | * communicate with `spoke-02`
10 | * allow HTTP/S internet traffic avoiding access to `*.google.com` and `*.microsoft.com`
11 |
12 |
13 | Create the following route tables in `west europe`:
14 |
15 | Route: `route-01`
16 |
17 | | Name | Address Prefix | Next hop type | Next hop IP addr |
18 | |---|---|---|---|
19 | | to-all | 0.0.0.0/0 | Virtual Appliance | 10.12.3.4 |
20 |
21 | | subnet Name | Virtual Network |
22 | |---|---|
23 | | default | spoke-01 |
24 |
25 | Route: `route-02`
26 |
27 | | Name | Address Prefix | Next hop type | Next hop IP addr |
28 | |---|---|---|---|
29 | | to-spoke-02 | 10.13.1.0/24 | Virtual Appliance | 10.12.3.4 |
30 |
31 | | subnet Name | Virtual Network |
32 | |---|---|
33 | | default | spoke-02 |
34 |
35 |
36 | Create the following `IP Groups` in `west europe`:
37 | * `group-spoke-01`: 10.13.1.0/24
38 | * `group-spoke-02`: 10.13.2.0/24
39 |
40 | Create the following Firewall Policy: `internet-policy`
41 |
42 | **Network Rules**:
43 |
44 | | priority | collection name | rule name | source | port | protocol | destination | Action |
45 | |---|---|---|---|---|---|---|---|
46 | | 1000 | net-coll-01| to-spoke-02 | group-spoke-01 | * | Any | group-spoke-02 | Allow |
47 | | 1000 | net-coll-01| to-spoke-01 | group-spoke-02 | * | Any | group-spoke-01 | Allow |
48 |
49 | **Application Rules**:
50 |
51 | | priority | collection name | rule name | source | protocol | destination| action |
52 | |---|---|---|---|---|---|---|
53 | |1000|filtered-collection| no-goog-msft| * | HTTP,HTTPS | *.google.com,
*.microsoft.com | Deny |
54 | |2000|allow-inet-coll| allow-inet| * | HTTP,HTTPS | * | Allow |
55 |
56 | Associate the policy `hub-fw-policy` to `lab-firewall` via Firewall Manager.
57 |
58 | ## Test solution
59 | * Verify mutual reachability via remote desktop client
60 |
61 | * Open browser and verify that google.com and microsoft.com are not available.
--------------------------------------------------------------------------------
/images/cross-on-premise-routing.drawio:
--------------------------------------------------------------------------------
1 | 7Vpdb6M4FP01eSzCX0Aem3ba3dXOqFKlncfKCS6xBnDWmEk6v36vA04Ak7aZJp2RtnkJvtc2cM+5x9dOJuSq2Nxqvlp+VqnIJzhMNxNyPcEYoRDDl7U8tZYQs8aSaZm2tr3hXv4QrmNrrWUqql5Ho1Ru5KpvXKiyFAvTs3Gt1brf7VHl/buueCY8w/2C5771q0zNsrEmON7b/xAyW7o7o2jaeAruOrdvUi15qtYdE/k0IVdaKdNcFZsrkdvoubg0424OeHcPpkVpXjPgdnZ5w/5iOImJ+co/b/LrfxcX7SzfeV63L7zIVZ1OcJTDpLO5hqvMXi3r+UXO577jGYszoDBAOAgDeL4bFPkDrJ8EaOvH9JAfv+CnAz805vtH2iJgnhysEBpgEDRmAMvKGpv3JrP1Uhpxv+ILa1wDq+3LmyKHFtrN1I26C6HQRmw6phaFW6EKYfQTdGm9JGwZ0eYEWJr2ek8wzNo+yw65sBvIW1Jnu7n3uMNFC/0RNKAeDSCmOOxgdqr42TBJSK/LXGYl2ObKGFXYeWCILLNZ277GCRgfVWnu27uOEP7o0Cf9yEcYByTyg49Ggo/icwWfecH3M2iKAxQlXg6dh+OvxoiQ02QEo9GrcMHvigv2cFHlxUqLwlegG81LCOYQgZWSpdk+FptN2DV05NWqWaUe5Uak/bDzNtwLCKTQDWzNihFCI+XV0o7YNmSxXbbc97UsMnjFXAIZbjjALtIHI3JRycoypJZ5CrDZ688ilXXxMKsrWYqqgovGF1Tfs9NgOUgxxixzPSgZDSIfyxiEfNr9nAlZcgjZC+xjeyt0wcunD3C3AA3QpeFvBm10ENqMG7HmTz7Al/dfJuQyYgwR3zm7vbNBE4AZDv+8g47w3SyOUJKAdEe8sPJZzqvVmAir2uSAxtWuPPXgdmr7N5+L/E5V0kjVU92hHBu1GiNUh2xO74tNZsvygK8rEizqCuYT+sEFApZXmedXKld6+6zkhiWMULDDqFTCvM5XqlKchkA7tXZKT3wCkbHSJw4ieibKxB5loNB9iS4wwDJmpBo9xBgogSkwhnoUeU42RkRmiLuTi22t1OyfED4kHz9qLWy4SmHWSn8DcYDGP1KbmucPXxrjw23z6tXpdIPiPuwkGYM9QNRHnp5reZ8eXgReoxUjK8U48m3pRkax/5CHHk8iOizQ4yDBPlVQQHymEHwmpiCfKh6QmVa1jbtWhrcIJdOjgrI7w+BzN2n4bLBYCNuBkCaMxUmM43jaL6JpPO7uxBEx4CULcZJQICel4UhUERRw3T7nivHYLmi4oSnTS3uks93J8KqSiz6vAYIy3SXOgL2PjyJa2AGV0eqb6HjSeDoP7YjcpthM6VToF6n9LFFE2jtU8hHslksjq52zaZEDmb73j6LGQt/e4c6uFp3Ftp9MA+gqVeuFaId0z42GszDcmwbRwYMYrjNhvIm2NNi98xuY4Zd0IGAacMbhN/HUqKy3Y14BP2AxS2HZJa/aLUMymoFM9ojSMqHLqtbkSe1QkQuZpvY2o/vuPmk78kHp8cQ7YpvWp8bU14ZohJjkXIsx8ouwj/R/a/o7sd4lbvJzCjCY5p3TP/lI/9On/8VgbYj9I+j3FQC3QXi5xjpfQYVfKKgiuwntuNlJCirX5/Sa+oqQ/vaa6njxm2gqC1gMu36KEY6BL+6Xm53C0gB1/ewnSy46vE+fiP2buMPz9xJk/zfD/4UgX1B2PG2PUWQMW92OuiR9btlz9DHUf13J5p+if8jLW0s2QPmZxEcHOHC0vgzvMpAxFj4rY+cWGP/X6A+BebvAINQH1W25XAU4jvkZ5AWa+3+9NKTZ/3mIfPoP
--------------------------------------------------------------------------------
/s2s-vpn-bicep/connect-gateways-bgp-to-be-verified.bicep:
--------------------------------------------------------------------------------
1 | param vnetGateway1Name string = 'on-prem-gateway'
2 | param vnetGateway1Rg string = 'rg-playground-onprem'
3 | param vnetGateway2Name string = 'lab-gateway'
4 | param vnetGateway2Rg string = 'rg-playground-hub'
5 | param gateway1Location string = 'francecentral'
6 | param gateway2Location string = 'westeurope'
7 | @secure()
8 | param psk string = 'password.123'
9 |
10 | resource vnetGateway1 'Microsoft.Network/virtualNetworkGateways@2019-09-01' existing = {
11 | name: vnetGateway1Name
12 | scope: resourceGroup(vnetGateway1Rg)
13 | }
14 |
15 | resource vnetGateway2 'Microsoft.Network/virtualNetworkGateways@2019-09-01' existing = {
16 | name: vnetGateway2Name
17 | scope: resourceGroup(vnetGateway2Rg)
18 | }
19 |
20 | // represents azure networks
21 | resource localGateway1 'Microsoft.Network/localNetworkGateways@2022-09-01' existing = {
22 | name: 'lab-local-gateway'
23 | scope: resourceGroup(vnetGateway1Rg)
24 | }
25 |
26 | // represents onprem networks
27 | resource localGateway2 'Microsoft.Network/localNetworkGateways@2022-09-01' existing = {
28 | name: 'lab-local-gateway'
29 | scope: resourceGroup(vnetGateway2Rg)
30 | }
31 |
32 | resource connection1 'Microsoft.Network/connections@2022-07-01' = {
33 | name: 'onprem-to-cloud'
34 | location: gateway1Location
35 | properties: {
36 | connectionType: 'IPsec'
37 | virtualNetworkGateway1: {
38 | id: vnetGateway1.id
39 | properties: {}
40 | }
41 | localNetworkGateway2: {
42 | id: localGateway1.id
43 | properties: {}
44 | }
45 | sharedKey: psk
46 | connectionProtocol: 'IKEv2'
47 | enableBgp: true
48 | }
49 | }
50 |
51 | resource connection2 'Microsoft.Network/connections@2022-07-01' = {
52 | name: 'cloud-to-onprem'
53 | location: gateway2Location
54 | properties: {
55 | connectionType: 'IPsec'
56 | virtualNetworkGateway1: {
57 | id: vnetGateway2.id
58 | properties: {}
59 | }
60 | localNetworkGateway2: {
61 | id: localGateway2.id
62 | properties: {}
63 | }
64 | sharedKey: psk
65 | connectionProtocol: 'IKEv2'
66 | enableBgp: true
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/scenarios/ipsec.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: connect on-prem and hub with a Site-to-Site IPSec connection
2 |
3 | In this solution I show how to configure an IPSec tunnel between an on-premises (simulated on Azure) site and a hub and spoke, using static routing without BGP protocol.
4 |
5 | Using static routing instead of BGP in this IPSec connection provides several advantages in certain scenarios like:
6 |
7 | - **Simplicity**: Static routing is easier to understand and configure, making it ideal for simple network topologies
8 | - **Predictability**: Routes are explicitly defined and don't change automatically, providing complete control over traffic flow
9 | - **Lower Resource Usage**: No BGP protocol overhead, resulting in slightly lower CPU and memory usage on network devices
10 | - **Better for Small Networks**: Ideal for environments with few subnets and stable network topology
11 |
12 | ## Pre-requisites
13 |
14 | In order to apply this solution you have to deploy `hub` and `on-premises` playgrounds.
15 |
16 | ## Solution
17 |
18 | In order to make this connection, you have to create 2 connections: one from on-prem to cloud and another from cloud to on-prem.
19 |
20 | 
21 |
22 | # Create Local Network Gateways
23 | Create the following gateways:
24 |
25 | | Name | IP Address | Address Space | Region |
26 | |---|---|---|---|
27 | |`cloud-net` | `lab-gateway-ip` (hub gateway public ip) | 10.0.0.0/8| West Europe |
28 | |`onprem-net`| `onprem-gateway-virtualip` (onprem gateway public ip) | 192.168.0.0/16 | France Central |
29 |
30 | # Connection onprem-to-cloud
31 | Open `on-prem-gateway`, go to Connections and add the following object:
32 | * Connection Name: `onprem-to-cloud`
33 | * Type: Site-to-Site (IPsec)
34 | * Virtual Network Gateway: `on-prem-gateway`
35 | * Local Network Gateway: `cloud-net`
36 | * Shared Key: `password.123`
37 | * IKE: IKEv2
38 |
39 |
40 | # Connection cloud-to-onprem
41 | Open `lab-gateway`, go to Connections and add the following object:
42 | * Connection Name: `cloud-to-onprem`
43 | * Type: Site-to-Site (IPsec)
44 | * Virtual Network Gateway: `lab-gateway`
45 | * Local Network Gateway: `onprem-net`
46 | * Shared Key: `password.123`
47 | * IKE: IKEv2
48 |
49 | After **few minutes**, you will see, on `on-prem-gateway` connections:
50 |
51 | | Name | Status | Connection Type | Peer |
52 | |---|---|---|---|
53 | |onprem-to-cloud | connected |Site-to-Site (IPsec)| cloud-net|
54 |
55 | and on `lab-gateway` connections:
56 |
57 | | Name | Status | Connection Type | Peer |
58 | |---|---|---|---|
59 | |cloud-to-onprem | connected |Site-to-Site (IPsec)| onprem-net |
60 |
61 | ## Test solution
62 | Via Bastion go to `W11-onprem` (`192.168.1.4`) and open an RDP connection to hub-vm-01 (`10.12.1.4`).
63 |
64 | Do the same test also in the opposite direction.
--------------------------------------------------------------------------------
/scenarios/logs.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: troubleshooting firewall rules with log Analytics
2 |
3 | ## Pre-requisites
4 |
5 | In order to apply this solution you have to deploy hub playground only.
6 |
7 | ## Solution
8 | First step is to create a Log Analytics Workspace named `playground-workspace` in `west-europe`.
9 | In order to analyze firewall logs go to `lab-firewall` -> Diagnostic Settings -> Add Diagnostic Settings:
10 | * Name: firewall-diagnostics
11 | * Logs: Category Group -> All logs
12 | * Metrics: All Metrics
13 | * Destination details: Send to Log Analytics Workspace -> `playground-workspace`
14 | * SAVE
15 |
16 | Prepare routing and firewall rules as described in [ping-any-to-any-firewall](./ping-any-to-any-firewall.md): when all these steps will be completed each machine will be able to communicate with any other via Firewall.
17 |
18 | ## Test solution
19 | Connect via RDP from `spoke-01-vm` to `spoke-02-vm` (`10.13.2.4`).
20 | After that the connection is estabilished, go to `lab-firewall` -> Logs -> click on `Run` button in **Network Rule Log Data** Box.
21 |
22 | In the Result pane, the first row should be something like:
23 |
24 | | Time | msg | Protocol | SourceIP | srcport| TargetIp | target port | Action |
25 | |---|---|----|---|---|---|---|---|
26 | |...|...|TCP|10.13.1.4|xxx|10.13.2.4|3389|**Allow**|
27 |
28 | > Log ingestion from Azure Firewall can require from 60 seconds to 15 minutes, so if nothing is displayed, wait some minute and try again.
29 |
30 | now to test the **deny** logging, change the firewall network policy as follow:
31 |
32 | | rule name | source | port | protocol | destination |
33 | |---|---|---|---|---|
34 | | all-to-all | group-spoke-01
group-spoke-03 | * | Any | group-spoke-01
group-spoke-02
group-spoke-03 |
35 |
36 | go back to `spoke-01-vm` and repeat the RDP connection. The connection will be **allowed**. This is **correct** because we have denied the traffic that starts from spoke-02 to any other spokes, but because our connection begins from spoke-01 it will be still possible.
37 |
38 | Now change the firewall network policy again as follow:
39 |
40 | | rule name | source | port | protocol | destination |
41 | |---|---|---|---|---|
42 | | all-to-all | group-spoke-02
group-spoke-03 | * | Any | group-spoke-01
group-spoke-02
group-spoke-03 |
43 |
44 | go back to `spoke-01-vm` and repeat the RDP connection. The connection will be **denied**. Infact now we have denied all the connections that begin from spoke-01.
45 |
46 | Now go to `lab-firewall` -> Logs -> click on `Run` button in **Network Rule Log Data** Box.
47 |
48 | In the Result pane, the first row should be something like:
49 |
50 | | Time | msg | Protocol | SourceIP | srcport| TargetIp | target port | Action |
51 | |---|---|----|---|---|---|---|---|
52 | |...|...|TCP|10.13.1.4|xxx|10.13.2.4|3389|**Deny**|
53 |
--------------------------------------------------------------------------------
/scenarios/ping-any-to-any-avnm.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: Allows machines in ANY spoke to communicate with ANY machine in ANY other spoke (Azure Virtual Network Manager)
2 |
3 | Azure Virtual Network Manager is a management service that enables you to group, configure, deploy, and manage virtual networks globally across subscriptions. With Virtual Network Manager, you can define network groups to identify and logically segment your virtual networks. Then you can determine the connectivity and security configurations you want and apply them across all the selected virtual networks in network groups at once.
4 |
5 | In the Hub and Spoke model, the **routing between different Spokes is a significant element**. Each spoke often identifies an application or a workload, and these enterprise elements have to interact with each other. In this scenario, Azure Virtual Network Manager (AVNM) can simplify and automate the deployment of connectivity between them.
6 |
7 | In this solution, I show you how configure the routing between spokes using the **Azure Vitual Network Manager**.
8 |
9 | The resulting overall architecture is shown in the following schema.
10 |
11 | 
12 |
13 | _Download a [draw.io file](../images/any-to-any-routing.drawio) of this architecture._
14 |
15 | ## Pre-requisites
16 |
17 | In order to apply this solution you have to deploy hub playground only.
18 |
19 | ## Solution
20 |
21 | Go to Azure Portal > Virtual Network Manager > Create
22 |
23 | Basics
24 | * Region: `westeurope`
25 | * Name `avnm-lab`
26 | * Features: Connectivity
27 |
28 | Management Scopes
29 |
30 | * the subscription where you deployed the hub playground
31 |
32 | Click Create.
33 |
34 | Once the service is deployed, we can deploy the connectivity between all spokes:
35 |
36 | Go to Azure Portal > Virtual Networks Manager > `avnm-lab` > Network groups > Create
37 | * Name `all-spokes`
38 | * click Create
39 |
40 | Once created go to `avnm-lab` > Network Groups > `all-spokes` > Add Virtual Networks:
41 | * `spoke-01`
42 | * `spoke-02`
43 | * `spoke-03`
44 | * click add
45 |
46 | go to `avnm-lab` > Configurations > Create connectivity configuration:
47 |
48 | * Name: `hub-conn-configuration`
49 | * Topology: `Mesh`
50 | * Mesh connectivity between region: `Enable`
51 | * Network groups: `all-spokes`
52 | * click Create
53 |
54 | go to `avnm-lab` > Deployments > Deploy Configuration > Deploy Connectivity configuration:
55 | * Connectivity Configuration: `hub-conn-configuration`
56 | * Target Regions: `westeurope` and `northeurope`
57 | * click Deploy
58 |
59 | ## Test Solution
60 | Test connections using remote desktop client and ssh from one machine to another.
61 |
62 | ## More Information
63 | * Azure [Virtual Network Manager](https://learn.microsoft.com/en-us/azure/virtual-network-manager/overview)
--------------------------------------------------------------------------------
/images/routing-with-bgp.drawio:
--------------------------------------------------------------------------------
1 | 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
--------------------------------------------------------------------------------
/scenarios/ipsec-bgp.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: connect on-prem and hub with a Site-to-Site IPSec connection with BGP
2 |
3 | In this solution I show how to configure an IPSec tunnel between an on-premises (simulated on Azure) site and a hub and spoke, using BGP protocol.
4 |
5 | Using BGP (Border Gateway Protocol) in this IPSec connection provides several advantages like:
6 |
7 | - **Dynamic Routing**: BGP automatically exchanges routing information between on-premises and Azure, eliminating the need to manually configure static routes
8 | - **Simplified Management**: Reduces administrative overhead by automating route management and updates
9 |
10 | ## Pre-requisites
11 |
12 | In order to apply this solution you have to deploy `hub` and `on-premises` playgrounds.
13 |
14 | ## Solution
15 |
16 | In order to make this connection, you have to create 2 connections: one from on-prem to cloud and another from cloud to on-prem.
17 |
18 | 
19 |
20 | # Enable BGP on virtual network gateways
21 | Go to Azure Portal > Virtual network gateways > VPN Gateways > `lab-gateway` > Configuration > Configure BGP
22 | * ASN: `65050`
23 | * click SAVE
24 |
25 | Go to Azure Portal > Virtual network gateways > VPN Gateways > `onprem-gateway` > Configuration > Configure BGP
26 | * ASN: `65051`
27 | * click SAVE
28 |
29 |
30 | # Create Local Network Gateways
31 | Go to Azure Portal > Local Network Gateways and create the following gateways:
32 |
33 | | Name | pub ip | ip range | configure BGP | ASN | BGP peer IP | region |
34 | |---|---|---|---|---|---|---|
35 | |`cloud-net` | `lab-gateway-ip` (hub gateway public ip) | // | yes | 65050 | 10.12.4.254 | West Europe |
36 | |`onprem-net`| `onprem-gateway-virtualip` (onprem gateway public ip) | // | yes | 65051 | 192.168.3.254 | France Central |
37 |
38 | # Connection onprem-to-cloud
39 | Go to Azure Portal > Virtual Network Gateways > VPN Gateways > `on-prem-gateway` > Connections > Add:
40 |
41 | * Connection Name: `onprem-to-cloud`
42 | * Type: Site-to-Site (IPsec)
43 | * Virtual Network Gateway: `on-prem-gateway`
44 | * Local Network Gateway: `cloud-net`
45 | * Shared Key: `password.123`
46 | * IKE: IKEv2
47 | * Enable BGP: `true`
48 |
49 |
50 | # Connection cloud-to-onprem
51 | Go to Azure Portal > Virtual Network Gateways > VPN Gateways > `lab-gateway` > Connections > Add:
52 |
53 | * Connection Name: `cloud-to-onprem`
54 | * Type: Site-to-Site (IPsec)
55 | * Virtual Network Gateway: `lab-gateway`
56 | * Local Network Gateway: `onprem-net`
57 | * Shared Key: `password.123`
58 | * IKE: IKEv2
59 | * Enable BGP: `true`
60 |
61 | After **few minutes**, you will see, on `on-prem-gateway` connections:
62 |
63 | | Name | Status | Connection Type | Peer |
64 | |---|---|---|---|
65 | |onprem-to-cloud | connected |Site-to-Site (IPsec)| cloud-net|
66 |
67 | and on `lab-gateway` connections:
68 |
69 | | Name | Status | Connection Type | Peer |
70 | |---|---|---|---|
71 | |cloud-to-onprem | connected |Site-to-Site (IPsec)| onprem-net |
72 |
73 | ## Test solution
74 | Via Bastion go to `W11-onprem` (`192.168.1.4`) and open an RDP connection to hub-vm-01 (`10.12.1.4`).
75 |
76 |
--------------------------------------------------------------------------------
/scenarios/solution-north-south-inspection.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: Use Azure Firewall for perimeter inspection (North/South Traffic)
2 |
3 | This solution shows how to inspect and eventually filter traffic with Azure Firewall between `spoke-01` and `on-prem-net` networks.
4 |
5 | ## Pre-requisites
6 |
7 | In order to apply this solution:
8 |
9 | 1. deploy **hub** playground
10 | 2. deploy **on-premise** playground
11 | 3. send firewall logs to a Log Analytics workspace, as [documented here](logs.md)
12 | 4. connect on-prem and hub with a Site-to-Site IPSec connection, as [documented here](ipsec.md)
13 |
14 |
15 | ## Solution
16 | When a S2S connection is established, routing between `on-prem-net` and `spoke-01` is possible thanks to the following configuration:
17 |
18 | 
19 |
20 | The configuration that allows to route all network traffic via Azure Firewall is the following:
21 |
22 | 
23 |
24 | ### Configure Firewall Policy
25 | With this policy Azure Firewall allows the communications bewteen `on-prem-net` and `spoke-01`.
26 |
27 | Create the following `IP Groups` in `west europe`:
28 | * `group-spoke-01`: 10.13.1.0/24
29 | * `group-on-prem`: 192.168.0.0/16
30 |
31 | Create the following Firewall Policy: `hub-fw-policy`
32 |
33 | **Network Rules**:
34 | * Rule Collection Name: `my-collection`
35 | * Rule Collection type: Network
36 | * Priority: `1000`
37 | * Rule Colletion action: `Allow`
38 | * Rule Collection group: `DefaultNetworkRuleCollectionGroup`
39 |
40 |
41 | | rule name | source | port | protocol | destination |
42 | |---|---|---|---|---|
43 | | on-prem-2-spoke-01 | group-on-prem | * | Any | group-spoke-01 |
44 | | spoke-01-2-on-prem | group-spoke-01 | * | Any | group-on-prem |
45 |
46 | Associate the policy `hub-fw-policy` to `lab-firewall` via Firewall Manager.
47 |
48 | ### Create User Defined Route for spoke-01
49 | Go to `spoke-01` -> peerings -> `spoke01-to-hub` and set Virtual network gateway to `None`.
50 |
51 | Create the following route table in `west europe`: `spokes-01-to-onprem`
52 |
53 | Propagate gateway routes: `NO`
54 |
55 | | Name | Address Prefix | Next hop type | Next hop IP addr |
56 | |---|---|---|---|
57 | | to-onprem | 192.168.0.0/16 | Virtual appliance | 10.12.3.4 |
58 |
59 | | subnet Name | Virtual Network |
60 | |---|---|
61 | | default | spoke-01 |
62 |
63 | ### Create User Defined Route for Azure Virtual Network Gateway's subnet
64 | Create the following route table in `west europe`: `gateway-routes`
65 |
66 | Propagate gateway routes: `NO`
67 |
68 | | Name | Address Prefix | Next hop type | Next hop IP addr |
69 | |---|---|---|---|
70 | | to-spoke-01 | 10.13.1.0/24 | Virtual Appliance | 10.12.3.4 |
71 |
72 | | subnet Name | Virtual Network |
73 | |---|---|
74 | | GatewaySubnet | hub-lab-net |
75 |
76 | ## Test Solution
77 |
78 | Connect via RDP from `w10-onprem` (`192.168.1.4`) to `spoke-01-vm` (`10.13.1.4`).
79 | After that the connection is estabilished, go to `lab-firewall` -> Logs -> click on `Run` button in **Network Rule Log Data** Box.
80 |
81 | In the Result pane, the first row should be something like:
82 |
83 | | Time | msg | Protocol | SourceIP | srcport| TargetIp | target port | Action |
84 | |---|---|----|---|---|---|---|---|
85 | |...|...|TCP|192.168.1.4|xxx|10.13.1.4|3389|**Allow**|
86 | |...|...|UDP|192.168.1.4|xxx|10.13.1.4|3389|**Allow**|
87 |
88 | > Log ingestion from Azure Firewall can require from 60 seconds to 15 minutes, so if nothing is displayed, wait some minute and try again.
89 |
--------------------------------------------------------------------------------
/images/waf-fw.drawio:
--------------------------------------------------------------------------------
1 | 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
--------------------------------------------------------------------------------
/s2s-vpn-bicep/connect-on-prem.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "metadata": {
5 | "_generator": {
6 | "name": "bicep",
7 | "version": "0.16.2.56959",
8 | "templateHash": "17768262978969656259"
9 | }
10 | },
11 | "parameters": {
12 | "gatewayOnPremName": {
13 | "type": "string",
14 | "defaultValue": "on-prem-gateway",
15 | "allowedValues": [
16 | "on-prem-gateway",
17 | "on-prem-2-gateway"
18 | ],
19 | "metadata": {
20 | "description": "Select the on-premises location to connect with the hub"
21 | }
22 | },
23 | "gatewayOnPremLocation": {
24 | "type": "string",
25 | "defaultValue": "francecentral"
26 | },
27 | "gatewayOnPremResourceGroup": {
28 | "type": "string",
29 | "defaultValue": "hub-and-spoke-playground"
30 | },
31 | "hubGatewayName": {
32 | "type": "string",
33 | "defaultValue": "lab-gateway"
34 | },
35 | "hubGatewayResourceGroup": {
36 | "type": "string",
37 | "defaultValue": "hub-and-spoke-playground"
38 | },
39 | "hubGatewayLocation": {
40 | "type": "string",
41 | "defaultValue": "westeurope"
42 | },
43 | "psk": {
44 | "type": "securestring",
45 | "defaultValue": "pAsSwOrD.123"
46 | },
47 | "enableBgp": {
48 | "type": "bool",
49 | "defaultValue": false
50 | }
51 | },
52 | "resources": [
53 | {
54 | "type": "Microsoft.Network/connections",
55 | "apiVersion": "2022-07-01",
56 | "name": "[format('{0}-to-cloud', parameters('gatewayOnPremName'))]",
57 | "location": "[parameters('gatewayOnPremLocation')]",
58 | "properties": {
59 | "connectionType": "Vnet2Vnet",
60 | "virtualNetworkGateway1": {
61 | "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('gatewayOnPremResourceGroup')), 'Microsoft.Network/virtualNetworkGateways', parameters('gatewayOnPremName'))]",
62 | "properties": {}
63 | },
64 | "virtualNetworkGateway2": {
65 | "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('hubGatewayResourceGroup')), 'Microsoft.Network/virtualNetworkGateways', parameters('hubGatewayName'))]",
66 | "properties": {}
67 | },
68 | "sharedKey": "[parameters('psk')]",
69 | "connectionProtocol": "IKEv2",
70 | "enableBgp": "[parameters('enableBgp')]"
71 | }
72 | },
73 | {
74 | "type": "Microsoft.Network/connections",
75 | "apiVersion": "2022-07-01",
76 | "name": "[format('cloud-to-{0}', parameters('gatewayOnPremName'))]",
77 | "location": "[parameters('hubGatewayLocation')]",
78 | "properties": {
79 | "connectionType": "Vnet2Vnet",
80 | "virtualNetworkGateway1": {
81 | "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('hubGatewayResourceGroup')), 'Microsoft.Network/virtualNetworkGateways', parameters('hubGatewayName'))]",
82 | "properties": {}
83 | },
84 | "virtualNetworkGateway2": {
85 | "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('gatewayOnPremResourceGroup')), 'Microsoft.Network/virtualNetworkGateways', parameters('gatewayOnPremName'))]",
86 | "properties": {}
87 | },
88 | "sharedKey": "[parameters('psk')]",
89 | "connectionProtocol": "IKEv2",
90 | "enableBgp": "[parameters('enableBgp')]"
91 | }
92 | }
93 | ]
94 | }
--------------------------------------------------------------------------------
/scenarios/name-resolution-with-azure-firewall.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: Configure a DNS on the cloud, and be sure that all machines are reachable via FQDN also from on-premise (Azure Firewall Version)
2 |
3 | read also [this blog post](https://nicolgit.github.io/dns-forwarding-azure-hub-and-spoke/) for more information on this solution.
4 |
5 | ## Pre-requisites
6 |
7 | In order to apply this solution:
8 |
9 | 1. deploy HUB playground
10 | 2. deploy ONPREMISE-2 playground
11 | 3. configure the DNS on the cloud as [documented here](dns.md)
12 | 4. configure a site-to-site VPN as [documented here](vnet-to-vnet-2.md)
13 |
14 | ## Solution
15 |
16 | The solution implemented is described in the following schema.
17 |
18 | 
19 |
20 | ### Configure Azure Firewall for DNS resolution and proxy
21 | Create the following Firewall Policy:
22 | * Name: `hub-fw-policy`
23 | * DNS: `Enabled`
24 | * DNS Servers: `Default (Azure Provided)`
25 | * DNS Proxy: `Enabled`
26 |
27 | Associate the policy `hub-fw-policy` to `hub-lab-vnet`.
28 |
29 | On `spoke-01` virtual network go to DNS Servers and set as DNS Server custom `10.12.3.4` (the internal firewall IP).
30 |
31 | On `spoke-02` virtual network go to DNS Servers and set as DNS Server custom `10.12.3.4` (the internal firewall IP).
32 |
33 | On `spoke-03` virtual network go to DNS Servers and set as DNS Server custom `10.12.3.4` (the internal firewall IP).
34 |
35 | On `hub-lab-net` virtual network go to DNS Servers and set as DNS Server custom `10.12.3.4` (the internal firewall IP).
36 |
37 | ### Configure a DNS machine on prem
38 | Open ssh on `lin-onprem` machine and install [Bind9](https://www.isc.org/bind/):
39 |
40 | ```
41 | sudo apt-get update
42 | sudo apt-get upgrade
43 | sudo apt-get install bind9
44 | ```
45 |
46 | Edit `/etc/bind/named.conf.options` file (i.e. with `sudo nano /etc/bind/named.conf.options`) and fill it with the following:
47 |
48 | ```
49 | options {
50 | directory "/var/cache/bind";
51 |
52 | allow-query { localhost; 10.20.1.0/24; };
53 |
54 | recursion yes;
55 | forwarders {
56 | 10.12.3.4;
57 | };
58 | forward only;
59 |
60 | dnssec-validation no; # needed for private dns zones
61 | auth-nxdomain no; # conform to RFC1035
62 | listen-on-v6 { any; };
63 | };
64 | ```
65 |
66 | > in this configuration, on-prem DNS (`10.20.1.4`) forwards all request to Azure Firewall (`10.12.3.4`) that acts as DNS proxy for the Azure managed DNS accessible from the hub. allow-query parameter allows machines on-prem to use this DNS. dnssec validation off is required to manage the private DNS zone.
67 |
68 | Restart Bind9 service to load the updated configuration.
69 |
70 | `sudo service bind9 restart`
71 |
72 | ### Configure Client machine on-prem-2
73 |
74 | Open ssh on `lin-onprem-2` and set `Lin-onprem` as DNS server. To do it type `sudo nano /etc/resolv.conf` and replace _nameserver_ row with the following:
75 |
76 | `nameserver 10.20.1.4`
77 | > this is `lin-onprem` machine IP
78 |
79 | # Test Solution
80 | From ssh on `Lin-onprem` machine type:
81 |
82 | `nslookup spoke-01-vm.cloudasset.internal 10.12.3.4` (direct query to Azure Firewall)
83 |
84 | or
85 |
86 | `nslookup spoke-01-vm.cloudasset.internal` (query to `lin-onprem` that forward the request to Azure Firewall)
87 |
88 | if the answer is
89 |
90 | ```
91 | Server:10.12.3.4
92 | Address:10.12.3.4#53
93 |
94 | Non-authoritative answer:
95 | Name:spoke-01-vm.cloudasset.interal
96 | Address: 10.13.1.4
97 | ```
98 |
99 | On premise machine `Lin-onprem-2` is able to resolve FQDN names for cloud machines (*.clouadasset.internal), with on premise DNS server - `Lin-onprem` - that forwards DNS queries to Azure Firewall on the cloud. Azure Firewall forwards the request to the Azure managed DNS. Azure managed DNS is able to resolve `cloudasset.internal` domain because `hub-lab-vnet` has been linked with the corresponding private zone.
100 |
101 |
--------------------------------------------------------------------------------
/scenarios/network-watcher.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: Use Azure Network Watcher for network logging and troubleshooting
2 |
3 | In this solution, we will use Azure Network Watcher and NGS flow logs for logging and network troubleshooting on the following virtual networks/subnets.
4 |
5 | * `hub-lab-net` > `default` subnet
6 | * `spoke-01` > `default` subnet
7 | * `spoke-02` > `default` subnet
8 |
9 | Common NSG flow logs troubleshooting use cases include:
10 |
11 | * **Network Monitoring**: Identify unknown or undesired traffic. Monitor traffic levels and bandwidth consumption. Filter flow logs by IP and port to understand application behavior. Export Flow Logs to analytics and visualization tools of your choice to set up monitoring dashboards.
12 | * **Usage monitoring and optimization**: Identify top talkers in your network. Combine with GeoIP data to identify cross-region traffic. Understand traffic growth for capacity forecasting. Use data to remove overtly restrictive traffic rules.
13 | * **Compliance**: Use flow data to verify network isolation and compliance with enterprise access rules
14 | * **Network forensics & Security analysis**: Analyze network flows from compromised IPs and network interfaces. Export flow logs to any SIEM or IDS tool of your choice.
15 |
16 | ## Pre-requisites
17 |
18 | In order to apply this solution, deploy the **hub** playground first.
19 |
20 | ## Solution
21 | ### 01 - Create a Storage Account
22 | Create an Azure Storage Account to collect the NSG flow logs.
23 |
24 | * Name: ``
25 | * Region: `west europe`
26 | * Performance: `Standard`
27 | * Redundancy: `LRS`
28 |
29 | Under Data Protection TAB, **disable** all recovery options.
30 |
31 |
32 | ### 02 - Create Network Security Groups
33 | Create a Network Security Group with the following characteristics:
34 |
35 | * Name: `nsg-playground`
36 | * region: `west europe`
37 | * inbound/outbound rules: leave all on defaults
38 |
39 | Associate NSG to the following subnets:
40 |
41 | | virtual network | subnet |
42 | |---|---|
43 | | `hub-lab-net` | `default` |
44 | | `spoke-01` | `default` |
45 | | `spoke-02` | `default` |
46 |
47 | under NSG flow logs, click on **create a NSG Flow log**:
48 | * Select NSG: `nsg-playground`
49 | * Storage Accounts: ``
50 | * Retention days: `20`
51 | * Traffic Analytics: `Enable`
52 | * Traffic Analytics processing interval: `10 minutes`
53 | * Log Analytics Workspace: ``
54 |
55 | ## Test Solution
56 | Simulate a connection:
57 |
58 | * Connect via RDP/Bastion to `spoke-01-vm`
59 | * From the virtual desktop connect via RDP to 10.12.1.4 (hub-vm-01)
60 |
61 | check data in Flow logs:
62 |
63 | * open: `` > Containers > Insights-logs-networksecuritygroupflowevent
64 | * open the following folder:
65 | * resourceId=
66 | * SUBSCRIPTIONS
67 | * ``
68 | * RESOURCEGROUPS
69 | * ``
70 | * PROVIDERS
71 | * MICROSOFT.NETWORK
72 | * NETWORKSECURITYGROUPS
73 | * year > month > day > hour > minute >
74 | * macAddress of spoke-01-vm
75 | * _you can find it on azure vm page under > Network interface > properties_
76 | * open file PT1H.json
77 |
78 | > Flow logs operate at Layer 4 and record all IP flows going in and out of an NSG. Logs are collected at 1-min interval through the Azure platform and do not affect customer resources or network performance in any way. Logs are written in the JSON format and show outbound and inbound flows on a per NSG rule basis.
79 | > here more information on the format and fields meaning:
80 |
81 | in json file, you will find FlowTuples to 10.12.1.4 on 3389 port
82 |
83 | > "1647878148,10.13.1.4, **10.12.1.4** ,51896, **3389** ,T,O,A,E,214,20643,278,36774",
84 |
85 | ### See Traffic Analytics
86 | Go to Azure search bar and type Network Watcher.
87 | Select Traffic Analytics.
88 |
89 | > remember that you have to wait at least 10 minutes before to see your data here.
90 |
91 |
--------------------------------------------------------------------------------
/scenarios/ipsec-active-active.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: connect on-prem and hub with a Site-to-Site IPSec ACTIVE-ACTIVE connection
2 |
3 | ## Pre-requisites
4 |
5 | In order to apply this solution you have to deploy `hub` and `on-premises` playgrounds.
6 |
7 | ## Solution
8 |
9 | In this configuration, each Azure gateway instance have a unique public IP address, and each establishes an IPsec/IKE S2S VPN tunnel to on-premises VPN device specified in local network gateway and connection. Note that both VPN tunnels are actually part of the same connection. You will need to configure your on-premises VPN device to accept or establish two S2S VPN tunnels to those two Azure VPN gateway public IP addresses.
10 |
11 | Because the Azure gateway instances are in active-active configuration, the traffic from your Azure virtual network to your on-premises network will be routed through both tunnels simultaneously, even if your on-premises VPN device may favor one tunnel over the other. For a single TCP or UDP flow, Azure attempts to use the same tunnel when sending packets to your on-premises network. However, your on-premises network could use a different tunnel to send packets to Azure.
12 |
13 | When a planned maintenance or unplanned event happens to one gateway instance, the IPsec tunnel from that instance to your on-premises VPN device will be disconnected. The corresponding routes on your VPN devices should be removed or withdrawn automatically so that the traffic will be switched over to the other active IPsec tunnel. On the Azure side, the switch over will happen automatically from the affected instance to the active instance.
14 |
15 | 
16 |
17 | More information on [Highly Available cross-premises and VNet-to-VNet connectivity](https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-highlyavailable#active-active-vpn-gateways).
18 |
19 | ### Enable Active-Active mode
20 | Go to Virtual Network Gateway `lab-gateway` in Configuration:
21 | * Active-active mode: Enables
22 | * Second Public IP Address: `hub-gateway-virtualip-2`
23 | * SKU: Standard
24 | * Configure BGP: disabled
25 |
26 | # create Local Network Gateways
27 | create the following gateways
28 |
29 | | Name | IP Address | Address Space | Region |
30 | |---|---|---|---|
31 | |cloud-net | (hub-gateway-virtualip) | 10.0.0.0/8| West Europe |
32 | |cloud-net-2 | (hub-gateway-virtualip-2) | 10.0.0.0/8| West Europe |
33 | |onprem-net| (onprem-gateway-virtualip) | 192.168.0.0/16 | France Central |
34 |
35 | # connection onprem-to-cloud (1)
36 | Open `on-prem-gateway`, go to Connections and add the following object
37 | * Connection Name: `onprem-to-cloud`
38 | * Type: Site-to-Site (IPsec)
39 | * virtual Network Gateway: `on-prem-gateway`
40 | * Local Network Gateway: `cloud-net`
41 | * Shared Key: `password.123`
42 | * IKE: IKEv2
43 |
44 |
45 | # connection cloud-to-onprem
46 | Open `lab-gateway`, go to Connections and add the following object
47 | * Connection Name: `cloud-to-onprem`
48 | * Type: Site-to-Site (IPsec)
49 | * virtual Network Gateway: `lab-gateway`
50 | * Local Network Gateway: `onprem-net`
51 | * Shared Key: `password.123`
52 | * IKE: IKEv2
53 |
54 | # connection onprem-to-cloud (2)
55 | Open `on-prem-gateway`, go to Connections and add the following object
56 | * Connection Name: `onprem-to-cloud-2`
57 | * Type: Site-to-Site (IPsec)
58 | * virtual Network Gateway: `on-prem-gateway`
59 | * Local Network Gateway: `cloud-net-2`
60 | * Shared Key: `password.123`
61 | * IKE: IKEv2
62 |
63 |
64 |
65 | after few minutes, you will see, on `on-prem-gateway` connections:
66 |
67 | | Name | Status | Connection Type | Peer |
68 | |---|---|---|---|
69 | |onprem-to-cloud | connected |Site-to-Site (IPsec)| cloud-net|
70 | |onprem-to-cloud-2 | connected |Site-to-Site (IPsec)| cloud-net-2|
71 |
72 | and on `lab-gateway` connections:
73 |
74 | | Name | Status | Connection Type | Peer |
75 | |---|---|---|---|
76 | |cloud-to-onprem | connected |Site-to-Site (IPsec)| onprem-net |
77 |
78 | ## Test solution
79 | Via bastion go to W10onprem (`192.168.1.4`) and open a RDP connection to hub-vm-01 (`10.12.1.4`).
80 |
81 | Do the same test also from hub-vm-01 to W10onprem.
--------------------------------------------------------------------------------
/scenarios/ping-any-to-any-gateway.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: Allows machines in ANY spoke to communicate with ANY machine in ANY other spoke (Virtual Network Gateway)
2 |
3 | The Azure Virtual Network Gateway is a specific type of virtual network gateway that is used to send encrypted traffic between an Azure virtual network and an on-premises location over the public Internet. It can also send traffic between Azure virtual networks. Over a virtual network gateway, you can send traffic from one virtual network to another in a very secure manner.
4 |
5 | The main capabilities of Azure Virtual Network Gateway include:
6 | * VPN Connectivity: It provides VPN connectivity, enabling secure communication channels between different locations.
7 | * Cross-Premises Connectivity: It allows you to connect your on-premises network to Azure's network, essentially extending your on-premises network to the cloud.
8 | * Inter-VNET Connectivity: It can be used to connect different Azure Virtual Networks, enabling resources in different Virtual Networks to communicate with each other.
9 | * Point-to-Site VPN: It enables individual clients to connect to the Azure network from anywhere, making it ideal for remote workers or individual devices.
10 | * Advanced Routing: It has advanced routing capabilities, including Border Gateway Protocol (BGP) support and dynamic routing.
11 |
12 | In the context of a hub and spoke architecture in Azure, the Virtual Network Gateway can be used to enable routing between different spokes. Normally, peering relationships in Azure are non-transitive, meaning that if spoke 1 is connected to the hub, and spoke 2 is also connected to the hub, spoke 1 and spoke 2 cannot communicate with each other by default.
13 |
14 | However, by leveraging a Virtual Network Gateway in the hub network, you can configure it to allow traffic to flow between spokes. This is done by enabling 'Use Remote Gateways' and 'Allow Gateway Transit' settings in the peering settings. The Virtual Network Gateway essentially acts as a router, directing traffic between different virtual networks. This can be particularly useful in scenarios where you have resources in different spokes that need to communicate with each other.
15 |
16 | In this solution, I show you how configure the routing between spokes using a **Virtual Network Gateway** in the middle.
17 |
18 | The resulting overall architecture is shown in the following schema.
19 |
20 | 
21 |
22 | _Download a [draw.io file](../images/any-to-any-routing.drawio) of this architecture._
23 |
24 | ## Pre-requisites
25 |
26 | In order to apply this solution you have to deploy hub playground only.
27 |
28 | ## Solution
29 |
30 | In order to allow this communication, via **Azure Virtual Gateway**:
31 |
32 | Verify that gateway transit is Enabled, on `hub-lab-net` > peerings
33 |
34 | | Name | Peering Status | Peer | Gateway Transit |
35 | |---|---|---|---|
36 | | hub-to-spoke01 | Connected | spoke-01 | Enabled |
37 | | hub-to-spoke02 | Connected | spoke-02 | Enabled |
38 | | hub-to-spoke03 | Connected | spoke-03 | Enabled |
39 |
40 | Create the following route table in `west europe`: `spokes-we-to-hub-routes`
41 |
42 | | Name | Address Prefix | Next hop type | Next hop IP addr |
43 | |---|---|---|---|
44 | | to-spoke-01 | 10.13.1.0/24 | Virtual network gateway | - |
45 | | to-spoke-02 | 10.13.2.0/24 | Virtual network gateway | - |
46 | | to-spoke-03 | 10.13.3.0/24 | Virtual network gateway | - |
47 |
48 |
49 | | subnet Name | Virtual Network |
50 | |---|---|
51 | | default | spoke-01 |
52 | | services | spoke-01 |
53 | | default | spoke-02 |
54 | | services | spoke-02 |
55 |
56 | Create the following route table in `north europe`: `spokes-ne-to-hub-routes`
57 |
58 | | Name | Address Prefix | Next hop type | Next hop IP addr |
59 | |---|---|---|---|
60 | | to-spoke-01 | 10.13.1.0/24 | Virtual network gateway | - |
61 | | to-spoke-02 | 10.13.2.0/24 | Virtual network gateway | - |
62 |
63 | | subnet Name | Virtual Network |
64 | |---|---|
65 | | default | spoke-03 |
66 | | services | spoke-03 |
67 |
68 | ## Test Solution
69 | Test connections using remote desktop client and ssh from one machine to another.
70 |
71 | ## More information
72 |
73 | * Azure [Virtual Network Gateway](https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-about-vpngateways)
74 | * Configure VPN [Gateway Transit](https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-peering-gateway-transit) for networks in peering
75 |
--------------------------------------------------------------------------------
/scenarios/ipsec-multiple-vpn-device.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: connect on-prem and hub with a Site-to-Site IPSec ACTIVE-ACTIVE connection
2 |
3 | # *** STILL TO BE REVIEWED ***
4 |
5 | ## Pre-requisites
6 |
7 | In order to apply this solution you have to deploy `hub` and `on-premises` playgrounds.
8 |
9 | ## Solution
10 |
11 | This configuration provides multiple active tunnels from the same Azure VPN gateway to your on-premises devices in the same location. There are some requirements and constraints:
12 |
13 | * You need to create multiple S2S VPN connections from your VPN devices to Azure. When you connect multiple VPN devices from the same on-premises network to Azure, you need to create one local network gateway for each VPN device, and one connection from your Azure VPN gateway to each local network gateway.
14 | * The local network gateways corresponding to your VPN devices must have unique public IP addresses in the "GatewayIpAddress" property.
15 | * BGP is required for this configuration. Each local network gateway representing a VPN device must have a unique BGP peer IP address specified in the "BgpPeerIpAddress" property.
16 | * You should use BGP to advertise the same prefixes of the same on-premises network prefixes to your Azure VPN gateway, and the traffic will be forwarded through these tunnels simultaneously.
17 | * You must use Equal-cost multi-path routing (ECMP).
18 | * Each connection is counted against the maximum number of tunnels for your Azure VPN gateway, 10 for Basic and Standard SKUs, and 30 for HighPerformance SKU.
19 |
20 | In this configuration, the Azure VPN gateway is still in active-standby mode, so the same failover behavior and brief interruption will still happen as described above. But this setup guards against failures or interruptions on your on-premises network and VPN devices.
21 |
22 |
23 | 
24 |
25 | More information on [Highly Available cross-premises and VNet-to-VNet connectivity](https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-highlyavailable#active-active-vpn-gateways).
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | ### Enable Active-Active mode
44 | Go to Virtual Network Gateway `lab-gateway` in Configuration:
45 | * Active-active mode: Enables
46 | * Second Public IP Address: `hub-gateway-virtualip-2`
47 | * SKU: Standard
48 | * Configure BGP: disabled
49 |
50 | # create Local Network Gateways
51 | create the following gateways
52 |
53 | | Name | IP Address | Address Space | Region |
54 | |---|---|---|---|
55 | |cloud-net | (hub-gateway-virtualip) | 10.0.0.0/8| West Europe |
56 | |cloud-net-2 | (hub-gateway-virtualip-2) | 10.0.0.0/8| West Europe |
57 | |onprem-net| (onprem-gateway-virtualip) | 192.168.0.0/16 | France Central |
58 |
59 | # connection onprem-to-cloud (1)
60 | Open `on-prem-gateway`, go to Connections and add the following object
61 | * Connection Name: `onprem-to-cloud`
62 | * Type: Site-to-Site (IPsec)
63 | * virtual Network Gateway: `on-prem-gateway`
64 | * Local Network Gateway: `cloud-net`
65 | * Shared Key: `password.123`
66 | * IKE: IKEv2
67 |
68 |
69 | # connection cloud-to-onprem
70 | Open `lab-gateway`, go to Connections and add the following object
71 | * Connection Name: `cloud-to-onprem`
72 | * Type: Site-to-Site (IPsec)
73 | * virtual Network Gateway: `lab-gateway`
74 | * Local Network Gateway: `onprem-net`
75 | * Shared Key: `password.123`
76 | * IKE: IKEv2
77 |
78 | # connection onprem-to-cloud (2)
79 | Open `on-prem-gateway`, go to Connections and add the following object
80 | * Connection Name: `onprem-to-cloud-2`
81 | * Type: Site-to-Site (IPsec)
82 | * virtual Network Gateway: `on-prem-gateway`
83 | * Local Network Gateway: `cloud-net-2`
84 | * Shared Key: `password.123`
85 | * IKE: IKEv2
86 |
87 |
88 |
89 | after few minutes, you will see, on `on-prem-gateway` connections:
90 |
91 | | Name | Status | Connection Type | Peer |
92 | |---|---|---|---|
93 | |onprem-to-cloud | connected |Site-to-Site (IPsec)| cloud-net|
94 | |onprem-to-cloud-2 | connected |Site-to-Site (IPsec)| cloud-net-2|
95 |
96 | and on `lab-gateway` connections:
97 |
98 | | Name | Status | Connection Type | Peer |
99 | |---|---|---|---|
100 | |cloud-to-onprem | connected |Site-to-Site (IPsec)| onprem-net |
101 |
102 | ## Test solution
103 | Via bastion go to W10onprem (`192.168.1.4`) and open a RDP connection to hub-vm-01 (`10.12.1.4`).
104 |
105 | Do the same test also from hub-vm-01 to W10onprem.
--------------------------------------------------------------------------------
/scenarios/sftp.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: Publish internal SFTP endpoint via Azure Firewall
2 |
3 | In this solution I show how to publish through Azure Firewall an SFTP endpoint delivered by an Azure Storage.
4 |
5 | The service is exposed by the Azure Firewall public IP.
6 |
7 | > Exposing an SFTP server through a firewall, rather than directly exposing it to the public, adds an additional layer of security. An Azure firewall can be configured to only allow certain types of traffic to pass through to the SFTP server, reducing the attack surface and making it more difficult for attackers to exploit vulnerabilities. Additionally, a firewall can monitor and log traffic, providing valuable information for detecting and responding to security incidents. It is generally considered a best practice to use a firewall to protect any publicly accessible server.
8 |
9 | The scenario is deployed into a Hub and Spoke network topology context. Specifically, the virtual network that hosts the Azure Storage is different from the virtual network that hosts the Azure Firewall.
10 |
11 | In this configuration, inbound SFTP traffic goes through both Azure Firewall, the peering, the spoke and finally the Azure Storage thanks to the private endpoint.
12 |
13 | The resulting overall architecture is shown in the following schema.
14 |
15 | 
16 |
17 | _Download a [draw.io file](../images/sftp.drawio) of this architecture._
18 |
19 | ## Pre-requisites
20 |
21 | In order to apply this solution you have to deploy HUB playground only.
22 |
23 | ## Solution
24 |
25 | ### Create Storage Account
26 |
27 | From Azure Portal go to Storage Accounts > Create:
28 |
29 | Basic
30 |
31 | * Storage account Name: `storage01`
32 | * Region: `west europe`
33 | * Redundancy: `LRS`
34 |
35 | Advanced
36 |
37 | * Hierarchical namespace: `Enable`
38 | * SFTP: `Enable`
39 |
40 | Networking
41 |
42 | * Networking access: `disable public access`
43 | * Private endpoint > Add private endpoint
44 | * Location: `West Europe`
45 | * Name: `storage-01-pe`
46 | * Storage sub resource: `blob`
47 | * Virtual network: `spoke-01`
48 | * Subnet: `Services`
49 | * Private DNS zone: `No`
50 |
51 | * click [Create]
52 |
53 | ### Configure Storage Account
54 |
55 | From Azure Portal go to Storage accounts > `storage01` > Containers > Create
56 |
57 | * Name: `test-container-01`
58 |
59 | Click [Create].
60 |
61 | From Azure Portal go to Storage accounts > `storage01` > SFTP > Add local user
62 |
63 | * username: `user01`
64 | * Authentication method: `SSH Password`
65 | * Container permissiona
66 | * Container: `test-container-01`
67 | * Permissions: `All`
68 |
69 | click [Add] and copy the generated password.
70 |
71 | ### Configure Azure Firewall
72 |
73 | Go to Azure Portal > Firewall Policies > Create:
74 |
75 | Basic Tab
76 |
77 | * Name: `hub-fw-policy`
78 | * Region: `West Europe`
79 | * Parent Policy:
80 |
81 | Rules Tab
82 |
83 | **DNAT rules**
84 |
85 | |priority | collection name | rule name | src | port | protocol | destination | translated address| translated port | action |
86 | |---|---|---|---|---|---|---|---|---|---|
87 | |1000 | coll01 |sftp | * | 22 | TCP UDP | `x.x.x.x` | `10.13.1.68` | 22 | Dnat |
88 |
89 | Where:
90 |
91 | * `x.x.x.x` is `lab-firewall` public IP
92 | * `10.13.1.68` is `storage-01-pe`'s private IP.
93 |
94 | Click [Create].
95 |
96 | Associate the policy `hub-fw-policy` to `lab-firewall` via Firewall Manager
97 |
98 |
99 | ## Test solution
100 | from internet, from your laptop:
101 |
102 | ```
103 | C:>sftp storage01.test-container-01.user01@x.x.x.x
104 | ECDSA key fingerprint is SHA256:0WNMHmCNJE1YFBpHNeADuT5h+PfJ/jJPtUDHCxCSrO0.
105 | This key is not known by any other names
106 | Are you sure you want to continue connecting (yes/no/[fingerprint])?
107 | Warning: Permanently added 'x.x.x.x' (ECDSA) to the list of known hosts.
108 | storage01.test-container@x.x.x.x's password:
109 | Connected to storage01.test-container-01.user01@x.x.x.x.
110 | sftp>
111 |
112 | ```
113 |
114 | You can successfully connect with the sftp server.
115 |
116 | ## More information
117 |
118 | * Azure Firewall [DNAT rules](https://learn.microsoft.com/en-us/azure/firewall/tutorial-firewall-dnat).
119 | * Azure Storage [SFTP Support](https://learn.microsoft.com/en-us/azure/storage/blobs/secure-file-transfer-protocol-support).
120 | * Azure [Private Endpont](https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview).
--------------------------------------------------------------------------------
/any-to-any-bicep/fw-policy.bicep:
--------------------------------------------------------------------------------
1 | param allowIpAddresses array = []
2 | param locationWE string = 'westeurope'
3 |
4 | @description('Basic, Standard or Premium tier')
5 | @allowed([ 'Basic', 'Standard', 'Premium' ])
6 | param firewallTier string = 'Premium'
7 |
8 | var ipGroups_all_spokes_subnets_name = 'all-spokes-subnets'
9 | var firewallPolicyName = 'my-firewall-policy'
10 |
11 | var ipGroupAddresses = concat([
12 | '10.13.1.0/24'
13 | '10.13.2.0/24'
14 | '10.13.3.0/24'
15 | ], allowIpAddresses)
16 |
17 | resource ipGroup 'Microsoft.Network/ipGroups@2020-05-01' = {
18 | name: ipGroups_all_spokes_subnets_name
19 | location: locationWE
20 | properties: {
21 | ipAddresses: ipGroupAddresses
22 | }
23 | }
24 |
25 | resource myFirewallPolicy 'Microsoft.Network/firewallPolicies@2020-05-01' = {
26 | name: firewallPolicyName
27 | location: locationWE
28 | properties: {
29 | threatIntelMode: 'Alert'
30 | sku: {
31 | tier: 'Premium'
32 | }
33 | }
34 | }
35 |
36 | resource toInternetCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2022-07-01' = {
37 | parent: myFirewallPolicy
38 | name: 'DefaultApplicationRuleCollectionGroup'
39 | properties: {
40 | priority: 300
41 | ruleCollections: [
42 | {
43 | ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
44 | action: {
45 | type: 'Allow'
46 | }
47 | rules: [
48 | {
49 | ruleType: 'ApplicationRule'
50 | name: 'allow-internet-traffic-out'
51 | protocols: [
52 | {
53 | protocolType: 'Http'
54 | port: 80
55 | }
56 | {
57 | protocolType: 'Https'
58 | port: 443
59 | }
60 | ]
61 | fqdnTags: []
62 | webCategories: []
63 | targetFqdns: [
64 | '*'
65 | ]
66 | targetUrls: []
67 | terminateTLS: false
68 | sourceAddresses: []
69 | destinationAddresses: []
70 | sourceIpGroups: [ ipGroup.id ]
71 | }
72 | ]
73 | name: 'internet-out-collection'
74 | priority: 200
75 | }
76 | {
77 | ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
78 | action: {
79 | type: 'Deny'
80 | }
81 | // Basic tier does not allow use of webCategories
82 | rules: firewallTier == 'Basic' ? null : [
83 | {
84 | ruleType: 'ApplicationRule'
85 | name: 'block-porn-sites'
86 | protocols: [
87 | {
88 | protocolType: 'Http'
89 | port: 80
90 | }
91 | {
92 | protocolType: 'Https'
93 | port: 443
94 | }
95 | ]
96 | fqdnTags: []
97 | webCategories: [
98 | 'Nudity'
99 | 'PornographyAndSexuallyExplicit'
100 | 'ChildInappropriate'
101 | ]
102 | targetFqdns: []
103 | targetUrls: []
104 | terminateTLS: false
105 | sourceAddresses: []
106 | destinationAddresses: []
107 | sourceIpGroups: [ ipGroup.id ]
108 | }
109 | ]
110 | name: 'block-some-stuff'
111 | priority: 150
112 | }
113 | ]
114 | }
115 | }
116 |
117 | resource anyToAnyCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2020-05-01' = {
118 | parent: myFirewallPolicy
119 | name: 'DefaultNetworkRuleCollectionGroup'
120 | dependsOn: [ toInternetCollectionGroup ] // RM deploys all the ruleCollectionGroups in parallel or at least not sequentially - https://learn.microsoft.com/en-us/answers/questions/673917/update-of-azure-firewall-policies-failes-faulted-r
121 | properties: {
122 | priority: 300
123 | ruleCollections: [
124 | {
125 | ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
126 | name: 'any-to-any-collection'
127 | priority: 1000
128 | action: {
129 | type: 'Allow'
130 | }
131 | rules: [
132 | {
133 | ruleType: 'NetworkRule'
134 | name: 'allow-spoke-to-spoke-traffic'
135 | ipProtocols: [ 'Any' ]
136 | sourceIpGroups: [
137 | ipGroup.id
138 | ]
139 | destinationPorts: [
140 | '*'
141 | ]
142 | destinationIpGroups: [
143 | ipGroup.id
144 | ]
145 | }
146 | ]
147 | }
148 | ]
149 | }
150 | }
151 |
152 | output policyid string = myFirewallPolicy.id
153 |
154 |
--------------------------------------------------------------------------------
/images/routing-without-bgp-fw.drawio:
--------------------------------------------------------------------------------
1 | 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
--------------------------------------------------------------------------------
/scenarios/routing-with-bgp.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: Routing cross hub with BGP
2 |
3 | This solution shows how to configure the routing across 4 network hubs, using Virtual Network Gateways, Vnet2Net Connections and the BGP Protocol, as shown in the following schema.
4 |
5 | 
6 |
7 | * an Azure [Virtual Network Gateway](https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-about-vpngateways) is composed of two or more VMs that are automatically configured and deployed to a specific subnet you create called the gateway subnet. The gateway VMs contain routing tables and run specific gateway services.
8 | * [VNet-to-VNet connections](https://learn.microsoft.com/en-us/azure/vpn-gateway/design#V2V) use a VPN gateway to provide a secure tunnel using IPsec/IKE to connect 2 virtual networks in a way similar to connecting a VNet to an on-premises site location.
9 | * [BGP is the standard routing protocol](https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-bgp-overview) commonly used in the Internet to exchange routing and reachability information between two or more networks. When used in the context of Azure Virtual Networks, BGP enables the Azure VPN Gateways called BGP peers or neighbors, to exchange "routes" that will inform both gateways on the availability and reachability for those prefixes to go through the gateways or routers involved. BGP can also enable transit routing among multiple networks by propagating routes a BGP gateway learns from one BGP peer to all other BGP peers.
10 |
11 | ## Pre-requisites
12 |
13 | In order to apply this solution:
14 |
15 | * deploy the `hub playground`
16 | * remove all peerings
17 |
18 | ## Solution
19 |
20 | Once all peering are removed, to allow routing across all hubs are required:
21 |
22 | 1. a Virtual Network Gateway in each spoke network
23 | 2. a Vnet-to-Vnet connection with BGP enabled between `hub-lab-net` and `Spoke01`
24 | 3. a Vnet-to-Vnet connection with BGP enabled between `spoke-01` and `spoke-02`
25 | 4. a Vnet-to-Vnet connection with BGP enabled between `spoke-02` and `spoke-03`
26 |
27 | ### Create a VPN Gateway in each spoke network
28 |
29 | Create the following Azure Virtual Network Gateways:
30 |
31 | | Name | Region | Type | VPN Type | SKU | Virtual Network | gw subnet range | IP | BGP | ANS
32 | |---|---|---|---|---|---|---|---|---|---|
33 | |spoke-01-gateway| west-europe | VPN | route based | VpnGw1 | spoke-01 | 10.13.1.128/26 | spoke-01-ip | Enabled | 65511
34 | |spoke-02-gateway| west-europe | VPN | route based | VpnGw1 | spoke-02 | 10.13.2.128/26 | spoke-02-ip | Enabled | 65512
35 | |spoke-03-gateway| north-europe | VPN | route based | VpnGw1 | spoke-03 | 10.13.3.128/26 | spoke-03-ip | Enabled | 65513
36 |
37 | On `lab-gateway` also **enable BGP** and set ANS=`65514`
38 |
39 | ### Create all the VNet2-VNet connections
40 | 2 VPN gateways to communicate need 2 connections: one from the first to the second, and another from the second to the first. Therefore, for our topologym the following connections are needed:
41 |
42 | | Connection Name | Type | From | To | PSK | Enable BGP |
43 | |---|---|---|---|---|---|
44 | | hub-to-spoke-01 | Vnet-2-Vnet | lab-gateway | spoke-01-gateway | password.123 | YES |
45 | | spoke-01-to-hub | Vnet-2-Vnet | spoke-01-gateway | lab-gateway | password.123 | YES |
46 | | spoke-01-to-02 | Vnet-2-Vnet | spoke-01-gateway | spoke-02-gateway | password.123 | YES |
47 | | spoke-02-to-01 | Vnet-2-Vnet | spoke-02-gateway | spoke-01-gateway | password.123 | YES |
48 | | spoke-02-to-03 | Vnet-2-Vnet | spoke-02-gateway | spoke-03-gateway | password.123 | YES |
49 | | spoke-03-to-02 | Vnet-2-Vnet | spoke-03-gateway | spoke-02-gateway | password.123 | YES |
50 |
51 | ## Test Solution
52 | Wait a few minutes, until all previously created connections have a `connected` status.
53 |
54 | Go to `lab-gateways` > bgp peers > learned routes to verify that all route are propagated. YOu will see the following:
55 |
56 | | network CIDR | next hop | local address | source peer | origin | AS path |
57 | |---|---|---|---|---|---|
58 | | 10.12.0.0/16 | - | 10.12.4.254 | 10.12.4.254 | Network | - |
59 | | 10.13.1.190/32 | - | 10.12.4.254 | 10.12.4.254 | Network | - |
60 | | 10.13.1.0/24 | 10.13.1.190 | 10.12.4.254 | 10.13.1.190 | EBgp | 65511 |
61 | | 10.13.2.0/24 | 10.13.1.190 | 10.12.4.254 | 10.13.1.190 | EBgp | 65511-65512 |
62 | | 10.13.3.0/24 | 10.13.1.190 | 10.12.4.254 |10.13.1.190 | EBgp | 65511-65512-65513 |
63 |
64 | Each Virtual Network Gateway in this context is an Autonomous System (AS), in this table, the `AS path` lists all the ASes that need to be traversed to reach the location where the `network` that the path is attached to is advertised from. As such, a traceroute should encounter those same ASes.
65 |
66 | From the table above, you can see that a machine in lab-hub-net to arrive to a machine in spoke-03 net (10.13.3.0/24), have to cross `65511` (`spoke-01-gateway`), `65512` (`spoke-02-gateway`) and `65513` (`spoke-03-gateway`).
67 |
68 | ### RDP connection test
69 | 1. connect to `hub-01-vm` via RDP/bastion
70 | 2. once on the vm, open remote desktop connection and connect to `10.13.2.4` (a Windows VM)
71 |
--------------------------------------------------------------------------------
/scenarios/ipsec-dual-redundancy.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: connect on-prem and hub with a Site-to-Site VPN with dual-redundancy, ACTIVE-ACTIVE connection
2 |
3 | ## Pre-requisites
4 |
5 | In order to apply this solution you have to deploy `hub` and `on-premises` playgrounds.
6 |
7 | ## Solution
8 | Here you create and set up the Azure VPN gateway in an active-active configuration, and create two local network gateways and two connections for your two on-premises VPN devices as described above. The result is a full mesh connectivity of 4 IPsec tunnels between your Azure virtual network and your on-premises network.
9 |
10 | All gateways and tunnels are active from the Azure side, so the traffic will be spread among all 4 tunnels simultaneously, although each TCP or UDP flow will again follow the same tunnel or path from the Azure side. Even though by spreading the traffic, you may see slightly better throughput over the IPsec tunnels, the primary goal of this configuration is for high availability. And due to the statistical nature of the spreading, it is difficult to provide the measurement on how different application traffic conditions will affect the aggregate throughput.
11 |
12 | This topology will require **two local network gateways** and **two connections** to support the pair of on-premises VPN devices, and **BGP is required** to allow the two connections to the same on-premises network.
13 |
14 | 
15 |
16 | More information on [dual-redundancy, active-active VPN gateways for both Azure and on-premises networks](https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-highlyavailable#dual-redundancy-active-active-vpn-gateways-for-both-azure-and-on-premises-networks).
17 |
18 | ### Enable Active-Active mode
19 | Go to Virtual Network Gateway `lab-gateway` in Configuration:
20 | * Active-active mode: Enables
21 | * Second Public IP Address: `hub-gateway-virtualip-2`
22 | * SKU: Basic
23 | * Configure BGP: enabled
24 | * ASN: 65513
25 | * Click *Save*
26 |
27 | Go to Virtual Network Gateway `on-prem-gateway` in Configuration:
28 | * Active-active mode: Enables
29 | * Second Public IP Address: `onprem-gateway-virtualip-2`
30 | * SKU: Basic
31 | * Configure BGP: enabled
32 | * ASN: 65514
33 | * Click *Save*
34 |
35 | # create Local Network Gateways
36 | create the following gateways
37 |
38 | | Name | IP Address | Address Space | Region | Configure BGP | ASN | BGP peer IP addr |
39 | |---|---|---|---|---|---|---|
40 | |cloud-net | (hub-gateway-virtualip) | 10.0.0.0/8| West Europe | yes | 65513 | 10.12.4.4 |
41 | |cloud-net-2 | (hub-gateway-virtualip-2) | 10.0.0.0/8| West Europe | yes | 65513 | 10.12.4.5 |
42 | |onprem-net| (onprem-gateway-virtualip) | 192.168.0.0/16 | France Central | yes | 65514 | 192.168.3.4 |
43 | |onprem-net-2| (onprem-gateway-virtualip-2) | 192.168.0.0/16 | France Central | yes | 65514 | 192.168.3.5 |
44 |
45 | > BGP peer IP addr is th "[default|second] Azure BGP peer IP address" you can see in Azure Virtual Network Gateway > Configuration pane
46 |
47 |
48 | # connection onprem-to-cloud
49 | Open `on-prem-gateway`, go to Connections and add the following object:
50 |
51 | * Connection Name: `onprem-to-cloud`
52 | * Type: Site-to-Site (IPsec)
53 | * virtual Network Gateway: `on-prem-gateway`
54 | * Local Network Gateway: `cloud-net`
55 | * Shared Key: `password.123`
56 | * Enable BGP: true
57 | * IKE: IKEv2
58 |
59 |
60 | # connection cloud-to-onprem
61 | Open `lab-gateway`, go to Connections and add the following object:
62 |
63 | * Connection Name: `cloud-to-onprem`
64 | * Type: Site-to-Site (IPsec)
65 | * virtual Network Gateway: `lab-gateway`
66 | * Local Network Gateway: `onprem-net`
67 | * Shared Key: `password.123`
68 | * Enable BGP: true
69 | * IKE: IKEv2
70 |
71 | # connection onprem-to-cloud-2
72 | Open `on-prem-gateway`, go to Connections and add the following object:
73 |
74 | * Connection Name: `onprem-to-cloud-2`
75 | * Type: Site-to-Site (IPsec)
76 | * virtual Network Gateway: `on-prem-gateway`
77 | * Local Network Gateway: `cloud-net-2`
78 | * Shared Key: `password.123`
79 | * Enable BGP: true
80 | * IKE: IKEv2
81 |
82 | # connection cloud-to-onprem-2
83 | Open `lab-gateway`, go to Connections and add the following object:
84 |
85 | * Connection Name: `cloud-to-onprem-2`
86 | * Type: Site-to-Site (IPsec)
87 | * virtual Network Gateway: `lab-gateway`
88 | * Local Network Gateway: `onprem-net-2`
89 | * Shared Key: `password.123`
90 | * Enable BGP: true
91 | * IKE: IKEv2
92 |
93 |
94 |
95 | after few minutes, you will see, on `on-prem-gateway` connections:
96 |
97 | | Name | Status | Connection Type | Peer |
98 | |---|---|---|---|
99 | |onprem-to-cloud | connected |Site-to-Site (IPsec)| cloud-net|
100 | |onprem-to-cloud-2 | connected |Site-to-Site (IPsec)| cloud-net-2|
101 |
102 | and on `lab-gateway` connections:
103 |
104 | | Name | Status | Connection Type | Peer |
105 | |---|---|---|---|
106 | |cloud-to-onprem | connected |Site-to-Site (IPsec)| onprem-net |
107 | |cloud-to-onprem-2 | connected |Site-to-Site (IPsec)| onprem-net |
108 |
109 | ## Test solution
110 | Via bastion go to W10onprem (`192.168.1.4`) and open a RDP connection to hub-vm-01 (`10.12.1.4`).
111 |
112 | Do the same test also from hub-vm-01 to W10onprem.
113 |
--------------------------------------------------------------------------------
/images/dns.drawio:
--------------------------------------------------------------------------------
1 | 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
--------------------------------------------------------------------------------
/scenarios/cross-on-premise-routing.md:
--------------------------------------------------------------------------------
1 | # SOLUTION - enable cross on-premise communication
2 |
3 | ## Pre-requisites
4 |
5 | in order to apply this solution you have to deploy hub, on prem and on prem 2 playgrounds.
6 |
7 | ## Solution
8 |
9 | ### Step 1 - enable BGP
10 | In resource Group `hub-and-spoke-playground` > `lab-gateway` > Configuration:
11 | * Configure BGP: **ENABLE**
12 | * ASN: **65514**
13 | * BGP peer IP: **10.12.4.254**
14 | * Save (if the portals shows an ERROR on save, just refresh the page, probably the configuration is ok anyway)
15 |
16 | In resource Group `on-premise-playground` > `on-prem-gateway` > Configuration:
17 | * Configure BGP: **ENABLE**
18 | * ASN: **65513**
19 | * BGP peer IP: **10.20.3.254**
20 | * Save (if the portals shows an ERROR on save, just refresh the page, probably the configuration is ok anyway)
21 |
22 | In resource Group `on-premise-2-playground` > `on-prem-2-gateway` > Configuration:
23 | * Configure BGP: **ENABLE**
24 | * ASN: **65512**
25 | * BGP peer IP: **192.168.3.254**
26 | * Save (if the portals shows an ERROR on save, just refresh the page, probably the configuration is ok anyway)
27 |
28 | ### Step 2: connecttion onprem-to-cloud
29 | Open `on-prem-gateway`, go to Connections and add the following object
30 | * Connection Name: `onprem-to-cloud`
31 | * Type: **VNet-to-VNet**
32 | * First virtual Network Gateway: `on-prem-gateway`
33 | * Second virtual Network Gateway: `lab-gateway`
34 | * Shared Key: `password.123`
35 | * BGP: **Enable**
36 | * IKE: **IKEv2**
37 |
38 | ### Step 3: connection cloud-to-onprem
39 | Open `lab-gateway`, go to Connections and add the following object
40 | * Connection Name: `cloud-to-onprem`
41 | * Type: VNet-to-VNet
42 | * First virtual Network Gateway: `lab-gateway`
43 | * Second virtual Network Gateway: `on-prem-gateway`
44 | * Shared Key: `password.123`
45 | * BGP: **Enable**
46 | * IKE: IKEv2
47 |
48 | ### Step 4: connection onprem-2-to-cloud
49 | Open `on-prem-2-gateway`, go to Connections and add the following object
50 | * Connection Name: `onprem-2-to-cloud`
51 | * Type: **VNet-to-VNet**
52 | * First virtual Network Gateway: `on-prem-2-gateway`
53 | * Second virtual Network Gateway: `lab-gateway`
54 | * Shared Key: `password.123`
55 | * BGP: **Enable**
56 | * IKE: **IKEv2**
57 |
58 | ### Step 5: connection cloud-to-onprem-2
59 | Open `lab-gateway`, go to Connections and add the following object
60 | * Connection Name: `cloud-to-onprem-2`
61 | * Type: **VNet-to-VNet**
62 | * First virtual Network Gateway: `lab-gateway`
63 | * Second virtual Network Gateway: `on-prem-gateway`
64 | * Shared Key: `password.123`
65 | * BGP: **Enable**
66 | * IKE: **IKEv2**
67 |
68 |
69 | ...after few minutes you will have the following connections estabilished in `lab-gateway` > connections:
70 |
71 | | Name | Status | Connection Type | Peer |
72 | |---|---|---|---|
73 | |cloud-to-onprem | connected |VNet-toVNet| on-prem-gateway |
74 | |cloud-to-onprem-2 | connected |VNet-toVNet| on-prem-2-gateway |
75 | |onprem-to-cloud | connected |VNet-toVNet| on-prem-gateway |
76 | |onprem-2-to-cloud | connected |VNet-toVNet| on-prem-2-gateway |
77 |
78 | 
79 |
80 |
81 | ## Test solution
82 | Verify in `lab-gateway` > BGP peers:
83 |
84 | | peer addr | local addr | ASN | Status |
85 | |---|---|---|---|
86 | | 10.20.3.254 | 10.12.4.254 | 65512 | connected |
87 | | 192.168.3.254 | 10.12.4.254 | 65513 | connected |
88 |
89 | and learned routes
90 |
91 | | Network | next hop | src peer | origin | AS path |
92 | |---|---|---|---|---|
93 | | 10.12.0.0/16 | - | 10.12.4.254 | network | |
94 | | 10.13.2.0/16 | - | 10.12.4.254 | network | |
95 | | 10.13.1.0/16 | - | 10.12.4.254 | network | |
96 | | 10.13.3.0/16 | - | 10.12.4.254 | network | |
97 | | 192.168.3.254/32 | - | 10.12.4.254 | network | |
98 | | 192.168.0.0/16 | 192.168.3.254 | 192.168.3.254 | Ebgp | 65513 |
99 | | 10.20.3.254/32 | - | 10.12.4.254 | network | |
100 | | 10.20.0.0/16 | 10.20.3.254 | 10.20.3.254 | Ebgp | 65512 |
101 |
102 | Verify in `on-premise-gateway` > BGP peers:
103 |
104 | | peer addr | local addr | ASN | Status |
105 | |---|---|---|---|
106 | | 10.12.4.254 | 192.168.3.254 | 65514 | connected |
107 |
108 | and learned routes
109 |
110 | | Network | next hop | src peer | origin | AS path |
111 | |---|---|---|---|---|
112 | | 192.168.0.0/16 | - | 192.168.3.254 | network | - |
113 | | 10.12.4.254/32 | - | 192.168.3.254 | network | - |
114 | | 10.12.0.0/16 | 10.12.4.254 | 10.12.4.254 | Ebgp | 65514 |
115 | | 10.13.2.0/16 | 10.12.4.254 | 10.12.4.254 | Ebgp | 65514 |
116 | | 10.13.1.0/16 | 10.12.4.254 | 10.12.4.254 | Ebgp | 65514 |
117 | | 10.13.3.0/16 | 10.12.4.254 | 10.12.4.254 | Ebgp | 65514 |
118 | | 10.20.0.0/16 | 10.12.4.254 | 10.20.3.254 | Ebgp | 65514-65512 |
119 |
120 | connect via SSH to `lin-onprem` machine onprem-2 and check RDP connection to `W10-onprem` machine:
121 |
122 | ```
123 | nicola@lin-onprem:~$ telnet 192.168.1.4 3389
124 | ```
125 |
126 | if the routing works you will receive the following message:
127 |
128 | ```
129 | Trying 192.168.1.4...
130 | Connected to 192.168.1.4.
131 | Escape character is '^]'.
132 | ^CConnection closed by foreign host.
133 | ```
134 |
135 | ## More information
136 | * BGP in Azure VPN Gateway Context: https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-bgp-overview
137 | * How to configure BGP: https://docs.microsoft.com/en-us/azure/vpn-gateway/bgp-howto
138 |
--------------------------------------------------------------------------------
/scenarios/routing-without-bgp-fw.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: Routing cross hubs without BGP
2 |
3 | This solution shows how to configure routing between all spokes of two network hubs, using Azure Firewalls and virtual network peerings as shown in the following schema.
4 |
5 | 
6 | _Download a [draw.io file](../images/routing-without-bgp-fw.drawio) of this architecture._
7 |
8 | ## Pre-requisites
9 |
10 | In order to apply this solution:
11 |
12 | * deploy the `hub playground`
13 | * deploy the `hub 02 playground`
14 |
15 | ## Solution
16 |
17 | Once both deployments are completed, the following steps are required to enable routing between all spokes:
18 |
19 | 1. create a peering between `hub-lab-net` and `hub-lab-02-net`
20 | 2. forward all traffic from spokes to the corresponding Azure Firewall
21 | 3. Configure routing between the 2 firewalls
22 | 4. Configure a network policy on each firewall that allows the transit
23 |
24 | ### Create a peering between `hub-lab-net` and `hub-lab-02-net`
25 |
26 | Go to `hub-lab-net` > Peerings and click to **add**:
27 |
28 | * Peering Network Name: `hub-to-hub-02`
29 | * Traffic to remote virtual network: `allow`
30 | * Traffic forwarded from remote virtual network: `allow`
31 | * Virtual network gateway or Route Server `none`
32 | * Remote virtual network Peering link name: `hub-02-to-hub`
33 | * Virtual Network: `hub-lab-02-net`
34 | * Traffic to remote virtual network: `allow`
35 | * Traffic forwarded from remote virtual network: `allow`
36 | * Virtual network gateway or Route Server `none`
37 |
38 | click **add**
39 |
40 | ### Forward all traffic from spokes to the corresponding Azure Firewall
41 |
42 | Create the following route tables:
43 |
44 | * route table name: `all-to-fw-westeurope`
45 | * Region: `west europe`
46 |
47 | Routes
48 |
49 | | Name | Address prefix | Next hop type | Next hop IP address |
50 | |---|---|---|---|
51 | | all | 0.0.0.0/0 | Virtual appliance | 10.12.3.4 |
52 |
53 | Subnets
54 |
55 | | Network | subnet |
56 | |---|---|
57 | | spoke-01 | default |
58 | | spoke-01 | services|
59 | | spoke-02 | default |
60 | | spoke-02 | services|
61 |
62 |
63 | * route table name: `all-to-fw-northeurope`
64 | * region: `north europe`
65 |
66 | Routes
67 |
68 | | Name | Address prefix | Next hop type | Next hop IP address |
69 | |---|---|---|---|
70 | | all | 0.0.0.0/0 | Virtual appliance | 10.12.3.4 |
71 |
72 | Subnets
73 |
74 | | Network | subnet |
75 | |---|---|
76 | | spoke-03 | default |
77 | | spoke-03 | services|
78 |
79 | * route table name: `all-to-fw-northeurope-02`
80 | * region: `north europe`
81 |
82 | Routes
83 |
84 | | Name | Address prefix | Next hop type | Next hop IP address |
85 | |---|---|---|---|
86 | | all | 0.0.0.0/0 | Virtual appliance | 10.14.3.4 |
87 |
88 | Subnets
89 |
90 | | Network | subnet |
91 | |---|---|
92 | | spoke-04 | default |
93 | | spoke-04 | services|
94 |
95 |
96 | ### Configure routing between the 2 firewalls
97 |
98 | Create the following route tables:
99 |
100 | * route table name: `firewall-hub`
101 | * Region: `west europe`
102 |
103 | Routes
104 |
105 | | Name | Address prefix | Next hop type | Next hop IP address |
106 | |---|---|---|---|
107 | | all1 | 10.15.1.0/24 | Virtual appliance | 10.14.3.4 |
108 | | all | 0.0.0.0/0 | internet | |
109 |
110 | Subnets
111 |
112 | | Network | subnet |
113 | |---|---|
114 | | hub-lab-net | AzureFirewallSubnet |
115 |
116 | * route table name: `firewall-hub-02`
117 | * Region: `north europe`
118 |
119 | Routes
120 |
121 | | Name | Address prefix | Next hop type | Next hop IP address |
122 | |---|---|---|---|
123 | | all1 | 10.13.1.0/24 | Virtual appliance | 10.12.3.4 |
124 | | all2 | 10.13.2.0/24 | Virtual appliance | 10.12.3.4 |
125 | | all3 | 10.13.3.0/24 | Virtual appliance | 10.12.3.4 |
126 | | all | 0.0.0.0/0 | internet | |
127 |
128 | Subnets
129 |
130 | | Network | subnet |
131 | |---|---|
132 | | hub-lab-02-net | AzureFirewallSubnet |
133 |
134 | ### Configure a network policy on each firewall that allows the transit
135 |
136 | Create the following Azure IP Group:
137 |
138 | * Name: `all-spokes-ipg`
139 | * region: `west europe`
140 | * IP Addresses:
141 | * 10.13.1.0/24
142 | * 10.13.2.0/24
143 | * 10.14.3.0/24
144 | * 10.15.1.0/24
145 |
146 | Create the following Firewall Policy:
147 |
148 | * Name: `my-firewall-policy`
149 | * policy tier: `standard`
150 | * add rules collection:
151 | * name: `my-rule-collection`
152 | * type: `network`
153 | * priority: `1000`
154 | * action: `allow`
155 |
156 | Rules
157 | | Name | Source type | Source | Protocol | Destination Ports | Destination Type | Destination
158 | |---|---|---|---|---|---|---|
159 | | all | ipgroup | `all-spokes-ipg` | any | * | ip address | * |
160 |
161 | associate this policy to both firewalls:
162 |
163 | * Azure Firewall Manager -> Virtual Networks -> `hub-lab-net` -> Security Provider -> Add Policy -> `my-firewall-policy`
164 | * Azure Firewall Manager -> Virtual Networks -> `hub-lab-02-net` -> Security Provider -> Add Policy -> `my-firewall-policy`
165 |
166 | ## Test Solution
167 | Now a machine connected to the `spoke-01` virtual network can connect to a machine connected to the `spoke-04` networrk.
168 |
169 | 1. connect to `spoke-01-vm` via RDP/bastion
170 | 2. once on the vm, open remote desktop connection and connect to `10.11.1.4` (a Windows VM)
171 |
172 | As bonus, because we also enabled outbound internet traffic from all spokes via firewalls, open Microsoft Edge and be sure you are able to open a web site.
--------------------------------------------------------------------------------
/scenarios/outbound-traffic-to-internet-firewall.md:
--------------------------------------------------------------------------------
1 | # Using Azure Firewall as a Gateway for All Outbound Traffic to the Internet
2 |
3 | Managing and securing outbound traffic is crucial for maintaining the integrity and performance of your network. Azure Firewall can be effectively used as a gateway for all outbound traffic to the internet.
4 |
5 | [Azure Firewall](https://learn.microsoft.com/en-us/azure/firewall/overview) is a managed, cloud-based network security service that protects your Azure Virtual Network resources. It is a stateful firewall as a service with built-in high availability and unrestricted cloud scalability. Azure Firewall provides both network and application-level protection across different subscriptions and virtual networks.
6 |
7 | Benefits of Using Azure Firewall for Outbound Traffic
8 |
9 | * Centralized Security Management: Azure Firewall allows you to manage and enforce [security policies](https://learn.microsoft.com/en-us/azure/firewall/policy-rule-sets) centrally, ensuring consistent security across your network.
10 | * Scalability: Azure Firewall [scales automatically](https://learn.microsoft.com/en-us/azure/firewall/firewall-performance) to meet your changing network traffic needs, providing high availability and resilience.
11 | * Advanced Threat Protection: With features like threat [intelligence-based filtering](https://learn.microsoft.com/en-us/azure/firewall/threat-intel), Azure Firewall can detect and block traffic from known malicious IP addresses and domains.
12 | * Integration with Other Azure Services: Azure Firewall integrates seamlessly with other Azure services like [Azure Monitor](https://learn.microsoft.com/en-us/azure/azure-monitor/fundamentals/overview), [Azure Sentinel](https://learn.microsoft.com/en-us/azure/sentinel/overview), and Azure Security Center, providing comprehensive security insights and management.
13 |
14 | In this walkthrough, I will show you how to set up Azure Firewall for this purpose.
15 |
16 | The target architecture will be the following:
17 |
18 | 
19 |
20 | _Download a [draw.io file](../images/outbound-traffic-internet-firewall.drawio) of this schema._
21 |
22 | ## Pre-requisites
23 |
24 | In order to apply this solution, deploy the **hub** playground first.
25 |
26 | ## Solution
27 |
28 | ### Create routing table in westeurope and associate it to spokes
29 |
30 | Go to Azure portal > route tables > click on Create
31 | * Region: `westeurope`
32 | * Name: `spokes-we-to-hub-routes`
33 | * Click CREATE
34 |
35 | Go to Azure Portal > route tables > `spokes-we-to-hub-routes` > routes > ADD
36 | * Name: `to-firewall`
37 | * Destination type: IP Address
38 | * IP Address: `0.0.0.0/0`
39 | * Next hop type: virtual appliance
40 | * next hop address: `10.12.3.4`
41 | * click CREATE
42 |
43 | Go to Azure Portal > route tables > `spokes-we-to-hub-routes` > subnets > associate
44 | |subnet name | virtual network |
45 | |------------|-----------------|
46 | | default | spoke-01 |
47 | | default | spoke-02 |
48 | | services | spoke-01 |
49 | | services | spoke-02 |
50 |
51 | ### Create routing table in northeurope and associate it to spoke
52 |
53 | Go to Azure portal > route tables > click on Create
54 | * Region: `northeurope`
55 | * Name: `spokes-ne-to-hub-routes`
56 | * Click CREATE
57 |
58 | Go to Azure Portal > route tables > `spokes-ne-to-hub-routes` > routes > ADD
59 | * Name: `to-firewall`
60 | * Destination type: IP Address
61 | * IP Address: `0.0.0.0/0`
62 | * Next hop type: virtual appliance
63 | * next hop address: `10.12.3.4`
64 | * click CREATE
65 |
66 | Go to Azure Portal > route tables > `spokes-ne-to-hub-routes` > subnets > associate
67 | |subnet name | virtual network |
68 | |------------|-----------------|
69 | | default | spoke-03 |
70 | | services | spoke-03 |
71 |
72 | ### Configure azure firewall policy
73 |
74 | Go to Azure Portal > Firewall Policies > Create
75 | * Name: `my-firewall-policy`
76 | * policy tier: premium
77 | * parent policy: none
78 | * Rules > Add a Rule Collection
79 | * Name: `rfc1918-collection`
80 | * rule collection type: Network
81 | * priority: `1000`
82 | * rule collection action: deny
83 | * rule name: `block-intranet-traffic`
84 | * source type: ip address
85 | * source: `10.13.1.0/24,10.13.2.0/24,10.13.3.0/24`
86 | * protocol: TCP + UDP
87 | * destination ports: `*`
88 | * destination type: ip address
89 | * destination: `10.0.0.0/8,172.16.0.0/12,192.168.0.0/16`
90 | * add
91 | * Rules > Add a Rule Collection
92 | * Name: `internet-collection`
93 | * rule collection type: Network
94 | * priority: `10000`
95 | * rule collection action: allow
96 | * rule name: `to-internet-rule`
97 | * source type: ip address
98 | * source: `10.13.1.0/24,10.13.2.0/24,10.13.3.0/24`
99 | * protocol: TCP + UDP
100 | * destination ports: `*`
101 | * destination type: ip address
102 | * destination: `*`
103 | * add
104 | * create
105 |
106 | Go to Azure Portal > Firewall manager > Azure Firewall policies
107 | * select `my-firewall-policy`
108 | * manage association > associate vnets
109 | * select `hub-lab-net`
110 | * cick add
111 |
112 | ## Test solution
113 |
114 | Go to Azure portal > virtual machines > `spoke-01-vm` > connect via bastion
115 |
116 | Install and run Noisy as described in to generate traffic.
117 |
118 | Wait a couple of minutes then go to Azure Portal > firewall > `lab-firewall` > logs > query hub > network rule log data > run
119 |
120 | you will see the outbound traffic data to internet from `10.13.1.4` (`spoke-01-vm`)
121 |
122 |
--------------------------------------------------------------------------------
/scenarios/elastic-san.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: create an Azure Elastic SAN and connect it to your Windows Server virtual machine
2 |
3 | Azure Elastic SAN is a fully integrated solution that simplifies deploying, scaling, managing, and configuring a storage area network (SAN), while also offering built-in cloud capabilities like high availability. Elastic SAN is interoperable with multiple types of compute resources such as Azure Virtual Machines, Azure VMware Solutions, and Azure Kubernetes Service.
4 |
5 | Azure Elastic SAN volumes can connect to a wide variety of compute resources using the Internet Small Computer Systems Interface (iSCSI) protocol. Because of this, rather than having to configure storage for each of your compute options, you can configure an Elastic SAN to serve as the storage solution for multiple compute options and manage it separately from each option.
6 |
7 | With an Elastic SAN, it's possible to scale your performance up to millions of IOPS, with double-digit GB/s throughput, and have single-digit millisecond latency. You can either increase your performance along with the storage capacity or increase the storage capacity without increasing the SAN's performance, potentially offering a lower total cost of ownership.
8 |
9 | One Elastic SAN can have one or more **Volume Groups**.
10 | One Volume Group can have one or more **Volumes**. A Volume Group is a management construct that you use to manage volumes at scale. Any settings or configurations applied to a Volume Group, such as virtual network rules, are inherited by any volumes associated with that Volume Group.
11 |
12 | A volume is a partition of the SAN's storage capacity.
13 |
14 | **In this page, I show how to connect an Azure Elastic SAN volume to a Windows Server
15 | virtual machine on Azure using the iSCSI protocol. **
16 | The Elastic SAN and the Virtual Machine are in the context of a hub and spoke architecture, so all the communication between the VM and the virtual SAN will occur through a private virtual network.
17 |
18 | The target architecture will be the following:
19 |
20 | 
21 |
22 | _Download a [draw.io file](../images/elastic-san.drawio) of this schema._
23 |
24 | ## Pre-requisites
25 |
26 | In order to apply this solution you have to deploy the `hub-playground` only.
27 | FOr this sample I have installed all hub's`` resources in `northeurope`.
28 |
29 | ## Solution
30 | Go to Elastic SANs > Create.
31 |
32 | * Name: `elastic-01`
33 | * Region: `north europe`
34 | * Redundancy: `LRS`
35 | * Resource provisioning on Elastic SAN:
36 | * Base: `1TiB`
37 | * Additional: `10TiB`
38 | * click CREATE
39 |
40 | Go to Elastic SANs > `elastic-01` > Networking and `disable` the public network access.
41 |
42 | ### Create a Volume Group and Volume
43 | Go to Elastic SANs > `elastic-01` > Volume groups > create
44 |
45 | * Name: `group-01`
46 | * Networking > create private endpoint
47 | * Location: `north europe`
48 | * Name: `elastic-01-private-endp`
49 | * Volume Group: `group-01`
50 | * Virtual Network: `spoke-01`
51 | * Subnet: `services`
52 | * Integrate with privare DNS zone: `yes` (`privatelink.blob.core.windows.net`)
53 | * click CREATE
54 |
55 | Go to Elastic SANs > `elastic-01` > Volumes > Create Volume
56 |
57 | * Volume Group: `group-01`
58 | * Name: `my-volume-01`
59 | * Source Type: `None`
60 | * Size: `10 GiB`
61 | * click CREATE
62 |
63 | Go to Private DNS Zones > `privatelink.blob.core.windows.net` > Virtual Network Link > Add
64 |
65 | * Link Name: `spoke-01-network-link`
66 | * Virtual Network: `Spoke-01`
67 | * click **CREATE**
68 |
69 | ## Test solution
70 |
71 | Go to Virtual Machines > `spoke-01-vm` > connect via bastion
72 |
73 | Open a `run as administrator` powershell ISE
74 |
75 | Install Multipath I/O, enable multipath support for iSCSI devices, and set a default load balancing policy using the following script:
76 |
77 | ```
78 | # Confirm iSCSI is running
79 | Get-Service -Name MSiSCSI
80 |
81 | # If it's not running, start it
82 | Start-Service -Name MSiSCSI
83 |
84 | # Set it to start automatically
85 | Set-Service -Name MSiSCSI -StartupType Automatic
86 |
87 | # Install Multipath-IO
88 | Add-WindowsFeature -Name 'Multipath-IO'
89 |
90 | # Verify if the installation was successful
91 | Get-WindowsFeature -Name 'Multipath-IO'
92 |
93 | # Enable multipath support for iSCSI devices
94 | Enable-MSDSMAutomaticClaim -BusType iSCSI
95 |
96 | # Set the default load balancing policy based on your requirements. In this example, we set it to round robin
97 | # which should be optimal for most workloads.
98 | mpclaim -L -M 2
99 | ```
100 |
101 | Go to Elastic SANs > `elastic-01` > Volumes > `my-volume-01` > Connect > Windows tab > View the code > copy to clipboard.
102 |
103 | Paste the code on the same powershell in a new script page, save it as e `.ps1` file and execute it.
104 |
105 | output will be similar to the following:
106 |
107 | ```
108 | my-volume-01 [iqn.xxxx-xx.net.windows.core.blob.ElasticSan.es-zzzzzzzzzz:my-volume-01]: Connecting to this volume
109 | Microsoft iSCSI Initiator Version 10.0 Build 17763
110 |
111 | The operation completed successfully.
112 |
113 | ...
114 |
115 | ```
116 |
117 | `iSCSI initiator` app on the virtual machine can give you more information about the virtual SAN connected to the server.
118 |
119 | The disk from elastic SAN is now available as storage in Windows.
120 |
121 | Go to Start menu > Disk Management: a popup will show you that a new disk is available. Select OK.
122 |
123 | In the list of disks you will se a Basic 10Gb Disk available and unallocated.
124 | Now you can format it and use as local storage of the machine.
125 |
126 | # More information
127 |
128 | *
129 | *
130 | *
131 |
132 |
--------------------------------------------------------------------------------
/on-prem-bicep/on-prem.bicep:
--------------------------------------------------------------------------------
1 | param location string = 'francecentral'
2 | param deployVM bool = true
3 | param username string = 'nicola'
4 | @secure()
5 | param password string = 'password.123'
6 | param virtualMachineSize string = 'Standard_D2_v5'
7 | param deployBastion bool = true
8 | param enableBgp bool = false
9 | param localGatewayFqdn string = ''
10 |
11 | var onPremNetworkName = 'on-prem-net'
12 |
13 | var bastionName = 'bastion-on-prem'
14 | var bastionIPName = 'bastion-on-prem-publicip'
15 |
16 | var vmOnPremDiskName = 'vm-w11-onprem-disk'
17 | var vmOnPremNicName = 'vm-w11-onprem-nic'
18 | var vmOnPremName = 'W11-onprem'
19 | var autoshutdownName = 'shutdown-computevm-${vmOnPremName}'
20 |
21 | var vnetGatewayIPName = 'onprem-gateway-virtualip'
22 | var vnetGatewayName = 'on-prem-gateway'
23 |
24 | var localGatewayName = 'lab-local-gateway'
25 |
26 | var subnets = concat(
27 | [
28 | { name: 'GatewaySubnet', properties: { addressPrefix: '192.168.3.0/24' } }
29 | { name: 'DefaultSubnet', properties: { addressPrefix: '192.168.1.0/24' } }
30 | ], deployBastion ? [
31 | {
32 | name: 'AzureBastionSubnet'
33 | properties: {
34 | addressPrefix: '192.168.2.0/24'
35 | }
36 | }
37 | ] : []
38 | )
39 |
40 | resource onpremvnet 'Microsoft.Network/virtualNetworks@2019-09-01' = {
41 | name: onPremNetworkName
42 | location: location
43 | properties: { addressSpace: { addressPrefixes: [ '192.168.0.0/16' ] }
44 | subnets: subnets
45 | }
46 | }
47 |
48 | resource bastionIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = if (deployBastion) {
49 | name: bastionIPName
50 | location: location
51 | sku: { name: 'Standard' }
52 | properties: { publicIPAllocationMethod: 'Static' }
53 | }
54 |
55 | resource bastion 'Microsoft.Network/bastionHosts@2019-09-01' = if (deployBastion) {
56 | name: bastionName
57 | location: location
58 | dependsOn: [ onpremvnet ]
59 | properties: {
60 | ipConfigurations: [ {
61 | name: 'ipconfig1'
62 | properties: {
63 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', onPremNetworkName, 'AzureBastionSubnet') }
64 | publicIPAddress: { id: bastionIP.id }
65 | }
66 | }
67 | ]
68 | }
69 | }
70 |
71 | resource onpremdisk 'Microsoft.Compute/disks@2019-07-01' = if (deployVM) {
72 | name: vmOnPremDiskName
73 | location: location
74 | properties: {
75 | creationData: { createOption: 'Empty' }
76 | diskSizeGB: 128
77 | }
78 | }
79 |
80 | resource vmonpremnic 'Microsoft.Network/networkInterfaces@2019-09-01' = if (deployVM) {
81 | name: vmOnPremNicName
82 | location: location
83 | dependsOn: [ onpremvnet ]
84 | properties: {
85 | ipConfigurations: [ {
86 | name: 'ipconfig1'
87 | properties: {
88 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', onPremNetworkName, 'DefaultSubnet') }
89 | privateIPAllocationMethod: 'Dynamic'
90 | }
91 | }
92 | ]
93 | }
94 | }
95 |
96 | resource vmonprem 'Microsoft.Compute/virtualMachines@2019-07-01' = if (deployVM) {
97 | name: vmOnPremName
98 | location: location
99 | dependsOn: []
100 | properties: {
101 | hardwareProfile: { vmSize: virtualMachineSize }
102 | storageProfile: {
103 | imageReference: { publisher: 'MicrosoftWindowsDesktop', offer: 'windows-11', sku: 'win11-24h2-ent', version: 'latest' }
104 | dataDisks: [ {
105 | lun: 0
106 | name: vmOnPremDiskName
107 | createOption: 'Attach'
108 | managedDisk: { id: onpremdisk.id }
109 | }
110 | ]
111 | }
112 | osProfile: {
113 | computerName: vmOnPremName
114 | adminUsername: username
115 | adminPassword: password
116 | windowsConfiguration: { enableAutomaticUpdates: true }
117 | }
118 | networkProfile: {
119 | networkInterfaces: [ {
120 | id: vmonpremnic.id
121 | }
122 | ]
123 | }
124 | }
125 | }
126 |
127 | resource shutdownVm04 'microsoft.devtestlab/schedules@2018-09-15' = if (deployVM) {
128 | name: autoshutdownName
129 | location: location
130 | properties: {
131 | status: 'Enabled'
132 | taskType: 'ComputeVmShutdownTask'
133 | timeZoneId: 'UTC'
134 | dailyRecurrence: { time: '20:00' }
135 | notificationSettings: { status: 'Disabled' }
136 | targetResourceId: vmonprem.id
137 | }
138 | }
139 |
140 | resource vnetGatewayIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = {
141 | name: vnetGatewayIPName
142 | location: location
143 | sku: { name: 'Standard' }
144 | properties: { publicIPAllocationMethod: 'Static'}
145 | }
146 |
147 | resource vnetGateway1 'Microsoft.Network/virtualNetworkGateways@2022-09-01' = {
148 | name: vnetGatewayName
149 | location: location
150 | dependsOn: [ onpremvnet ]
151 | properties: {
152 | ipConfigurations: [ {
153 | name: 'ipconfig1'
154 | properties: {
155 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', onPremNetworkName, 'GatewaySubnet') }
156 | publicIPAddress: { id: vnetGatewayIP.id }
157 | }
158 | }
159 | ]
160 | gatewayType: 'Vpn'
161 | vpnType: 'RouteBased'
162 | enableBgp: enableBgp
163 | bgpSettings: enableBgp ? {
164 | asn: 65510
165 | } : null
166 | sku: { name: 'VpnGw1', tier: 'VpnGw1' }
167 | }
168 | }
169 |
170 | // if we enable bgp we also need a local gateway for the connection
171 | resource localGateway 'Microsoft.Network/localNetworkGateways@2022-09-01' = if (enableBgp) {
172 | name: localGatewayName
173 | location: location
174 | properties: {
175 | bgpSettings: {
176 | asn: 65511
177 | bgpPeeringAddress: '192.168.3.254'
178 | }
179 | localNetworkAddressSpace: {
180 | addressPrefixes: [
181 | '10.12.0.0/16'
182 | '10.13.1.0/24'
183 | '10.13.2.0/24'
184 | '10.13.3.0/24'
185 | ]
186 | }
187 | fqdn: localGatewayFqdn
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/scenarios/ping-any-to-any-firewall.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: Allows machines in ANY spoke to communicate with ANY machine in ANY other spoke (Azure Firewall)
2 |
3 | In the dynamic realm of cloud computing, efficiently managing and scaling your networks are of paramount importance.
4 | An integral part of this network management is setting up a 'Hub and Spoke' topology and enabling connectivity between spokes.
5 |
6 | In the Hub and Spoke model, the **routing between different Spokes is a significant element**. Each spoke identify often an application or a workload, and there enterprise elements have to interact each other.
7 |
8 | There are 3 main approaches to allow this traffic:
9 |
10 | * Direct spokes peering (implemented by-hand or automated with a Virtual Networks Manager)
11 | * Virtual Network Gateway in the Hub
12 | * Firewall in the Hub
13 |
14 | each approach has pros and cons.
15 |
16 | ### Direct Spokes Peering
17 | Pros:
18 | * Direct Spokes peering allows more efficient data transfer as it reduces latency caused by passing through the hub.
19 |
20 | Cons:
21 | * It may create a complex network with multiple connection points, making it harder to monitor and manage.
22 | * Potential security risks can increase as each Spoke is exposed to the other, increasing the risk of vulnerability.
23 |
24 | ### Network Gateway in the Hub as Default Gateway
25 |
26 | Pros:
27 |
28 | * It is an appliance already used for S2S VPN so you do not need an additional element to manage.
29 | * The network topology is simple because only one peering for each spoke is required.
30 | * Streamlines operations by providing a uniform route for data traffic, potentially increasing efficiency
31 |
32 | Cons:
33 |
34 | * It might introduce additional latency as all traffic must pass through the hub before reaching the destination Spoke.
35 | * Potential for the hub add for the gateway **to become a bottleneck**, because it already is used also for S2S and P2S VPNs.
36 | * Potential security risks can increase as each Spoke is exposed to the other, increasing the risk of vulnerability.
37 |
38 | ### Firewall in the Hub as Network Appliance
39 |
40 | Pros:
41 |
42 | * Streamlines operations by providing a uniform route for data traffic, potentially increasing efficiency
43 | * Enhanced security measures as the firewall provides a protective layer preventing unauthorized access.
44 | * It ensures that all traffic can be inspected and sanitized before it reaches its destination.
45 | * Centralized network policy enforcement improves manageability and control.
46 |
47 | Cons
48 |
49 | * It might introduce additional latency as all traffic must pass through the hub before reaching the destination Spoke.
50 | * Increased costs due to the need for the firewall appliance and traffic logging
51 | * Potential for the firewall to be a single point of failure if not properly configured.
52 |
53 |
54 | In this solution, I show you how configure the routing between spokes using an **Azure Firewall** in the middle.
55 |
56 | Azure Firewall (PaaS) holds a significant edge over a traditional firewall on Azure.
57 | Here are a few reasons why:
58 | * **Fully Integrated**: Azure Firewall is natively integrated with Azure. This means that it works seamlessly with other Azure services and tools, including Azure Monitor, Azure Security Center and Azure Log Analytics.
59 | * **Scalability**: Unlike traditional firewalls which may need manual configuration to scale, Azure Firewall PaaS automatically scales up and down based on network traffic loads.
60 | * **Advanced Threat Intelligence**: Azure Firewall PaaS is backed by Microsoft's global threat intelligence, helping to identify and block known malicious IPs and domains.
61 | * **Built-in High Availability**: Azure Firewall PaaS offers built-in high availability, eliminating the need for additional load balancers or other devices to ensure constant uptime.
62 | * **Zero Maintenance**: As a fully managed service, Azure Firewall PaaS doesn't require any patching or maintenance.
63 |
64 | The resulting overall architecture is shown in the following schema.
65 |
66 | 
67 |
68 | _Download a [draw.io file](../images/any-to-any-routing.drawio) of this architecture._
69 |
70 |
71 | ## Pre-requisites
72 |
73 | In order to apply this solution you have to deploy hub playground only.
74 |
75 | ## Solution
76 |
77 | In order to allow this communication, via **Azure Firewall**:
78 |
79 | Create the following route table in `west europe`: `spokes-we-to-hub-routes`
80 |
81 | | Name | Address Prefix | Next hop type | Next hop IP addr |
82 | |---|---|---|---|
83 | | to-spoke-01 | 10.13.1.0/24 | Virtual Appliance | 10.12.3.4 |
84 | | to-spoke-02 | 10.13.2.0/24 | Virtual Appliance | 10.12.3.4 |
85 | | to-spoke-03 | 10.13.3.0/24 | Virtual Appliance | 10.12.3.4 |
86 |
87 |
88 | | subnet Name | Virtual Network |
89 | |---|---|
90 | | default | spoke-01 |
91 | | services | spoke-01 |
92 | | default | spoke-02 |
93 | | services | spoke-02 |
94 |
95 | Create the following route table in `north europe`: `spokes-ne-to-hub-routes`
96 |
97 | | Name | Address Prefix | Next hop type | Next hop IP addr |
98 | |---|---|---|---|
99 | | to-spoke-01 | 10.13.1.0/24 | Virtual Appliance | 10.12.3.4 |
100 | | to-spoke-02 | 10.13.2.0/24 | Virtual Appliance | 10.12.3.4 |
101 |
102 | | subnet Name | Virtual Network |
103 | |---|---|
104 | | default | spoke-03 |
105 | | services | spoke-03 |
106 |
107 | Create the following `IP Groups` in `west europe`:
108 | * `group-spoke-01`: 10.13.1.0/24
109 | * `group-spoke-02`: 10.13.2.0/24
110 | * `group-spoke-03`: 10.13.3.0/24
111 |
112 | Create the following Firewall Policy: `hub-fw-policy`
113 |
114 | **Network Rules**:
115 | * Rule Collection Name: `my-collection`
116 | * Rule Collection type: Network
117 | * Priority: `1000`
118 | * Rule Colletion action: `Allow`
119 | * Rule Collection group: `DefaultNetworkRuleCollectionGroup`
120 |
121 |
122 | | rule name | source | port | protocol | destination |
123 | |---|---|---|---|---|
124 | | all-to-all | group-spoke-01
group-spoke-02
group-spoke-03 | * | Any | group-spoke-01
group-spoke-02
group-spoke-03 |
125 |
126 | Associate the policy `hub-fw-policy` to `lab-firewall` via Firewall Manager.
127 |
128 | ## Test Solution
129 | Test connections using remote desktop client and ssh from one machine to another.
130 |
131 | # More information
132 | * Azure [Firewall](https://learn.microsoft.com/en-us/azure/firewall/overview)
133 | * Azure [route tables](https://learn.microsoft.com/en-us/azure/virtual-network/manage-route-table)
134 | * Azure [Firewall policy](https://learn.microsoft.com/en-us/azure/firewall-manager/policy-overview)
--------------------------------------------------------------------------------
/any-to-any-bicep/any-to-any.bicep:
--------------------------------------------------------------------------------
1 | @description('Basic, Standard or Premium tier')
2 | @allowed([ 'Basic', 'Standard', 'Premium' ])
3 | param firewallTier string = 'Premium'
4 |
5 | param disableBgpRoutePropagation bool = false
6 |
7 | @description('Additional IP addresses or subnets to add in the firewall rules')
8 | param allowIpAddresses array = []
9 |
10 | var routeTables_all_to_firewall_we_name = 'all-to-firewall-we'
11 | var routeTables_all_to_firewall_ne_name = 'all-to-firewall-ne'
12 |
13 | var hubName = 'hub-lab-net'
14 | var spoke01Name = 'spoke-01'
15 | var spoke02Name = 'spoke-02'
16 | var spoke03Name = 'spoke-03'
17 |
18 | param locationWE string = 'westeurope'
19 | param locationNE string = 'northeurope'
20 |
21 | var firewallName = 'lab-firewall'
22 | var firewallIPName = 'lab-firewall-ip'
23 | var firewallIpAddress = '10.12.3.4'
24 |
25 | module fwPolicy './fw-policy.bicep' = {
26 | name: 'fwPolicyDeploy'
27 | params: {
28 | firewallTier: firewallTier
29 | locationWE: locationWE
30 | allowIpAddresses: allowIpAddresses
31 | }
32 | }
33 |
34 | resource routeTableWE 'Microsoft.Network/routeTables@2020-05-01' = {
35 | name: routeTables_all_to_firewall_we_name
36 | location: locationWE
37 | properties: {
38 | disableBgpRoutePropagation: disableBgpRoutePropagation
39 | routes: [
40 | {
41 | name: 'all-to-firewall-we'
42 | properties: {
43 | addressPrefix: '0.0.0.0/0'
44 | nextHopType: 'VirtualAppliance'
45 | nextHopIpAddress: firewallIpAddress
46 | }
47 | }
48 | ]
49 | }
50 | }
51 |
52 | resource routeTableGateway 'Microsoft.Network/routeTables@2020-05-01' = {
53 | name: 'gateway-route'
54 | location: locationWE
55 | properties: {
56 | disableBgpRoutePropagation: disableBgpRoutePropagation
57 | routes: [
58 | {
59 | name: 'spoke-01'
60 | properties: {
61 | addressPrefix: '10.13.1.0/24'
62 | nextHopType: 'VirtualAppliance'
63 | nextHopIpAddress: firewallIpAddress
64 | }
65 | }
66 | {
67 | name: 'spoke-02'
68 | properties: {
69 | addressPrefix: '10.13.2.0/24'
70 | nextHopType: 'VirtualAppliance'
71 | nextHopIpAddress: firewallIpAddress
72 | }
73 | }
74 | {
75 | name: 'spoke-03'
76 | properties: {
77 | addressPrefix: '10.13.3.0/24'
78 | nextHopType: 'VirtualAppliance'
79 | nextHopIpAddress: firewallIpAddress
80 | }
81 | }
82 | ]
83 | }
84 | }
85 |
86 | resource routeTableNE 'Microsoft.Network/routeTables@2020-05-01' = {
87 | name: routeTables_all_to_firewall_ne_name
88 | location: locationNE
89 | properties: {
90 | disableBgpRoutePropagation: disableBgpRoutePropagation
91 | routes: [
92 | {
93 | name: 'all-to-firewall-ne'
94 | properties: {
95 | addressPrefix: '0.0.0.0/0'
96 | nextHopType: 'VirtualAppliance'
97 | nextHopIpAddress: firewallIpAddress
98 | }
99 | }
100 | ]
101 | }
102 | }
103 |
104 | resource subnetS01default 'Microsoft.Network/virtualNetworks/subnets@2020-05-01' = {
105 | name: '${spoke01Name}/default'
106 | properties: {
107 | addressPrefix: '10.13.1.0/26'
108 | routeTable: {
109 | id: routeTableWE.id
110 | }
111 | privateEndpointNetworkPolicies: 'Enabled'
112 | }
113 | }
114 |
115 | resource subnetS01services 'Microsoft.Network/virtualNetworks/subnets@2020-05-01' = {
116 | name: '${spoke01Name}/services'
117 | dependsOn: [ // possible race condition where the route table is being associated with two different subnets at the same time
118 | subnetS01default
119 | ]
120 | properties: {
121 | addressPrefix: '10.13.1.64/26'
122 | routeTable: {
123 | id: routeTableWE.id
124 | }
125 | privateEndpointNetworkPolicies: 'Enabled'
126 | }
127 | }
128 |
129 | resource subnetS02default 'Microsoft.Network/virtualNetworks/subnets@2020-05-01' = {
130 | name: '${spoke02Name}/default'
131 | properties: {
132 | addressPrefix: '10.13.2.0/26'
133 | routeTable: {
134 | id: routeTableWE.id
135 | }
136 | privateEndpointNetworkPolicies: 'Enabled'
137 | }
138 | }
139 |
140 | resource subnetS02services 'Microsoft.Network/virtualNetworks/subnets@2020-05-01' = {
141 | name: '${spoke02Name}/services'
142 | dependsOn: [ // possible race condition where the route table is being associated with two different subnets at the same time
143 | subnetS02default
144 | ]
145 | properties: {
146 | addressPrefix: '10.13.2.64/26'
147 | routeTable: {
148 | id: routeTableWE.id
149 | }
150 | privateEndpointNetworkPolicies: 'Enabled'
151 | }
152 | }
153 |
154 | resource subnetS03default 'Microsoft.Network/virtualNetworks/subnets@2020-05-01' = {
155 | name: '${spoke03Name}/default'
156 | properties: {
157 | addressPrefix: '10.13.3.0/26'
158 | routeTable: {
159 | id: routeTableNE.id
160 | }
161 | privateEndpointNetworkPolicies: 'Enabled'
162 | }
163 | }
164 |
165 | resource subnetS03services 'Microsoft.Network/virtualNetworks/subnets@2020-05-01' = {
166 | name: '${spoke03Name}/services'
167 | dependsOn: [ // possible race condition where the route table is being associated with two different subnets at the same time
168 | subnetS03default
169 | ]
170 | properties: {
171 | addressPrefix: '10.13.3.64/26'
172 | routeTable: {
173 | id: routeTableNE.id
174 | }
175 | privateEndpointNetworkPolicies: 'Enabled'
176 | }
177 | }
178 |
179 | resource subnetGateway 'Microsoft.Network/virtualNetworks/subnets@2020-05-01' = {
180 | name: '${hubName}/GatewaySubnet'
181 | properties: {
182 | addressPrefix: '10.12.4.0/24'
183 | routeTable: {
184 | id: routeTableGateway.id
185 | }
186 | }
187 | }
188 |
189 | resource firewallIP 'Microsoft.Network/publicIPAddresses@2019-09-01' = {
190 | name: firewallIPName
191 | location: locationWE
192 | sku: { name: 'Standard' }
193 | properties: { publicIPAllocationMethod: 'Static' }
194 | }
195 |
196 | resource azureFirewalls_lab_firewall_name_resource 'Microsoft.Network/azureFirewalls@2022-07-01' = {
197 | name: firewallName
198 | location: locationWE
199 | properties: {
200 | sku: { name: 'AZFW_VNet', tier: firewallTier }
201 | ipConfigurations: [ {
202 | name: 'ipconfig1'
203 | properties: {
204 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', hubName, 'AzureFirewallSubnet') }
205 | publicIPAddress: { id: firewallIP.id }
206 | }
207 | }
208 | ]
209 | firewallPolicy: {
210 | id: fwPolicy.outputs.policyid
211 | }
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/scenarios/p2s-vpn-certificate.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: Configure a P2S VPN with Certificate Authentication
2 |
3 | This solution shows how to configure a P2S VPN to allow individual clients running Windows, Linux, or macOS to an Azure Virtual Network using **Azure certificate authentication**. Point-to-site VPN connections are useful when you want to connect to your VNet from a remote location, such as when you're telecommuting from home or a conference.
4 |
5 | Point-to-site VPN can use one of the following protocols:
6 |
7 | * **OpenVPN® Protoco**l, an SSL/TLS based VPN protocol. A TLS VPN solution can penetrate firewalls, since most firewalls open TCP port 443 outbound, which TLS uses. OpenVPN can be used to connect from Android, iOS (versions 11.0 and above), Windows, Linux, and Mac devices (macOS versions 10.13 and above).
8 |
9 | * S**ecure Socket Tunneling Protocol** (SSTP), a proprietary TLS-based VPN protocol. A TLS VPN solution can penetrate firewalls, since most firewalls open TCP port 443 outbound, which TLS uses. SSTP is only supported on Windows devices. Azure supports all versions of Windows that have SSTP and support TLS 1.2 (Windows 8.1 and later).
10 |
11 | * **IKEv2 VPN**, a standards-based IPsec VPN solution. IKEv2 VPN can be used to connect from Mac devices (macOS versions 10.11 and above).
12 |
13 | Before Azure accepts a P2S VPN connection, the user has to be authenticated first. There are two mechanisms that Azure offers to authenticate a connecting user:
14 |
15 | * **Certificate Authentication**: When using the native Azure certificate authentication, a client certificate that is present on the device is used to authenticate the connecting user.
16 | * **Azure Active Directory Authentication**: Azure AD authentication allows users to connect to Azure using their Azure Active Directory credentials. Native Azure AD authentication is only supported for OpenVPN protocol and also requires the use of the Azure VPN Client
17 |
18 | More information:
19 | * on P2S VPN: https://learn.microsoft.com/en-us/azure/vpn-gateway/point-to-site-about
20 | * configure Certificate Authentication: https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-howto-point-to-site-resource-manager-portal
21 |
22 |
23 | ## Pre-requisites
24 |
25 | In order to apply this solution:
26 |
27 | 1. deploy the **hub** playground
28 | 2. deploy the **on-premise** playground
29 |
30 | ## Solution
31 |
32 | ### Generate Root Certificates
33 | Point-to-site connections use certificates to authenticate. The PowerShell cmdlets that you use will use to generate certificates are part of the operating system and don't work on other versions of Windows. The host operating system is only used to generate the certificates. Once the certificates are generated, you can upload them or install them on any supported client operating system.
34 |
35 | * Access to `W10-OnPrem` via RDP/Bastion
36 | * Create the self-signed root certificate `playground-certificate` with the following powershell command
37 |
38 | ```
39 | $cert = New-SelfSignedCertificate -Type Custom -KeySpec Signature `
40 | -Subject "CN=playground-certificate" -KeyExportPolicy Exportable `
41 | -HashAlgorithm sha256 -KeyLength 2048 `
42 | -CertStoreLocation "Cert:\CurrentUser\My" -KeyUsageProperty Sign -KeyUsage CertSign
43 | ```
44 |
45 | * create a client certificate `playground-client-certificate` with the following powershell command
46 |
47 | ```
48 | New-SelfSignedCertificate -Type Custom -DnsName MyP2SClientCert01 -KeySpec Signature `
49 | -Subject "CN=playground-client-certificate" -KeyExportPolicy Exportable `
50 | -HashAlgorithm sha256 -KeyLength 2048 `
51 | -CertStoreLocation "Cert:\CurrentUser\My" `
52 | -Signer $cert -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.2")
53 |
54 | ```
55 |
56 | For more information, [this article shows](https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-certificates-point-to-site) you how to create a self-signed root certificate and generate client certificates using PowerShell on Windows 10 or later, or Windows Server 2016 or later.
57 |
58 | ### Export the root certificate
59 |
60 | * Open `certmgr.exe` and locate the self-signed root certificate, in "Certificates - **Current User**\Personal\Certificates\`playground-certificate`", and right-click. Click All Tasks -> Export. This opens the Certificate Export Wizard.
61 | * select **NO, do NOT export the private key**
62 | * select `Base-64 encoded (.CER)`
63 | * filename: `c:\root-cert-exported.cer`
64 |
65 | ### Export the client certificate
66 | Because we will test the P2S VPN from the client computer used to create the certificate, you do not need to export it. If you want to create a P2S connection from a client computer other than the one you used to generate the client certificates, you need to install a client certificate. When installing a client certificate, you need the password that was created when the client certificate was exported.
67 |
68 | Make sure the client certificate was exported as a .pfx along with the entire certificate chain (which is the default). Otherwise, the root certificate information isn't present on the client computer and the client won't be able to authenticate properly.
69 |
70 | more information: https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-certificates-point-to-site#clientexport
71 |
72 |
73 | ### Add the VPN client address pool
74 | The client address pool is a range of private IP addresses that you specify. The clients that connect over a point-to-site VPN dynamically receive an IP address from this range. Use a private IP address range that doesn't overlap with the on-premises location that you connect from, or the VNet that you want to connect to. If you configure multiple protocols and SSTP is one of the protocols, then the configured address pool is split between the configured protocols equally.
75 |
76 | Go to `lab-gateway` > Point 2 Site Configuration > Configure Now:
77 | * Address pool: `10.14.1.0/24`
78 | * Tunnel Type: `Open VPN`
79 | * Authentication Type: `Azure Certificate`
80 | * Root Certificate Name: `my-root-certificate`
81 | * open file `c:\root-cert-exported.cer` saved before with Notepad and copy all the text between ---BEGIN--- and ---END CERTIFICATE--- rows in the `public certificate data` field:
82 |
83 | ```
84 | -----BEGIN CERTIFICATE-----
85 | MIIC/TCCAeWgAwIBAgIQF2MY6gVnqKRB9OqCSvetITANBgkqhkiG9w0BAQsFADAh
86 | MR8wHQYDVQQDDBZwbGF5Z3JvdW5kLWNlcnRpZmljYXRlMB4XDTIyMTAxOTA4MzE1
87 | NFoXDTIzMTAxOTA4NTE1NFowITEfMB0GA1UEAwwWcGxheWdyb3VuZC1jZXJ0aWZp
88 |
89 | ... ...
90 |
91 | jHX5Wtexk5abytL0fjBB4SMzPkzVC+bGmSBdsZHuYnsmYyw9KaflYAAob9WK3a8U
92 | gQ==
93 | -----END CERTIFICATE-----
94 | ```
95 |
96 | * click **SAVE**
97 |
98 | ## Test Solution
99 | * Download VPN connection data from Azure Portal on `W10-OnPrem`.
100 | * Download and install the latest version of the Azure VPN Client install files using one of the following links: https://aka.ms/azvpnclientdownload
101 | * open Azure VPN Client and click on (+) > import
102 | * select azurevpnconfig.xml
103 | * Authentication Type: `Certificate`
104 | * Certificate Information: select `playground-client-certificate`
105 | * Click SAVE
106 | * Click CONNECT
107 |
108 | Once connected, open a `remote desktop connection` app and connect to `10.13.1.4`
109 |
--------------------------------------------------------------------------------
/scenarios/frontdoor.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: Expose via Azure Front Door an internal web server located on a VM on a spoke
2 |
3 | This scenario demonstrates how to securely expose an internal web server using Azure Front Door without directly exposing the VM to the internet.
4 |
5 | > Azure Front Door is a modern cloud Content Delivery Network (CDN) service that provides fast, reliable, and secure access to your global applications.
6 | It operates at the edge of Microsoft's global network, directing web traffic to the fastest and most available application backend.
7 | Front Door provides your users with a global, scalable, and secure entry point to your web applications.
8 |
9 | Keeping the origin server (backend) private offers several security benefits:
10 |
11 | 1. **Reduced attack surface**: Prevents direct attacks on your origin infrastructure
12 | 2. **DDoS protection**: Front Door helps mitigate DDoS attacks by absorbing traffic at the edge
13 | 3. **Enhanced security posture**: Implements defense-in-depth by hiding your backend resources
14 | 4. **Enforced traffic routing**: All traffic must pass through Front Door's security controls
15 | 5. **Centralized WAF protection**: Web Application Firewall can filter malicious traffic at the edge
16 |
17 | Azure Frontdoor, Azure Application gateway and Azure Load balancers all allow to expose an internal resource on internet. When choose azure frontdoor vs Application gatewa or Azure load balancers?
18 |
19 | Azure Frontdoor and Azure Application gateway, while both services provide WAF and routing capabilities, they have different strengths:
20 |
21 | - **Global vs Regional**: Front Door is a global service, while Application Gateway is regional
22 | - **Edge security**: Front Door filters traffic at Microsoft's edge locations worldwide
23 | - **Scalability**: Front Door auto-scales globally without manual intervention
24 | - **Protocol optimization**: Front Door optimizes HTTP/S protocols over Microsoft's global network
25 | - **Multi-region architecture**: Better suited for applications with global reach and multi-region backends
26 |
27 | Azure Front Door provides also several advantages over a simple public load balancer:
28 |
29 | - **Content delivery network**: Caches content closer to users for faster delivery
30 | - **SSL offloading**: Handles SSL/TLS termination at the edge
31 | - **Intelligent routing**: Directs users to closest/healthiest backend
32 | - **Security features**: Built-in WAF capabilities to protect against web vulnerabilities
33 | - **Global health probes**: Continuous monitoring of backend health across regions
34 |
35 | The final architecture involves the following resources:
36 |
37 | - Azure Front Door Premium with WAF
38 | - Private Link Service
39 | - Internal Load Balancer
40 | - Virtual Machine in a spoke network running a web server
41 |
42 | as shown below:
43 |
44 | 
45 |
46 | _Download a [draw.io file](../images/frontdoor.drawio) of this schema._
47 |
48 | ## Pre-requisites
49 |
50 | In order to apply this solution you have to deploy the `hub-playground` only.
51 | For this sample I have installed all hub's resources in `northeurope`.
52 |
53 | ## Solution
54 |
55 | ### Activate a web server on `spoke-01-vm`
56 |
57 | Go to Azure Portal > virtual machines > spoke-01-vm > connect via bastion
58 |
59 | Once logged in, open PowerShell and type the following:
60 |
61 | ``` powershell
62 | Install-WindowsFeature -name Web-Server -IncludeManagementTools
63 | Remove-Item -Path 'C:\inetpub\wwwroot\iisstart.htm'
64 | Add-Content -Path 'C:\inetpub\wwwroot\iisstart.htm' -Value $($env:computername)
65 | ```
66 |
67 | ### Create an internal load balancer
68 |
69 | Go to Azure Portal > Load balancers > Create > standard load balancer
70 |
71 | #### Basic
72 | * Name: `nlb-01`
73 | * Region: `northeurope`
74 |
75 | #### Frontend IP configuration
76 |
77 | Add frontend IP Configuration:
78 | * Name: `nlb-frontend-01`
79 | * IP version: IPv4
80 | * Virtual network: `spoke-01`
81 | * Subnet: `default`
82 | * Assignment: Dynamic
83 | * Availability zone: Zone redundant
84 |
85 |
86 | #### Backend pools
87 |
88 | Add a backend pool:
89 | * Name: `nlb-backend-01`
90 | * Backend pool configuration: NIC
91 | * Resource name: `spoke-01-vm`
92 | * Click **SAVE**
93 |
94 | #### Inbound rules
95 |
96 | Add a load balancing rule:
97 |
98 | * Name: `lb-rule-01`
99 | * IP version: IPv4
100 | * Frontend IP address: `nlb-frontend-01`
101 | * Backend pool: `nlb-backend-01`
102 | * Protocol: TCP
103 | * Inbound port: `80`
104 | * Backend port: `80`
105 | * Create a new health probe:
106 | * Name: `probe-01`
107 | * Protocol: HTTP
108 | * Port: `80`
109 | * Path: `/`
110 | * Interval: `5` sec
111 | * Click **SAVE**
112 | * Click **SAVE**
113 |
114 | #### Outbound rule
115 |
116 | None.
117 |
118 | #### Review and create
119 |
120 | Click **CREATE**
121 |
122 | ### Create a Private Link Service
123 |
124 | Go to Azure Portal > Private Link Services > Create
125 |
126 | #### Basics
127 | * Name: `pls-nlb-01`
128 | * Region: `northeurope`
129 |
130 | #### Outbound settings
131 | * Load Balancer: `nlb-01`
132 | * Load balancer frontend IP address: `10.13.1.5`
133 | * Source NAT subnet: `default`
134 |
135 | #### Review and create
136 | Click **CREATE**.
137 |
138 | ### Create an Azure Front Door
139 |
140 | Go to Azure Portal > Front Doors > Create > Azure Front Door/quick create
141 |
142 | #### Configuration
143 | * Name: `afd-01`
144 | * Tier: Premium
145 | * Endpoint name: `afd-01`
146 | * SKU: Standard
147 | * Origin type: Custom
148 | * Origin host name: `10.13.1.5` (internal load balancer, front end IP)
149 | * Private link service: ENABLE
150 | * Select private link: In my directory
151 | * Resource: `pls-nlb-01`
152 | * Request message: `please approve me`
153 | * WAF policy: Create new:
154 | * Name: `afdwaf01`
155 | * Bot protection: ON
156 | * Click **CREATE**
157 | * Click **CREATE**
158 |
159 | Go to Azure Portal > Private Link Services > **pls-nlb-01** > Private endpoint connections.
160 | You will find 1 private endpoint connection, select it, then click to **APPROVE**
161 |
162 | The connection state should change to Approved. It might take a couple of minutes for the connection to fully establish.
163 | You can now access your internal load balancer from Azure Front Door.
164 |
165 | Go to Azure Portal > **afd-01** > Front Door Manager > **default-route** > Update and change forwarding protocol to **HTTP only** (because your backend VM exposes an HTTP-only web server).
166 |
167 | ## Test solution
168 | Because Azure Front Door is a globally distributed service, each update can require up to 10-20 minutes to propagate everywhere. After this time, open the `afd-01` endpoint hostname public URL found on the overview page (something like `https://afd-01-abcefgh.b01.azurefd.net`). You will see `spoke-01-vm` displayed in your browser.
169 |
170 | ## More information
171 |
172 | * [Azure Front Door with Private Link](https://learn.microsoft.com/en-us/azure/frontdoor/private-link)
173 | * [Creating a Private Link Service](https://learn.microsoft.com/en-us/azure/private-link/create-private-link-service-portal?tabs=dynamic-ip)
174 | * [Enabling Private Link with internal load balancers](https://learn.microsoft.com/en-us/azure/frontdoor/standard-premium/how-to-enable-private-link-internal-load-balancer)
--------------------------------------------------------------------------------
/on-prem-2-bicep/on-prem-2.bicep:
--------------------------------------------------------------------------------
1 | param location string = 'germanywestcentral'
2 | param username string = 'nicola'
3 | @secure()
4 | param password string = 'password.123'
5 | param virtualMachineSKU string = 'Standard_D2_v5'
6 |
7 | var onPremNetworkName = 'on-prem-net-2'
8 |
9 | var bastionName = 'bastion-on-prem-2'
10 | var bastionIPName = 'bastion-on-prem-2-publicip'
11 |
12 | var vmOnPremLinux01DiskName = 'vm-linux-onprem-01-disk'
13 | var vmOnPremLinux01NicName = 'vm-linux-01-onprem-nic'
14 | var vmOnPremLinux01Name = 'lin-onprem'
15 | var autoshutdownLinux01Name = 'shutdown-computevm-${vmOnPremLinux01Name}'
16 |
17 | var vmOnPremLinux02DiskName = 'vm-linux-onprem-02-disk'
18 | var vmOnPremLinux02NicName = 'vm-linux-02-onprem-nic'
19 | var vmOnPremLinux02Name = 'lin-onprem-2'
20 | var autoshutdownLinux02Name = 'shutdown-computevm-${vmOnPremLinux02Name}'
21 |
22 | var vnetGatewayIPName = 'onprem-2-gateway-virtualip'
23 | var vnetGatewayName = 'on-prem-2-gateway'
24 |
25 | resource onpremvnet2 'Microsoft.Network/virtualNetworks@2019-09-01' = {
26 | name: onPremNetworkName
27 | location: location
28 | properties: { addressSpace: { addressPrefixes: [ '10.20.0.0/16' ] }
29 | subnets: [
30 | { name: 'GatewaySubnet', properties: { addressPrefix: '10.20.3.0/24' } }
31 | { name: 'AzureBastionSubnet', properties: { addressPrefix: '10.20.2.0/24' } }
32 | { name: 'DefaultSubnet', properties: { addressPrefix: '10.20.1.0/24' } }
33 | ]
34 | }
35 | }
36 |
37 | resource bastionIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = {
38 | name: bastionIPName
39 | location: location
40 | sku: { name: 'Standard' }
41 | properties: { publicIPAllocationMethod: 'Static' }
42 | }
43 |
44 | resource bastion 'Microsoft.Network/bastionHosts@2019-09-01' = {
45 | name: bastionName
46 | location: location
47 | dependsOn: [ onpremvnet2 ]
48 | properties: {
49 | ipConfigurations: [ {
50 | name: 'ipconfig1'
51 | properties: {
52 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', onPremNetworkName, 'AzureBastionSubnet') }
53 | publicIPAddress: { id: bastionIP.id }
54 | }
55 | }
56 | ]
57 | }
58 | }
59 |
60 | resource onpremdisk01 'Microsoft.Compute/disks@2019-07-01' = {
61 | name: vmOnPremLinux01DiskName
62 | location: location
63 | properties: {
64 | creationData: { createOption: 'Empty' }
65 | diskSizeGB: 128
66 | }
67 | }
68 |
69 | resource vmonpremnic01 'Microsoft.Network/networkInterfaces@2019-09-01' = {
70 | name: vmOnPremLinux01NicName
71 | location: location
72 | dependsOn: [ onpremvnet2 ]
73 | properties: {
74 | ipConfigurations: [ {
75 | name: 'ipconfig1'
76 | properties: {
77 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', onPremNetworkName, 'DefaultSubnet') }
78 | privateIPAllocationMethod: 'Dynamic'
79 | }
80 | }
81 | ]
82 | }
83 | }
84 |
85 | resource vmonprem01 'Microsoft.Compute/virtualMachines@2019-07-01' = {
86 | name: vmOnPremLinux01Name
87 | location: location
88 | dependsOn: [ ]
89 | properties: {
90 | hardwareProfile: { vmSize: virtualMachineSKU }
91 | storageProfile: {
92 | imageReference: { publisher: 'Canonical', offer: 'UbuntuServer', sku: '19_04-gen2', version: 'latest'}
93 | dataDisks: [ {
94 | lun: 0
95 | name: vmOnPremLinux01DiskName
96 | createOption: 'Attach'
97 | managedDisk: { id: onpremdisk01.id }
98 | }
99 | ]
100 | }
101 | osProfile: {
102 | computerName: vmOnPremLinux01Name
103 | adminUsername: username
104 | adminPassword: password
105 | linuxConfiguration: {
106 | disablePasswordAuthentication: false
107 | provisionVMAgent: true
108 | }
109 | }
110 | networkProfile: {
111 | networkInterfaces: [ {
112 | id: vmonpremnic01.id
113 | }
114 | ]
115 | }
116 | }
117 | }
118 |
119 | resource shutdownVm01 'microsoft.devtestlab/schedules@2018-09-15' = {
120 | name: autoshutdownLinux01Name
121 | location: location
122 | properties: {
123 | status: 'Enabled'
124 | taskType: 'ComputeVmShutdownTask'
125 | timeZoneId: 'UTC'
126 | dailyRecurrence: { time: '20:00' }
127 | notificationSettings: { status: 'Disabled' }
128 | targetResourceId: vmonprem01.id
129 | }
130 | }
131 |
132 | resource vnetGatewayIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = {
133 | name: vnetGatewayIPName
134 | location: location
135 | sku: { name: 'Standard'}
136 | properties: { publicIPAllocationMethod: 'Static' }
137 | }
138 |
139 | resource vnetGateway 'Microsoft.Network/virtualNetworkGateways@2019-09-01' = {
140 | name: vnetGatewayName
141 | location: location
142 | dependsOn: [ onpremvnet2 ]
143 | properties: {
144 | ipConfigurations: [ {
145 | name: 'ipconfig1'
146 | properties: {
147 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', onPremNetworkName, 'GatewaySubnet') }
148 | publicIPAddress: { id: vnetGatewayIP.id }
149 | }
150 | }
151 | ]
152 | gatewayType: 'Vpn'
153 | vpnType: 'RouteBased'
154 | enableBgp: false
155 | sku: { name: 'VpnGw1', tier: 'VpnGw1' }
156 | }
157 | }
158 |
159 | resource onpremdisk02 'Microsoft.Compute/disks@2019-07-01' = {
160 | name: vmOnPremLinux02DiskName
161 | location: location
162 | properties: {
163 | creationData: { createOption: 'Empty' }
164 | diskSizeGB: 128
165 | }
166 | }
167 |
168 | resource vmonpremnic02 'Microsoft.Network/networkInterfaces@2019-09-01' = {
169 | name: vmOnPremLinux02NicName
170 | location: location
171 | dependsOn: [ onpremvnet2 ]
172 | properties: {
173 | ipConfigurations: [ {
174 | name: 'ipconfig1'
175 | properties: {
176 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', onPremNetworkName, 'DefaultSubnet') }
177 | privateIPAllocationMethod: 'Dynamic'
178 | }
179 | }
180 | ]
181 | }
182 | }
183 |
184 | resource vmonprem02 'Microsoft.Compute/virtualMachines@2019-07-01' = {
185 | name: vmOnPremLinux02Name
186 | location: location
187 | dependsOn: [ ]
188 | properties: {
189 | hardwareProfile: { vmSize: virtualMachineSKU }
190 | storageProfile: {
191 | imageReference: { publisher: 'Canonical', offer: 'UbuntuServer', sku: '19_04-gen2', version: 'latest'}
192 | dataDisks: [ {
193 | lun: 0
194 | name: vmOnPremLinux02DiskName
195 | createOption: 'Attach'
196 | managedDisk: { id: onpremdisk02.id }
197 | }
198 | ]
199 | }
200 | osProfile: {
201 | computerName: vmOnPremLinux02Name
202 | adminUsername: username
203 | adminPassword: password
204 | linuxConfiguration: {
205 | disablePasswordAuthentication: false
206 | provisionVMAgent: true
207 | }
208 | }
209 | networkProfile: {
210 | networkInterfaces: [ {
211 | id: vmonpremnic02.id
212 | }
213 | ]
214 | }
215 | }
216 | }
217 |
218 | resource shutdownVm02 'microsoft.devtestlab/schedules@2018-09-15' = {
219 | name: autoshutdownLinux02Name
220 | location: location
221 | properties: {
222 | status: 'Enabled'
223 | taskType: 'ComputeVmShutdownTask'
224 | timeZoneId: 'UTC'
225 | dailyRecurrence: { time: '20:00' }
226 | notificationSettings: { status: 'Disabled' }
227 | targetResourceId: vmonprem02.id
228 | }
229 | }
230 |
--------------------------------------------------------------------------------
/scenarios/name-resolution-with-dns-private-resolver.md:
--------------------------------------------------------------------------------
1 |
2 | # SOLUTION: Configure a DNS on the cloud, and be sure that all machines are reachable via FQDN also from on-premise (DNS Resolver version)
3 |
4 | In this walktrough I show how use an [Azure private DNS resolver](https://learn.microsoft.com/en-us/azure/dns/dns-private-resolver-overview) and [Azure private DNS zone](https://learn.microsoft.com/en-us/azure/dns/private-dns-privatednszone) to allow DNS name resolution between on-premises and a hub-and-spoke architecture on Azure, connected via a S2S VPN.
5 |
6 | Since the on-premise network is also implemented on Azure, I plan to use an instance of Azure Private DNS resolver on this side as well. In a real-world scenario, the on-premise network would likely have a more traditional DNS server configured properly.
7 |
8 | > The hub and spoke network is a network topology that helps in the organization of virtual networks. The hub acts as a central point of communication while the spokes connect to the hub, thereby forming a hub and spoke architecture. Additionally, organizations can also connect their on-premises network with the Azure hub and spoke network to create a hybrid architecture.
9 |
10 | While using the hub and spoke network, it is essential to enable communication between the cloud resources and on-premises resources. This communication requires a naming resolution system that can resolve domain names to IP addresses. The Azure Private DNS resolver can help with this, allowing for the resolution of domain names to IP addresses.
11 |
12 | **Azure Private DNS resolver** is a Platform-as-a-Service (PaaS) offering that provides scalable and high-performance DNS resolution for virtual networks. Unlike a VM with DNS on it, it is not necessary to manage and maintain the infrastructure required for a PaaS private resolver. The PaaS private resolver offers many advantages such as built-in security features, the ability to configure custom domain names, and integration with other Azure services. Additionally, PaaS private resolver can scale automatically, allowing for quick provisioning of DNS resolution resources as needed. Overall, PaaS private resolver is a more efficient and scalable solution than a VM with DNS on it.
13 |
14 | **Azure Private DNS zone** provides a reliable, secure DNS service to manage and resolve domain names in a virtual network without the need to add a custom DNS solution. By using private DNS zones, you can use your own custom domain names rather than the Azure-provided names available today. You can link a private DNS zone to one or more virtual networks by creating virtual network links. You can also enable the autoregistration feature to automatically manage the life cycle of the DNS records for the virtual machines that get deployed in a virtual network.
15 |
16 | This "solution" implements the following:
17 |
18 | 
19 |
20 | We have an on-premise network connected to a hub on Azure. the hub have 3 spokes, each with a VM installed. Azure private DNS zone service with auto registration enabled allows to populate the DNS with all the required information. Once the private DNS zone is associated to a specific virtual network, the default Azure DNS is able to resolve al the names on the private zone.
21 |
22 | The DNS query process when using an Azure DNS Private Resolver is summarized below:
23 |
24 | 1. A client in a virtual network issues a DNS query.
25 | 2. If the DNS servers for this virtual network are specified as custom, then the query is forwarded to the specified IP addresses.
26 | 3. If Default (Azure-provided) DNS servers are configured in the virtual network, and there are Private DNS zones linked to the same virtual network, these zones are consulted.
27 | 4. If the query doesn't match a Private DNS zone linked to the virtual network, then Virtual network links for DNS forwarding rulesets are consulted.
28 | 5. If no ruleset links are present, then Azure DNS is used to resolve the query.
29 | 6. If ruleset links are present, the DNS forwarding rules are evaluated.
30 | 7. If a suffix match is found, the query is forwarded to the specified address.
31 | 8. If multiple matches are present, the longest suffix is used.
32 | 9. If no match is found, no DNS forwarding occurs and Azure DNS is used to resolve the query.
33 |
34 | In this scenario, the private DNS resolver on-premise is forwarding DNS queries for the `cloudasset.internal` zone to the Azure Private DNS resolver in the hub. The Azure Private DNS resolver in the hub then forwards the query to the Azure default DNS, which knows about the `cloudasset.internal` zone because of the private zone link that has been configured. This allows for successful resolution of cloud resources from the on-premises network.
35 |
36 | ## Pre-requisites
37 |
38 | 1. deploy HUB playground
39 | 2. deploy ONPREMISE-2 playground
40 | 3. configure a site-to-site VPN as [documented here](vnet-to-vnet-2.md)
41 | 4. configure a DNS in the cloud as [documented here](dns.md)
42 |
43 | ## Solution
44 |
45 | ### Create subnet for DNS Resolver in the Hub and on-premise
46 | Open Virtual Networks > `hub-lab-net` > Subnet > add subnet:
47 | * Name: `dns-resolver-subnet-01`
48 | * Subnet address range: `10.12.0.0/28`
49 |
50 | Open Virtual Networks > `onprem-net` > Subnet > add subnet:
51 | * Name: `inbound-subnet-02`
52 | * Subnet address range: `192.168.0.0/28`
53 |
54 | Open Virtual Networks > `onprem-net` > Subnet > add subnet:
55 | * Name: `outbound-subnet-02`
56 | * Subnet address range: `192.168.0.16/28`
57 |
58 |
59 | ### Create DNS resolver in the cloud
60 | Open DNS private resolvers > Create
61 | * Name: `hub-dns-resolver-01`
62 | * v-net: `hub-lab-net`
63 | * Inbound endpoint:
64 | * name: `inbound-endpoint-01`
65 | * subnet: `dns-resolver-subnet-01`
66 | * IP: static > `10.12.0.5`
67 | * CREATE
68 |
69 | ### create DNS resolver on-premise
70 | Open DNS private resolvers > Create
71 | * Name: `onprem-dns-resolver-02`
72 | * v-net: `on-prem-net`
73 | * Inbound endpoint:
74 | * name: `inbound-endpoint-02`
75 | * subnet: `inbound-subnet-02`
76 | * IP: static > `192.168.0.5`
77 | * Outbound endpoint:
78 | * name: `outbound-endpoint-02`
79 | * subnet: `outbound-subnet-02`
80 | * Ruleset > Add:
81 | * Name: `ruleset-01`
82 | * Endpoint(s): `outbound-subnet-02`
83 | * Rules: Add
84 | * Name: `cloudasset-stuff`
85 | * Domain: `cloudasset.internal.`
86 | * Destination: `10.12.0.5:53` (inbound IP DNS resolver in cloud)
87 | * CREATE
88 |
89 | ### configure new on-prem DNS server
90 |
91 | Open virtual networks > `on-prem-net` > DNS servers > `192.168.0.5` (onprem DNS resolver inbound IP)
92 |
93 | Reboot `W11-onprem` VM.
94 |
95 | ## Test solution
96 | via RDP Bastion, connect to `wm11-onprem` machine.
97 | Open command prompt and type:
98 |
99 | ```
100 | nslookup spoke-01-vm.cloudasset.internal
101 | nslookup spoke-02-vm.cloudasset.internal
102 | nslookup spoke-03-vm.cloudasset.internal
103 | nslookup hub-vm.cloudasset.internal
104 | ```
105 |
106 | you will be able to resolve names as shown below.
107 |
108 | ```
109 | C:\Users\nicola>nslookup spoke-01-vm.cloudasset.internal
110 | Name: spoke-01-vm.cloudasset.internal
111 | Address: 10.13.1.4
112 |
113 | C:\Users\nicola>nslookup spoke-02-vm.cloudasset.internal
114 | Name: spoke-02-vm.cloudasset.internal
115 | Address: 10.13.2.4
116 |
117 | C:\Users\nicola>nslookup spoke-03-vm.cloudasset.internal
118 | Name: spoke-03-vm.cloudasset.internal
119 | Address: 10.13.3.4
120 |
121 | C:\Users\nicola>nslookup hub-vm.cloudasset.internal
122 | Name: hub-vm.cloudasset.internal
123 | Address: 10.12.1.4
124 | ```
125 |
--------------------------------------------------------------------------------
/images/elastic-san.drawio:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/scenarios/publish-waf-fw.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: Secure a WEB workload with both Azure Firewall Premium and Azure Web Application Firewall
2 |
3 | To secure a web Azure application workloads, you use protective measures, such as authentication and encryption, in the applications themselves. You can also add security layers to the virtual machine (VM) networks that host the applications. The layers protect inbound flows from users. They also protect outbound flows to the internet that your application might require.
4 |
5 | This solutions shows how secure a workload with Azure Firewall Premium and Azure Application Firewall, when it is hosted in a Hub-and-spoke architecture.
6 |
7 | * [Hub-and-Spoke reference architecture](https://docs.microsoft.com/en-us/azure/architecture/reference-architectures/hybrid-networking/hub-spoke): a hub virtual network acts as a central point of connectivity to many spoke virtual networks. The hub can also be used as the connectivity point to your on-premises networks. The spoke virtual networks peer with the hub and can be used to isolate workloads. The benefits of using a hub and spoke configuration include cost savings, overcoming subscription limits, and workload isolation.
8 | * [Azure Firewall Premium](https://docs.microsoft.com/en-us/azure/firewall/premium-features): is a managed next-generation firewall that offers NAT, packet filtering, threat intelligence to identify malicious IP address, TLS inspection and Intrusion Detection and Protection System (IDPS)
9 | * [Azure Application Gateway](https://docs.microsoft.com/en-us/azure/application-gateway/) with Web Application Firewall: is a managed web traffic load balancer and HTTP(S) full reverse proxy that can do Secure Socket Layer (SSL) encryption and decryption. Application Gateway also uses [Web Application Firewall](https://docs.microsoft.com/en-us/azure/web-application-firewall) to inspect web traffic and detect attacks at the HTTP layer
10 |
11 | the proposed solution is shown in the image below:
12 |
13 | 
14 |
15 | In this option, inbound web traffic goes through both Azure Firewall and WAF. The WAF provides protection at the web application layer. Azure Firewall acts as a central logging and control point, and it inspects traffic between the Application Gateway and the backend servers. The Application Gateway and Azure Firewall aren't sitting in parallel, but one after the other. Gateway and workload are deployed in 2 spoke connected via a central hub.
16 |
17 | more information on this approach: [Application Gateway before Firewall scenario](https://docs.microsoft.com/en-us/azure/architecture/example-scenario/gateway/firewall-application-gateway#application-gateway-before-firewall)
18 |
19 | ## Pre-requisites
20 |
21 | In order to apply this solution you have to deploy hub playground only.
22 |
23 | ## Solution
24 |
25 | ### Step 1 - Create Gateway Virtual Network
26 | Create the following Virtual Network:
27 | * Name: `spoke-gateway`
28 | * Region: `west-europe`
29 | * IPv4 address space: `10.13.4.0/24`
30 | * subnet
31 | * Name: `AppGatewaySubnet`
32 | * subnet range: `10.13.4.0/24`
33 |
34 | create network peering with `hub-lab-net`
35 |
36 | * Peering Name to hub: `gateway-to-hub`
37 | * Peering Name from hub: `hub-to-gateway`
38 |
39 | ### Step 2 - create Application Gateway with WAF
40 |
41 | Create the following Azure Application Gateway
42 |
43 | * Name: `hub-gateway`
44 | * Region: `west-europe`
45 | * Tier: `waf-v2`
46 | * Minimum instance count: `1`
47 | * Create WAF Policy
48 | * Name: hub-gateway-waf-policy
49 | * Virtual Network: `spoke-gateway`
50 | * Virtual Network: `AppGatewaySubnet`
51 | * Front ends > Create public IP
52 | * Name: `hub-gateway-ip`
53 | * Backends > Add backend pool
54 | * Name: `backend-01`
55 | * Target type `IP`: `10.13.3.4`
56 | * Configuration > Add a routing rule
57 | * Name: `rule-01`
58 | * Priority: `100`
59 | * Listener
60 | * Name: `Listener-01`
61 | * Frontend: `public ip`
62 | * protocol: `http`
63 | * type: `basic`
64 | * Backend targets
65 | * type: backend pool > `backend-01`
66 | * backend settings > new
67 | * Name: `settings-01`
68 | * protocol: `HTTP`
69 | * port: `80`
70 | * Hoverride new hostname: `NO`
71 | * clock **ADD**
72 |
73 |
74 | ### Step 3 - Create user defined routing tables
75 |
76 |
77 | Create the following route table in `west europe`: `spokes-we-to-hub-routes`
78 |
79 | | Name | Address Prefix | Next hop type | Next hop IP addr |
80 | |---|---|---|---|
81 | | to-spoke-03 | 10.13.3.0/24 | Virtual Appliance | 10.12.3.4 (fw) |
82 |
83 |
84 | | subnet Name | Virtual Network |
85 | |---|---|
86 | | AppGatewaySubnet | spoke-gateway |
87 |
88 | Create the following route table in `north europe`: `spokes-ne-to-hub-routes`
89 |
90 | | Name | Address Prefix | Next hop type | Next hop IP addr |
91 | |---|---|---|---|
92 | | to-gateway | 10.13.4.0/24 | Virtual Appliance | 10.12.3.4 (fw) |
93 |
94 | | subnet Name | Virtual Network |
95 | |---|---|
96 | | default | spoke-03 |
97 |
98 | ### Step 04 - configure firewall for routing
99 |
100 | Create the following `IP Groups` in `west europe`:
101 | * `group-01`: `10.13.3.0/24`, `10.13.4.0/24`
102 |
103 | Create the following Firewall Policy: `hub-fw-policy`
104 |
105 | **Network Rules**:
106 | * Rule Collection Name: `my-collection`
107 | * Rule Collection type: `Network`
108 | * Priority: `1000`
109 | * Rule Colletion action: `Allow`
110 | * Rule Collection group: `DefaultNetworkRuleCollectionGroup`
111 |
112 |
113 | | rule name | source | port | protocol | destination |
114 | |---|---|---|---|---|
115 | | all-to-all | `group-01` | * | Any | `group-01` |
116 |
117 | Associate the policy `hub-fw-policy` to `lab-firewall` via Firewall Manager.
118 |
119 | **Enable logging**
120 |
121 | Create a Log Analytics Workspace named `playground-workspace` in `west-europe`.
122 | In order to analyze firewall logs go to `lab-firewall` -> Diagnostic Settings -> Add Diagnostic Settings:
123 | * Name: `firewall-diagnostics `
124 | * Logs: Category Group -> All logs
125 | * Metrics: All Metrics
126 | * Destination details: Send to Log Analytics Workspace -> `playground-workspace`
127 | * click **SAVE**
128 |
129 | ### Step 5 - install a web server on `spoke-03-vm`
130 |
131 | Access to spoke-03-vm via Bastion and type
132 |
133 | ```
134 | sudo apt-get update
135 | sudo apt-get upgrade
136 | sudo apt install nginx -y
137 | ```
138 |
139 | test web server installation status
140 |
141 | `wget http://10.13.3.4 -S -O -`
142 |
143 | response
144 |
145 | ```
146 | Connecting to 10.13.3.4:80... connected.
147 | HTTP request sent, awaiting response...
148 | HTTP/1.1 200 OK
149 | Server: nginx/1.14.0 (Ubuntu)
150 |
151 | ...
152 | ```
153 |
154 | # Test Solution
155 |
156 | from a brower open `http://xxxx/` where **xxx** is `hub-gateway-ip`'s public IP you should see the following:
157 |
158 | ```
159 | Welcome to nginx!
160 | If you see this page, the nginx web server is successfully installed and working. Further configuration is required.
161 |
162 | For online documentation and support please refer to nginx.org.
163 | Commercial support is available at nginx.com.
164 |
165 | Thank you for using nginx.
166 | ```
167 |
168 | go to `lab-firewall` -> Logs -> click on `Run` button in **Network Rule Log Data** Box.
169 |
170 | In the Result pane, the first row should be something like:
171 |
172 | | Time | msg | Protocol | SourceIP | srcport| TargetIp | target port | Action |
173 | |---|---|----|---|---|---|---|---|
174 | |...|...|TCP|10.13.4.6|xxx|10.13.3.4|80|**Allow**|
175 |
176 | > Log ingestion from Azure Firewall can require from 60 seconds to 15 minutes, so if nothing is displayed, wait some minute and try again.
177 |
--------------------------------------------------------------------------------
/scenarios/p2s-vpn-certificate-always-on.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: Configure a P2S VPN with Certificate Authentication and user tunnel Always-ON
2 |
3 | This solution shows how to configure a P2S VPN to allow individual clients running Windows 10 to an Azure Virtual Network using **Azure certificate authentication** and **always On VPN user tunnel**.
4 |
5 | A new feature of the Windows 10 or later VPN client, Always On, is the ability to maintain a VPN connection. With Always On, the active VPN profile can connect automatically and remain connected based on triggers, such as user sign-in, network state change, or device screen active.
6 |
7 | You can use gateways with Always On to establish persistent user tunnels and device tunnels to Azure.
8 |
9 | Always On VPN connections include either of two types of tunnels:
10 |
11 | * Device tunnel: Connects to specified VPN servers before users sign in to the device. Pre-sign-in connectivity scenarios and device management use a device tunnel.
12 |
13 | * User tunnel: Connects only after users sign in to the device. By using user tunnels, you can access organization resources through VPN servers.
14 |
15 | Device tunnels and user tunnels operate independent of their VPN profiles. They can be connected at the same time, and they can use different authentication methods and other VPN configuration settings, as appropriate.
16 |
17 | More information on this procedure: https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-howto-always-on-user-tunnel
18 |
19 | ## Pre-requisites
20 |
21 | In order to apply this solution, you have to deploy [Configure a P2S VPN with Certificate Authentication solution](p2s-vpn-certificate.md) before.
22 |
23 | ## Solution
24 | * Uninstall Azure client VPN, if you installed to test the pre-requisite solution.
25 | * in point-to-site configuration change Tunnel type to `IKEv2`, the only supported protocol
26 | * Copy the following text, and save it as usercert.ps1:
27 |
28 | ```
29 | Param(
30 | [string]$xmlFilePath,
31 | [string]$ProfileName
32 | )
33 |
34 | $a = Test-Path $xmlFilePath
35 | echo $a
36 |
37 | $ProfileXML = Get-Content $xmlFilePath
38 |
39 | echo $XML
40 |
41 | $ProfileNameEscaped = $ProfileName -replace ' ', '%20'
42 |
43 | $Version = 201606090004
44 |
45 | $ProfileXML = $ProfileXML -replace '<', '<'
46 | $ProfileXML = $ProfileXML -replace '>', '>'
47 | $ProfileXML = $ProfileXML -replace '"', '"'
48 |
49 | $nodeCSPURI = './Vendor/MSFT/VPNv2'
50 | $namespaceName = "root\cimv2\mdm\dmmap"
51 | $className = "MDM_VPNv2_01"
52 |
53 | $session = New-CimSession
54 |
55 | try
56 | {
57 | $newInstance = New-Object Microsoft.Management.Infrastructure.CimInstance $className, $namespaceName
58 | $property = [Microsoft.Management.Infrastructure.CimProperty]::Create("ParentID", "$nodeCSPURI", 'String', 'Key')
59 | $newInstance.CimInstanceProperties.Add($property)
60 | $property = [Microsoft.Management.Infrastructure.CimProperty]::Create("InstanceID", "$ProfileNameEscaped", 'String', 'Key')
61 | $newInstance.CimInstanceProperties.Add($property)
62 | $property = [Microsoft.Management.Infrastructure.CimProperty]::Create("ProfileXML", "$ProfileXML", 'String', 'Property')
63 | $newInstance.CimInstanceProperties.Add($property)
64 |
65 | $session.CreateInstance($namespaceName, $newInstance)
66 | $Message = "Created $ProfileName profile."
67 | Write-Host "$Message"
68 | }
69 | catch [Exception]
70 | {
71 | $Message = "Unable to create $ProfileName profile: $_"
72 | Write-Host "$Message"
73 | exit
74 | }
75 | $Message = "Complete."
76 | Write-Host "$Message"
77 | ```
78 |
79 | Copy the following text, and save it as `VPNProfile.xml` in the same folder as `usercert.ps1`. Edit the following text to match your environment:
80 |
81 | * *Servers*: azuregateway-1234-56-78dc.cloudapp.net - Can be found in the generic\VpnSettings.xml in the downloaded profile zip file
82 |
83 | * *Routes*: can be found in `generic\VpnSettings.xml`
84 |
85 | ```
86 |
87 |
88 | azuregateway-aa420069-de4b-479d-92ec-2976794df855-abdb6574d293.vpn.azure.com
89 | IKEv2
90 |
91 | Eap
92 |
93 |
94 | 1300013truefalsefalsefalsefalse
95 |
96 |
97 |
98 | SplitTunnel
99 |
100 | true
101 |
102 |
103 |
104 | 10.12.0.0
105 | 16
106 |
107 |
108 | 10.13.2.0
109 | 24
110 |
111 |
112 | 10.13.3.0
113 | 24
114 |
115 |
116 | 10.13.1.0
117 | 24
118 |
119 |
120 |
121 | 192.168.3.4, 192.168.3.5
122 |
123 |
124 | true
125 | true
126 |
127 | true
128 |
129 |
130 | ```
131 |
132 |
133 | * Run Powershell as Administrator
134 | * In PowerShell, switch to the folder where `usercert.ps1` and `VPNProfile.xml` are located, and run the following command: `.\usercert.ps1 .\VPNProfile.xml UserAlwaysOn`
135 |
136 | result:
137 |
138 | ```
139 | AlwaysOn :
140 | ByPassForLocal :
141 | DeviceTunnel :
142 | DnsSuffix :
143 | EdpModeId :
144 | InstanceID : UserAlwaysOn
145 | LockDown :
146 | ParentID : ./Vendor/MSFT/VPNv2
147 | ProfileXML :
148 | RegisterDNS :
149 | RememberCredentials :
150 | TrustedNetworkDetection :
151 | PSComputerName :
152 |
153 | Created UserTest profile.
154 | Complete.
155 |
156 | ```
157 |
158 |
159 | ## Test Solution
160 |
161 | * Under VPN Settings, look for the UserTest entry, and then select Connect.
162 | * If the connection succeeds, you've successfully configured an Always On user tunnel.
163 | * open a `remote desktop connection` app and connect to `10.13.1.4`
164 |
--------------------------------------------------------------------------------
/scenarios/app-gateway-01.md:
--------------------------------------------------------------------------------
1 | # SOLUTION - publish internal web app via Azure Application Gateway on private and public IPs
2 |
3 | In this solution I show how to expose through Azure Application Gateway both externally (internet) in HTTP and internally (intranet) in HTTPS an HTTPS web service delivered by a windows VM, with a certificates emitted by a privare certification authority.
4 |
5 | The service is exposed by the Azure Application Gateway, via HTTP and HTTPS, (both externally and internally).
6 |
7 | The scenario is deployed into a Hub and Spoke network topology context. Specifically, the virtual network that hosts the Web Service VM is different from the virtual network that hosts the Azure Application Gateway.
8 |
9 | Traffic between these 2 spokes is secured by an Azure Firewall in the hub virtual network.
10 |
11 | In this configuration, inbound web traffic goes through both Azure Firewall and WAF. The WAF provides protection at the web application layer. Azure Firewall acts as a central logging and control point, and it inspects traffic between the Application Gateway and the backend servers. With Azure Firewall Premium, this design can support end-to-end scenarios, where the Azure Firewall applies TLS inspection to do IDPS on the encrypted traffic between the Application Gateway and the web backend.
12 |
13 | This design is appropriate for applications that need to know incoming client source IP addresses, for example to serve geolocation-specific content or for logging. Application Gateway in front of Azure Firewall captures the incoming packet's source IP address in the X-forwarded-for header, so the web server can see the original IP address in this header.
14 |
15 | The resulting overall architecture is shown in the following schema.
16 |
17 | 
18 | _download drawio version of this image [here](../images/app-gateway-01.drawio)._
19 |
20 | ## Pre-requisites
21 | In order to apply this solution you have to deploy the `hub-01` and the `any-to-any` routing, so that you have a fully configured hub-and-spoke network with firewall and routing between spokes.
22 |
23 | ## Solution
24 |
25 | The steps to configure the solution are as follows:
26 |
27 | 1. generate 2 self signed certificate
28 | 2. Create a DNS private zone
29 | 3. configure spoke-01-vm as HTTPS orgin web site
30 | 4. configure the Azure Application Gateway
31 |
32 | ### Generate 2 certificates
33 | We will use `mkcert` to generate 2 certificates:
34 |
35 | * `web.spoke01.com`: the HTTPS name for web server
36 | * `published.spoke01.com`: the HTTPS name for the application Gateway private Front-End
37 |
38 | Install [mkcer](https://github.com/FiloSottile/mkcert) to create a local certification authority and generate 2 web certificates.
39 |
40 | ```
41 | mkcert -install
42 | mkcert -pkcs12 web.spoke01.com
43 | mkcert -pkcs12 published.spoke01.com
44 | ```
45 |
46 | this will create 2 files
47 |
48 | ```
49 | web.spoke01.com.p12
50 | published.spoke01.com.p12
51 | ```
52 |
53 | rename `published.spoke01.com.p12` to `published.spoke01.com.pfx` (_p12 and pfx file format are the same, but Azure Application gateway requires this extension!_)
54 |
55 | Convert root certificate in .cer, so we can load in Azure Application Gateway
56 | ```
57 | mkcert -CAROOT
58 |
59 | openssl x509 -inform PEM -in -outform DER -out rootCA.cer
60 | ```
61 |
62 | ### Configure the private DNS Zone
63 | Create a DNS private zone.
64 | From Azure Portal > Private DNS Zones > Create:
65 | * name: `spoke01.com`
66 | * click **Create**
67 |
68 | In this private DNS zone create 2 A records:
69 |
70 | | name | type | ipaddress |
71 | |---|---|---|
72 | | `web` | `A` | `10.13.1.4` |
73 | | `published` | `A` | `10.13.2.100` |
74 |
75 | unded Virtual Network Links, add 2 links:
76 |
77 | | link name | virtual network | enable auto registration |
78 | |---|---|---|
79 | | `spoke-01-link` | `spoke-01` | NO |
80 | | `spoke-02-link` | `spoke-02` | NO |
81 |
82 | ### configure `spoke-01-vm` as HTTPS orgin web site
83 |
84 | Install the certificate `web-on-spoke-01` on `spoke-01-vm`
85 |
86 | * right-click > Install PFX
87 | * select: `local machine`
88 | * password: `changeit`
89 | * set: mark this key as exportable
90 | * set: include all extended properties
91 | * set: automatically select the certificate store
92 |
93 | Configure IIS on `spoke-01-vm`, from Windows server mamager:
94 | * add roles and features
95 | * select web server (IIS)
96 | * click «**Install**»
97 |
98 | Configure SSL web site: from Internet Information Services > Default Web Site > SSL Settings > Bindings > Add:
99 | * Type: `https`
100 | * IP address: `10.13.1.4`
101 | * hostname: `web.spoke01.com`
102 | * SSL certificate: `web.spoke01.com`
103 |
104 | Test web site and connection: from `spoke-02-vm` brose with Microsot Edge tp https://web.spoke01.com/
105 | you will receive a "_your connection isn't private_" message. Go to advanced> ignore to see the IIS web site.
106 |
107 | ### Configure the Azure Application Gateway
108 |
109 | It is required to create a new routing table on Application Gateway subnet that allows direct traffic to internet. From Azure Portal > Route tables > Create
110 | * region: `west europe`
111 | * name: `spoke-02-services-rt`
112 | * click **create**
113 |
114 | When the route table is created, goes to Settings > Routes > add:
115 | * name: `to-hub`
116 | * destination type: **IP address**
117 | * IPs: `10.13.1.0/24`
118 | * next hop: `Virtual Appliance`
119 | * IP: `10.12.3.4`
120 |
121 | Associate this route table to (Azure Portal > `spoke-02` > subnets):
122 | * vnet: `spoke-02`
123 | * subnet: `services`
124 |
125 | Create an Azure Application Gateway: From Azure Portal > Aplication Gateways > Create Application Gateway:
126 | * Name: `lab-public-gateway`
127 | * region: `west europe`
128 | * tier: `standard V2`
129 | * Virtual network: `spoke-02`
130 | * subnet: `services`
131 | * FrontEnd
132 | * Address type: `both`
133 | * public IP Name (**new**): `lab-app-gw-ip`
134 | * private IP Address: `10.13.2.100`
135 | * Backends > add backend pool
136 | * Name: `spoke-01-pool`
137 | * IP/FQDN: `10.13.1.4`
138 | * Configuration > Add a routing rule
139 | * name: `spoke-01-rule`
140 | * priority: `1000`
141 | * Listener
142 | * Name: `listener-public`
143 | * frontend IP: `public`
144 | * protocol: `http`
145 | * listener type: `basic`
146 | * backend targets
147 | * type: `backend pool`
148 | * backend target: `spoke-01-pool`
149 | * backend settings > add new:
150 | * name: `spoke-01-settings`
151 | * backend protocol: `https`
152 | * Use well known CA certificate: `NO`
153 | * CER certificate: **rootCA.cer** file
154 | * override backend path:
155 | * override with new hostname: `yes`
156 | * hostname: `web.spoke01.com`
157 | * create custom probes: `no`
158 | * click **ADD**
159 | * Review and Create > **Create**
160 |
161 | Once the Application Gateway is created, add the internal listener and routing rule
162 |
163 | From your Azure Applicationg Gateway > Settings > Listers > Add Listener:
164 | * Name: listener-private
165 | * Front end IP: private
166 | * protocol: HTTPS
167 | * Certificate > Add
168 | * upload a certificate
169 | * cert name: published-certificate
170 | * PFX File: `published.spoke01.com.pfx` created above
171 | * password: `changeit`
172 | * listener-type: basic
173 | * click **ADD**
174 |
175 | From your Azure Applicationg Gateway > Settings > Rules > Add Routing Rule:
176 | * Name: Rule-internal
177 | * Priority: 2000
178 | * Listener: Listener-private
179 | * Backend Targets
180 | * target-type: backend-pool
181 | * backend-target: spoke-01-pool
182 | * backend-settings: spoke-01-settings
183 | * click
184 |
185 | ## Test solution
186 |
187 | From your local machine open a browser and type _http:///_
188 | From spoke-02-vm open Edge ant type https://published.spoke01.com/: you will receive a "_your connection isn't private_" message. Go to advanced > ignore to see the IIS web site.
189 |
190 | ## More information
191 |
192 | * How Application Gateway works: https://learn.microsoft.com/en-us/azure/application-gateway/how-application-gateway-works
193 | * Firewall topologies: https://learn.microsoft.com/en-us/azure/architecture/example-scenario/gateway/firewall-application-gateway
194 | * local personal certification authority: https://github.com/FiloSottile/mkcert
195 | * use mkcert with IIS: https://medium.com/@aweber01/locally-trusted-development-certificates-with-mkcert-and-iis-e09410d92031
196 | *
197 |
--------------------------------------------------------------------------------
/on-prem-bicep/on-prem.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "metadata": {
5 | "_generator": {
6 | "name": "bicep",
7 | "version": "0.36.1.42791",
8 | "templateHash": "5865605153906493223"
9 | }
10 | },
11 | "parameters": {
12 | "location": {
13 | "type": "string",
14 | "defaultValue": "francecentral"
15 | },
16 | "deployVM": {
17 | "type": "bool",
18 | "defaultValue": true
19 | },
20 | "username": {
21 | "type": "string",
22 | "defaultValue": "nicola"
23 | },
24 | "password": {
25 | "type": "securestring",
26 | "defaultValue": "password.123"
27 | },
28 | "virtualMachineSize": {
29 | "type": "string",
30 | "defaultValue": "Standard_D2_v5"
31 | },
32 | "deployBastion": {
33 | "type": "bool",
34 | "defaultValue": true
35 | },
36 | "enableBgp": {
37 | "type": "bool",
38 | "defaultValue": false
39 | },
40 | "localGatewayFqdn": {
41 | "type": "string",
42 | "defaultValue": ""
43 | }
44 | },
45 | "variables": {
46 | "onPremNetworkName": "on-prem-net",
47 | "bastionName": "bastion-on-prem",
48 | "bastionIPName": "bastion-on-prem-publicip",
49 | "vmOnPremDiskName": "vm-w11-onprem-disk",
50 | "vmOnPremNicName": "vm-w11-onprem-nic",
51 | "vmOnPremName": "W11-onprem",
52 | "autoshutdownName": "[format('shutdown-computevm-{0}', variables('vmOnPremName'))]",
53 | "vnetGatewayIPName": "onprem-gateway-virtualip",
54 | "vnetGatewayName": "on-prem-gateway",
55 | "localGatewayName": "lab-local-gateway",
56 | "subnets": "[concat(createArray(createObject('name', 'GatewaySubnet', 'properties', createObject('addressPrefix', '192.168.3.0/24')), createObject('name', 'DefaultSubnet', 'properties', createObject('addressPrefix', '192.168.1.0/24'))), if(parameters('deployBastion'), createArray(createObject('name', 'AzureBastionSubnet', 'properties', createObject('addressPrefix', '192.168.2.0/24'))), createArray()))]"
57 | },
58 | "resources": [
59 | {
60 | "type": "Microsoft.Network/virtualNetworks",
61 | "apiVersion": "2019-09-01",
62 | "name": "[variables('onPremNetworkName')]",
63 | "location": "[parameters('location')]",
64 | "properties": {
65 | "addressSpace": {
66 | "addressPrefixes": [
67 | "192.168.0.0/16"
68 | ]
69 | },
70 | "subnets": "[variables('subnets')]"
71 | }
72 | },
73 | {
74 | "condition": "[parameters('deployBastion')]",
75 | "type": "Microsoft.Network/publicIPAddresses",
76 | "apiVersion": "2020-08-01",
77 | "name": "[variables('bastionIPName')]",
78 | "location": "[parameters('location')]",
79 | "sku": {
80 | "name": "Standard"
81 | },
82 | "properties": {
83 | "publicIPAllocationMethod": "Static"
84 | }
85 | },
86 | {
87 | "condition": "[parameters('deployBastion')]",
88 | "type": "Microsoft.Network/bastionHosts",
89 | "apiVersion": "2019-09-01",
90 | "name": "[variables('bastionName')]",
91 | "location": "[parameters('location')]",
92 | "properties": {
93 | "ipConfigurations": [
94 | {
95 | "name": "ipconfig1",
96 | "properties": {
97 | "subnet": {
98 | "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('onPremNetworkName'), 'AzureBastionSubnet')]"
99 | },
100 | "publicIPAddress": {
101 | "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('bastionIPName'))]"
102 | }
103 | }
104 | }
105 | ]
106 | },
107 | "dependsOn": [
108 | "[resourceId('Microsoft.Network/publicIPAddresses', variables('bastionIPName'))]",
109 | "[resourceId('Microsoft.Network/virtualNetworks', variables('onPremNetworkName'))]"
110 | ]
111 | },
112 | {
113 | "condition": "[parameters('deployVM')]",
114 | "type": "Microsoft.Compute/disks",
115 | "apiVersion": "2019-07-01",
116 | "name": "[variables('vmOnPremDiskName')]",
117 | "location": "[parameters('location')]",
118 | "properties": {
119 | "creationData": {
120 | "createOption": "Empty"
121 | },
122 | "diskSizeGB": 128
123 | }
124 | },
125 | {
126 | "condition": "[parameters('deployVM')]",
127 | "type": "Microsoft.Network/networkInterfaces",
128 | "apiVersion": "2019-09-01",
129 | "name": "[variables('vmOnPremNicName')]",
130 | "location": "[parameters('location')]",
131 | "properties": {
132 | "ipConfigurations": [
133 | {
134 | "name": "ipconfig1",
135 | "properties": {
136 | "subnet": {
137 | "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('onPremNetworkName'), 'DefaultSubnet')]"
138 | },
139 | "privateIPAllocationMethod": "Dynamic"
140 | }
141 | }
142 | ]
143 | },
144 | "dependsOn": [
145 | "[resourceId('Microsoft.Network/virtualNetworks', variables('onPremNetworkName'))]"
146 | ]
147 | },
148 | {
149 | "condition": "[parameters('deployVM')]",
150 | "type": "Microsoft.Compute/virtualMachines",
151 | "apiVersion": "2019-07-01",
152 | "name": "[variables('vmOnPremName')]",
153 | "location": "[parameters('location')]",
154 | "properties": {
155 | "hardwareProfile": {
156 | "vmSize": "[parameters('virtualMachineSize')]"
157 | },
158 | "storageProfile": {
159 | "imageReference": {
160 | "publisher": "MicrosoftWindowsDesktop",
161 | "offer": "windows-11",
162 | "sku": "win11-24h2-ent",
163 | "version": "latest"
164 | },
165 | "dataDisks": [
166 | {
167 | "lun": 0,
168 | "name": "[variables('vmOnPremDiskName')]",
169 | "createOption": "Attach",
170 | "managedDisk": {
171 | "id": "[resourceId('Microsoft.Compute/disks', variables('vmOnPremDiskName'))]"
172 | }
173 | }
174 | ]
175 | },
176 | "osProfile": {
177 | "computerName": "[variables('vmOnPremName')]",
178 | "adminUsername": "[parameters('username')]",
179 | "adminPassword": "[parameters('password')]",
180 | "windowsConfiguration": {
181 | "enableAutomaticUpdates": true
182 | }
183 | },
184 | "networkProfile": {
185 | "networkInterfaces": [
186 | {
187 | "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('vmOnPremNicName'))]"
188 | }
189 | ]
190 | }
191 | },
192 | "dependsOn": [
193 | "[resourceId('Microsoft.Compute/disks', variables('vmOnPremDiskName'))]",
194 | "[resourceId('Microsoft.Network/networkInterfaces', variables('vmOnPremNicName'))]"
195 | ]
196 | },
197 | {
198 | "condition": "[parameters('deployVM')]",
199 | "type": "Microsoft.DevTestLab/schedules",
200 | "apiVersion": "2018-09-15",
201 | "name": "[variables('autoshutdownName')]",
202 | "location": "[parameters('location')]",
203 | "properties": {
204 | "status": "Enabled",
205 | "taskType": "ComputeVmShutdownTask",
206 | "timeZoneId": "UTC",
207 | "dailyRecurrence": {
208 | "time": "20:00"
209 | },
210 | "notificationSettings": {
211 | "status": "Disabled"
212 | },
213 | "targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', variables('vmOnPremName'))]"
214 | },
215 | "dependsOn": [
216 | "[resourceId('Microsoft.Compute/virtualMachines', variables('vmOnPremName'))]"
217 | ]
218 | },
219 | {
220 | "type": "Microsoft.Network/publicIPAddresses",
221 | "apiVersion": "2020-08-01",
222 | "name": "[variables('vnetGatewayIPName')]",
223 | "location": "[parameters('location')]",
224 | "sku": {
225 | "name": "Standard"
226 | },
227 | "properties": {
228 | "publicIPAllocationMethod": "Static"
229 | }
230 | },
231 | {
232 | "type": "Microsoft.Network/virtualNetworkGateways",
233 | "apiVersion": "2022-09-01",
234 | "name": "[variables('vnetGatewayName')]",
235 | "location": "[parameters('location')]",
236 | "properties": {
237 | "ipConfigurations": [
238 | {
239 | "name": "ipconfig1",
240 | "properties": {
241 | "subnet": {
242 | "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('onPremNetworkName'), 'GatewaySubnet')]"
243 | },
244 | "publicIPAddress": {
245 | "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('vnetGatewayIPName'))]"
246 | }
247 | }
248 | }
249 | ],
250 | "gatewayType": "Vpn",
251 | "vpnType": "RouteBased",
252 | "enableBgp": "[parameters('enableBgp')]",
253 | "bgpSettings": "[if(parameters('enableBgp'), createObject('asn', 65510), null())]",
254 | "sku": {
255 | "name": "VpnGw1",
256 | "tier": "VpnGw1"
257 | }
258 | },
259 | "dependsOn": [
260 | "[resourceId('Microsoft.Network/virtualNetworks', variables('onPremNetworkName'))]",
261 | "[resourceId('Microsoft.Network/publicIPAddresses', variables('vnetGatewayIPName'))]"
262 | ]
263 | },
264 | {
265 | "condition": "[parameters('enableBgp')]",
266 | "type": "Microsoft.Network/localNetworkGateways",
267 | "apiVersion": "2022-09-01",
268 | "name": "[variables('localGatewayName')]",
269 | "location": "[parameters('location')]",
270 | "properties": {
271 | "bgpSettings": {
272 | "asn": 65511,
273 | "bgpPeeringAddress": "192.168.3.254"
274 | },
275 | "localNetworkAddressSpace": {
276 | "addressPrefixes": [
277 | "10.12.0.0/16",
278 | "10.13.1.0/24",
279 | "10.13.2.0/24",
280 | "10.13.3.0/24"
281 | ]
282 | },
283 | "fqdn": "[parameters('localGatewayFqdn')]"
284 | }
285 | }
286 | ]
287 | }
--------------------------------------------------------------------------------
/scenarios/apim-stv1.md:
--------------------------------------------------------------------------------
1 | # SOLUTION: deploy a private Azure API Management stv1 platform in one spoke
2 |
3 | In this solution, I will show how to deploy an APIM version stv1 in a virtual private network.
4 |
5 |
6 |
7 | APIM configuration, on the other hand, is more complex. APIM is a service that is composed of, and interacts with, numerous other Azure infrastructure components, so the private deployment exposes us to challenges that we need to solve:
8 |
9 | * A **network security group** attached to the APIM subnet is required to explicitly allow inbound connectivity because the load balancer used internally by API Management is secure by default and rejects all inbound traffic.
10 | * **Service endpoints** in the subnet to dependent services such as Azure Storage, Event Hubs, and Azure SQL are also required because, in this context, the user route (`0.0.0.0/0`) through the Azure firewall would interrupt connectivity with API Management.
11 | * A Standard SKU **public IPv4** address. The public IP address in this configuration is used only for management operations.
12 | * A **Private DNS zone** is also required to resolve internal APIM endpoints.
13 |
14 | the final architecture is shown in the image below.
15 |
16 | .....
17 |
18 |
19 | ## Pre-requisites
20 |
21 | In order to apply this solution you have to deploy the `hub-01` and the `any-to-any` routing, so that you have a fully configured hub-and-spoke network with firewall and routing between spokes.
22 |
23 | ## Solution
24 | We will do the following:
25 |
26 | * Deploy and configure an Azure API Manager (APIM) in the `services` subnet of `spoke-02` network.
27 |
28 |
29 | Go to Azure Portal > Azure AI | Azure OpenAI > Create
30 | * region: West Europe
31 | * Name: `aoai-01`
32 | * pricing tier: `standard S0`
33 |
34 | Network
35 | * Type: `disabled`
36 | * add private endpoint
37 | * Location: west europe
38 | * Name: `aoai-01-pe`
39 | * Virtual Network: `spoke-01`
40 | * Subnet: `services`
41 | * Integrate with private dns zone: No
42 | * click [create]
43 |
44 | Take note ok the `KEY1` found in Resource Management > Keys and Endpoint > KEY1.
45 |
46 | Go to Azure Portal > private DNS zone > create
47 | * name: `aoai-01.openai.azure.com`
48 | * Click [Create]
49 |
50 | Go to Azure Portal > Private DNS zones > `aoai-01.openai.azure.com` > + Record set
51 |
52 | * Name: `*`
53 | * Type: `A`
54 | * IP: `10.13.1.68` (Azure Open AI private endpoint IP)
55 | * Click [OK]
56 |
57 | Go to Virtual Network links > Add
58 | * name: `spoke-01-link`
59 | * subnet `spoke-01`
60 | * click [OK]
61 |
62 | Go to Virtual Network links > Add
63 | * name: `spoke-02-link`
64 | * subnet `spoke-02`
65 | * click [OK]
66 |
67 | ### API Management Service public IP
68 | Go to Azure Portal > Public IP addresses > Create
69 |
70 | Basic
71 | * Region: West Europe
72 | * Name: `apiman-ip-02`
73 | * IP version: `v4`
74 | * SKU: `developer` (stv1)
75 | * DNS name label: `apiman-02-ip`
76 | * click [create]
77 |
78 | ### API Management subnet NSG
79 | Go top Azure Portal > Network security groups > create
80 | * Name: `apiman-nsg`
81 | * Region: `West Europe`
82 | * click [create]
83 |
84 | add the following inbound rules
85 |
86 | | priority | name | port src/dst | protocol | source/service tag | destination/service tag | action |
87 | |----|----|----|----|----|----|----|
88 | | 1000 |apimanagement-inbound | */3443| TCP | ApiManagement | Virtual Network| Allow
89 | | 1100 |mgmt-endpoint-portal | */ * | TCP | AzureLoadBalancer | VirtualNetwork | allow
90 |
91 |
92 | add the following outbound rules
93 |
94 | | priority | name | port src/dst | protocol | source/service tag| destination/service tag | action |
95 | |----|----|----|----|----|----|----|
96 | | 1000 |to-storage | */443| TCP | Virtual Network | Storage| Allow
97 | | 1100 |to-sqlserver | */1443| TCP | Virtual Network | Sql| Allow
98 | | 1200 |to-azuremonitor | */1886,443| TCP | Virtual Network | Azure Monitor| Allow
99 |
100 | Go to Azure Portal > Network security groups > `apiman-nsg` > subnets > associate:
101 | * virtual network: `spoke-02`
102 | * subnet: `services`
103 |
104 | ### API Management subnet route table
105 | Go to Azure Portal > Route Table > Create
106 |
107 | * Region: West Europe
108 | * Name: `apim-02-to-firewall`
109 | * click [create]
110 |
111 | Go to Azure Portal > Route Table > `apim-02-to-firewall` > Routes > Add
112 |
113 | | Name | Type | Addr prefix | next hope type | next hope ip addr |
114 | |----|----|---|----|----|
115 | | apim2internet | Service Tag | ApiManagement | internet | |
116 | | 2firewall | Ip Addresses |0.0.0.0/0 | virtual appliance | 10.12.3.4 |
117 |
118 | Go to Azure Portal > Virtual Networks > `Spoke-02` > subnets > `services`
119 |
120 | * Route Table: `apim-02-to-firewall`
121 | * click [save]
122 |
123 | ### API Management subnet Service Endpoints
124 |
125 | Go to Azure Portal > Virtual Networks > `Spoke-02` > subnets > `services` > Endpoints > add
126 |
127 | * Microsoft.EventHub
128 | * Microsoft.Sql
129 | * Microsoft.Storage
130 |
131 | Click [save]
132 |
133 | ### Api Management Service Instance
134 | Go to Azure Portal > API Management Service > Create
135 |
136 | Basics
137 | * Region: `West Europe`
138 | * Name: `apiman-02`
139 | * Organization: `contoso`
140 | * Organization email: `admin@contoso.com`
141 | * Tier: `Premium` (stv1)
142 | * click [create]
143 |
144 | Go to Azure Portal > API Management Service > `apiman-02` > Network > Virtual Network:
145 |
146 | * Virtual Network: `internal`
147 | * Location: `West Europe`
148 | * Virtual Network: `spoke-02`
149 | * Management public IP address: `apiman-ip-02`
150 | * subnet : `services`
151 | * click [save]
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 | Go to Azure Portal > private DNS zone > create
163 | * name: `azure-api.net`
164 | * Click [Create]
165 |
166 | Go to Azure Portal > Private DNS zones > `azure-api.net` > + Record set
167 |
168 | | Name | Type | IP |
169 | |----|----|----|
170 | | `apiman-02` | A | `10.13.2.68` |
171 | | `apiman-02.portal` | A | `10.13.2.68` |
172 | | `apiman-02.developer` | A | `10.13.2.68` |
173 | | `apiman-02.management` | A | `10.13.2.68` |
174 | | `apiman-02.scm` | A | `10.13.2.68` |
175 |
176 |
177 | Go to Virtual Network links > Add
178 | * name: `spoke-03-link`
179 | * subnet `spoke-03`
180 | * click [OK]
181 |
182 | Go to Virtual Network links > Add
183 | * name: `spoke-02-link`
184 | * subnet `spoke-02`
185 | * click [OK]
186 |
187 | Go to Virtual Network links > Add
188 | * name: `hub-link`
189 | * subnet `hub-lab-net`
190 | * click [OK]
191 |
192 |
193 | ### Import OpenAI Rest interface in APIM using its swagger specification
194 |
195 | Azure OpenAI provides you with REST API references, that can easily be imported into Azure API Management. In this scenario we will expose via APIM the **completition endpoint**.
196 |
197 | * download the swagger specification (`swagger.json`) for the API version you want to expose from [the official AOAI endpoint swagger specifications](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#completions) i.e. `2022-12-01`
198 | * replace '{endpoint}' string with your deployed api
199 |
200 | ```
201 | (Get-Content .\swagger.json).Replace('{endpoint}', 'aoai-01.openai.azure.com') | Set-Content .\swagger.json
202 | ```
203 |
204 | Go to Azure Portal > API Management Services > `apiman-02` > APIs > create from definition > Open API > upload `swagger.json` file > create
205 |
206 | Azure OpenAI is protected using the concept of API keys. An API key is a unique access code that acts as a secure identifier. We will use Azure API Management policies to append an API key to our operations.
207 |
208 | Go to Azure Portal > API Management Services > `apiman-02` > APIs > All Operations > Inbound processing > Policy > Edit and insert the following XML
209 |
210 | ```
211 |
212 |
213 |
214 | {{APPPSVC-KEY}}
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 | ```
224 |
225 | For security reasons, we want to use Named values in Azure API Management and don’t paste the API key directly into our policy.
226 |
227 | For this go to Azure Portal > API Management Services > `apiman-02` > Named Values > Add
228 |
229 | * Name: `APPSVC-KEY`
230 | * Display Name: `APPPSVC-KEY`
231 | * Type: Secret
232 | * Value: `KEY1` (the KEY1 value you taken above from Open AI)
233 | * click [SAVE]
234 |
235 | Now disable subscription key required at API level, so that we can easily test the call in our environment.
236 |
237 | For this open Azure Portal > API Management Services > `apiman-02` > `Azure OpenAI Service API` > Settings > Subscription:
238 |
239 | * Subscription Required > `NO`
240 | * click [SAVE]
241 |
242 | 
243 |
244 | _download drawio version of this image [here](../images/aoai-deployment.drawio)._
245 |
246 |
247 | ## Test solution
248 | Finally, you are able to test your API call in Azure API Management.
249 |
250 | Connect via bastion/RDP to `spoke-02-vm` and test API call locally.
251 |
252 | Open Azure Portal > API Management Services > `apiman-02` > `Azure OpenAI Service API` > Test Tab > Test `Create a completition...`
253 |
254 | * deployment-id: `mygpt`
255 | * api-verison: `2022-12-01`
256 | * click [Send]
257 |
258 | you should receive an HTTP 200 answer.
259 |
260 | Connect via bastion/ssh to `spoke-03-vm` and type the following:
261 |
262 | ```
263 | curl https://apiman-02.azure-api.net/deployments/mygpt/completions?api-version=2022-12-01\
264 | -H "Content-Type: application/json" \
265 | -d "{
266 | \"prompt\": \"Once upon a time\",
267 | \"max_tokens\": 50
268 | }"
269 |
270 | ```
271 | you should receive a json message coming from Azure Open AI.
272 |
273 |
274 | > {"id":"cmpl-8ugbMFmX3n2P...","object":"text_completion","created":...,"model":"gpt-35-turbo-instruct","choices":[{"text":" some scadians dressed themselves as “lawmen” and rode out to confront the proprietors of Masker’s Serpent. They “remained a few minutes” and left. No one was arrested. No one does anything on Cape Cod that anyone","index":0,"finish_reason":"length","logprobs":null}],"usage":{"prompt_tokens":4,"completion_tokens":50,"total_tokens":54}}
275 |
276 | ## More information
277 |
278 |
279 | * Azure API Management Service https://learn.microsoft.com/en-us/azure/api-management/
280 | * vnet internal deployment: https://learn.microsoft.com/en-us/azure/api-management/api-management-using-with-internal-vnet
281 | * Deploy your Azure API Management instance to a virtual network - internal mode https://learn.microsoft.com/en-us/azure/api-management/api-management-using-with-internal-vnet?tabs=stv2
282 |
283 | * Private endpoint https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview
284 | * Publish Open AI via APIM https://techcommunity.microsoft.com/t5/apps-on-azure-blog/build-an-enterprise-ready-azure-openai-solution-with-azure-api/ba-p/3907562
285 | * Understanding Azure API Management policies: https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-policies
286 |
287 |
288 |
--------------------------------------------------------------------------------