Azure Functions with a Static Outbound IP Address

“So to complete our configuration we just need your outbound static IP…” This is something that pops up again and again, specially if you work integrating legacy systems, like banks, government agencies or other older systems that requires a static IP Address to add to firewall inbound rules.

In the past I had to use on of the subscription tiers from Azure API Management or in some cases deploy the code within a self hosted service in a Virtual Machine. And both of them are valid options if you already have one of those components in place. But in a couple of my last projects, Not only I needed to implement a new component (in this case API Management) just to fulfil this requirement. I also had to change my design a bit, because API Management didn’t support one of the streaming requirements I had (but that is a story for another post).

But I wouldn’t be writing a post just to complain about this (not that this has never happened before) – I actually found another solution – one that I’ve tried and discarded earlier this year, but thanks to some good work from the App Services Engineering team, is finally a viable solution: Azure Functions + VNET Integration + NAT Gateways!

How does it work?

According to Microsoft Docs:

“NAT gateway resources are part of Virtual Network NAT and provide outbound Internet connectivity for one or more subnets of a virtual network. The subnet of the virtual network states which NAT gateway will be used. NAT provides source network address translation (SNAT) for a subnet. NAT gateway resources specify which static IP addresses virtual machines use when creating outbound flows.”

Designing virtual networks with NAT gateway resources

So, the NAT Gateways provides a way to specify a static IP addresses to be used as outbound address to any outbound traffic that is leaving a subnet towards the internet. Sounds like something we would like to do right?

When I first learned about this resource around the end of 2019, I’ve tried to configure the following scenario:

But I got stuck on an issue – NAT Gateway were not compatible with the App Service Plans for either Azure Function Consumption Premium or Dedicated plans. Doing some research I found that the NAT Gateway at that point was not compatible with the basic load balancer and any products that used basic load balancer.

But just this week, the App Services team provided us with some good news:

You can read their blog post here. So whatever incompatibility between the NAT Gateway and the App service plans are now solved! So let’s try to get it working…

Pre-requisites

In order to implement this you will need the following items:

  • An Azure Function configured with an App Service plan that supports VNET integration (Premium Consumption, or one of the following dedicated plans: Standard, Premium V2 or Premium V3),
  • A Virtual Network with at least one available subnet,
  • A standard IP Address and
  • A NAT Gateway.

You can find more information about how to configure the NAT Gateway and associate it to a subnet here. The configuration is pretty simple – just requires the standard IP address and the VNET/Subnet associated.

Notice that a single NAT Gateway can be connected to multiple subnets. So as long as applications requires the same outbound IP addresses, they can be integrated to subnets connected to the same NAT Gateway.

Azure Function Configuration

The configuration for Azure Functions is quite straightforward. First you need to go to Networking (1) and select configuration (2).

After that you need to click on + Add Vnet (1), then select an existing Virtual Network (2), click on select existing (3) and choose one of the available subnets (4) and finally click Ok (5).

Once the VNET configuration is completed, it should look like this:

To confirm that the subnet is configured to connect to the NAT Gateway, you can click on the subnet name, which will bring you to the subnet configuration page. Here, you can confirm that the NAT Gateway is assigned to that subnet.

I’ve noticed (and some other people also noticed) that when you add the VNET integration to the Azure Function (or web app), sometimes the NAT Gateway configuration is removed. I am not sure if that happens all the time, as I couldn’t reproduce it every time. But just bear that in mind – go back to the configuration of the subnet and make sure that it is associated to the NAT Gateway as a precaution. Thanks J.V. for highlighting this in the comments!

Once the configuration is completed in the VNET side, you will need to force all outgoing traffic to go through the VNET – this will force the traffic going to the internet to be routed through public IP address associated to the NAT Gateway. This can be achieved by including a new app setting entry on your configuration. You must include the following entry:

  • WEBSITE_VNET_ROUTE_ALL = 1

To do this, search for Configuration on your side blade (1), click on Configuration (2), click on add to include a new value (3), add WEBSITE_VNET_ROUTE_ALL as the Name and 1 as the Value, then click OK (4). Don’t forget to click save (5) to persist your changes

Testing your configuration

A simple way to test your configuration is to make a http request to a website that would return the IP address of the caller. I’ve used a PowerShell script to do that, taking the advantage of Azure Function support for PowerShell Core. You can find the code in the gist below:

A whole new world…

The ability to have Azure Functions and Web Apps having a static outbound IP is a feature that have been requested for some time now. This will open up a number of scenarios that were not possible today with pure serverless and will simplify a lot the integration with other systems, in special legacy and on-premises systems that still depend on a static IP address to configure inbound access.

That alone would already be fantastic news! But another recent announcement from the Enterprise Integration team combined with this makes those news even more exciting… The new Logic Apps runtime, which is an extension of Azure Functions Runtime, can also leverage from this configuration. So not only core Azure Functions implementations can take advantage of this implementation, but Logic Apps will soon be able to leverage from that as well. Actually, I’ve tested the same configuration on a logic apps (preview) resource and it behave as expected!

I’ve been doing a bit more research on the Logic Apps (preview) and what I found so far is that the managed connectors are still using their own IP addresses. For example, I’ve tried to connect to the an FTP server using the SFTP-SSH connector and it presented an IP from the managed connector in Australia East region. Hopefully this is something that will be addressed as the preview moves close to GA. Otherwise we will have to replace connector functionality with Azure Functions (still better than the original situation, but not ideal).

In Summary

  • App Services can now leverage from the NAT Gateway functionality to expose a static outbound IP address to the internet.
  • Azure Function with either a Premium Consumption or Dedicated app service plan can also leverage from the same functionality.
  • Logic Apps (preview), which are based on the Azure Functions runtime will also benefit from the same type of configuration.
  • This will open up a number of scenarios that were not easily implemented before – in special when integrating with on-premises or legacy systems.

Sharing is caring...

17 thoughts on “Azure Functions with a Static Outbound IP Address”

  1. Hello,
    … great article, thank you …

    I tried it, unfortunately, it does not work for me…
    And I have no idea how to diagnose where the problem is. Could you please point me what and how to check? Thank you.

    [Solved] First problem was that when I created VNet, IP, NAT, Functions in sequence… then when I configured Functions-Network-VNetIntegration – it (silently) unset NAT on subnet. I had to stop Function app, disconnect VNetIntegration, and reconnect it back, and THAN set used subnet on NAT. Now it is shown as used in subnet settings.

    [Solved] After deploy of new version of Function code – the executed version was still the old version. I am not sure whether it did anything, but I add a property specifying appServicePlan to deploy goal. Or may be it just took dozens of minutes to update?

    My set up (order of creating):
    – Resource group
    – App Service Plan – linux (S1)
    – VNet
    – Storage account
    – Static public IP (standard SKU)
    – NAT
    – Functions (+Insights), using dedicated plan, java runtime, HttpTriggered function calling Apache.HttpClient request to ifconfig.me/ip, deployed by maven plugin

    Unfortunately, it is NOT called from expected IP.
    – In function properties, there are props: Virtual IP, Outbound IPs, AdditionalOutboundIPs, but no mention of my dedicated static IP I used in NAT.
    – In subnet (called NAT-subnet) where NAT is activated, “Delegated to” is set to
    “Microsoft.Web/serverfarms” (done by assigning VNet integration).

    It all looks OK, but it does not work. Any idea what to check? Could you post set of CLI commands to help me to diagnose?
    Thank you and have a nice day.

    1. Thanks JV for catching this. I was sure I added that piece, but obviously not! 😉. I’ve updated the post to include this info now.

      I’ve also added a not about your previous comment, as I also noticed that. I will try to follow up with Microsoft to find if it is a bug or something that we would expect it to happen first time you connect the subnet.

    2. Hello,

      I have tried all the way.
      I have created NAT G/W and attached to Subnet as well as created Function app in the P1V2 SKU.
      Did the VNet integration too but when i am trying to connect to my Private Azure storage account and whitelisted the NAT G/W outbound IP address.
      It gives me the 403 not authorize access but when i am doing my storage account to public works fine.

      Even i have added WEBSITE_VNET_ROUTE_ALL=1
      I am unable to get where exact issue is, could you please help me in it.

  2. I was able to configure function in vnet+nat and everything works fine except the 25 port. We need the static outbound IP to be able to whitelist it on SMTP servers during the connection via 25 port. When I try to initiate new SMTP connection via 25 port it doesn’t work. It works when I set WEBSITE_VNET_ROUTE_ALL = 0 for function.
    We have the Enterprise subscription and port 25 is opened for our resources.
    Any ideas?

    1. The only thing I can think of is if you have any specific NSG rule that don’t allow outbound on that particular range on port 25 (or only allow on particular ports).

    2. Hi Vitalii,
      We have the same problems here. Have you already found a solution for this?

  3. I’ve been looking at this for a similar use case we have, but to get this to route through the Azure firewall rather than the NAT gateway (we have a security requirement for all ingress/egress traffic to route through the core firewall). In theory, I don’t see why this wouldn’t work but not having much luck so far.

    1. Bobby, we did implemented that in another client, or rather the client implemented it. If I am not wrong, they had to create a static route to push outgoing traffic on the subnet associated to the VNET integration to the firewall. Unfortunatelly, networking is not one of my core skills, but hopefully this would help you with that.

  4. Connecting the Virtual Network to the Azure Function is not allowed.

    I get “this virtual network has no gateway”.

    Do I need to create a VPN Gateway (as I read somewhere). This costs a lot!

    1. Steven, I didn’t need to have a VPN Gateway connected. But one requirement I know is needed is that the Azure Function and the VNET must be in the same region, otherwise you will need an Azure Virtual Network Gateway as per MS documentation here.

      1. Eventually, I Found the solution tot my problem. My app service Plan was An P2v1, while V2 is the minimum required, it appears.

        Anyway, thanks for your reply.

        1. Yep, P2V1 still uses the old type of VNET (which is the one that requires Gateway). The regional VNET Integration is only supported by V2 series. Really happy that you found the problem!

          1. Hello,

            I have tried all the way.
            I have created NAT G/W and attached to Subnet as well as created Function app in the P1V2 SKU.
            Did the VNet integration too but when i am trying to connect to my Private Azure storage account and whitelisted the NAT G/W outbound IP address.
            It gives me the 403 not authorize access but when i am doing my storage account to public works fine.

            Even i have added WEBSITE_VNET_ROUTE_ALL=1
            I am unable to get where exact issue is, could you please help me in it.

          2. Vrushal,

            Can you please explain a bit more what you trying to achieve? seems like you trying to access a Private storage account? Are you trying to block access to the storage account using the Firewall setting on Storage Account?

            Wouldn’t be simpler to allow access to a specific subnet from that VNET (i.e. the same subnet you added the Azure Function to). Feels like there are extra components in your setup (but I might be totally wrong, since I not sure how it is setup).

            I would try:

            1) Setup VNET Integration in Function
            2) Set WEBSITE_VNET_ROUTE_ALL to the function
            3) Set the storage account to only allow access from the VNET/SUBNET that is assigned to the Function

            Sorry for the late reply and I hope this helps, mate!

  5. Hi as per the above question, I have an Azure Function that fires off an email

    This works well however we seem to be getting flagged on Spamhaus lately via the IBK

    so I then setup a NAT gateway and this works on port 80 and port 443 but cannot send traffic out on ports 25 and 587?

    has anyone else had or solved this problem? I see Vitalii above has/had the same issue?

    Kind regards
    Simon

Leave a Reply to J.V. Cancel reply

Your email address will not be published. Required fields are marked *