Skip to main content
Multi-Cloud · lesson 5 of 8
Multi-Cloud intermediate Lesson 5 of 8

Also part of: Networking Cloud Security

Cloud Networking Basics: What's the Same Everywhere

VPCs, subnets, and firewall rules share one model everywhere — the same allow-443-deny-rest rule as a security group, an NSG rule, and a GCP rule.

By SkillByExample · Published

cloud aws azure gcp networking security

Restrict inbound access to port 443 only, deny everything else. The task, the default posture, and even the “stateful response traffic is automatically allowed” behavior are identical across all three clouds. Only the object you configure it on changes.

The mapping

ConceptAWSAzureGCP
Isolated networkVPC (regional)VNet (regional)VPC network (global, multi-region)
Network subdivisionSubnetSubnetSubnet
Traffic filterSecurity group (attached to ENI/instance)Network Security Group (attached to subnet or NIC)Firewall rule (attached via target tags/service account, defined on the VPC)
Default postureDeny all inbound, allow all outboundDeny all inbound below built-in AllowVnetInBound, allow outboundImplied deny-all ingress, implied allow-all egress
Can write explicit deny?No — allow-list onlyYes — deny rules with priorityYes — deny rules with priority

AWS: security group

aws ec2 create-security-group \
  --group-name web-https-only \
  --description "Allow inbound 443 only" \
  --vpc-id vpc-0a1b2c3d4e5f60718

aws ec2 authorize-security-group-ingress \
  --group-id sg-0f1e2d3c4b5a69788 \
  --protocol tcp --port 443 --cidr 0.0.0.0/0
$ aws ec2 authorize-security-group-ingress --group-id sg-0f1e2d3c4b5a69788 --protocol tcp --port 443 --cidr 0.0.0.0/0
{
    "Return": true,
    "SecurityGroupRules": [
        {
            "SecurityGroupRuleId": "sgr-0a1b2c3d4e5f60718",
            "GroupId": "sg-0f1e2d3c4b5a69788",
            "IsEgress": false,
            "IpProtocol": "tcp",
            "FromPort": 443,
            "ToPort": 443,
            "CidrIpv4": "0.0.0.0/0"
        }
    ]
}

Nothing was written to “deny everything else” — there’s no deny rule in the output, and there doesn’t need to be. A newly created security group starts with zero inbound rules, which already means deny-all; adding the 443 allow rule is the entire task. This is the one place AWS’s model is simpler than the other two, precisely because it can’t express anything more complex than allow-lists.

Azure: NSG rule

az network nsg create --resource-group learn-rg --name web-https-only-nsg

az network nsg rule create \
  --resource-group learn-rg --nsg-name web-https-only-nsg \
  --name allow-https --priority 100 \
  --direction Inbound --access Allow --protocol Tcp \
  --source-address-prefixes '*' --destination-port-ranges 443
$ az network nsg rule create --resource-group learn-rg --nsg-name web-https-only-nsg --name allow-https --priority 100 --direction Inbound --access Allow --protocol Tcp --source-address-prefixes '*' --destination-port-ranges 443
{
  "access": "Allow",
  "destinationPortRange": "443",
  "direction": "Inbound",
  "name": "allow-https",
  "priority": 100,
  "protocol": "Tcp",
  "provisioningState": "Succeeded",
  "sourceAddressPrefix": "*"
}

priority: 100 matters because NSGs evaluate rules in ascending priority order and stop at the first match. Below every custom rule, Azure adds built-in rules at priority 65000+ (DenyAllInBound is the last one) — so “deny everything else” here is that built-in rule, not something you write, unless you explicitly want a higher-priority deny for a specific source ahead of a broader allow.

GCP: firewall rule

gcloud compute firewall-rules create allow-https \
  --network=default \
  --direction=INGRESS \
  --action=ALLOW \
  --rules=tcp:443 \
  --source-ranges=0.0.0.0/0 \
  --target-tags=web
$ gcloud compute firewall-rules create allow-https --network=default --direction=INGRESS --action=ALLOW --rules=tcp:443 --source-ranges=0.0.0.0/0 --target-tags=web
Creating firewall...done.
NAME          NETWORK  DIRECTION  PRIORITY  ALLOW   DENY  DISABLED
allow-https   default  INGRESS    1000      tcp:443       False

--target-tags=web is the part with no direct security-group equivalent: the rule applies only to instances carrying the web network tag, not to every instance in the VPC. Every GCP VPC also carries an implied, unlisted deny-all-ingress rule at the lowest priority (65535) that this rule (priority 1000) takes precedence over — visible only when you list with --format verbose enough to include implied rules.

The one real divergence

Try to write “allow 443 from anywhere except 198.51.100.0/24” as a single rule set:

  • Azure: two rules — a higher-priority (lower number) Deny for 198.51.100.0/24, then a lower-priority Allow for 0.0.0.0/0.
  • GCP: same idea — a lower-priority-number deny-blocklist rule ranked ahead of the broader allow-https rule.
  • AWS: cannot be expressed in one security group at all. A security group has no deny rules; the only route is a separate mechanism entirely — a Network ACL at the subnet level, which does support explicit deny and is evaluated before the security group.

That’s the one place where “the model is the same everywhere” breaks down: AWS split allow-only (security group, stateful, instance-level) and allow/deny (NACL, stateless, subnet-level) into two different objects with two different evaluation semantics, where Azure and GCP each solved it with a single stateful, priority-ordered object.

Practice

1. Add a rule to each cloud's config above that also allows port 22 from a single IP, not from anywhere.
aws ec2 authorize-security-group-ingress --group-id sg-0f1e2d3c4b5a69788 --protocol tcp --port 22 --cidr 203.0.113.50/32

az network nsg rule create --resource-group learn-rg --nsg-name web-https-only-nsg --name allow-ssh-admin --priority 110 --direction Inbound --access Allow --protocol Tcp --source-address-prefixes 203.0.113.50/32 --destination-port-ranges 22

gcloud compute firewall-rules create allow-ssh-admin --network=default --direction=INGRESS --action=ALLOW --rules=tcp:22 --source-ranges=203.0.113.50/32 --target-tags=web

All three take a /32 CIDR the same way — narrowing a source range to one address needs no special syntax on any of them.

2. Write the AWS NACL rules that achieve "deny 198.51.100.0/24, allow everything else on 443" — the thing a security group alone cannot do.
aws ec2 create-network-acl-entry --network-acl-id acl-0123456789abcdef0 \
  --rule-number 100 --protocol tcp --port-range From=443,To=443 \
  --cidr-block 198.51.100.0/24 --rule-action deny --ingress

aws ec2 create-network-acl-entry --network-acl-id acl-0123456789abcdef0 \
  --rule-number 200 --protocol tcp --port-range From=443,To=443 \
  --cidr-block 0.0.0.0/0 --rule-action allow --ingress

--rule-number 100 before 200 is doing the same job as Azure’s --priority 100 before 110 and GCP’s implicit priority ordering — lower number evaluated first, first match wins.

3. List the implied/default rules on a GCP VPC that never show up in `firewall-rules list` output by default.
gcloud compute networks describe default --format="yaml(name)"

The implied deny-all-ingress and allow-all-egress rules are documented behavior, not objects you can list directly — they exist only as the fallback when no explicit rule matches, which is worth knowing so an empty firewall-rules list doesn’t get mistaken for “everything is open.”

4. Confirm the stateful-return-traffic claim: allow inbound 443 only, with no outbound rule at all, and check that an HTTPS response still reaches the client.

On all three clouds, a request that matches the inbound-443 allow rule gets its response traffic passed automatically, with zero outbound rules configured — try loading the page from a browser and it works, because “stateful” means the return leg of an already-allowed connection is tracked and permitted without a matching rule of its own.

Next: RDS, Azure Database for PostgreSQL, and Cloud SQL — failover, backup mechanics, and pricing for an equivalent instance size.

Frequently Asked Questions

Is an AWS security group the same thing as an Azure NSG or a GCP firewall rule?
Functionally yes — all three control which traffic reaches a resource by IP, port, and protocol. The scoping differs: a security group attaches directly to instances/ENIs, an NSG attaches to a subnet or a network interface, and a GCP firewall rule is defined at the VPC network level and applies via target tags or service accounts. The rule logic (allow/deny by port and source) is the same idea in all three places.
Can an AWS security group have an explicit deny rule?
No — this is the one real divergence. Security groups are allow-list only: everything not explicitly allowed is implicitly denied, and there is no way to write a deny rule at all. Azure NSGs and GCP firewall rules both support explicit deny rules with priority ordering, so you can allow a broad range and then carve out an explicit deny for one address inside it — something a security group structurally cannot express.
Are security groups, NSGs, and firewall rules stateful?
AWS security groups and GCP firewall rules are both stateful by default — allow an inbound request and the matching response traffic is automatically allowed back out. Azure NSGs are also stateful for established connections. None of the three require a matching outbound rule just to let a response back through, which is a common early misconception when writing rules by hand.
What is the GCP equivalent of an AWS VPC?
A VPC network — the name is literally the same. Subnets, routes, and firewall rules all sit inside it, matching AWS's VPC/subnet/security-group model almost one for one. The main structural difference is that a GCP VPC network is global (a single VPC can have subnets in multiple regions), whereas an AWS VPC is regional and a matching multi-region setup needs VPC peering or a transit gateway.