diff --git a/modules/vpc/README.md b/modules/vpc/README.md
new file mode 100644
index 0000000..a6df13a
--- /dev/null
+++ b/modules/vpc/README.md
@@ -0,0 +1,67 @@
+
+
+# VPC Module
+A basic module used to create a GCP VPC Network with a Subnet and Private Service Connect Subnet, intended to be used by StreamNative Cloud.
+
+
+## Requirements
+
+No requirements.
+
+## Providers
+
+No providers.
+
+## Modules
+
+| Name | Source | Version |
+|------|--------|---------|
+| [cloud\_router](#module\_cloud\_router) | terraform-google-modules/cloud-router/google | ~> 5.0 |
+| [network](#module\_network) | terraform-google-modules/network/google | >= 4.1.0, < 7.2.0 |
+
+## Resources
+
+No resources.
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [network\_name](#input\_network\_name) | The name of the VPC | `string` | n/a | yes |
+| [project](#input\_project) | The GCP project to deploy to | `string` | n/a | yes |
+| [psc\_subnet\_name](#input\_psc\_subnet\_name) | The name of the PSC subnet, can be left empty to auto-generate | `string` | `""` | no |
+| [psc\_vpc\_cidr](#input\_psc\_vpc\_cidr) | The CIDR block for the private service connect | `string` | `"10.1.0.0/18"` | no |
+| [region](#input\_region) | The GCP region to deploy to | `string` | n/a | yes |
+| [secondary\_ip\_range\_pods](#input\_secondary\_ip\_range\_pods) | The secondary IP range for pods | `string` | `"192.168.0.0/18"` | no |
+| [secondary\_ip\_range\_pods\_name](#input\_secondary\_ip\_range\_pods\_name) | The name of the secondary IP range for pods | `string` | `"ip-range-pods"` | no |
+| [secondary\_ip\_range\_services](#input\_secondary\_ip\_range\_services) | The secondary IP range for services | `string` | `"192.168.64.0/18"` | no |
+| [secondary\_ip\_range\_services\_name](#input\_secondary\_ip\_range\_services\_name) | The name of the secondary IP range for services | `string` | `"ip-range-svc"` | no |
+| [subnet\_name](#input\_subnet\_name) | The name of the subnet, can be left empty to auto-generate | `string` | `""` | no |
+| [vpc\_cidr](#input\_vpc\_cidr) | The CIDR block for the VPC | `string` | `"10.0.0.0/16"` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [network](#output\_network) | n/a |
+| [psc\_subnet\_name](#output\_psc\_subnet\_name) | n/a |
+| [secondary\_ip\_range\_pods](#output\_secondary\_ip\_range\_pods) | n/a |
+| [secondary\_ip\_range\_pods\_name](#output\_secondary\_ip\_range\_pods\_name) | n/a |
+| [secondary\_ip\_range\_services](#output\_secondary\_ip\_range\_services) | n/a |
+| [secondary\_ip\_range\_services\_name](#output\_secondary\_ip\_range\_services\_name) | n/a |
+| [subnet\_name](#output\_subnet\_name) | n/a |
+
\ No newline at end of file
diff --git a/modules/vpc/main.tf b/modules/vpc/main.tf
new file mode 100644
index 0000000..8c7bd45
--- /dev/null
+++ b/modules/vpc/main.tf
@@ -0,0 +1,72 @@
+# Copyright 2023 StreamNative, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+locals {
+ subnet_name = var.subnet_name != "" ? var.subnet_name : "${var.network_name}-${var.region}"
+ psc_subnet_name = "${local.subnet_name}-psc"
+}
+
+module "network" {
+ source = "terraform-google-modules/network/google"
+ version = ">= 4.1.0, < 7.2.0"
+ # TODO: wait for fix release https://github.com/terraform-google-modules/terraform-google-network/pull/479
+ # this bug will make properties on subnet won't take effect, like purpose
+
+ project_id = var.project
+ network_name = var.network_name
+
+ subnets = [
+ {
+ subnet_name = local.subnet_name
+ subnet_ip = var.vpc_cidr
+ subnet_region = var.region
+ subnet_private_access = "true"
+ },
+ {
+ subnet_name = local.psc_subnet_name
+ subnet_ip = var.psc_vpc_cidr
+ subnet_region = var.region
+ purpose = "PRIVATE_SERVICE_CONNECT"
+ },
+ ]
+
+ secondary_ranges = {
+ (local.subnet_name) = [
+ {
+ range_name = var.secondary_ip_range_pods_name
+ ip_cidr_range = var.secondary_ip_range_pods
+ },
+ {
+ range_name = var.secondary_ip_range_services_name
+ ip_cidr_range = var.secondary_ip_range_services
+ },
+ ]
+ }
+}
+
+// TODO implement firewall rules for privateservice connect
+
+module "cloud_router" {
+ source = "terraform-google-modules/cloud-router/google"
+ version = "~> 5.0"
+
+ project = var.project
+ name = "${var.network_name}-sn-router"
+ network = module.network.network_name
+ region = var.region
+
+ nats = [{
+ name = var.nat_gateway_name
+ }]
+}
diff --git a/modules/vpc/outputs.tf b/modules/vpc/outputs.tf
new file mode 100644
index 0000000..2188a31
--- /dev/null
+++ b/modules/vpc/outputs.tf
@@ -0,0 +1,41 @@
+# Copyright 2023 StreamNative, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+output "network" {
+ value = module.network.network_name
+}
+
+output "subnet_name" {
+ value = module.network.subnets_names[0]
+}
+
+output "psc_subnet_name" {
+ value = local.psc_subnet_name
+}
+
+output "secondary_ip_range_pods" {
+ value = var.secondary_ip_range_pods
+}
+
+output "secondary_ip_range_pods_name" {
+ value = var.secondary_ip_range_pods_name
+}
+
+output "secondary_ip_range_services" {
+ value = var.secondary_ip_range_services
+}
+
+output "secondary_ip_range_services_name" {
+ value = var.secondary_ip_range_services_name
+}
diff --git a/modules/vpc/variables.tf b/modules/vpc/variables.tf
new file mode 100644
index 0000000..3e02609
--- /dev/null
+++ b/modules/vpc/variables.tf
@@ -0,0 +1,82 @@
+# Copyright 2023 StreamNative, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+variable "project" {
+ type = string
+ description = "The GCP project to deploy to"
+}
+
+variable "region" {
+ type = string
+ description = "The GCP region to deploy to"
+}
+
+variable "network_name" {
+ type = string
+ description = "The name of the VPC"
+}
+
+variable "subnet_name" {
+ type = string
+ default = ""
+ description = "The name of the subnet, can be left empty to auto-generate"
+}
+
+variable "vpc_cidr" {
+ type = string
+ default = "10.0.0.0/16"
+ description = "The CIDR block for the VPC"
+}
+
+variable "psc_subnet_name" {
+ type = string
+ default = ""
+ description = "The name of the PSC subnet, can be left empty to auto-generate"
+}
+
+variable "psc_vpc_cidr" {
+ type = string
+ default = "10.1.0.0/18"
+ description = "The CIDR block for the private service connect"
+}
+
+variable "secondary_ip_range_pods" {
+ type = string
+ default = "192.168.0.0/18"
+ description = "The secondary IP range for pods"
+}
+
+variable "secondary_ip_range_services" {
+ type = string
+ default = "192.168.64.0/18"
+ description = "The secondary IP range for services"
+}
+
+variable "secondary_ip_range_pods_name" {
+ type = string
+ default = "ip-range-pods"
+ description = "The name of the secondary IP range for pods"
+}
+
+variable "secondary_ip_range_services_name" {
+ type = string
+ default = "ip-range-svc"
+ description = "The name of the secondary IP range for services"
+}
+
+variable "nat_gateway_name" {
+ type = string
+ default = "sn-nat-gateway"
+ description = "The name of Cloud NAT Gateway"
+}