Hi All. I’m trying to build an egress VPC in AWS. ...
# golang
i
Hi All. I’m trying to build an egress VPC in AWS. I want the traffic to flow through the new AWS Network Firewall and then to the NAT. So I’m setting up the route tables to point to the endpoint (network interface) of each network firewall subnet attachment in my VPC. There is one firewall subnet per az and I’m usually in 4 AZs. The image shows just one of those AZs. I’ve created the following network firewall:
Copy code
go
import (
	"fmt"

	"<http://github.com/pulumi/pulumi-aws/sdk/v3/go/aws/ec2|github.com/pulumi/pulumi-aws/sdk/v3/go/aws/ec2>"
	"<http://github.com/pulumi/pulumi-aws/sdk/v3/go/aws/ec2transitgateway|github.com/pulumi/pulumi-aws/sdk/v3/go/aws/ec2transitgateway>"
	"<http://github.com/pulumi/pulumi-aws/sdk/v3/go/aws/networkfirewall|github.com/pulumi/pulumi-aws/sdk/v3/go/aws/networkfirewall>"
	"<http://github.com/pulumi/pulumi/sdk/v2/go/pulumi|github.com/pulumi/pulumi/sdk/v2/go/pulumi>"
)
---
	vpc.NetworkFirewall, err = networkfirewall.NewFirewall(ctx, args.Vpc.Name, &networkfirewall.FirewallArgs{
		Name:                           pulumi.String(args.Vpc.Name),
		Description:                    pulumi.String("default policy"),
		DeleteProtection:               nil,
		FirewallPolicyArn:              networkFirewallPolicy.Arn,
		FirewallPolicyChangeProtection: nil,
		SubnetChangeProtection:         nil,
		SubnetMappings:                 firewallSubnets,
		Tags:                           nil,
		VpcId:                          vpc.VPC.ID(),
	}, pulumi.Parent(vpc))
	if err != nil {
		ctx.Log.Error(fmt.Sprintf("Error creating firewall: %s", err), nil)
	}
My Pulumi State Shows the VPC Endpoints are there and under
Firewall Status
of the resource: `firewallStatuses`:
Copy code
json
[
  {
    "syncStates": [
      {
        "attachments": [
          {
            "endpointId": "vpce-038548f15fa6927f9",
            "subnetId": "subnet-057838182632334cf"
          }
        ],
        "availabilityZone": "us-west-2b"
      },
      {
        "attachments": [
          {
            "endpointId": "vpce-052c1fe00e3002431",
            "subnetId": "subnet-0f24562826576b492"
          }
        ],
        "availabilityZone": "us-west-2d"
      },
      {
        "attachments": [
          {
            "endpointId": "vpce-033845950fa44833d",
            "subnetId": "subnet-02512de4df02e02cd"
          }
        ],
        "availabilityZone": "us-west-2a"
      },
      {
        "attachments": [
          {
            "endpointId": "vpce-0f21d83ca409e4aaf",
            "subnetId": "subnet-049fbb85c726d1b36"
          }
        ],
        "availabilityZone": "us-west-2c"
      }
    ]
  }
]
I just don’t know how to get those out of the Firewall in golang. The docs show an outpu of the NetworkFirewall:
FirewallStatuses
[]FirewallFirewallStatus
So I would think I would just range over this and get the ID’s one at a time. But you can’t range over it and my IDE thinks that the type of
NetworkFirewall.FirewallStatuses
is
networkfirewall.FirewallFirewallStatusArrayOutput
I can’t seem to get anything useful out of this. Any thoughts?
1
I need this to happen in the same pulumi up command so the same execution that creates the VPC also creates the firewall and the subnets and the route tables.
b
@icy-london-58403 you can range over it, but you'll need to use an
apply
because
networkfirewall.FirewallFirewallStatusArrayOutput
isn't actually known until it's created. I don't have a concrete example of your particular use case, but there's an example of ranging over an
ArrayOutput
here: https://github.com/pulumi/examples/blob/e942d6130cd402d48db56889581ce2db1879803f/kubernetes-go-guestbook/components/serviceDeployment.go#L55-L63
i
@billowy-army-68599 let me give it a try. Thanks
@billowy-army-68599 any chance you know what’s wrong with this?
Copy code
func (vpc *VPC) GetFirewallEndpointIDByZone(ctx *pulumi.Context, zone string) pulumi.StringOutput {
	ready := pulumi.All(vpc.NetworkFirewall.FirewallStatuses)
	return ready.ApplyT(func(statuses []networkfirewall.FirewallFirewallStatus) string {
		found := false
		for _, status := range statuses {
			for _, syncState := range status.SyncStates {
				getZoneResult, err := aws.GetAvailabilityZone(ctx, &aws.GetAvailabilityZoneArgs{
					AllAvailabilityZones: nil,
					Filters:              []aws.GetAvailabilityZoneFilter{},
					Name:                 syncState.AvailabilityZone,
					State:                nil,
					ZoneId:               nil,
				})
				if err != nil {
					ctx.Log.Error(fmt.Sprintf("Error: %s", err), nil)
				}
				if getZoneResult.ZoneId == zone {
					found = true
					return *syncState.Attachments[0].EndpointId
				}
			}
		}
		if !found {
			ctx.Log.Error(fmt.Sprintf("Error: Could not find firewall endpoint id in zone %s", zone), nil)
		}
		return ""
	}).(pulumi.StringOutput)
}
Error:
panic: applier must have 1 input parameter assignable from []interface {}
b
hmm, not offhand. @lemon-agent-27707 any ideas?
w
Why did you want to use
All
here? That returns an
ArrayOutput
which has an underlying type of
[]interface{}
which is why applyt expects the argument to be of that type. You could change it to match what the error notes, and then convert. But I have a feeling you just don’t need the All call at all? (And would then pass a more strongly typed value to ApplyT so that you could use the applier you have above).
i
@white-balloon-205
All
is just being used out of inexperience and ignorance
im trying it without
That did it!
Copy code
// GetFirewallEndpointIDByZone
func (vpc *VPC) GetFirewallEndpointIDByZone(ctx *pulumi.Context, zone string) pulumi.StringOutput {
	ready := vpc.NetworkFirewall.FirewallStatuses
	return ready.ApplyT(func(statuses []networkfirewall.FirewallFirewallStatus) string {
		found := false
		for _, status := range statuses {
			for _, syncState := range status.SyncStates {
				getZoneResult, err := aws.GetAvailabilityZone(ctx, &aws.GetAvailabilityZoneArgs{
					AllAvailabilityZones: nil,
					Filters:              []aws.GetAvailabilityZoneFilter{},
					Name:                 syncState.AvailabilityZone,
					State:                nil,
					ZoneId:               nil,
				})
				if err != nil {
					ctx.Log.Error(fmt.Sprintf("Error: %s", err), nil)
				}
				if getZoneResult.ZoneId == zone {
					found = true
					return *syncState.Attachments[0].EndpointId
				}
			}
		}
		if !found {
			ctx.Log.Error(fmt.Sprintf("Error: Could not find firewall endpoint id in zone %s", zone), nil)
		}
		return ""
	}).(pulumi.StringOutput)
}
@white-balloon-205 and @billowy-army-68599 thanks so much 😃
Building a Proof of Concept Multip VPC Cross Account network that utilizes the AWS Network Firewall and Transit Gateway to share NAT gateways to all other VPCs as well as provide firewall rules for ingress and egress. I wouldn’t mind contributing it to some examples repo when I’m done -- if such a thing exists or is needed.
Diagram is work in progress too. But it’s roughly correct
w
Awesome! We’re working on pulling together a site to link out to great components/examples - this looks like a great addition. @billowy-army-68599 is likely the right person to help make sure this gets added there!
👍 1