The following picture shows the high-level architecture as below


Or we can see the following picture provides a more detailed view of the infrastructure on Azure


We can easily see that, this is a best approach to build our infrastructure more securely. Today, I will show you how we can build this infrastructure step by step with individual services.

First, we will focus on Azure Storage Account

After creating resources, it looks like 


Let's start


1. Create a Resource group 

resource "azurerm_resource_group" "rg" {
  name = "rg-sd2488-non-prod-weu-infra"
  location = "westeurope"
  tags = {
    Owner="sd2488"
  }
}

2. Create a Storage Account

resource "azurerm_storage_account" "st" {
  name                     = "stsd2488nonprodweu"
  resource_group_name      = azurerm_resource_group.rg.name
  location                 = azurerm_resource_group.rg.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
  min_tls_version = "TLS1_2"
  network_rules {
    default_action             = "Deny"
    ip_rules                   = []
  }

  tags = {
    Owner="sd2488"
  }
}

3. Create a Virtual Network with 2 subnets

resource "azurerm_virtual_network" "vnet" {
  name                = "vnet-sd2488-non-prod-weu"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  address_space       = ["10.1.0.0/16"]  

  tags = {
    Owner="sd2488"
  }
}

resource "azurerm_subnet" "snet_endpoint" {
  name = "PrivateSubnet"
  virtual_network_name = azurerm_virtual_network.vnet.name
  resource_group_name = azurerm_resource_group.rg.name
  address_prefixes      = ["10.1.0.0/24"]  
 
}

resource "azurerm_subnet" "snet_bas" {
  name = "AzureBastionSubnet"
  virtual_network_name = azurerm_virtual_network.vnet.name
  resource_group_name = azurerm_resource_group.rg.name
  address_prefixes      = ["10.1.1.0/24"]
}

As we can see, there are 2 subnets

- The PrivateSubnet, it has been used for creating private endpoint. It is also included one Virtual machine for testing purpose.

- The AzureBastionSubnet, it has been used for AzureBastion service which helping to connect from somewhere to Virtual machine more securely.

4. Next, we are going to create a Public IP address and Azure Bastion.

resource "azurerm_public_ip" "pip" {
  name                = "pip-sd2488-non-prod-weu"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  allocation_method   = "Static"
  sku                 = "Standard"
}

resource "azurerm_bastion_host" "bas" {
  name                = "bas-sd2488-non-prod-weu"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  ip_configuration {
    name                 = "bas-configuration"
    subnet_id            = azurerm_subnet.snet_bas.id
    public_ip_address_id = azurerm_public_ip.pip.id
  }
}

5. Next, we will create a private endpoint

Before creating private endpoint, we will create a Private DNS Zone

resource "azurerm_private_dns_zone" "pdns_st" {
  name                = "privatelink.blob.core.windows.net"
  resource_group_name = azurerm_resource_group.rg.name
}

Then,

resource "azurerm_private_endpoint" "pep_st" {
  name                = "pep-sd2488-st-non-prod-weu"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  subnet_id           = azurerm_subnet.snet_endpoint.id

  private_service_connection {
    name                           = "sc-sta"
    private_connection_resource_id = azurerm_storage_account.st.id
    subresource_names              = ["blob"]
    is_manual_connection           = false
  }

  private_dns_zone_group {
    name                 = "dns-group-sta"
    private_dns_zone_ids = [azurerm_private_dns_zone.pdns_st.id]
  }
}

This step, we need to link the DNS Zone with Vnet and define A record in DNS Zone

resource "azurerm_private_dns_zone_virtual_network_link" "dns_vnet_lnk_sta" {
  name                  = "lnk-dns-vnet-sta"
  resource_group_name   = azurerm_resource_group.rg.name
  private_dns_zone_name = azurerm_private_dns_zone.pdns_st.name
  virtual_network_id    = azurerm_virtual_network.vnet.id
}

resource "azurerm_private_dns_a_record" "dns_a_sta" {
  name                = "sta_a_record"
  zone_name           = azurerm_private_dns_zone.pdns_st.name
  resource_group_name = azurerm_resource_group.rg.name
  ttl                 = 300
  records             = [azurerm_private_endpoint.pep_st.private_service_connection.0.private_ip_address]
}

It seems almost thing done. Now, we are going to create an Virtual Machine and verify

6. Create an Virtual machine

Before creating Virtual machine, we need to create Network Interface and Network Security Group and link 2 services with each together

resource "azurerm_network_security_group" "nsg" {
  name                = "nsg-sd2488-non-prod-weu"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
 
  tags = {
    Owner="sd2488"
  }
}

resource "azurerm_network_interface" "nic" {
  name                = "nic-sd2488-non-prod-weu"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
 
  ip_configuration {
    name                          = "nic-configuration"
    subnet_id                     = azurerm_subnet.snet_endpoint.id
    private_ip_address_allocation = "Dynamic"
  }
}

resource "azurerm_network_interface_security_group_association" "nsgnic" {
  network_interface_id      = azurerm_network_interface.nic.id
  network_security_group_id = azurerm_network_security_group.nsg.id
}

resource "azurerm_windows_virtual_machine" "vm" {
  name                = "vm-sd2488-non"
  location              = azurerm_resource_group.rg.location
  resource_group_name   = azurerm_resource_group.rg.name
  size                = "Standard_F2"
  admin_username      = "adminuser"
  admin_password      = "P@$$w0rd1234!"  
  network_interface_ids = [
    azurerm_network_interface.nic.id,
  ]
 
  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }
 
  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = "2019-Datacenter"
    version   = "latest"
  }
}

After applying Terraform by command "terraform apply"



8. Verifying the result

8.1 Using Storage Explorer try to access

Fantasic, it can't access directly from somewhere

8.2 Using Azure Bastion to remote Azure Virtual Machine, let see

Go to the Azure Portal and Select VM


And click to the button Connect then select Bastion and keep clicking to Use Bastion button


When remoting success fully, we need to install Azure Storage Explorer also then we try to get Access Key from the Storage Account and connect from Explorer


After that trying to create a blob container and see! Wow, it's a amazing, we can create a container

8.3 Using nslookup the FQDN which is generated after creating dns_a_record


Reference: 

https://learn.microsoft.com/en-us/azure/private-link/create-private-endpoint-portal?tabs=dynamic-ip