diff --git a/aws_gke_oidc_config/.terraform-docs.yml b/aws_gke_oidc_config/.terraform-docs.yml
new file mode 100644
index 00000000..0727059d
--- /dev/null
+++ b/aws_gke_oidc_config/.terraform-docs.yml
@@ -0,0 +1,12 @@
+content: |-
+ {{ .Header }}
+
+ ## Example
+
+ ```hcl
+ {{ include "examples/main.tf" }}
+ ```
+
+ {{ .Inputs }}
+
+ {{ .Outputs }}
diff --git a/aws_gke_oidc_config/CHANGELOG.md b/aws_gke_oidc_config/CHANGELOG.md
new file mode 100644
index 00000000..79cad37d
--- /dev/null
+++ b/aws_gke_oidc_config/CHANGELOG.md
@@ -0,0 +1,6 @@
+## 0.1.0
+* PR [#239](https://github.com/mozilla/terraform-modules/pull/239) - feat: add aws_gke_oidc_config and aws_gke_oidc_role modules
+```
+add aws_gke_oidc_config and aws_gke_oidc_role modules
+```
+
diff --git a/aws_gke_oidc_config/README.md b/aws_gke_oidc_config/README.md
new file mode 100644
index 00000000..245e4f7d
--- /dev/null
+++ b/aws_gke_oidc_config/README.md
@@ -0,0 +1,36 @@
+
+# AWS-GKE OIDC Config
+This module will create an AWS OIDC config that creates a trust relationship between a GKE cluster & AWS account.
+
+Once this module has been invoked for a given account + GKE cluster, the `aws_gke_oidc_role` module can be used
+to create any number of roles to be used by GKE workloads.
+
+See the `aws_gke_oidc_role` for complete usage instructions
+
+## Example
+
+```hcl
+/*
+ * Creates an OIDC trust relationship between the global-platform-admin-mgmt cluster & the authenticated AWS account.
+ */
+
+module "oidc_config" {
+ source = "../."
+ gcp_region = "us-west1"
+ gcp_project_id = "moz-fx-platform-mgmt-global"
+ gke_cluster_name = "global-platform-admin-mgmt"
+}
+```
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [gcp\_project\_id](#input\_gcp\_project\_id) | ID of the GKE cluster's project | `string` | n/a | yes |
+| [gcp\_region](#input\_gcp\_region) | GKE cluster's GCP region | `string` | n/a | yes |
+| [gke\_cluster\_name](#input\_gke\_cluster\_name) | GKE cluster name | `string` | n/a | yes |
+
+## Outputs
+
+No outputs.
+
\ No newline at end of file
diff --git a/aws_gke_oidc_config/examples/main.tf b/aws_gke_oidc_config/examples/main.tf
new file mode 100644
index 00000000..5297c14c
--- /dev/null
+++ b/aws_gke_oidc_config/examples/main.tf
@@ -0,0 +1,10 @@
+/*
+ * Creates an OIDC trust relationship between the global-platform-admin-mgmt cluster & the authenticated AWS account.
+ */
+
+module "oidc_config" {
+ source = "../."
+ gcp_region = "us-west1"
+ gcp_project_id = "moz-fx-platform-mgmt-global"
+ gke_cluster_name = "global-platform-admin-mgmt"
+}
diff --git a/aws_gke_oidc_config/main.tf b/aws_gke_oidc_config/main.tf
new file mode 100644
index 00000000..41d69e1f
--- /dev/null
+++ b/aws_gke_oidc_config/main.tf
@@ -0,0 +1,25 @@
+/*
+ * # AWS-GKE OIDC Config
+ * This module will create an AWS OIDC config that creates a trust relationship between a GKE cluster & AWS account.
+ *
+ * Once this module has been invoked for a given account + GKE cluster, the `aws_gke_oidc_role` module can be used
+ * to create any number of roles to be used by GKE workloads.
+ *
+ * See the `aws_gke_oidc_role` for complete usage instructions
+*/
+
+resource "aws_iam_openid_connect_provider" "gke_oidc" {
+ url = "https://container.googleapis.com/v1/projects/${var.gcp_project_id}/locations/${var.gcp_region}/clusters/${var.gke_cluster_name}"
+
+ client_id_list = [
+ "sts.amazonaws.com"
+ ]
+
+ thumbprint_list = [
+ data.tls_certificate.gke_oidc.certificates.0.sha1_fingerprint
+ ]
+}
+
+data "tls_certificate" "gke_oidc" {
+ url = "https://container.googleapis.com/v1/projects/${var.gcp_project_id}/locations/${var.gcp_region}/clusters/${var.gke_cluster_name}"
+}
diff --git a/aws_gke_oidc_config/variables.tf b/aws_gke_oidc_config/variables.tf
new file mode 100644
index 00000000..0a24bd16
--- /dev/null
+++ b/aws_gke_oidc_config/variables.tf
@@ -0,0 +1,16 @@
+### Required
+
+variable "gcp_region" {
+ description = "GKE cluster's GCP region"
+ type = string
+}
+
+variable "gcp_project_id" {
+ description = "ID of the GKE cluster's project"
+ type = string
+}
+
+variable "gke_cluster_name" {
+ description = "GKE cluster name"
+ type = string
+}
diff --git a/aws_gke_oidc_config/versions.tf b/aws_gke_oidc_config/versions.tf
new file mode 100644
index 00000000..36a9084e
--- /dev/null
+++ b/aws_gke_oidc_config/versions.tf
@@ -0,0 +1,9 @@
+terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 4.37"
+ }
+ }
+ required_version = "~> 1.0"
+}
diff --git a/aws_gke_oidc_role/.terraform-docs.yml b/aws_gke_oidc_role/.terraform-docs.yml
new file mode 100644
index 00000000..8ae8a91f
--- /dev/null
+++ b/aws_gke_oidc_role/.terraform-docs.yml
@@ -0,0 +1,16 @@
+content: |-
+ {{ .Header }}
+
+ ## Example
+
+ ```hcl
+ {{ include "examples/role_and_config/main.tf" }}
+ ```
+
+ ```hcl
+ {{ include "examples/role_with_policy/main.tf" }}
+ ```
+
+ {{ .Inputs }}
+
+ {{ .Outputs }}
diff --git a/aws_gke_oidc_role/CHANGELOG.md b/aws_gke_oidc_role/CHANGELOG.md
new file mode 100644
index 00000000..79cad37d
--- /dev/null
+++ b/aws_gke_oidc_role/CHANGELOG.md
@@ -0,0 +1,6 @@
+## 0.1.0
+* PR [#239](https://github.com/mozilla/terraform-modules/pull/239) - feat: add aws_gke_oidc_config and aws_gke_oidc_role modules
+```
+add aws_gke_oidc_config and aws_gke_oidc_role modules
+```
+
diff --git a/aws_gke_oidc_role/README.md b/aws_gke_oidc_role/README.md
new file mode 100644
index 00000000..7fc25aeb
--- /dev/null
+++ b/aws_gke_oidc_role/README.md
@@ -0,0 +1,128 @@
+
+# AWS-GKE OIDC Role
+This module will create an AWS role that will allow a specified GKE service account to assume it.
+
+Requires that `../aws_gke_oidc_config` has been applied for a given AWS account + GKE cluster combination
+if you get an error about the `aws_iam_openid_connect_provider` data source being missing, apply that module.
+
+After creating these resources, add the following environment variables, volumes, and volume mounts to your pod definition:
+* env:
+```
+- name: AWS_REGION
+ value:
+- name: AWS_ROLE_ARN
+ value:
+- name: AWS_WEB_IDENTITY_TOKEN_FILE
+ value: /var/run/secrets/eks.amazonaws.com/serviceaccount/token
+- name: AWS_STS_REGIONAL_ENDPOINTS
+ value: regional
+```
+* volumes:
+```
+- name: aws-token
+ projected:
+ defaultMode: 420
+ sources:
+ - serviceAccountToken:
+ audience: sts.amazonaws.com
+ expirationSeconds: 86400
+ path: token
+```
+* volumeMounts:
+```
+- mountPath: /var/run/secrets/eks.amazonaws.com/serviceaccount/
+ name: aws-token
+```
+
+## Example
+
+```hcl
+/*
+* Example of creating both an OIDC config & role to utilize it
+*/
+
+module "oidc_config" {
+ source = "../../../aws_gke_oidc_config/"
+ gcp_region = "us-west1"
+ gcp_project_id = "moz-fx-platform-mgmt-global"
+ gke_cluster_name = "global-platform-admin-mgmt"
+}
+
+module "oidc_role" {
+ depends_on = [module.oidc_config]
+ source = "../.././"
+ role_name = "opst-1509-oidc-test"
+ aws_region = "us-west-1"
+ gcp_region = "us-west1"
+ gke_cluster_name = "global-platform-admin-mgmt"
+ gcp_project_id = "moz-fx-platform-mgmt-global"
+ gke_namespace = "atlantis-sandbox"
+ gke_service_account = "atlantis-sandbox"
+ iam_policy_arns = []
+}
+```
+
+```hcl
+/*
+* Example of creating just the role. The module will infer the OIDC url from vars.
+* Attaches an inline ec2 policy & managed AWS ViewOnly policy.
+* Resulting role will be assumable by the "foo" service account in the "bar" namespace of the "baz" cluster
+*/
+
+module "oidc_role" {
+ source = "../.././"
+ role_name = "oidc-example-role"
+ aws_region = "us-west-1"
+ gcp_region = "us-west1"
+ gke_cluster_name = "baz"
+ gcp_project_id = "example-project"
+ gke_namespace = "bar"
+ gke_service_account = "foo"
+ iam_policy_arns = [aws_iam_policy.example_policy.arn, data.aws_iam_policy.view_only.arn]
+}
+
+resource "aws_iam_policy" "example_policy" {
+ name = "example_policy"
+ path = "/"
+ description = "My example policy"
+
+ # Terraform's "jsonencode" function converts a
+ # Terraform expression result to valid JSON syntax.
+ policy = jsonencode({
+ Version = "2012-10-17"
+ Statement = [
+ {
+ Action = [
+ "ec2:Describe*",
+ ]
+ Effect = "Allow"
+ Resource = "*"
+ },
+ ]
+ })
+}
+
+data "aws_iam_policy" "view_only" {
+ arn = "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"
+}
+```
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [aws\_region](#input\_aws\_region) | AWS region | `string` | n/a | yes |
+| [gcp\_project\_id](#input\_gcp\_project\_id) | GKE cluster's project ID | `string` | n/a | yes |
+| [gcp\_region](#input\_gcp\_region) | GKE cluster's GCP region | `string` | n/a | yes |
+| [gke\_cluster\_name](#input\_gke\_cluster\_name) | GKE cluster name | `string` | n/a | yes |
+| [gke\_namespace](#input\_gke\_namespace) | Namespace for GKE workload | `string` | n/a | yes |
+| [gke\_service\_account](#input\_gke\_service\_account) | GKE service account to grant role assumption privilleges | `string` | n/a | yes |
+| [iam\_policy\_arns](#input\_iam\_policy\_arns) | One or more policy arns to attach to created AWS role | `list(string)` | n/a | yes |
+| [role\_name](#input\_role\_name) | Name to give the AWS role | `string` | n/a | yes |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [role\_arn](#output\_role\_arn) | ARN for the GKE-AWS connector role |
+
\ No newline at end of file
diff --git a/aws_gke_oidc_role/examples/role_and_config/main.tf b/aws_gke_oidc_role/examples/role_and_config/main.tf
new file mode 100644
index 00000000..1361d165
--- /dev/null
+++ b/aws_gke_oidc_role/examples/role_and_config/main.tf
@@ -0,0 +1,23 @@
+/*
+* Example of creating both an OIDC config & role to utilize it
+*/
+
+module "oidc_config" {
+ source = "../../../aws_gke_oidc_config/"
+ gcp_region = "us-west1"
+ gcp_project_id = "moz-fx-platform-mgmt-global"
+ gke_cluster_name = "global-platform-admin-mgmt"
+}
+
+module "oidc_role" {
+ depends_on = [module.oidc_config]
+ source = "../.././"
+ role_name = "opst-1509-oidc-test"
+ aws_region = "us-west-1"
+ gcp_region = "us-west1"
+ gke_cluster_name = "global-platform-admin-mgmt"
+ gcp_project_id = "moz-fx-platform-mgmt-global"
+ gke_namespace = "atlantis-sandbox"
+ gke_service_account = "atlantis-sandbox"
+ iam_policy_arns = []
+}
diff --git a/aws_gke_oidc_role/examples/role_and_config/providers.tf b/aws_gke_oidc_role/examples/role_and_config/providers.tf
new file mode 100644
index 00000000..b2c3630a
--- /dev/null
+++ b/aws_gke_oidc_role/examples/role_and_config/providers.tf
@@ -0,0 +1,13 @@
+terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = "~> 5.0"
+ }
+ }
+}
+
+# Configure the AWS Provider
+provider "aws" {
+ region = "us-west-2"
+}
diff --git a/aws_gke_oidc_role/examples/role_with_policy/main.tf b/aws_gke_oidc_role/examples/role_with_policy/main.tf
new file mode 100644
index 00000000..b8388452
--- /dev/null
+++ b/aws_gke_oidc_role/examples/role_with_policy/main.tf
@@ -0,0 +1,42 @@
+/*
+* Example of creating just the role. The module will infer the OIDC url from vars.
+* Attaches an inline ec2 policy & managed AWS ViewOnly policy.
+* Resulting role will be assumable by the "foo" service account in the "bar" namespace of the "baz" cluster
+*/
+
+module "oidc_role" {
+ source = "../.././"
+ role_name = "oidc-example-role"
+ aws_region = "us-west-1"
+ gcp_region = "us-west1"
+ gke_cluster_name = "baz"
+ gcp_project_id = "example-project"
+ gke_namespace = "bar"
+ gke_service_account = "foo"
+ iam_policy_arns = [aws_iam_policy.example_policy.arn, data.aws_iam_policy.view_only.arn]
+}
+
+resource "aws_iam_policy" "example_policy" {
+ name = "example_policy"
+ path = "/"
+ description = "My example policy"
+
+ # Terraform's "jsonencode" function converts a
+ # Terraform expression result to valid JSON syntax.
+ policy = jsonencode({
+ Version = "2012-10-17"
+ Statement = [
+ {
+ Action = [
+ "ec2:Describe*",
+ ]
+ Effect = "Allow"
+ Resource = "*"
+ },
+ ]
+ })
+}
+
+data "aws_iam_policy" "view_only" {
+ arn = "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"
+}
diff --git a/aws_gke_oidc_role/examples/role_with_policy/providers.tf b/aws_gke_oidc_role/examples/role_with_policy/providers.tf
new file mode 100644
index 00000000..b2c3630a
--- /dev/null
+++ b/aws_gke_oidc_role/examples/role_with_policy/providers.tf
@@ -0,0 +1,13 @@
+terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = "~> 5.0"
+ }
+ }
+}
+
+# Configure the AWS Provider
+provider "aws" {
+ region = "us-west-2"
+}
diff --git a/aws_gke_oidc_role/main.tf b/aws_gke_oidc_role/main.tf
new file mode 100644
index 00000000..f2a5f296
--- /dev/null
+++ b/aws_gke_oidc_role/main.tf
@@ -0,0 +1,51 @@
+/*
+ * # AWS-GKE OIDC Role
+ * This module will create an AWS role that will allow a specified GKE service account to assume it.
+ *
+ * Requires that `../aws_gke_oidc_config` has been applied for a given AWS account + GKE cluster combination
+ * if you get an error about the `aws_iam_openid_connect_provider` data source being missing, apply that module.
+ *
+ * After creating these resources, add the following environment variables, volumes, and volume mounts to your pod definition:
+ * * env:
+ * ```
+ * - name: AWS_REGION
+ * value:
+ * - name: AWS_ROLE_ARN
+ * value:
+ * - name: AWS_WEB_IDENTITY_TOKEN_FILE
+ * value: /var/run/secrets/eks.amazonaws.com/serviceaccount/token
+ * - name: AWS_STS_REGIONAL_ENDPOINTS
+ * value: regional
+ * ```
+ * * volumes:
+ * ```
+ * - name: aws-token
+ * projected:
+ * defaultMode: 420
+ * sources:
+ * - serviceAccountToken:
+ * audience: sts.amazonaws.com
+ * expirationSeconds: 86400
+ * path: token
+ * ```
+ * * volumeMounts:
+ * ```
+ * - mountPath: /var/run/secrets/eks.amazonaws.com/serviceaccount/
+ * name: aws-token
+ * ```
+*/
+
+module "iam_assumable_role_for_oidc" {
+ source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc"
+ version = "~> v5.9"
+ create_role = true
+ role_name = var.role_name
+ role_description = "Role for ${var.gke_cluster_name}/${var.gke_namespace}/${var.gke_service_account} to assume"
+ provider_url = replace(data.aws_iam_openid_connect_provider.gke_oidc.url, "https://", "")
+ role_policy_arns = var.iam_policy_arns
+ oidc_fully_qualified_subjects = ["system:serviceaccount:${var.gke_namespace}:${var.gke_service_account}"]
+}
+
+data "aws_iam_openid_connect_provider" "gke_oidc" {
+ url = "https://container.googleapis.com/v1/projects/${var.gcp_project_id}/locations/${var.gcp_region}/clusters/${var.gke_cluster_name}"
+}
diff --git a/aws_gke_oidc_role/outputs.tf b/aws_gke_oidc_role/outputs.tf
new file mode 100644
index 00000000..da87ea5a
--- /dev/null
+++ b/aws_gke_oidc_role/outputs.tf
@@ -0,0 +1,4 @@
+output "role_arn" {
+ description = "ARN for the GKE-AWS connector role"
+ value = module.iam_assumable_role_for_oidc.iam_role_name
+}
diff --git a/aws_gke_oidc_role/variables.tf b/aws_gke_oidc_role/variables.tf
new file mode 100644
index 00000000..3c58917b
--- /dev/null
+++ b/aws_gke_oidc_role/variables.tf
@@ -0,0 +1,41 @@
+### Required
+
+variable "iam_policy_arns" {
+ description = "One or more policy arns to attach to created AWS role"
+ type = list(string)
+}
+
+variable "role_name" {
+ description = "Name to give the AWS role"
+ type = string
+}
+
+variable "aws_region" {
+ description = "AWS region"
+ type = string
+}
+
+variable "gcp_project_id" {
+ description = "GKE cluster's project ID"
+ type = string
+}
+
+variable "gcp_region" {
+ description = "GKE cluster's GCP region"
+ type = string
+}
+
+variable "gke_cluster_name" {
+ description = "GKE cluster name"
+ type = string
+}
+
+variable "gke_namespace" {
+ description = "Namespace for GKE workload"
+ type = string
+}
+
+variable "gke_service_account" {
+ description = "GKE service account to grant role assumption privilleges"
+ type = string
+}
diff --git a/aws_gke_oidc_role/versions.tf b/aws_gke_oidc_role/versions.tf
new file mode 100644
index 00000000..36a9084e
--- /dev/null
+++ b/aws_gke_oidc_role/versions.tf
@@ -0,0 +1,9 @@
+terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 4.37"
+ }
+ }
+ required_version = "~> 1.0"
+}