Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions az-auth
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/bin/bash

# Copyright 2025 Worklytics, Co.

# Sandbox Azure CLI auth to the current directory to prevent conflicts with other Azure tenants
export AZURE_CONFIG_DIR="${PWD}/.azure"

COLORSCHEME_SH="$(dirname "$0")/set-term-colorscheme.sh"
if [ -f "$COLORSCHEME_SH" ]; then
source "$COLORSCHEME_SH"
else
ERR='\033[0;31m'; SUCCESS='\033[0;32m'; WARN='\033[1;33m'; INFO='\033[0;34m'; CODE='\033[0;36m'; NC='\033[0m'
fi

printf "Sandboxing Azure CLI authentication state to ${INFO}${AZURE_CONFIG_DIR}${NC}\n"

if ! az -v &> /dev/null ; then
printf "${ERR}Azure CLI not available.${NC}\n"
exit 1
fi

TENANT_ID=$1
if [ -f "terraform.tfvars" ] && [ -z "$TENANT_ID" ]; then
if ! terraform -v &> /dev/null ; then
printf "${ERR}Terraform not available.${NC}\n"
exit 1
fi

TENANT_ID=$(grep -E "^msft_tenant_id" terraform.tfvars | awk -F'=' '{print $2}' | tr -d '"' | xargs)
TENANT_ID_PATTERN='^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'

if [[ ! "$TENANT_ID" =~ $TENANT_ID_PATTERN ]]; then
printf "${ERR}Error: Failed to parse Microsoft Tenant ID from terraform.tfvars; you can pass as an argument to this tool. Example:${NC}\n"
printf "Usage: ${INFO}./az-auth.sh <tenant_id>${NC}\n"
printf "Parsed value was: ${INFO}$TENANT_ID${NC}\n"
exit 1
fi
fi

CURRENT_TENANT_ID=$(az account show --query tenantId -o tsv 2>/dev/null)

if [ "$CURRENT_TENANT_ID" != "$TENANT_ID" ]; then
if [ -z "$CURRENT_TENANT_ID" ]; then
printf "No current active Azure session.\n"
else
printf "Current tenant is ${INFO}${CURRENT_TENANT_ID}${NC}.\r\n"
fi
printf "Azure (Microsoft 365) tenant will be forced to ${SUCCESS}${TENANT_ID}${NC}, parsed from your ${INFO}terraform.tfvars${NC}. If you pick user from different tenant, auth will fail.\r\n"
TENANT_ID_CLAUSE="--tenant ${TENANT_ID}"
az login --allow-no-subscriptions $TENANT_ID_CLAUSE
else
printf "Current Azure account is already authenticated against the specified tenant ID ${INFO}${TENANT_ID}${NC}. ${SUCCESS}OK${NC}.\n"
fi

2 changes: 1 addition & 1 deletion google-workspace.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ provider "google" {


module "worklytics_connectors_google_workspace" {
source = "git::https://github.com/worklytics/psoxy//infra/modules/worklytics-connectors-google-workspace?ref=v0.6.0"
source = "git::https://github.com/worklytics/psoxy//infra/modules/worklytics-connectors-google-workspace?ref=v0.6.1"

google_workspace_connector_settings = var.google_workspace_connector_settings

Expand Down
9 changes: 6 additions & 3 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ terraform {

# general cases
module "worklytics_connectors" {
source = "git::https://github.com/worklytics/psoxy//infra/modules/worklytics-connectors?ref=v0.6.0"
source = "git::https://github.com/worklytics/psoxy//infra/modules/worklytics-connectors?ref=v0.6.1"

enabled_connectors = var.enabled_connectors
connector_settings = var.connector_settings
Expand Down Expand Up @@ -111,7 +111,7 @@ locals {
}

module "psoxy" {
source = "git::https://github.com/worklytics/psoxy//infra/modules/aws-host?ref=v0.6.0"
source = "git::https://github.com/worklytics/psoxy//infra/modules/aws-host?ref=v0.6.1"

environment_name = var.environment_name
aws_account_id = var.aws_account_id
Expand Down Expand Up @@ -140,6 +140,8 @@ module "psoxy" {
secrets_store_implementation = var.secrets_store_implementation
bulk_sanitized_expiration_days = var.bulk_sanitized_expiration_days
bulk_input_expiration_days = var.bulk_input_expiration_days
allowed_data_access_ip_blocks = var.allowed_data_access_ip_blocks
allowed_webhook_ip_blocks = var.allowed_webhook_ip_blocks
api_connectors = local.api_connectors
bulk_connectors = local.bulk_connectors
webhook_collectors = { for k, v in var.webhook_collectors : k => merge(
Expand All @@ -154,6 +156,7 @@ module "psoxy" {
custom_side_outputs = var.custom_side_outputs
todo_step = local.max_auth_todo_step
todos_as_local_files = var.todos_as_local_files
enable_remote_resources = true


# vpc_config = {
Expand Down Expand Up @@ -185,7 +188,7 @@ locals {
module "connection_in_worklytics" {
for_each = local.all_instances

source = "git::https://github.com/worklytics/psoxy//infra/modules/worklytics-proxy-connection-aws?ref=v0.6.0"
source = "git::https://github.com/worklytics/psoxy//infra/modules/worklytics-proxy-connection-aws?ref=v0.6.1"

proxy_instance_id = each.key
worklytics_host = var.worklytics_host
Expand Down
8 changes: 4 additions & 4 deletions msft-365.tf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# BEGIN MSFT

module "worklytics_connectors_msft_365" {
source = "git::https://github.com/worklytics/psoxy//infra/modules/worklytics-connectors-msft-365?ref=v0.6.0"
source = "git::https://github.com/worklytics/psoxy//infra/modules/worklytics-connectors-msft-365?ref=v0.6.1"

msft_365_connector_settings = var.msft_365_connector_settings

Expand Down Expand Up @@ -50,7 +50,7 @@ data "aws_region" "current" {
module "cognito_identity_pool" {
count = local.msft_365_enabled ? 1 : 0 # only provision identity pool if MSFT-365 connectors are enabled

source = "git::https://github.com/worklytics/psoxy//infra/modules/aws-cognito-pool?ref=v0.6.0"
source = "git::https://github.com/worklytics/psoxy//infra/modules/aws-cognito-pool?ref=v0.6.1"

developer_provider_name = local.developer_provider_name
name = "${local.env_qualifier}-azure-ad-federation"
Expand All @@ -72,7 +72,7 @@ locals {
module "cognito_identity" {
count = local.msft_365_enabled ? 1 : 0 # only provision identity pool if MSFT-365 connectors are enabled

source = "git::https://github.com/worklytics/psoxy//infra/modules/aws-cognito-identity-cli?ref=v0.6.0"
source = "git::https://github.com/worklytics/psoxy//infra/modules/aws-cognito-identity-cli?ref=v0.6.1"


aws_region = data.aws_region.current.id
Expand Down Expand Up @@ -109,7 +109,7 @@ locals {
module "msft_connection_auth_federation" {
for_each = local.provision_entraid_apps ? local.enabled_to_entraid_object : local.shared_to_entraid_object

source = "git::https://github.com/worklytics/psoxy//infra/modules/azuread-federated-credentials?ref=v0.6.0"
source = "git::https://github.com/worklytics/psoxy//infra/modules/azuread-federated-credentials?ref=v0.6.1"

application_id = each.value.connector_id
display_name = "${local.env_qualifier}AccessFromAWS"
Expand Down
78 changes: 78 additions & 0 deletions preflight
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/bin/bash

# script to check if Terraform cloud providers have basic authentication
# configured correctly based on variables.tf and environment variables
set -e

if [ -f "$(dirname "$0")/set-term-colorscheme.sh" ]; then
source "$(dirname "$0")/set-term-colorscheme.sh"
elif [ -f "$(dirname "$0")/../../tools/set-term-colorscheme.sh" ]; then
source "$(dirname "$0")/../../tools/set-term-colorscheme.sh"
else
# Fallbacks
ERR='\033[0;31m'; SUCCESS='\033[0;32m'; WARN='\033[1;33m'; INFO='\033[0;34m'; CODE='\033[0;36m'; NC='\033[0m'
fi

printf "${INFO}Running preflight checks for Terraform providers ...${NC}\n\n"

# ======== AWS Check ========
if grep -q 'provider "aws"' *.tf 2>/dev/null || grep -q 'aws_assume_role_arn' variables.tf 2>/dev/null; then
printf "Checking AWS Provider Auth...\n"
if ! aws sts get-caller-identity &> /dev/null; then
printf " ${ERR}AWS CLI is not authenticated.${NC} Run 'aws configure' or 'aws sso login'.\n"
else
printf " ${SUCCESS}AWS CLI is authenticated.${NC}\n"
# Attempt to extract assumed role from tfvars if it exists
if [ -f "terraform.tfvars" ]; then
ASSUME_ROLE=$(grep "aws_assume_role_arn" terraform.tfvars | cut -d "=" -f 2 | tr -d ' "') || true
if [[ -n "$ASSUME_ROLE" && "$ASSUME_ROLE" != "null" ]]; then
printf " Attempting to assume AWS Role: ${CODE}${ASSUME_ROLE}${NC} ...\n"
if aws sts assume-role --role-arn "$ASSUME_ROLE" --role-session-name "preflight-test" &> /dev/null; then
printf " ${SUCCESS}Successfully assumed AWS Role from TFVars.${NC}\n"
else
printf " ${ERR}Failed to assume AWS role: $ASSUME_ROLE.${NC} Check trust policies and permissions.\n"
fi
fi
fi
fi
printf "\n"
fi

# ======== GCP Check ========
if grep -q 'provider "google"' *.tf 2>/dev/null || grep -q 'provider "google-workspace"' *.tf 2>/dev/null || grep -q 'provider "google-beta"' *.tf 2>/dev/null; then
printf "Checking GCP Provider Auth...\n"
if ! gcloud auth list --filter="status:ACTIVE" --format="value(account)" 2>/dev/null | grep -q '@'; then
printf " ${ERR}Google Cloud CLI is not authenticated.${NC} Run 'gcloud auth login' and 'gcloud auth application-default login'.\n"
else
printf " ${SUCCESS}Google Cloud CLI is authenticated.${NC}\n"
# Attempt to extract impersonated service account if configured
if [ -f "terraform.tfvars" ]; then
IMPERSONATE_SA=$(grep "gcp_terraform_sa_account_email" terraform.tfvars | cut -d "=" -f 2 | tr -d ' "') || true
if [[ -n "$IMPERSONATE_SA" && "$IMPERSONATE_SA" != "null" ]]; then
printf " Attempting to impersonate GCP Service Account: ${CODE}${IMPERSONATE_SA}${NC} ...\n"
# Testing impersonation by getting an access token
if gcloud auth print-access-token --impersonate-service-account="$IMPERSONATE_SA" &> /dev/null; then
printf " ${SUCCESS}Successfully impersonated GCP Service Account via gcloud.${NC}\n"
else
printf " ${ERR}Failed to impersonate Service Account: ${IMPERSONATE_SA}.${NC}\n"
fi
fi
fi
fi
printf "\n"
fi

# ======== Azure/AzureAD Check ========
if grep -q 'provider "azuread"' *.tf 2>/dev/null || grep -q 'provider "azurerm"' *.tf 2>/dev/null; then
printf "Checking Azure Provider Auth...\n"
if ! az account show &> /dev/null; then
printf " ${ERR}Azure CLI is not authenticated.${NC} Run 'az login' or 'az login --allow-no-subscription'.\n"
else
printf " ${SUCCESS}Azure CLI is authenticated.${NC}\n"
TENANT_ID=$(az account show --query tenantId -o tsv)
printf " Current Azure Tenant: ${CODE}${TENANT_ID}${NC}\n"
fi
printf "\n"
fi

printf "${SUCCESS}Preflight checks complete.${NC}\n"
40 changes: 35 additions & 5 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,40 @@ variable "lookup_table_builders" {
}
}

variable "allowed_data_access_ip_blocks" {
description = <<-EOT
IPs or CIDR blocks allowed to make data access requests. On AWS, enforces at IAM (assume-role aws:SourceIp) and in the proxy. Use null (default) for no restriction. If set, the list must contain at least one value. See docs/configuration/ip-allowlisting.md.
EOT
type = list(string)
nullable = true
default = null

validation {
condition = var.allowed_data_access_ip_blocks == null || try(length(var.allowed_data_access_ip_blocks) > 0, false)
error_message = "allowed_data_access_ip_blocks must be null (allow all) or a non-empty list; an empty list is invalid."
}
}

variable "allowed_webhook_ip_blocks" {
description = <<-EOT
IPs or CIDR blocks allowed to send webhooks. On AWS, enforces at IAM (webhook-test-caller aws:SourceIp) and in the proxy. Use null (default) for no restriction. If set, the list must contain at least one value. See docs/configuration/ip-allowlisting.md.
EOT
type = list(string)
nullable = true
default = null

validation {
condition = var.allowed_webhook_ip_blocks == null || try(length(var.allowed_webhook_ip_blocks) > 0, false)
error_message = "allowed_webhook_ip_blocks must be null (allow all) or a non-empty list; an empty list is invalid."
}
}

variable "connector_settings" {
type = map(string)
default = {}
description = "Connector-specific settings."
}

variable "todos_as_outputs" {
type = bool
description = "whether to render TODOs as outputs (former useful if you're using Terraform Cloud/Enterprise, or somewhere else where the filesystem is not readily accessible to you)"
Expand All @@ -481,8 +515,4 @@ variable "todos_as_local_files" {
default = true
}

variable "connector_settings" {
type = map(string)
default = {}
description = "Connector-specific settings."
}

Loading