diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7c1cae9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,38 @@
+# Local .terraform directories
+**/.terraform/*
+
+# terraform lock file.
+**/.terraform.lock.hcl
+
+# .tfstate files
+*.tfstate
+*.tfstate.*
+
+# Crash log files
+crash.log
+crash.*.log
+
+# Exclude all .tfvars files, which are likely to contain sensitive data,
+# such as password, private keys, and other secrets. These should not be
+# part of version control as they are data points which are potentially
+# sensitive and subject to change depending on the environment.
+*.tfvars
+*.tfvars.json
+
+# Ignore override files as they are usually used to override resources
+# locally and so are not checked in
+override.tf
+override.tf.json
+*_override.tf
+*_override.tf.json
+
+# Include override files you do wish to add to version control using negated pattern
+# !example_override.tf
+
+# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
+# example: *tfplan*
+
+# Ignore CLI configuration files
+.terraformrc
+terraform.rc
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f521b08
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# tfmod-istio-tools
+# tfmod-kiali
diff --git a/chart/kiali/.helmignore b/chart/kiali/.helmignore
new file mode 100644
index 0000000..0e8a0eb
--- /dev/null
+++ b/chart/kiali/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/chart/kiali/Chart.yaml b/chart/kiali/Chart.yaml
new file mode 100644
index 0000000..f6f22a3
--- /dev/null
+++ b/chart/kiali/Chart.yaml
@@ -0,0 +1,24 @@
+apiVersion: v2
+name: kiali
+description: A Helm chart for Kubernetes
+
+# A chart can be either an 'application' or a 'library' chart.
+#
+# Application charts are a collection of templates that can be packaged into versioned archives
+# to be deployed.
+#
+# Library charts provide useful utilities or functions for the chart developer. They're included as
+# a dependency of application charts to inject those utilities and functions into the rendering
+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+type: application
+
+# This is the chart version. This version number should be incremented each time you make changes
+# to the chart and its templates, including the app version.
+# Versions are expected to follow Semantic Versioning (https://semver.org/)
+version: 0.1.0
+
+# This is the version number of the application being deployed. This version number should be
+# incremented each time you make changes to the application. Versions are not expected to
+# follow Semantic Versioning. They should reflect the version the application is using.
+# It is recommended to use it with quotes.
+appVersion: "1.16.0"
diff --git a/chart/kiali/templates/_helpers.tpl b/chart/kiali/templates/_helpers.tpl
new file mode 100644
index 0000000..1a082cd
--- /dev/null
+++ b/chart/kiali/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "kiali.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "kiali.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "kiali.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "kiali.labels" -}}
+helm.sh/chart: {{ include "kiali.chart" . }}
+{{ include "kiali.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "kiali.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "kiali.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "kiali.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "kiali.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/chart/kiali/templates/kiali.yaml b/chart/kiali/templates/kiali.yaml
new file mode 100644
index 0000000..6d4a312
--- /dev/null
+++ b/chart/kiali/templates/kiali.yaml
@@ -0,0 +1,32 @@
+apiVersion: kiali.io/v1alpha1
+kind: Kiali
+metadata:
+ name: {{ include "kiali.fullname" . }}
+ labels:
+ {{- include "kiali.labels" . | nindent 4 }}
+spec:
+ image_version: "operator_version"
+ istio_namespace: {{ .Values.istioNamespace | quote }}
+ deployment:
+ accessible_namespaces: "**"
+ external_services:
+ grafana:
+ auth:
+ type: "basic"
+ username: {{ .Values.grafanaUserName | quote }}
+ password: "secret:{{ .Values.grafanaSecretName }}:{{ .Values.grafanaSecretPasswordKey }}"
+ in_cluster_url: {{ .Values.grafanaInClusterUrl | quote}}
+ url: {{ .Values.grafanaPublicUrl | quote }}
+ prometheus:
+ url: {{ .Values.prometheusInClusterUrl | quote }}
+ tracing:
+ in_cluster_url: {{ .Values.jaegerInClusterUrl | quote }}
+ auth:
+ strategy: {{ .Values.kialiAuthStrategy }}
+{{ if eq .Values.kialiAuthStrategy "openid" }}
+ openid:
+ client_id: {{ .Values.openid.clientId | quote }}
+ disable_rbac: {{ .Values.openid.disableRbac }}
+ issuer_uri: {{ .Values.openid.issuerUri | quote }}
+ username_claim: {{ .Values.openid.username_claim | quote }}
+{{- end }}
diff --git a/chart/kiali/templates/secret.yaml b/chart/kiali/templates/secret.yaml
new file mode 100644
index 0000000..38ec42d
--- /dev/null
+++ b/chart/kiali/templates/secret.yaml
@@ -0,0 +1,10 @@
+{{ if .Values.openid.secret }}
+apiVersion: v1
+kind: Secret
+metadata:
+ name: kiali
+ labels:
+ {{- include "kiali.labels" . | nindent 4 }}
+stringData:
+ oidc-secret: {{ .Values.openid.secret | quote }}
+{{- end }}
diff --git a/chart/kiali/values.yaml b/chart/kiali/values.yaml
new file mode 100644
index 0000000..7c31d29
--- /dev/null
+++ b/chart/kiali/values.yaml
@@ -0,0 +1,21 @@
+
+publicHostname: "kiali"
+publicDomain: "cluster.domain"
+
+istioNamespace: "istio-system"
+prometheusInClusterUrl: "http://loki-prometheus-server.logging.svc.cluster.local/"
+jaegerInClusterUrl: "http://istio-jaeger-query.istio-tools.svc.cluster.local:16686/"
+grafanaInClusterUrl: "http://loki-grafana.logging.svc.cluster.local/"
+grafanaPublicUrl: "https://grafana.cluster.domain/"
+grafanaUserName: "admin"
+grafanaSecretName: "kiali"
+grafanaSecretPasswordKey: "grafana_password"
+
+kialiAuthStrategy: openid
+openid:
+ clientId: "sso_admin_client_id"
+ secret: "sso_admin_client_secret"
+ disableRbac: true
+ issuerUri: "https://keycloak.cluster.domain/realms/sso_admin_realm"
+ usernameClaim: "username_claim"
+
diff --git a/copy_images.tf b/copy_images.tf
new file mode 100644
index 0000000..279ea35
--- /dev/null
+++ b/copy_images.tf
@@ -0,0 +1,46 @@
+locals {
+ kiali_operator_key = format("%v#%v", "istio-tools/kiali-operator", var.kiali_application_version)
+ kiali_key = format("%v#%v", "istio-tools/kiali", var.kiali_application_version)
+
+ image_config = [
+ ## Images for Kiali
+ {
+ enabled = true
+ dest_path = null
+ name = "istio-tools/kiali-operator"
+ source_image = "kiali/kiali-operator"
+ source_registry = "quay.io"
+ source_tag = var.kiali_application_version
+ tag = var.kiali_application_version
+ },
+ {
+ enabled = true
+ dest_path = null
+ name = "istio-tools/kiali"
+ source_image = "kiali/kiali"
+ source_registry = "quay.io"
+ source_tag = var.kiali_application_version
+ tag = var.kiali_application_version
+ },
+ ]
+}
+
+module "images" {
+ source = "git@github.e.it.census.gov:terraform-modules/aws-ecr-copy-images.git/?ref=2.0.2"
+
+ profile = var.profile
+ application_name = var.cluster_name
+ image_config = local.image_config
+ tags = {}
+
+ ### optional
+ ## account_alias = ""
+ ## account_id = ""
+ ## destination_password = ""
+ ## destination_username = ""
+ ## override_prefixes = {}
+ ## region = ""
+ ## source_password = ""
+ ## source_username = ""
+}
+
diff --git a/main.tf b/main.tf
new file mode 100644
index 0000000..07983e2
--- /dev/null
+++ b/main.tf
@@ -0,0 +1,221 @@
+
+locals {
+ kiali_public_url = format("https://kiali.%v/", var.cluster_domain)
+ kiali_internal_url = format("http://kiali.%v.svc.cluster.local:20001/", var.namespace)
+
+ have_keycloak = (
+ try(length(var.keycloak_namespace), 0) > 0 &&
+ try(length(var.sso_admin_client_id), 0) > 0 &&
+ try(length(var.sso_admin_client_secret), 0) > 0 &&
+ try(length(var.keycloak_public_url), 0) > 0 &&
+ try(length(var.keycloak_realm), 0) > 0
+ ) ? true : false
+
+ keycloak_issuer_uri = (
+ local.have_keycloak ?
+ format("%v/realms/%v",
+ var.keycloak_public_url,
+ var.keycloak_realm
+ )
+ : "")
+
+ kiali_oidc_secret = local.have_keycloak ? "ensure_secret kiali oidc-secret \"${var.sso_admin_client_secret}\"" : ";"
+
+ preinstall_script = <ref=1.0.0"
+ source = "git@github.it.census.gov:SOA/tfmod-gogatekeeper.git//"
+
+ namespace = local.ns
+ application_name = "kiali"
+ upstream_url = local.kiali_internal_url
+ redirection_url = local.kiali_public_url
+ client_id = var.sso_admin_client_id
+ client_secret = var.sso_admin_client_secret
+ keycloak_public_url = var.keycloak_public_url
+ gogatekeeper_chart_version = var.gogatekeeper_chart_version
+ gogatekeeper_registry = var.gogatekeeper_registry
+ gogatekeeper_repository = var.gogatekeeper_repository
+ gogatekeeper_tag = var.gogatekeeper_tag
+}
diff --git a/outputs.tf b/outputs.tf
new file mode 100644
index 0000000..21d1432
--- /dev/null
+++ b/outputs.tf
@@ -0,0 +1,8 @@
+
+output "kiali_public_url" {
+ value = format("https://kiali.%v/", var.cluster_domain)
+}
+
+output "kiali_internal_url" {
+ value = format("http://kiali.%v.svc.cluster.local:20001/", var.namespace)
+}
diff --git a/requirements.tf b/requirements.tf
new file mode 100644
index 0000000..32e5c6f
--- /dev/null
+++ b/requirements.tf
@@ -0,0 +1,22 @@
+terraform {
+ required_version = ">= 0.13"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 5.14.0"
+ }
+ helm = {
+ source = "hashicorp/helm"
+ version = ">= 2.11.0"
+ }
+ kubernetes = {
+ source = "hashicorp/kubernetes"
+ version = ">= 2.23.0"
+ }
+ null = {
+ source = "hashicorp/null"
+ version = ">= 3.2.1"
+ }
+ }
+}
diff --git a/variables.tf b/variables.tf
new file mode 100644
index 0000000..67859c8
--- /dev/null
+++ b/variables.tf
@@ -0,0 +1,175 @@
+variable "profile" {
+ description = "The AWS_PROFILE to use while running the scripts."
+ default = ""
+}
+
+variable "cluster_name" {
+ description = "The name of the cluster into which the tools are deployed."
+}
+
+variable "cluster_domain" {
+ description = "The domain name used to reference ingresses for the cluster"
+ default = ""
+}
+
+variable "enable_kiali" {
+ description = "Indicates if Kiali should be installed for managing the service mesh"
+ type = bool
+ default = true
+}
+
+variable "enable_jaeger" {
+ description = "Indicates if Jaegertracing should be installed to monitor api calls within the service mesh"
+ type = bool
+ default = true
+}
+
+variable "namespace" {
+ description = "The namespace to create and into which the tools are deployed."
+ default = "istio-tools"
+}
+
+variable "create_namespace" {
+ description = "Indicates whether the `namespace` needs to be created ('true') or already exists (not `true`)"
+ type = bool
+ default = true
+}
+
+variable "istio_namespace" {
+ description = "The namespace where istio has been deployed."
+ default = "istio-system"
+}
+
+variable "operators_namespace" {
+ description = "The namespace into which all operators are to be deployed."
+}
+
+variable "keycloak_namespace" {
+ description = "The namespace holding the keycloak instance."
+ default = null
+}
+
+variable "grafana_namespace" {
+ description = "The namespace holding the grafana instance, used to look up the grafana password."
+}
+
+variable "grafana_service_name" {
+ description = "The service name of the grafana instance."
+ default = "grafana"
+}
+
+variable "grafana_secret_name" {
+ description = "The secret in the holding the grafana admin password."
+}
+
+variable "grafana_public_url" {
+ description = "The URL incoming traffic from outisde the cluster uses to access grafana."
+}
+
+variable "grafana_internal_url" {
+ description = "The url within the cluster to use to access grafana."
+}
+
+variable "prometheus_internal_url" {
+ description = "The url within the cluster to use to query the prometheus server."
+}
+
+variable "jaeger_internal_url" {
+ description = "The url within the cluster to use to query the jaegertracing."
+}
+
+variable "sso_admin_client_id" {
+ description = "The client id to use for SSO"
+ default = null
+}
+
+variable "sso_admin_client_secret" {
+ description = "The secret associated with the sso_admin_client_id"
+ default = null
+}
+
+variable "keycloak_public_url" {
+ description = "The hostname used with the cluster domain to access keycloak"
+ default = null
+}
+
+variable "keycloak_realm" {
+ description = "The existing keycloak realm in which the client should be created"
+ default = null
+}
+
+variable "kiali_hostname" {
+ description = "The hostname to use for the kiali deployment"
+ default = "kiali"
+}
+
+variable "jaeger_hostname" {
+ description = "The hsotname to use for the jaegertracing deployment"
+ default = "jaeger"
+}
+
+# helm repo add kiali https://kiali.org/helm-charts
+# helm search repo kiali/kiali-operator
+variable "kiali_operator_version" {
+ description = "The version of kiali to install"
+ default = "1.73.0"
+}
+
+# See the [APP VERSION] found while determining kiali_operator_version
+# helm show values kiali/kiali-operator | grep tag:
+variable "kiali_application_version" {
+ description = "The version of kiali to install"
+ default = "v1.73.0"
+}
+
+# helm repo add jaegertracing https://jaegertracing.github.io/helm-charts
+# helm search repo jaegertracing/jaeger-operator
+variable "jaeger_operator_version" {
+ description = "The version of jaegertracing to install"
+ default = "2.46.2"
+}
+
+# See the [APP VERSION] found while determining jaeger_oeprator_version
+variable "jaeger_application_version" {
+ description = "The version of jaegertracing to install"
+ default = "1.46.0"
+}
+
+# jaegertracing requires elasticsearch v7
+# opensearch is a fork of elasticsearch v7 with OSS licensing
+# hosted opensearch in govcloud is 2.7.0 as of 29 Aug 2023
+# helm repo add opensearch https://opensearch-project.github.io/helm-charts/
+# to match govcloud hosted opensearch
+# helm search repo opensearch/opensearch -l | grep '[[:space:]]2.7' | head -2
+# to find the latest version of opensearch
+# helm search repo opensearch/opensearch
+variable "opensearch_chart_version" {
+ description = "The version of opensearch to install"
+ default = "2.14.1"
+}
+
+# The [APP VERSION] found while determining opensearch_chart_version
+variable "opensearch_version" {
+ description = "The version of opensearch to install"
+ default = "2.9.0"
+}
+
+variable "gogatekeeper_chart_version" {
+ description = "When SSO information is supplied, use this gogatekeeper chart version to protect kiali/jaeger"
+ default = null
+}
+
+variable "gogatekeeper_registry" {
+ description = "When SSO information is supplied, use gogatekeeper in this registry to protect kiali/jaeger"
+ default = null
+}
+
+variable "gogatekeeper_repository" {
+ description = "When SSO information is supplied, use gogatekeeper in this repository to protect kiali/jaeger"
+ default = null
+}
+
+variable "gogatekeeper_tag" {
+ description = "When SSO information is supplied, use gogatekeeper with this tag to protect kiali/jaeger"
+ default = null
+}