When managing access to cloud resources, one common challenge is dynamically updating network security rules to allow access based on the current public IP address of the user or system. Whether you’re connecting to a virtual machine for administrative purposes or creating secure connections to other resources, manually updating your IP address in your Network Security Group (NSG) rules is tedious and prone to errors. Fortunately, Terraform offers a way to solve this problem by dynamically looking up your public IP address using the http provider’s data source. This method ensures that your NSG rules are always up-to-date with your current IP address without manual intervention.

To start, use a data source from Terraform’s http provider to retrieve your public IP address. Services like https://ifconfig.me/ip provide a simple API endpoint that returns the IP address of the request origin. The following snippet demonstrates how to set up the data source:

data "http" "my_ip" {
  url = "https://ifconfig.me/ip"
}

The data source retrieves your IP address as a raw string, which can then be used in your Terraform configurations. It’s a good practice to clean up this value to ensure no extraneous whitespace or newline characters interfere with its usage. The chomp() function is perfect for this purpose, trimming any unwanted characters from the IP address string.

Once you have the IP address, you can reference it in your NSG rules. For example, you can create a Network Security Group to allow SSH access only from your current public IP address. Here’s how you can integrate the dynamically retrieved IP:

resource "azurerm_network_security_group" "public" {
  name                = "nsg-${var.application_name}-${var.environment_name}-public"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name

  security_rule {
    name                       = "ssh"
    priority                   = 100
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "22"
    source_address_prefix      = chomp(data.http.my_ip.body)
    destination_address_prefix = "*"
  }

}

This approach uses a nested security_rule block within the azurerm_network_security_group resource. However, it’s often better to define security rules as independent resources. This modular approach improves readability, allows for easier rule management, and prevents conflicts since nested rules and standalone rules cannot coexist in the same NSG.

Here’s how to define the SSH rule as a separate resource:

resource "azurerm_network_security_rule" "allow_ssh" {
  name                        = "ssh"
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "22"
  destination_address_prefix  = chomp(data.http.my_ip.body)
  resource_group_name         = azurerm_resource_group.main.name
  network_security_group_name = azurerm_network_security_group.public.name
}

This independent resource can be associated with an NSG through its name, providing greater flexibility and clarity in rule management. Ensure you remove any nested security_rule blocks from the parent azurerm_network_security_group resource to avoid configuration conflicts.

Finally, associate the NSG with your subnet to enforce these security rules effectively. This can be done using the azurerm_subnet_network_security_group_association resource:

resource "azurerm_subnet_network_security_group_association" "alpha_ssh_access" {
  subnet_id                 = azurerm_subnet.alpha.id
  network_security_group_id = azurerm_network_security_group.ssh_access.id
}

If you’re curious about the actual value retrieved by the http data source, you can use the terraform console to inspect it. Simply input data.http.my_ip.body, and Terraform will return the raw value, such as 74.x.x.212. With the chomp() function applied, this value is cleanly integrated into your configurations.

In conclusion, dynamically looking up your public IP address with Terraform is an efficient way to keep your Network Security Group rules up-to-date. This method saves time, reduces the risk of errors, and ensures your infrastructure remains secure. Whether you’re managing one environment or multiple, leveraging Terraform’s capabilities to automate such tasks can greatly enhance your workflow. By defining rules independently and using Terraform’s features like data sources and functions, you can achieve a cleaner, more maintainable configuration.