Configure GCP VPC Flow Logs

The gcp:networkmanagement/vpcFlowLogsConfig:VpcFlowLogsConfig resource, part of the Pulumi GCP provider, configures VPC Flow Logs collection for networks, subnets, VPN tunnels, or Interconnect attachments. This guide focuses on three capabilities: network-level and subnet-level logging, VPN tunnel traffic capture, and Interconnect attachment monitoring.

Flow log configurations reference existing VPC infrastructure and use project numbers from the organizations.getProject data source. The examples are intentionally small. Combine them with your own sampling rates, aggregation intervals, and filtering expressions.

Enable flow logs for an entire VPC network

Organizations often start by enabling flow logs at the network level to capture traffic from all VMs, VPN tunnels, and Interconnect attachments within a VPC.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const project = gcp.organizations.getProject({});
const network = new gcp.compute.Network("network", {name: "basic-network-test-network"});
const network_test = new gcp.networkmanagement.VpcFlowLogsConfig("network-test", {
    vpcFlowLogsConfigId: "basic-network-test-id",
    location: "global",
    network: pulumi.all([project, network.name]).apply(([project, name]) => `projects/${project.number}/global/networks/${name}`),
});
import pulumi
import pulumi_gcp as gcp

project = gcp.organizations.get_project()
network = gcp.compute.Network("network", name="basic-network-test-network")
network_test = gcp.networkmanagement.VpcFlowLogsConfig("network-test",
    vpc_flow_logs_config_id="basic-network-test-id",
    location="global",
    network=network.name.apply(lambda name: f"projects/{project.number}/global/networks/{name}"))
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/networkmanagement"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		if err != nil {
			return err
		}
		network, err := compute.NewNetwork(ctx, "network", &compute.NetworkArgs{
			Name: pulumi.String("basic-network-test-network"),
		})
		if err != nil {
			return err
		}
		_, err = networkmanagement.NewVpcFlowLogsConfig(ctx, "network-test", &networkmanagement.VpcFlowLogsConfigArgs{
			VpcFlowLogsConfigId: pulumi.String("basic-network-test-id"),
			Location:            pulumi.String("global"),
			Network: network.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("projects/%v/global/networks/%v", project.Number, name), nil
			}).(pulumi.StringOutput),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var project = Gcp.Organizations.GetProject.Invoke();

    var network = new Gcp.Compute.Network("network", new()
    {
        Name = "basic-network-test-network",
    });

    var network_test = new Gcp.NetworkManagement.VpcFlowLogsConfig("network-test", new()
    {
        VpcFlowLogsConfigId = "basic-network-test-id",
        Location = "global",
        Network = Output.Tuple(project, network.Name).Apply(values =>
        {
            var project = values.Item1;
            var name = values.Item2;
            return $"projects/{project.Apply(getProjectResult => getProjectResult.Number)}/global/networks/{name}";
        }),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.compute.Network;
import com.pulumi.gcp.compute.NetworkArgs;
import com.pulumi.gcp.networkmanagement.VpcFlowLogsConfig;
import com.pulumi.gcp.networkmanagement.VpcFlowLogsConfigArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
            .build());

        var network = new Network("network", NetworkArgs.builder()
            .name("basic-network-test-network")
            .build());

        var network_test = new VpcFlowLogsConfig("network-test", VpcFlowLogsConfigArgs.builder()
            .vpcFlowLogsConfigId("basic-network-test-id")
            .location("global")
            .network(network.name().applyValue(_name -> String.format("projects/%s/global/networks/%s", project.number(),_name)))
            .build());

    }
}
resources:
  network-test:
    type: gcp:networkmanagement:VpcFlowLogsConfig
    properties:
      vpcFlowLogsConfigId: basic-network-test-id
      location: global
      network: projects/${project.number}/global/networks/${network.name}
  network:
    type: gcp:compute:Network
    properties:
      name: basic-network-test-network
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The network property references an existing VPC network using the format projects/{project_number}/global/networks/{name}. The vpcFlowLogsConfigId provides a unique identifier for this configuration. Setting location to “global” applies the configuration across all regions where the network exists.

Scope flow logs to a specific subnet

When you need granular control over which traffic gets logged, subnet-level configuration lets you target specific IP ranges without capturing the entire network.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const project = gcp.organizations.getProject({});
const network = new gcp.compute.Network("network", {
    name: "basic-subnet-test-network",
    autoCreateSubnetworks: false,
});
const subnetwork = new gcp.compute.Subnetwork("subnetwork", {
    name: "basic-subnet-test-subnetwork",
    ipCidrRange: "10.2.0.0/16",
    region: "us-central1",
    network: network.id,
});
const subnet_test = new gcp.networkmanagement.VpcFlowLogsConfig("subnet-test", {
    vpcFlowLogsConfigId: "basic-subnet-test-id",
    location: "global",
    subnet: pulumi.all([project, subnetwork.name]).apply(([project, name]) => `projects/${project.number}/regions/us-central1/subnetworks/${name}`),
});
import pulumi
import pulumi_gcp as gcp

project = gcp.organizations.get_project()
network = gcp.compute.Network("network",
    name="basic-subnet-test-network",
    auto_create_subnetworks=False)
subnetwork = gcp.compute.Subnetwork("subnetwork",
    name="basic-subnet-test-subnetwork",
    ip_cidr_range="10.2.0.0/16",
    region="us-central1",
    network=network.id)
subnet_test = gcp.networkmanagement.VpcFlowLogsConfig("subnet-test",
    vpc_flow_logs_config_id="basic-subnet-test-id",
    location="global",
    subnet=subnetwork.name.apply(lambda name: f"projects/{project.number}/regions/us-central1/subnetworks/{name}"))
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/networkmanagement"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		if err != nil {
			return err
		}
		network, err := compute.NewNetwork(ctx, "network", &compute.NetworkArgs{
			Name:                  pulumi.String("basic-subnet-test-network"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		subnetwork, err := compute.NewSubnetwork(ctx, "subnetwork", &compute.SubnetworkArgs{
			Name:        pulumi.String("basic-subnet-test-subnetwork"),
			IpCidrRange: pulumi.String("10.2.0.0/16"),
			Region:      pulumi.String("us-central1"),
			Network:     network.ID(),
		})
		if err != nil {
			return err
		}
		_, err = networkmanagement.NewVpcFlowLogsConfig(ctx, "subnet-test", &networkmanagement.VpcFlowLogsConfigArgs{
			VpcFlowLogsConfigId: pulumi.String("basic-subnet-test-id"),
			Location:            pulumi.String("global"),
			Subnet: subnetwork.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("projects/%v/regions/us-central1/subnetworks/%v", project.Number, name), nil
			}).(pulumi.StringOutput),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var project = Gcp.Organizations.GetProject.Invoke();

    var network = new Gcp.Compute.Network("network", new()
    {
        Name = "basic-subnet-test-network",
        AutoCreateSubnetworks = false,
    });

    var subnetwork = new Gcp.Compute.Subnetwork("subnetwork", new()
    {
        Name = "basic-subnet-test-subnetwork",
        IpCidrRange = "10.2.0.0/16",
        Region = "us-central1",
        Network = network.Id,
    });

    var subnet_test = new Gcp.NetworkManagement.VpcFlowLogsConfig("subnet-test", new()
    {
        VpcFlowLogsConfigId = "basic-subnet-test-id",
        Location = "global",
        Subnet = Output.Tuple(project, subnetwork.Name).Apply(values =>
        {
            var project = values.Item1;
            var name = values.Item2;
            return $"projects/{project.Apply(getProjectResult => getProjectResult.Number)}/regions/us-central1/subnetworks/{name}";
        }),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.compute.Network;
import com.pulumi.gcp.compute.NetworkArgs;
import com.pulumi.gcp.compute.Subnetwork;
import com.pulumi.gcp.compute.SubnetworkArgs;
import com.pulumi.gcp.networkmanagement.VpcFlowLogsConfig;
import com.pulumi.gcp.networkmanagement.VpcFlowLogsConfigArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
            .build());

        var network = new Network("network", NetworkArgs.builder()
            .name("basic-subnet-test-network")
            .autoCreateSubnetworks(false)
            .build());

        var subnetwork = new Subnetwork("subnetwork", SubnetworkArgs.builder()
            .name("basic-subnet-test-subnetwork")
            .ipCidrRange("10.2.0.0/16")
            .region("us-central1")
            .network(network.id())
            .build());

        var subnet_test = new VpcFlowLogsConfig("subnet-test", VpcFlowLogsConfigArgs.builder()
            .vpcFlowLogsConfigId("basic-subnet-test-id")
            .location("global")
            .subnet(subnetwork.name().applyValue(_name -> String.format("projects/%s/regions/us-central1/subnetworks/%s", project.number(),_name)))
            .build());

    }
}
resources:
  subnet-test:
    type: gcp:networkmanagement:VpcFlowLogsConfig
    properties:
      vpcFlowLogsConfigId: basic-subnet-test-id
      location: global
      subnet: projects/${project.number}/regions/us-central1/subnetworks/${subnetwork.name}
  network:
    type: gcp:compute:Network
    properties:
      name: basic-subnet-test-network
      autoCreateSubnetworks: false
  subnetwork:
    type: gcp:compute:Subnetwork
    properties:
      name: basic-subnet-test-subnetwork
      ipCidrRange: 10.2.0.0/16
      region: us-central1
      network: ${network.id}
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The subnet property narrows the scope to a specific subnetwork using the format projects/{project_number}/regions/{region}/subnetworks/{name}. This configuration only captures traffic from VMs within that subnet’s IP range, reducing log volume compared to network-wide logging.

Capture traffic through VPN tunnels

Hybrid cloud deployments that connect on-premises networks via VPN need visibility into tunnel traffic for troubleshooting and security analysis.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const project = gcp.organizations.getProject({});
const network = new gcp.compute.Network("network", {name: "basic-test-network"});
const targetGateway = new gcp.compute.VPNGateway("target_gateway", {
    name: "basic-test-gateway",
    network: network.id,
});
const vpnStaticIp = new gcp.compute.Address("vpn_static_ip", {name: "basic-test-address"});
const frEsp = new gcp.compute.ForwardingRule("fr_esp", {
    name: "basic-test-fresp",
    ipProtocol: "ESP",
    ipAddress: vpnStaticIp.address,
    target: targetGateway.id,
});
const frUdp500 = new gcp.compute.ForwardingRule("fr_udp500", {
    name: "basic-test-fr500",
    ipProtocol: "UDP",
    portRange: "500",
    ipAddress: vpnStaticIp.address,
    target: targetGateway.id,
});
const frUdp4500 = new gcp.compute.ForwardingRule("fr_udp4500", {
    name: "basic-test-fr4500",
    ipProtocol: "UDP",
    portRange: "4500",
    ipAddress: vpnStaticIp.address,
    target: targetGateway.id,
});
const tunnel = new gcp.compute.VPNTunnel("tunnel", {
    name: "basic-test-tunnel",
    peerIp: "15.0.0.120",
    sharedSecret: "a secret message",
    targetVpnGateway: targetGateway.id,
}, {
    dependsOn: [
        frEsp,
        frUdp500,
        frUdp4500,
    ],
});
const vpn_test = new gcp.networkmanagement.VpcFlowLogsConfig("vpn-test", {
    vpcFlowLogsConfigId: "basic-test-id",
    location: "global",
    vpnTunnel: pulumi.all([project, tunnel.name]).apply(([project, name]) => `projects/${project.number}/regions/us-central1/vpnTunnels/${name}`),
});
const route = new gcp.compute.Route("route", {
    name: "basic-test-route",
    network: network.name,
    destRange: "15.0.0.0/24",
    priority: 1000,
    nextHopVpnTunnel: tunnel.id,
});
import pulumi
import pulumi_gcp as gcp

project = gcp.organizations.get_project()
network = gcp.compute.Network("network", name="basic-test-network")
target_gateway = gcp.compute.VPNGateway("target_gateway",
    name="basic-test-gateway",
    network=network.id)
vpn_static_ip = gcp.compute.Address("vpn_static_ip", name="basic-test-address")
fr_esp = gcp.compute.ForwardingRule("fr_esp",
    name="basic-test-fresp",
    ip_protocol="ESP",
    ip_address=vpn_static_ip.address,
    target=target_gateway.id)
fr_udp500 = gcp.compute.ForwardingRule("fr_udp500",
    name="basic-test-fr500",
    ip_protocol="UDP",
    port_range="500",
    ip_address=vpn_static_ip.address,
    target=target_gateway.id)
fr_udp4500 = gcp.compute.ForwardingRule("fr_udp4500",
    name="basic-test-fr4500",
    ip_protocol="UDP",
    port_range="4500",
    ip_address=vpn_static_ip.address,
    target=target_gateway.id)
tunnel = gcp.compute.VPNTunnel("tunnel",
    name="basic-test-tunnel",
    peer_ip="15.0.0.120",
    shared_secret="a secret message",
    target_vpn_gateway=target_gateway.id,
    opts = pulumi.ResourceOptions(depends_on=[
            fr_esp,
            fr_udp500,
            fr_udp4500,
        ]))
vpn_test = gcp.networkmanagement.VpcFlowLogsConfig("vpn-test",
    vpc_flow_logs_config_id="basic-test-id",
    location="global",
    vpn_tunnel=tunnel.name.apply(lambda name: f"projects/{project.number}/regions/us-central1/vpnTunnels/{name}"))
route = gcp.compute.Route("route",
    name="basic-test-route",
    network=network.name,
    dest_range="15.0.0.0/24",
    priority=1000,
    next_hop_vpn_tunnel=tunnel.id)
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/networkmanagement"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		if err != nil {
			return err
		}
		network, err := compute.NewNetwork(ctx, "network", &compute.NetworkArgs{
			Name: pulumi.String("basic-test-network"),
		})
		if err != nil {
			return err
		}
		targetGateway, err := compute.NewVPNGateway(ctx, "target_gateway", &compute.VPNGatewayArgs{
			Name:    pulumi.String("basic-test-gateway"),
			Network: network.ID(),
		})
		if err != nil {
			return err
		}
		vpnStaticIp, err := compute.NewAddress(ctx, "vpn_static_ip", &compute.AddressArgs{
			Name: pulumi.String("basic-test-address"),
		})
		if err != nil {
			return err
		}
		frEsp, err := compute.NewForwardingRule(ctx, "fr_esp", &compute.ForwardingRuleArgs{
			Name:       pulumi.String("basic-test-fresp"),
			IpProtocol: pulumi.String("ESP"),
			IpAddress:  vpnStaticIp.Address,
			Target:     targetGateway.ID(),
		})
		if err != nil {
			return err
		}
		frUdp500, err := compute.NewForwardingRule(ctx, "fr_udp500", &compute.ForwardingRuleArgs{
			Name:       pulumi.String("basic-test-fr500"),
			IpProtocol: pulumi.String("UDP"),
			PortRange:  pulumi.String("500"),
			IpAddress:  vpnStaticIp.Address,
			Target:     targetGateway.ID(),
		})
		if err != nil {
			return err
		}
		frUdp4500, err := compute.NewForwardingRule(ctx, "fr_udp4500", &compute.ForwardingRuleArgs{
			Name:       pulumi.String("basic-test-fr4500"),
			IpProtocol: pulumi.String("UDP"),
			PortRange:  pulumi.String("4500"),
			IpAddress:  vpnStaticIp.Address,
			Target:     targetGateway.ID(),
		})
		if err != nil {
			return err
		}
		tunnel, err := compute.NewVPNTunnel(ctx, "tunnel", &compute.VPNTunnelArgs{
			Name:             pulumi.String("basic-test-tunnel"),
			PeerIp:           pulumi.String("15.0.0.120"),
			SharedSecret:     pulumi.String("a secret message"),
			TargetVpnGateway: targetGateway.ID(),
		}, pulumi.DependsOn([]pulumi.Resource{
			frEsp,
			frUdp500,
			frUdp4500,
		}))
		if err != nil {
			return err
		}
		_, err = networkmanagement.NewVpcFlowLogsConfig(ctx, "vpn-test", &networkmanagement.VpcFlowLogsConfigArgs{
			VpcFlowLogsConfigId: pulumi.String("basic-test-id"),
			Location:            pulumi.String("global"),
			VpnTunnel: tunnel.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("projects/%v/regions/us-central1/vpnTunnels/%v", project.Number, name), nil
			}).(pulumi.StringOutput),
		})
		if err != nil {
			return err
		}
		_, err = compute.NewRoute(ctx, "route", &compute.RouteArgs{
			Name:             pulumi.String("basic-test-route"),
			Network:          network.Name,
			DestRange:        pulumi.String("15.0.0.0/24"),
			Priority:         pulumi.Int(1000),
			NextHopVpnTunnel: tunnel.ID(),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var project = Gcp.Organizations.GetProject.Invoke();

    var network = new Gcp.Compute.Network("network", new()
    {
        Name = "basic-test-network",
    });

    var targetGateway = new Gcp.Compute.VPNGateway("target_gateway", new()
    {
        Name = "basic-test-gateway",
        Network = network.Id,
    });

    var vpnStaticIp = new Gcp.Compute.Address("vpn_static_ip", new()
    {
        Name = "basic-test-address",
    });

    var frEsp = new Gcp.Compute.ForwardingRule("fr_esp", new()
    {
        Name = "basic-test-fresp",
        IpProtocol = "ESP",
        IpAddress = vpnStaticIp.IPAddress,
        Target = targetGateway.Id,
    });

    var frUdp500 = new Gcp.Compute.ForwardingRule("fr_udp500", new()
    {
        Name = "basic-test-fr500",
        IpProtocol = "UDP",
        PortRange = "500",
        IpAddress = vpnStaticIp.IPAddress,
        Target = targetGateway.Id,
    });

    var frUdp4500 = new Gcp.Compute.ForwardingRule("fr_udp4500", new()
    {
        Name = "basic-test-fr4500",
        IpProtocol = "UDP",
        PortRange = "4500",
        IpAddress = vpnStaticIp.IPAddress,
        Target = targetGateway.Id,
    });

    var tunnel = new Gcp.Compute.VPNTunnel("tunnel", new()
    {
        Name = "basic-test-tunnel",
        PeerIp = "15.0.0.120",
        SharedSecret = "a secret message",
        TargetVpnGateway = targetGateway.Id,
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            frEsp,
            frUdp500,
            frUdp4500,
        },
    });

    var vpn_test = new Gcp.NetworkManagement.VpcFlowLogsConfig("vpn-test", new()
    {
        VpcFlowLogsConfigId = "basic-test-id",
        Location = "global",
        VpnTunnel = Output.Tuple(project, tunnel.Name).Apply(values =>
        {
            var project = values.Item1;
            var name = values.Item2;
            return $"projects/{project.Apply(getProjectResult => getProjectResult.Number)}/regions/us-central1/vpnTunnels/{name}";
        }),
    });

    var route = new Gcp.Compute.Route("route", new()
    {
        Name = "basic-test-route",
        Network = network.Name,
        DestRange = "15.0.0.0/24",
        Priority = 1000,
        NextHopVpnTunnel = tunnel.Id,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.compute.Network;
import com.pulumi.gcp.compute.NetworkArgs;
import com.pulumi.gcp.compute.VPNGateway;
import com.pulumi.gcp.compute.VPNGatewayArgs;
import com.pulumi.gcp.compute.Address;
import com.pulumi.gcp.compute.AddressArgs;
import com.pulumi.gcp.compute.ForwardingRule;
import com.pulumi.gcp.compute.ForwardingRuleArgs;
import com.pulumi.gcp.compute.VPNTunnel;
import com.pulumi.gcp.compute.VPNTunnelArgs;
import com.pulumi.gcp.networkmanagement.VpcFlowLogsConfig;
import com.pulumi.gcp.networkmanagement.VpcFlowLogsConfigArgs;
import com.pulumi.gcp.compute.Route;
import com.pulumi.gcp.compute.RouteArgs;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
            .build());

        var network = new Network("network", NetworkArgs.builder()
            .name("basic-test-network")
            .build());

        var targetGateway = new VPNGateway("targetGateway", VPNGatewayArgs.builder()
            .name("basic-test-gateway")
            .network(network.id())
            .build());

        var vpnStaticIp = new Address("vpnStaticIp", AddressArgs.builder()
            .name("basic-test-address")
            .build());

        var frEsp = new ForwardingRule("frEsp", ForwardingRuleArgs.builder()
            .name("basic-test-fresp")
            .ipProtocol("ESP")
            .ipAddress(vpnStaticIp.address())
            .target(targetGateway.id())
            .build());

        var frUdp500 = new ForwardingRule("frUdp500", ForwardingRuleArgs.builder()
            .name("basic-test-fr500")
            .ipProtocol("UDP")
            .portRange("500")
            .ipAddress(vpnStaticIp.address())
            .target(targetGateway.id())
            .build());

        var frUdp4500 = new ForwardingRule("frUdp4500", ForwardingRuleArgs.builder()
            .name("basic-test-fr4500")
            .ipProtocol("UDP")
            .portRange("4500")
            .ipAddress(vpnStaticIp.address())
            .target(targetGateway.id())
            .build());

        var tunnel = new VPNTunnel("tunnel", VPNTunnelArgs.builder()
            .name("basic-test-tunnel")
            .peerIp("15.0.0.120")
            .sharedSecret("a secret message")
            .targetVpnGateway(targetGateway.id())
            .build(), CustomResourceOptions.builder()
                .dependsOn(                
                    frEsp,
                    frUdp500,
                    frUdp4500)
                .build());

        var vpn_test = new VpcFlowLogsConfig("vpn-test", VpcFlowLogsConfigArgs.builder()
            .vpcFlowLogsConfigId("basic-test-id")
            .location("global")
            .vpnTunnel(tunnel.name().applyValue(_name -> String.format("projects/%s/regions/us-central1/vpnTunnels/%s", project.number(),_name)))
            .build());

        var route = new Route("route", RouteArgs.builder()
            .name("basic-test-route")
            .network(network.name())
            .destRange("15.0.0.0/24")
            .priority(1000)
            .nextHopVpnTunnel(tunnel.id())
            .build());

    }
}
resources:
  vpn-test:
    type: gcp:networkmanagement:VpcFlowLogsConfig
    properties:
      vpcFlowLogsConfigId: basic-test-id
      location: global
      vpnTunnel: projects/${project.number}/regions/us-central1/vpnTunnels/${tunnel.name}
  tunnel:
    type: gcp:compute:VPNTunnel
    properties:
      name: basic-test-tunnel
      peerIp: 15.0.0.120
      sharedSecret: a secret message
      targetVpnGateway: ${targetGateway.id}
    options:
      dependsOn:
        - ${frEsp}
        - ${frUdp500}
        - ${frUdp4500}
  targetGateway:
    type: gcp:compute:VPNGateway
    name: target_gateway
    properties:
      name: basic-test-gateway
      network: ${network.id}
  network:
    type: gcp:compute:Network
    properties:
      name: basic-test-network
  vpnStaticIp:
    type: gcp:compute:Address
    name: vpn_static_ip
    properties:
      name: basic-test-address
  frEsp:
    type: gcp:compute:ForwardingRule
    name: fr_esp
    properties:
      name: basic-test-fresp
      ipProtocol: ESP
      ipAddress: ${vpnStaticIp.address}
      target: ${targetGateway.id}
  frUdp500:
    type: gcp:compute:ForwardingRule
    name: fr_udp500
    properties:
      name: basic-test-fr500
      ipProtocol: UDP
      portRange: '500'
      ipAddress: ${vpnStaticIp.address}
      target: ${targetGateway.id}
  frUdp4500:
    type: gcp:compute:ForwardingRule
    name: fr_udp4500
    properties:
      name: basic-test-fr4500
      ipProtocol: UDP
      portRange: '4500'
      ipAddress: ${vpnStaticIp.address}
      target: ${targetGateway.id}
  route:
    type: gcp:compute:Route
    properties:
      name: basic-test-route
      network: ${network.name}
      destRange: 15.0.0.0/24
      priority: 1000
      nextHopVpnTunnel: ${tunnel.id}
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The vpnTunnel property references a VPN tunnel using the format projects/{project_number}/regions/{region}/vpnTunnels/{name}. The example shows the VPN infrastructure dependencies: a VPNGateway, forwarding rules for ESP and UDP protocols, and the tunnel itself. Flow logs capture traffic flowing through the tunnel in both directions.

Monitor Interconnect attachment traffic

Enterprise deployments using Dedicated or Partner Interconnect need flow logs to track traffic between Google Cloud and on-premises data centers.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const project = gcp.organizations.getProject({});
const network = new gcp.compute.Network("network", {name: "basic-interconnect-test-network"});
const router = new gcp.compute.Router("router", {
    name: "basic-interconnect-test-router",
    network: network.name,
    bgp: {
        asn: 16550,
    },
});
const attachment = new gcp.compute.InterconnectAttachment("attachment", {
    name: "basic-interconnect-test-id",
    edgeAvailabilityDomain: "AVAILABILITY_DOMAIN_1",
    type: "PARTNER",
    router: router.id,
    mtu: "1500",
});
const interconnect_test = new gcp.networkmanagement.VpcFlowLogsConfig("interconnect-test", {
    vpcFlowLogsConfigId: "basic-interconnect-test-id",
    location: "global",
    interconnectAttachment: pulumi.all([project, attachment.name]).apply(([project, name]) => `projects/${project.number}/regions/us-east4/interconnectAttachments/${name}`),
});
import pulumi
import pulumi_gcp as gcp

project = gcp.organizations.get_project()
network = gcp.compute.Network("network", name="basic-interconnect-test-network")
router = gcp.compute.Router("router",
    name="basic-interconnect-test-router",
    network=network.name,
    bgp={
        "asn": 16550,
    })
attachment = gcp.compute.InterconnectAttachment("attachment",
    name="basic-interconnect-test-id",
    edge_availability_domain="AVAILABILITY_DOMAIN_1",
    type="PARTNER",
    router=router.id,
    mtu="1500")
interconnect_test = gcp.networkmanagement.VpcFlowLogsConfig("interconnect-test",
    vpc_flow_logs_config_id="basic-interconnect-test-id",
    location="global",
    interconnect_attachment=attachment.name.apply(lambda name: f"projects/{project.number}/regions/us-east4/interconnectAttachments/{name}"))
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/networkmanagement"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		if err != nil {
			return err
		}
		network, err := compute.NewNetwork(ctx, "network", &compute.NetworkArgs{
			Name: pulumi.String("basic-interconnect-test-network"),
		})
		if err != nil {
			return err
		}
		router, err := compute.NewRouter(ctx, "router", &compute.RouterArgs{
			Name:    pulumi.String("basic-interconnect-test-router"),
			Network: network.Name,
			Bgp: &compute.RouterBgpArgs{
				Asn: pulumi.Int(16550),
			},
		})
		if err != nil {
			return err
		}
		attachment, err := compute.NewInterconnectAttachment(ctx, "attachment", &compute.InterconnectAttachmentArgs{
			Name:                   pulumi.String("basic-interconnect-test-id"),
			EdgeAvailabilityDomain: pulumi.String("AVAILABILITY_DOMAIN_1"),
			Type:                   pulumi.String("PARTNER"),
			Router:                 router.ID(),
			Mtu:                    pulumi.String("1500"),
		})
		if err != nil {
			return err
		}
		_, err = networkmanagement.NewVpcFlowLogsConfig(ctx, "interconnect-test", &networkmanagement.VpcFlowLogsConfigArgs{
			VpcFlowLogsConfigId: pulumi.String("basic-interconnect-test-id"),
			Location:            pulumi.String("global"),
			InterconnectAttachment: attachment.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("projects/%v/regions/us-east4/interconnectAttachments/%v", project.Number, name), nil
			}).(pulumi.StringOutput),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var project = Gcp.Organizations.GetProject.Invoke();

    var network = new Gcp.Compute.Network("network", new()
    {
        Name = "basic-interconnect-test-network",
    });

    var router = new Gcp.Compute.Router("router", new()
    {
        Name = "basic-interconnect-test-router",
        Network = network.Name,
        Bgp = new Gcp.Compute.Inputs.RouterBgpArgs
        {
            Asn = 16550,
        },
    });

    var attachment = new Gcp.Compute.InterconnectAttachment("attachment", new()
    {
        Name = "basic-interconnect-test-id",
        EdgeAvailabilityDomain = "AVAILABILITY_DOMAIN_1",
        Type = "PARTNER",
        Router = router.Id,
        Mtu = "1500",
    });

    var interconnect_test = new Gcp.NetworkManagement.VpcFlowLogsConfig("interconnect-test", new()
    {
        VpcFlowLogsConfigId = "basic-interconnect-test-id",
        Location = "global",
        InterconnectAttachment = Output.Tuple(project, attachment.Name).Apply(values =>
        {
            var project = values.Item1;
            var name = values.Item2;
            return $"projects/{project.Apply(getProjectResult => getProjectResult.Number)}/regions/us-east4/interconnectAttachments/{name}";
        }),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.compute.Network;
import com.pulumi.gcp.compute.NetworkArgs;
import com.pulumi.gcp.compute.Router;
import com.pulumi.gcp.compute.RouterArgs;
import com.pulumi.gcp.compute.inputs.RouterBgpArgs;
import com.pulumi.gcp.compute.InterconnectAttachment;
import com.pulumi.gcp.compute.InterconnectAttachmentArgs;
import com.pulumi.gcp.networkmanagement.VpcFlowLogsConfig;
import com.pulumi.gcp.networkmanagement.VpcFlowLogsConfigArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
            .build());

        var network = new Network("network", NetworkArgs.builder()
            .name("basic-interconnect-test-network")
            .build());

        var router = new Router("router", RouterArgs.builder()
            .name("basic-interconnect-test-router")
            .network(network.name())
            .bgp(RouterBgpArgs.builder()
                .asn(16550)
                .build())
            .build());

        var attachment = new InterconnectAttachment("attachment", InterconnectAttachmentArgs.builder()
            .name("basic-interconnect-test-id")
            .edgeAvailabilityDomain("AVAILABILITY_DOMAIN_1")
            .type("PARTNER")
            .router(router.id())
            .mtu("1500")
            .build());

        var interconnect_test = new VpcFlowLogsConfig("interconnect-test", VpcFlowLogsConfigArgs.builder()
            .vpcFlowLogsConfigId("basic-interconnect-test-id")
            .location("global")
            .interconnectAttachment(attachment.name().applyValue(_name -> String.format("projects/%s/regions/us-east4/interconnectAttachments/%s", project.number(),_name)))
            .build());

    }
}
resources:
  interconnect-test:
    type: gcp:networkmanagement:VpcFlowLogsConfig
    properties:
      vpcFlowLogsConfigId: basic-interconnect-test-id
      location: global
      interconnectAttachment: projects/${project.number}/regions/us-east4/interconnectAttachments/${attachment.name}
  network:
    type: gcp:compute:Network
    properties:
      name: basic-interconnect-test-network
  router:
    type: gcp:compute:Router
    properties:
      name: basic-interconnect-test-router
      network: ${network.name}
      bgp:
        asn: 16550
  attachment:
    type: gcp:compute:InterconnectAttachment
    properties:
      name: basic-interconnect-test-id
      edgeAvailabilityDomain: AVAILABILITY_DOMAIN_1
      type: PARTNER
      router: ${router.id}
      mtu: 1500
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The interconnectAttachment property references an Interconnect attachment using the format projects/{project_number}/regions/{region}/interconnectAttachments/{name}. The attachment requires a Router with BGP configuration. Flow logs capture traffic crossing the Interconnect link.

Beyond these examples

These snippets focus on specific flow log configuration features: network, subnet, VPN, and Interconnect attachment scoping. They’re intentionally minimal rather than full network monitoring solutions.

The examples reference pre-existing infrastructure such as VPC networks, subnets, VPN gateways and tunnels, Interconnect attachments, and project numbers from the organizations.getProject data source. They focus on configuring flow log collection rather than provisioning the underlying network resources.

To keep things focused, common flow log patterns are omitted, including:

  • Sampling rate tuning (flowSampling)
  • Aggregation interval configuration (aggregationInterval)
  • Metadata field selection (metadata, metadataFields)
  • Traffic filtering (filterExpr)
  • State management (state property for enabling/disabling)
  • Resource labels and descriptions

These omissions are intentional: the goal is to illustrate how each flow log scope is wired, not provide drop-in monitoring modules. See the VPC Flow Logs Config resource reference for all available configuration options.

Let's configure GCP VPC Flow Logs

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Configuration Basics
What types of resources can I configure VPC Flow Logs for?
You can configure flow logs for Networks, Subnets, Interconnect Attachments, or VPN Tunnels. Specify one target resource using the corresponding property (network, subnet, interconnectAttachment, or vpnTunnel).
Can I configure flow logs for multiple resource types at once?
No, each VPC Flow Logs configuration targets a single resource. Choose one of network, subnet, interconnectAttachment, or vpnTunnel.
What location should I use for VPC Flow Logs configurations?
Use global as the location. All examples in the schema use location: "global".
Can I create a VPC Flow Logs configuration in a disabled state?
No, new configurations must be created with state set to ENABLED. You can update the state to DISABLED after creation.
Sampling & Filtering
How do I disable VPC Flow Logs without deleting the configuration?
Set the state property to DISABLED. Don’t use flowSampling: 0.0, as setting the sampling rate to 0.0 is not allowed.
What's the default sampling rate for VPC Flow Logs?
The default flowSampling is 1.0 (100%), meaning all collected logs are reported. Valid values are in the range (0, 1].
What's the default aggregation interval for flow logs?
The default aggregationInterval is INTERVAL_5_SEC. Other options include INTERVAL_30_SEC, INTERVAL_1_MIN, INTERVAL_5_MIN, INTERVAL_10_MIN, and INTERVAL_15_MIN.
Metadata Configuration
Why can't I specify custom metadata fields?
The metadataFields property can only be used when metadata is set to CUSTOM_METADATA. The default metadata value is INCLUDE_ALL_METADATA.
Resource Management
What properties can't be changed after creating a VPC Flow Logs configuration?
The location, project, and vpcFlowLogsConfigId properties are immutable and cannot be changed after creation.
Why aren't all my labels showing up in the labels property?
The labels field is non-authoritative and only manages labels present in your configuration. Use the effectiveLabels output property to see all labels on the resource, including those set by other clients and services.

Using a different cloud?

Explore networking guides for other cloud providers: