DEV Community

Cover image for Creating a Private Endpoint for Azure Storage Account using Terraform
Nam Phuong Tran
Nam Phuong Tran

Posted on

Creating a Private Endpoint for Azure Storage Account using Terraform

Enhancing the security of our infrastructure is paramount. Today, I'll guide you through the process of setting up a private endpoint for Azure Storage Account using Terraform, step by step, leveraging distinct services.
One of the best architectures is highly recommended from MS official as below

Image description

Deeping dive to the detail we can see how it goes behind the scenes

Image description

We can see that, all almost connections to the Azure services such as: Storage account, Container registry, Keyvault, etc. It should be used private endpoint for more secure.
So, Today we will focus on the Azure Storage Account first. After we creating private endpoint for Storage account it looks like

Image description

Let’s start
Step 1: Create a Resource Group

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

Step 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"
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Create a Virtual Network with 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"]
}
Enter fullscreen mode Exit fullscreen mode

The Virtual Network comprises two subnets:
The PrivateSubnet: This is intended for creating the private endpoint. It also includes a Virtual Machine for testing purposes.
The AzureBastionSubnet: This subnet is designated for the Azure Bastion service, which enables secure connections to Virtual Machines.
Step 4: Set Up 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
  }
}
Enter fullscreen mode Exit fullscreen mode

Create a Public IP address and configure Azure Bastion.
Step 5: Establish a Private Endpoint
Before creating the private endpoint, generate a Private DNS Zone. Link the DNS Zone with the Virtual Network (VNet) and define the A record within the 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]
  }
}
Enter fullscreen mode Exit fullscreen mode

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]
}
Enter fullscreen mode Exit fullscreen mode

It seems almost thing done. Now, we are going to create an Virtual Machine and verify
Step 6: Create a 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"
  }
}
Enter fullscreen mode Exit fullscreen mode

Creating a Virtual Machine involves setting up a Network Interface and a Network Security Group. These components need to be connected.
Execute the Terraform script with the "terraform apply" command.
After running the command successfully, the resources will be created on Azure portal. It looks like

Image description

Image description

Step 7: Now, we will verify the result.
Step 7.1: Accessing through Storage Explorer

Image description

As we can see the result. If trying to access the Storage Account through Storage Explorer. It should be evident that direct access is prohibited.
Step 7.2: Now, we will use virtual machine that is the same virtual network as Remote Access using Azure Bastion

Image description

Using Azure Bastion to remotely access the Azure Virtual Machine is the next step. This entails connecting to the VM through the Azure Portal and selecting the Bastion option.

Image description

Image description

Install Azure Storage Explorer to access the VM, retrieve the Access Key from the Storage Account, and establish a connection through the Explorer. Create a blob container as an additional validation.

Image description

Step 7.3: Using nslookup for FQDN Verification

Image description

To confirm the setup, we can use the nslookup command for the FQDN (fully qualified domain name) generated after creating the DNS A record. This step helps ensure that the DNS record is accurately resolving to the expected IP address.
By following these meticulous steps, we create a private endpoint for an Azure Storage Account, significantly bolstering the security of our infrastructure.

Top comments (1)

Collapse
 
dwhitlockg1001 profile image
DWhitlockG1001

Great walkthrough, thank you. On the diagrams, specifically the second one, what tool was used to create it?