Securing Azure Applications with Terraform: Best Practices and Strategies

  • Thread Author
Securing your cloud applications isn’t just about keeping hackers at bay—it’s an art form. In today’s deep dive, we explore how to secure application networks in Azure using Terraform, deploying subnets, private endpoints, private DNS, and network security groups (NSGs) like a pro. Whether you’re a seasoned cloud architect or just starting out, buckle up for a detailed walk through a modern, segmented approach to application security in Azure.

Designing a Secure Azure Architecture​

When you deploy an application to Azure, one of the first questions you should ask is: “Who can get in?” Azure’s recommended approach is not to leave your app wide open to the internet. Instead, separate your services into dedicated segments, giving you granular control over access. In our example, the architecture separates two services—each in its own subnet—while ensuring they can communicate securely.
Key components of this approach include:
• Subnet creation and delegation
• Deployment of private endpoints
• Integration of Azure Private Link
• Configuration of private DNS and NSGs
By keeping your resources organized and isolated within their designated subnets, you automatically gain enhanced security features such as IP allocation, automatic routing, and service-specific policy enforcement.

Understanding Subnet Delegation​

Before we get to the Terraform code, let’s understand why subnet delegation is a game changer. In Azure, subnet delegation automates many networking tasks. When you delegate a subnet—for example, to Microsoft.Web/serverFarms—you allow Azure to manage:
• IP Allocation: No need to manually calculate IP reserves
• Routing: Azure finely tunes routing protocols behind the scenes
• Conflict Prevention: Exclusive subnet usage avoids accidental overlaps
• Policy Enforcement: Security and routing policies are automatically applied
In our sample Terraform code, the initial error encountered was something like:
  "PrivateEndpointCreationNotAllowedAsSubnetIsDelegated"
This error occurs because a subnet, once delegated to a service (such as Azure Functions under Microsoft.Web/serverFarms), is reserved exclusively for that purpose. Attempting to create a private endpoint directly in that subnet violates Azure’s rules. The solution? Use separate, dedicated subnets for different functionalities.

Private Endpoints and the Role of Azure Private Link​

A private endpoint in Azure provides your resource with a private IP address within your virtual network—essentially serving as a secure connection between services. When you set up a private endpoint, an underlying network interface (NIC) is automatically created, carrying your private IP.
However, as mentioned above, trying to mix delegated subnets (used for App Services or function apps) and private endpoints doesn’t work. Azure won’t allow a private endpoint to be attached to a subnet that’s already been delegated.
Enter Azure Private Link. Think of it as the missing bridge between your private endpoint and your target Azure service. But why do you need it? Simply put, Private Link ensures that requests sent via a private endpoint traverse the secure, internal Azure backbone network rather than the public internet. This dramatically reduces your app’s exposure to external threats while ensuring that the connection remains robust and efficient.

Terraform in Action: Building a Secure Deployment​

The beauty of Terraform is that it allows you to codify your entire infrastructure. Let’s break down the Terraform code excerpt provided in our case study. The first part of the code sets up essential resources like the storage account, service plan, and Function Apps.

Example snippet for creating a storage account and service plan:​

resource "azurerm_storage_account" "sa1" {
name = "dnsexamplesa"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
account_tier = "Standard"
account_replication_type = "LRS"
}

resource "azurerm_service_plan" "asp" {
name = "dns-asp"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
os_type = "Windows"
sku_name = "P1v2"
}​

Following that, each Function App (app1 and app2) is created within its own subnet. In the initial code, both Function Apps were deployed into subnets (subnet1 and subnet2) without delegation, which would lead to the error noted above. To fix this, a delegation block was added in the subnet definitions:

resource "azurerm_subnet" "subnet1" {
name = "subnet1"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = [ "10.0.1.0/24" ]

delegation {
name = "delegation"
service_delegation {
name = "Microsoft.Web/serverFarms"
actions = [
"Microsoft.Network/virtualNetworks/subnets/join/action",
"Microsoft.Network/virtualNetworks/subnets/prepareNetworkPolicies/action",
]
}
}
}​

Now, with subnet delegation in place, our Function Apps are neatly isolated and managed. But what about private endpoints? As we already encountered, trying to add a private endpoint directly to these delegated subnets results in a conflict because Azure reserves them exclusively for App Service-related traffic.
The solution is to create a third, new subnet specifically for private endpoints.

resource "azurerm_subnet" "subnet3" {
name = "subnet3"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = [ "10.0.3.0/24" ]
}
resource "azurerm_private_endpoint" "app1_pe" {
name = "app1-pe"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
subnet_id = azurerm_subnet.subnet3.id
}

resource "azurerm_private_endpoint" "app2_pe" {
name = "app2-pe"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
subnet_id = azurerm_subnet.subnet3.id
}​

By isolating private endpoints in a dedicated subnet (subnet3), you steer clear of any conflicts with delegated subnets and maintain the integrity of both your application deployments and private connectivity.

Bringing It All Together: A Practical Perspective​

Imagine deploying two critical services that need to interact securely—each service can be thought of as residing in its own secure vault (subnet). Their communications are safe because they’re connected by a dedicated private bridge (via Azure Private Link), ensuring no unauthorized entry can occur. Moreover, by using subnet delegation for the vaults containing your function apps, you reduce the risk of configuration errors and let Azure take care of complex networking details.
Here’s a quick rundown of the best practices highlighted in this approach:
  1. Segregate your services into distinct subnets.
  2. Use subnet delegation to ensure that services like Function Apps get dedicated network policies and routing.
  3. Create dedicated subnets for private endpoints to avoid conflicts with delegated subnets.
  4. Employ Azure Private Link to securely bridge the gap between private endpoints and your Azure services.
  5. Optionally, integrate network security groups (NSGs) to further control traffic flows and manage inbound/outbound access.
Each of these steps not only improves security but also simplifies management by compartmentalizing responsibilities within your virtual network.

Lessons Learned and Further Considerations​

This deployment scenario provides several crucial lessons:
• Plan Ahead: Networking errors like “PrivateEndpointCreationNotAllowedAsSubnetIsDelegated” could otherwise cost you valuable time. Knowing that delegated subnets cannot host private endpoints encourages proactive infrastructure planning.
• Leverage Azure’s Automation: With delegation, Azure handles many low-level tasks—including IP allocation and routing—allowing you to focus on higher-order security policies and application logic.
• Embrace Terraform: Infrastructure as Code tools like Terraform offer the power to reproduce exact network configurations. This repeatability boosts reliability and streamlines troubleshooting.
• Security is Layered: While segregating applications via subnets and private endpoints shields your core services, further security can be applied with NSGs. These groups can define granular rules ensuring only approved traffic reaches your services.
An inquisitive mind might ask: Is this extra complexity worth it? When you consider the threat landscape today—where cyberattacks are increasingly common—the answer is a resounding yes. By investing time in a segmented, well-delegated network architecture, you are proactively reducing potential attack vectors. It’s like building a fortress with moats, drawbridges, and secret passageways, each layer adding an extra hurdle for intruders.

Conclusion and the Road Ahead​

In summary, securing your application network in Azure using Terraform is both a powerful and flexible way to ensure that your services are safely compartmentalized. By dividing your deployment into segregated subnets, applying delegated policies, and establishing private endpoints with dedicated subnets, you create an environment where security is a fundamental design principle rather than an afterthought.
As you continue to evolve your Azure deployments, consider further enhancements:
• Integrate NSGs to fine-tune your security posture.
• Monitor network traffic closely to detect anomalies.
• Regularly update your Terraform scripts as Azure adds new features or tweaks existing ones.
• Explore additional Azure security services like Azure Firewall or DDoS protection for a multi-layered defense strategy.
By following these best practices, you not only safeguard your applications but also streamline network management—ensuring that scaling up doesn’t mean opening up the doors to unwanted visitors.
Remember, in the ever-changing landscape of cloud security, a secure network design made today sets the stage for robust, enduring defenses tomorrow. Happy coding and may your networks remain impenetrable!

Source: Medium
 

Back
Top