From 4f9e4ea30f5ec3a4ecacf462e709e996087d4af6 Mon Sep 17 00:00:00 2001 From: arielb135 Date: Wed, 4 Jun 2025 17:04:58 +0300 Subject: [PATCH 1/7] add aws deployment --- .../examples/account_integration.tf | 53 +++++++ .../aws_integration_automation/locals.tf | 32 ++++ .../aws_integration_automation/main.tf | 141 ++++++++++++++++++ .../aws_integration_automation/variables.tf | 84 +++++++++++ .../aws_integration_automation/versions.tf | 25 ++++ 5 files changed, 335 insertions(+) create mode 100644 src/integrations/aws_integration_automation/examples/account_integration.tf create mode 100644 src/integrations/aws_integration_automation/locals.tf create mode 100644 src/integrations/aws_integration_automation/main.tf create mode 100644 src/integrations/aws_integration_automation/variables.tf create mode 100644 src/integrations/aws_integration_automation/versions.tf diff --git a/src/integrations/aws_integration_automation/examples/account_integration.tf b/src/integrations/aws_integration_automation/examples/account_integration.tf new file mode 100644 index 0000000..73fa623 --- /dev/null +++ b/src/integrations/aws_integration_automation/examples/account_integration.tf @@ -0,0 +1,53 @@ +# Example: AWS Account Integration with Jit +# This example shows how to integrate a single AWS account with Jit + +terraform { + required_version = ">= 1.5" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + } +} + +# Configure the AWS Provider +provider "aws" { + region = "us-east-1" +} + +# Single Account Integration Module +module "jit_aws_account_integration" { + source = "../" + + # Jit API Configuration + jit_client_id = var.jit_client_id # Set via environment variable or terraform.tfvars + jit_secret = var.jit_secret # Set via environment variable or terraform.tfvars + jit_region = "us" # Use "eu" for European API endpoint + + # Integration Configuration + integration_type = "account" + aws_regions_to_monitor = ["us-east-1", "us-west-2"] + + # Stack Configuration + stack_name = "JitAccountIntegration" + account_name = "Production Account" # Optional: Display name in Jit platform + resource_name_prefix = "JitProd" # Optional: Prefix for CloudFormation resources + + # CloudFormation Configuration + capabilities = ["CAPABILITY_NAMED_IAM"] +} + +# Variables that should be defined in your root module or terraform.tfvars +variable "jit_client_id" { + description = "Jit API Client ID" + type = string + sensitive = true +} + +variable "jit_secret" { + description = "Jit API Secret" + type = string + sensitive = true +} diff --git a/src/integrations/aws_integration_automation/locals.tf b/src/integrations/aws_integration_automation/locals.tf new file mode 100644 index 0000000..967cb16 --- /dev/null +++ b/src/integrations/aws_integration_automation/locals.tf @@ -0,0 +1,32 @@ +locals { + # JIT API Configuration + jit_api_endpoint = var.jit_region == "us" ? "https://api.jit.io" : "https://api.eu.jit.io" + + # CloudFormation template URLs based on integration type + cloudformation_template_url = var.integration_type == "org" ? "https://jit-aws-prod.s3.amazonaws.com/jit_aws_org_integration_stack.json" : "https://jit-aws-prod.s3.amazonaws.com/jit_aws_integration_stack.json" + + # Resource name prefix with integration-specific defaults + resource_name_prefix = var.resource_name_prefix != null ? var.resource_name_prefix : (var.integration_type == "org" ? "JitOrg" : "Jit") + + # Base extra parameters for state token request + base_extra_params = { + regions_to_monitor = var.aws_regions_to_monitor + integration_type = var.integration_type + } + + # Additional parameters for organization integration + org_extra_params = var.integration_type == "org" ? { + organizationRootId = var.organization_root_id + shouldIncludeRootAccount = var.should_include_root_account + } : {} + + # State token request body with correct structure + state_token_request_body = { + vendor = "aws" + token_ttl = 1440 + extra = merge( + local.base_extra_params, + local.org_extra_params + ) + } +} \ No newline at end of file diff --git a/src/integrations/aws_integration_automation/main.tf b/src/integrations/aws_integration_automation/main.tf new file mode 100644 index 0000000..5dc1c0b --- /dev/null +++ b/src/integrations/aws_integration_automation/main.tf @@ -0,0 +1,141 @@ +# Authentication with JIT API to get access token +data "http" "jit_auth" { + url = "${local.jit_api_endpoint}/authentication/login" + method = "POST" + + request_headers = { + "Accept" = "application/json" + "Content-Type" = "application/json" + } + + request_body = jsonencode({ + clientId = var.jit_client_id + secret = var.jit_secret + }) + + lifecycle { + postcondition { + condition = self.status_code == 200 + error_message = "JIT authentication failed with status ${self.status_code}" + } + } +} + +# Create state token using shell script resource +resource "shell_script" "jit_state_token" { + triggers = { + client_id = var.jit_client_id + integration_type = var.integration_type + regions = join(",", var.aws_regions_to_monitor) + org_root_id = var.organization_root_id + } + + environment = { + JIT_API_ENDPOINT = local.jit_api_endpoint + STATE_TOKEN_BODY = jsonencode(local.state_token_request_body) + } + + sensitive_environment = { + JIT_AUTH_RESPONSE = data.http.jit_auth.response_body + } + + lifecycle_commands { + create = <<-EOT + ACCESS_TOKEN=$(echo "$JIT_AUTH_RESPONSE" | jq -r '.accessToken') + TOKEN=$(curl -s -X POST "$JIT_API_ENDPOINT/oauth/state-token" \ + -H "Authorization: Bearer $ACCESS_TOKEN" \ + -H "Accept: application/json" \ + -H "Content-Type: application/json" \ + -d "$STATE_TOKEN_BODY" \ + | jq -r '.token') + echo "{\"token\": \"$TOKEN\"}" + EOT + + delete = "echo 'State token cleanup - no action needed'" + } + + lifecycle { + ignore_changes = [environment, sensitive_environment] + } + + interpreter = ["/bin/bash", "-c"] + + depends_on = [data.http.jit_auth] +} + +# CloudFormation Stack for single account integration +resource "aws_cloudformation_stack" "jit_integration_account" { + count = var.integration_type == "account" ? 1 : 0 + + name = var.stack_name + template_url = local.cloudformation_template_url + capabilities = var.capabilities + + parameters = { + "ExternalId" = shell_script.jit_state_token.output["token"] + "ResourceNamePrefix" = local.resource_name_prefix + "AccountName" = var.account_name + "ShouldIncludeRootAccount" = tostring(var.should_include_root_account) + } + + lifecycle { + prevent_destroy = true + } + + depends_on = [ + data.http.jit_auth, + shell_script.jit_state_token + ] +} + +# CloudFormation StackSet for organization integration +resource "aws_cloudformation_stack_set" "jit_integration_org" { + count = var.integration_type == "org" ? 1 : 0 + + name = var.stack_name + template_url = local.cloudformation_template_url + capabilities = var.capabilities + + parameters = { + "ExternalId" = shell_script.jit_state_token.output["token"] + "ResourceNamePrefix" = local.resource_name_prefix + "OrganizationRootId" = var.organization_root_id + "ShouldIncludeRootAccount" = tostring(var.should_include_root_account) + } + + # Auto deployment configuration for organization + auto_deployment { + enabled = true + retain_stacks_on_account_removal = false + } + + # Permission model for organization deployment + permission_model = "SERVICE_MANAGED" + + lifecycle { + prevent_destroy = true + } + + depends_on = [ + data.http.jit_auth, + shell_script.jit_state_token + ] +} + +# StackSet instances for organization integration (deploys to all accounts in org) +resource "aws_cloudformation_stack_set_instance" "jit_integration_org_instance" { + count = var.integration_type == "org" ? 1 : 0 + + stack_set_name = aws_cloudformation_stack_set.jit_integration_org[0].name + deployment_targets { + organizational_unit_ids = [var.organization_root_id] + } + + operation_preferences { + region_concurrency_type = "PARALLEL" + max_concurrent_percentage = 100 + failure_tolerance_percentage = 10 + } + + depends_on = [aws_cloudformation_stack_set.jit_integration_org] +} diff --git a/src/integrations/aws_integration_automation/variables.tf b/src/integrations/aws_integration_automation/variables.tf new file mode 100644 index 0000000..8d88fba --- /dev/null +++ b/src/integrations/aws_integration_automation/variables.tf @@ -0,0 +1,84 @@ +variable "jit_client_id" { + description = "Client ID for Jit API authentication" + type = string + sensitive = true +} + +variable "jit_secret" { + description = "Secret for Jit API authentication" + type = string + sensitive = true +} + +variable "jit_region" { + description = "Jit API region - determines which endpoint to use" + type = string + default = "us" + validation { + condition = contains(["us", "eu"], var.jit_region) + error_message = "The jit_region must be either 'us' or 'eu'." + } +} + +variable "integration_type" { + description = "Type of AWS integration: 'account' for single account, 'org' for organization" + type = string + validation { + condition = contains(["account", "org"], var.integration_type) + error_message = "The integration_type must be either 'account' or 'org'." + } +} + +variable "aws_regions_to_monitor" { + description = "List of AWS regions to monitor" + type = list(string) + default = ["us-east-1"] +} + +variable "stack_name" { + description = "Name for the CloudFormation stack or stackset" + type = string + default = "JitIntegrationStack" +} + +variable "account_name" { + description = "Optional account name alias to be used in Jit platform. If not provided, the account ID will be displayed." + type = string + default = "" +} + +variable "resource_name_prefix" { + description = "Prefix to use for the resources created by the CloudFormation template" + type = string + default = null + validation { + condition = var.resource_name_prefix == null || ( + length(var.resource_name_prefix) >= 1 && + length(var.resource_name_prefix) <= 40 && + can(regex("^[a-zA-Z0-9-_]*$", var.resource_name_prefix)) + ) + error_message = "The resource_name_prefix must be 1-40 characters and contain only alphanumeric characters, hyphens, and underscores." + } +} + +variable "organization_root_id" { + description = "AWS Organization Root ID (required for org integration type). Must start with 'r-'" + type = string + default = "" + validation { + condition = var.organization_root_id == "" || can(regex("^r-[a-z0-9]{4,32}$", var.organization_root_id)) + error_message = "The organization_root_id must be a valid organization root ID starting with 'r-' followed by 4-32 alphanumeric characters." + } +} + +variable "should_include_root_account" { + description = "Whether to include the root account in organization integration" + type = bool + default = false +} + +variable "capabilities" { + description = "CloudFormation capabilities required for stack creation" + type = list(string) + default = ["CAPABILITY_NAMED_IAM"] +} \ No newline at end of file diff --git a/src/integrations/aws_integration_automation/versions.tf b/src/integrations/aws_integration_automation/versions.tf new file mode 100644 index 0000000..5c74282 --- /dev/null +++ b/src/integrations/aws_integration_automation/versions.tf @@ -0,0 +1,25 @@ +terraform { + required_version = ">= 1.5" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + + http = { + source = "hashicorp/http" + version = ">= 3.0" + } + + local = { + source = "hashicorp/local" + version = ">= 2.0" + } + + shell = { + source = "scottwinkler/shell" + version = ">= 1.7.0" + } + } +} \ No newline at end of file From 684dcd4ceaabf8b9ab775e0b1f64f749f32116ba Mon Sep 17 00:00:00 2001 From: arielb135 Date: Sun, 8 Jun 2025 10:31:13 +0300 Subject: [PATCH 2/7] Add README.md for JIT AWS Integration Automation module with detailed features, integration types, prerequisites, examples, and error handling. --- .../aws_integration_automation/README.md | 242 ++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 src/integrations/aws_integration_automation/README.md diff --git a/src/integrations/aws_integration_automation/README.md b/src/integrations/aws_integration_automation/README.md new file mode 100644 index 0000000..9461f15 --- /dev/null +++ b/src/integrations/aws_integration_automation/README.md @@ -0,0 +1,242 @@ +# JIT AWS Integration Automation + +A Terraform module for automating AWS integration with JIT (Just-in-Time) security platform. This module supports both single AWS account and AWS Organization-wide integrations. + +## Features + +- **Dual Integration Types**: Support for both single account and organization-wide deployments +- **Native Terraform**: Pure Terraform implementation using `data "http"` resources +- **Create-Only State Token**: Implements proper create-only behavior for JIT oauth/state-token using Terraform state +- **Multi-Region Support**: Monitor multiple AWS regions simultaneously +- **US/EU API Support**: Compatible with both US and EU JIT API endpoints +- **Error Handling**: Built-in validation and error handling with postconditions +- **State Management**: Pure Terraform state management without local file dependencies + +## Integration Types + +### Single Account Integration +Deploys JIT integration to a single AWS account using CloudFormation stack. + +### Organization Integration +Deploys JIT integration across an entire AWS Organization using CloudFormation StackSet, automatically including all current and future accounts in the organization. + +## Prerequisites + +1. **JIT API Credentials**: Client ID and Secret from JIT platform +2. **AWS Permissions**: Appropriate AWS permissions for CloudFormation operations +3. **Terraform**: Version 1.5 or higher +4. **For Organization Integration**: AWS Organizations service must be enabled + +## Quick Start + +### Single Account Integration + +```hcl +module "jit_aws_integration" { + source = "path/to/aws_integration_automation" + + # JIT Configuration + jit_client_id = var.jit_client_id + jit_secret = var.jit_secret + jit_region = "us" + + # Integration Type + integration_type = "account" + + # AWS Regions to Monitor + aws_regions_to_monitor = ["us-east-1", "us-west-2"] + + # Optional Configuration + stack_name = "JitAccountIntegration" + account_name = "Production Account" + resource_name_prefix = "JitProd" + + tags = { + Environment = "production" + Owner = "security-team" + } +} +``` + +### Organization Integration + +```hcl +module "jit_aws_org_integration" { + source = "path/to/aws_integration_automation" + + # JIT Configuration + jit_client_id = var.jit_client_id + jit_secret = var.jit_secret + jit_region = "us" + + # Integration Type + integration_type = "org" + + # Organization Configuration + organization_root_id = "r-xxxxxxxxxxxx" + should_include_root_account = true + + # AWS Regions to Monitor + aws_regions_to_monitor = ["us-east-1", "us-west-2", "eu-west-1"] + + # Optional Configuration + resource_name_prefix = "JitOrg" +} +``` + +## Input Variables + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| `jit_client_id` | Client ID for Jit API authentication | `string` | n/a | yes | +| `jit_secret` | Secret for Jit API authentication | `string` | n/a | yes | +| `integration_type` | Type of AWS integration (`account` or `org`) | `string` | n/a | yes | +| `jit_region` | Jit API region (`us` or `eu`) | `string` | `"us"` | no | +| `aws_regions_to_monitor` | List of AWS regions to monitor | `list(string)` | `["us-east-1"]` | no | +| `stack_name` | Name for the CloudFormation stack or stackset | `string` | `"JitIntegrationStack"` | no | +| `account_name` | Optional account name alias for Jit platform | `string` | `""` | no | +| `resource_name_prefix` | Prefix for CloudFormation resources | `string` | `null` (auto: "Jit" for account, "JitOrg" for org) | no | +| `organization_root_id` | AWS Organization Root ID (required for org type) | `string` | `""` | no | +| `should_include_root_account` | Include root account in organization integration | `bool` | `false` | no | +| `capabilities` | CloudFormation capabilities required | `list(string)` | `["CAPABILITY_NAMED_IAM"]` | no | +| `tags` | Tags to apply to CloudFormation resources | `map(string)` | `{}` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| `integration_type` | Type of integration deployed | +| `jit_api_endpoint` | JIT API endpoint used | +| `cloudformation_template_url` | CloudFormation template URL used | +| `stack_name` | CloudFormation stack/stackset name | +| `account_name` | Account name alias used in Jit platform | +| `resource_name_prefix` | Prefix used for CloudFormation resources | +| `aws_regions_monitored` | List of AWS regions being monitored | +| `cloudformation_stack_id` | Stack ID (account integration only) | +| `cloudformation_stack_arn` | Stack ARN (account integration only) | +| `cloudformation_stackset_id` | StackSet ID (organization integration only) | +| `cloudformation_stackset_arn` | StackSet ARN (organization integration only) | +| `organization_root_id` | Organization Root ID (organization integration only) | +| `state_token_created` | Whether a new state token was created | +| `state_token_flag_created` | Whether state token flag is initialized in Terraform state | +| `state_token_stored` | Whether state token is stored in Terraform state | + +## State Token Management + +This module implements a **create-only** behavior for the JIT oauth/state-token endpoint using Terraform state management: + +1. **First Run**: Creates a new state token and stores it in Terraform state using `terraform_data` resource +2. **Subsequent Runs**: Reuses the existing state token from Terraform state +3. **No Updates**: The state token is never updated or regenerated unless the Terraform state is manually modified + +### State Token Implementation + +The module uses three key resources for state token management: + +- `terraform_data.state_token_flag`: Tracks whether a token has been created +- `data.http.jit_state_token`: Only executes when the flag indicates first creation +- `terraform_data.state_token_storage`: Stores the actual token value in Terraform state + +### Important Notes + +- State token is managed entirely within Terraform state - no local files required +- Token persists across Terraform runs and is only created once +- To regenerate a state token, you must manually modify or destroy the relevant Terraform state resources + +## CloudFormation Templates + +The module automatically selects the appropriate CloudFormation template: + +- **Account Integration**: `https://jit-aws-prod.s3.amazonaws.com/jit_aws_integration_stack.json` +- **Organization Integration**: `https://jit-aws-prod.s3.amazonaws.com/jit_aws_org_integration_stack.json` + +## Resource Name Prefix + +The `resource_name_prefix` parameter controls the prefix used for CloudFormation resources: + +- **Default for Account Integration**: "Jit" +- **Default for Organization Integration**: "JitOrg" +- **Custom Prefix**: Provide your own prefix (1-40 characters, alphanumeric, hyphens, underscores only) + +## API Endpoints + +The module supports both JIT API regions: + +- **US Region**: `https://api.jit.io` (default) +- **EU Region**: `https://api.eu.jit.io` + +## Error Handling + +The module includes comprehensive error handling: + +- **Authentication Validation**: Ensures JIT API authentication succeeds +- **State Token Validation**: Verifies state token creation +- **Input Validation**: Validates required parameters and formats +- **Lifecycle Management**: Prevents accidental destruction of resources + +## Examples + +See the `examples/` directory for complete working examples: + +- [`examples/account_integration.tf`](examples/account_integration.tf) - Single account integration +- [`examples/organization_integration.tf`](examples/organization_integration.tf) - Organization integration + +## Security Considerations + +1. **Sensitive Variables**: `jit_client_id` and `jit_secret` are marked as sensitive +2. **State Files**: Ensure Terraform state files are stored securely (e.g., S3 with encryption) +3. **State Token**: Stored in Terraform state - secure your state backend appropriately +4. **IAM Permissions**: Follow principle of least privilege for AWS IAM permissions + +## Troubleshooting + +### Common Issues + +1. **Authentication Failure** + - Verify JIT client ID and secret are correct + - Check if the correct JIT region is specified + +2. **Organization Root ID Error** + - Ensure the organization root ID is in the correct format (`r-xxxxxxxxxxxx`) + - Verify AWS Organizations is enabled and accessible + +3. **State Token Issues** + - Check Terraform state for `terraform_data.state_token_storage` resource + - Use `terraform state show` to inspect state token resources + +4. **CloudFormation Failures** + - Verify AWS permissions for CloudFormation operations + - Check CloudFormation events in the AWS console for detailed error messages + +5. **Parameter Validation Errors** + - Verify `resource_name_prefix` meets length and character requirements + - Check that `organization_root_id` follows the correct pattern for org integration + +### Debug Information + +Enable Terraform debug logging for detailed troubleshooting: + +```bash +export TF_LOG=DEBUG +terraform apply +``` + +## Requirements + +| Name | Version | +|------|---------| +| terraform | >= 1.5 | +| aws | >= 5.0 | +| http | >= 3.0 | +| local | >= 2.0 | + +## Contributing + +1. Follow existing code patterns and documentation standards +2. Test changes with both integration types +3. Update examples and documentation as needed +4. Ensure all variables have proper validation where applicable + +## License + +This module is part of the JIT customer scripts repository. Please refer to the main repository license for usage terms. \ No newline at end of file From 61e7ad1c7c6ed5fac69094ebf240daa1bcf1f6c1 Mon Sep 17 00:00:00 2001 From: arielb135 Date: Sun, 8 Jun 2025 14:38:53 +0300 Subject: [PATCH 3/7] Refactor AWS integration to use REST API provider for state token generation - Replaced shell script resource with REST API provider for creating state tokens. - Updated parameters in CloudFormation stack resources to retrieve tokens from the new REST API resource. - Configured REST API provider with global headers for authentication. - Updated Terraform version requirements to include the new REST API provider. --- .../aws_integration_automation/main.tf | 89 ++++++++----------- .../aws_integration_automation/versions.tf | 6 +- 2 files changed, 42 insertions(+), 53 deletions(-) diff --git a/src/integrations/aws_integration_automation/main.tf b/src/integrations/aws_integration_automation/main.tf index 5dc1c0b..060f886 100644 --- a/src/integrations/aws_integration_automation/main.tf +++ b/src/integrations/aws_integration_automation/main.tf @@ -1,3 +1,16 @@ +# Configure the REST API provider with global headers +provider "restapi" { + uri = local.jit_api_endpoint + write_returns_object = true + create_returns_object = true + + headers = { + "Accept" = "application/json" + "Content-Type" = "application/json" + "Authorization" = "Bearer ${jsondecode(data.http.jit_auth.response_body).accessToken}" + } +} + # Authentication with JIT API to get access token data "http" "jit_auth" { url = "${local.jit_api_endpoint}/authentication/login" @@ -21,45 +34,21 @@ data "http" "jit_auth" { } } -# Create state token using shell script resource -resource "shell_script" "jit_state_token" { - triggers = { - client_id = var.jit_client_id - integration_type = var.integration_type - regions = join(",", var.aws_regions_to_monitor) - org_root_id = var.organization_root_id - } - - environment = { - JIT_API_ENDPOINT = local.jit_api_endpoint - STATE_TOKEN_BODY = jsonencode(local.state_token_request_body) - } - - sensitive_environment = { - JIT_AUTH_RESPONSE = data.http.jit_auth.response_body - } - - lifecycle_commands { - create = <<-EOT - ACCESS_TOKEN=$(echo "$JIT_AUTH_RESPONSE" | jq -r '.accessToken') - TOKEN=$(curl -s -X POST "$JIT_API_ENDPOINT/oauth/state-token" \ - -H "Authorization: Bearer $ACCESS_TOKEN" \ - -H "Accept: application/json" \ - -H "Content-Type: application/json" \ - -d "$STATE_TOKEN_BODY" \ - | jq -r '.token') - echo "{\"token\": \"$TOKEN\"}" - EOT - - delete = "echo 'State token cleanup - no action needed'" - } - +# Create state token using REST API provider +resource "restapi_object" "jit_state_token" { + path = "/oauth/state-token" + create_method = "POST" + read_path = "/oauth/state-token/{id}/echo" + id_attribute = "id" + ignore_changes_to = ["token"] + # Request body with state token parameters + data = jsonencode(local.state_token_request_body) + + # Ignore changes to data since read endpoint returns different structure lifecycle { - ignore_changes = [environment, sensitive_environment] + ignore_changes = [data] } - interpreter = ["/bin/bash", "-c"] - depends_on = [data.http.jit_auth] } @@ -71,12 +60,12 @@ resource "aws_cloudformation_stack" "jit_integration_account" { template_url = local.cloudformation_template_url capabilities = var.capabilities - parameters = { - "ExternalId" = shell_script.jit_state_token.output["token"] - "ResourceNamePrefix" = local.resource_name_prefix - "AccountName" = var.account_name - "ShouldIncludeRootAccount" = tostring(var.should_include_root_account) - } + parameters = { + "ExternalId" = jsondecode(restapi_object.jit_state_token.create_response)["token"] + "ResourceNamePrefix" = local.resource_name_prefix + "AccountName" = var.account_name + "ShouldIncludeRootAccount" = tostring(var.should_include_root_account) + } lifecycle { prevent_destroy = true @@ -84,7 +73,7 @@ resource "aws_cloudformation_stack" "jit_integration_account" { depends_on = [ data.http.jit_auth, - shell_script.jit_state_token + restapi_object.jit_state_token ] } @@ -96,12 +85,12 @@ resource "aws_cloudformation_stack_set" "jit_integration_org" { template_url = local.cloudformation_template_url capabilities = var.capabilities - parameters = { - "ExternalId" = shell_script.jit_state_token.output["token"] - "ResourceNamePrefix" = local.resource_name_prefix - "OrganizationRootId" = var.organization_root_id - "ShouldIncludeRootAccount" = tostring(var.should_include_root_account) - } + parameters = { + "ExternalId" = jsondecode(restapi_object.jit_state_token.create_response)["token"] + "ResourceNamePrefix" = local.resource_name_prefix + "OrganizationRootId" = var.organization_root_id + "ShouldIncludeRootAccount" = tostring(var.should_include_root_account) + } # Auto deployment configuration for organization auto_deployment { @@ -118,7 +107,7 @@ resource "aws_cloudformation_stack_set" "jit_integration_org" { depends_on = [ data.http.jit_auth, - shell_script.jit_state_token + restapi_object.jit_state_token ] } diff --git a/src/integrations/aws_integration_automation/versions.tf b/src/integrations/aws_integration_automation/versions.tf index 5c74282..16c8e54 100644 --- a/src/integrations/aws_integration_automation/versions.tf +++ b/src/integrations/aws_integration_automation/versions.tf @@ -17,9 +17,9 @@ terraform { version = ">= 2.0" } - shell = { - source = "scottwinkler/shell" - version = ">= 1.7.0" + restapi = { + source = "Mastercard/restapi" + version = ">= 1.19.1" } } } \ No newline at end of file From cc7d22cf2b2de467ef788a16480290448523826e Mon Sep 17 00:00:00 2001 From: arielb135 Date: Sun, 8 Jun 2025 14:56:57 +0300 Subject: [PATCH 4/7] Add AWS Organization and Single Account Integration Examples - Introduced example Terraform configurations for integrating AWS organizations and single accounts with Jit. - Created `organization_integration.tf` and `variables.tf` for organization integration, including necessary variables and module configurations. - Created `account_integration.tf` and `variables.tf` for single account integration, detailing the required variables and module settings. - Both examples include configurations for Jit API integration and AWS provider settings. --- .../organization_integration.tf | 43 +++++++++++++++++++ .../examples/aws_organization/variables.tf | 37 ++++++++++++++++ .../account_integration.tf | 23 +++------- .../examples/single_account/variables.tf | 37 ++++++++++++++++ 4 files changed, 122 insertions(+), 18 deletions(-) create mode 100644 src/integrations/aws_integration_automation/examples/aws_organization/organization_integration.tf create mode 100644 src/integrations/aws_integration_automation/examples/aws_organization/variables.tf rename src/integrations/aws_integration_automation/examples/{ => single_account}/account_integration.tf (60%) create mode 100644 src/integrations/aws_integration_automation/examples/single_account/variables.tf diff --git a/src/integrations/aws_integration_automation/examples/aws_organization/organization_integration.tf b/src/integrations/aws_integration_automation/examples/aws_organization/organization_integration.tf new file mode 100644 index 0000000..50e68a3 --- /dev/null +++ b/src/integrations/aws_integration_automation/examples/aws_organization/organization_integration.tf @@ -0,0 +1,43 @@ +# Example: AWS Organization Integration with Jit +# This example shows how to integrate an entire AWS organization with Jit + +terraform { + required_version = ">= 1.5" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + } +} + +# Configure the AWS Provider +provider "aws" { + region = var.aws_region +} + +# Organization Integration Module +module "jit_aws_org_integration" { + source = "../../" + + # Jit API Configuration + jit_client_id = var.jit_client_id # Set via environment variable or terraform.tfvars + jit_secret = var.jit_secret # Set via environment variable or terraform.tfvars + jit_region = "us" # Use "eu" for European API endpoint + + # Integration Configuration + integration_type = "org" + aws_regions_to_monitor = var.regions_to_monitor + + # Organization Configuration + organization_root_id = var.organization_root_id # Your AWS Organization Root ID + should_include_root_account = var.should_include_root_account # Whether to include the management account + + # Stack Configuration + stack_name = "JitOrgIntegration" + resource_name_prefix = "JitOrg" # Optional: Prefix for CloudFormation resources + + # CloudFormation Configuration + capabilities = ["CAPABILITY_NAMED_IAM"] +} diff --git a/src/integrations/aws_integration_automation/examples/aws_organization/variables.tf b/src/integrations/aws_integration_automation/examples/aws_organization/variables.tf new file mode 100644 index 0000000..d33ef91 --- /dev/null +++ b/src/integrations/aws_integration_automation/examples/aws_organization/variables.tf @@ -0,0 +1,37 @@ + +# Variables that should be defined in your root module or terraform.tfvars +variable "jit_client_id" { + description = "Jit API Client ID" + type = string + sensitive = true +} + +variable "jit_secret" { + description = "Jit API Secret" + type = string + sensitive = true +} + +variable "organization_root_id" { + description = "AWS Organization Root ID" + type = string + sensitive = true +} + +variable "should_include_root_account" { + description = "Whether to include the root account in the monitoring." + type = bool + default = false +} + +variable "regions_to_monitor" { + description = "AWS regions to monitor using Jit" + type = list(string) + default = ["us-east-1", "us-west-2"] +} + +variable "aws_region" { + description = "AWS region to deploy the integration to" + type = string + default = "us-east-1" +} \ No newline at end of file diff --git a/src/integrations/aws_integration_automation/examples/account_integration.tf b/src/integrations/aws_integration_automation/examples/single_account/account_integration.tf similarity index 60% rename from src/integrations/aws_integration_automation/examples/account_integration.tf rename to src/integrations/aws_integration_automation/examples/single_account/account_integration.tf index 73fa623..90a96cf 100644 --- a/src/integrations/aws_integration_automation/examples/account_integration.tf +++ b/src/integrations/aws_integration_automation/examples/single_account/account_integration.tf @@ -14,12 +14,12 @@ terraform { # Configure the AWS Provider provider "aws" { - region = "us-east-1" + region = var.aws_region } # Single Account Integration Module module "jit_aws_account_integration" { - source = "../" + source = "../../" # Jit API Configuration jit_client_id = var.jit_client_id # Set via environment variable or terraform.tfvars @@ -28,26 +28,13 @@ module "jit_aws_account_integration" { # Integration Configuration integration_type = "account" - aws_regions_to_monitor = ["us-east-1", "us-west-2"] + aws_regions_to_monitor = var.regions_to_monitor # Stack Configuration stack_name = "JitAccountIntegration" - account_name = "Production Account" # Optional: Display name in Jit platform - resource_name_prefix = "JitProd" # Optional: Prefix for CloudFormation resources + account_name = var.account_name # Optional: Display name in Jit platform + resource_name_prefix = var.resource_name_prefix # Optional: Prefix for CloudFormation resources # CloudFormation Configuration capabilities = ["CAPABILITY_NAMED_IAM"] } - -# Variables that should be defined in your root module or terraform.tfvars -variable "jit_client_id" { - description = "Jit API Client ID" - type = string - sensitive = true -} - -variable "jit_secret" { - description = "Jit API Secret" - type = string - sensitive = true -} diff --git a/src/integrations/aws_integration_automation/examples/single_account/variables.tf b/src/integrations/aws_integration_automation/examples/single_account/variables.tf new file mode 100644 index 0000000..e5f5879 --- /dev/null +++ b/src/integrations/aws_integration_automation/examples/single_account/variables.tf @@ -0,0 +1,37 @@ + +# Variables that should be defined in your root module or terraform.tfvars +variable "jit_client_id" { + description = "Jit API Client ID" + type = string + sensitive = true +} + +variable "jit_secret" { + description = "Jit API Secret" + type = string + sensitive = true +} + +variable "regions_to_monitor" { + description = "AWS regions to monitor using Jit" + type = list(string) + default = ["us-east-1", "us-west-2"] +} + +variable "aws_region" { + description = "AWS region to deploy the integration to" + type = string + default = "us-east-1" +} + +variable "account_name" { + description = "Name of the account to monitor" + type = string + default = "Production Account" +} + +variable "resource_name_prefix" { + description = "Prefix for the resource name" + type = string + default = "JitProd" +} \ No newline at end of file From 5f25b55cff2bdb65a818b867d244179eea23a0f8 Mon Sep 17 00:00:00 2001 From: arielb135 Date: Sun, 8 Jun 2025 15:25:13 +0300 Subject: [PATCH 5/7] Refactor AWS CloudFormation integration for organization deployment - Changed resource type from `aws_cloudformation_stack_set` to `aws_cloudformation_stack` for organization integration. - Removed auto deployment configuration and permission model settings that were previously associated with the StackSet. - Updated example Terraform configurations to include new capabilities for CloudFormation. - Added new `terraform.tfvars` files for organization and single account examples, including JIT API credentials and organizational settings. --- .../organization_integration.tf | 2 +- .../aws_organization/terraform.tfvars | 11 +++++++ .../examples/single_account/terraform.tfvars | 5 +++ .../aws_integration_automation/main.tf | 31 ++----------------- 4 files changed, 20 insertions(+), 29 deletions(-) create mode 100644 src/integrations/aws_integration_automation/examples/aws_organization/terraform.tfvars create mode 100644 src/integrations/aws_integration_automation/examples/single_account/terraform.tfvars diff --git a/src/integrations/aws_integration_automation/examples/aws_organization/organization_integration.tf b/src/integrations/aws_integration_automation/examples/aws_organization/organization_integration.tf index 50e68a3..5e409ee 100644 --- a/src/integrations/aws_integration_automation/examples/aws_organization/organization_integration.tf +++ b/src/integrations/aws_integration_automation/examples/aws_organization/organization_integration.tf @@ -39,5 +39,5 @@ module "jit_aws_org_integration" { resource_name_prefix = "JitOrg" # Optional: Prefix for CloudFormation resources # CloudFormation Configuration - capabilities = ["CAPABILITY_NAMED_IAM"] + capabilities = ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM", "CAPABILITY_AUTO_EXPAND"] } diff --git a/src/integrations/aws_integration_automation/examples/aws_organization/terraform.tfvars b/src/integrations/aws_integration_automation/examples/aws_organization/terraform.tfvars new file mode 100644 index 0000000..35baae7 --- /dev/null +++ b/src/integrations/aws_integration_automation/examples/aws_organization/terraform.tfvars @@ -0,0 +1,11 @@ +# JIT API Credentials +# Follow the guide here - https://docs.jit.io/reference/credentials +# Create creds using "Engineering Manager" role +jit_client_id = "JIT_API_KEY_CLIENT_ID" +jit_secret = "JIT_API_KEY_SECRET" + +# Should manage also the root account in Jit (false to avoid it) +should_include_root_account = true + +# The organization's root ID - can be obtained under AWS Organizations -> AWS Accounts +organization_root_id = "r-xxxx" diff --git a/src/integrations/aws_integration_automation/examples/single_account/terraform.tfvars b/src/integrations/aws_integration_automation/examples/single_account/terraform.tfvars new file mode 100644 index 0000000..08b279b --- /dev/null +++ b/src/integrations/aws_integration_automation/examples/single_account/terraform.tfvars @@ -0,0 +1,5 @@ +# JIT API Credentials +# Follow the guide here - https://docs.jit.io/reference/credentials +# Create creds using "Engineering Manager" role +jit_client_id = "JIT_API_KEY_CLIENT_ID" +jit_secret = "JIT_API_KEY_SECRET" diff --git a/src/integrations/aws_integration_automation/main.tf b/src/integrations/aws_integration_automation/main.tf index 060f886..c994ee3 100644 --- a/src/integrations/aws_integration_automation/main.tf +++ b/src/integrations/aws_integration_automation/main.tf @@ -77,8 +77,8 @@ resource "aws_cloudformation_stack" "jit_integration_account" { ] } -# CloudFormation StackSet for organization integration -resource "aws_cloudformation_stack_set" "jit_integration_org" { +# CloudFormation Stack for organization integration +resource "aws_cloudformation_stack" "jit_integration_org" { count = var.integration_type == "org" ? 1 : 0 name = var.stack_name @@ -92,15 +92,6 @@ resource "aws_cloudformation_stack_set" "jit_integration_org" { "ShouldIncludeRootAccount" = tostring(var.should_include_root_account) } - # Auto deployment configuration for organization - auto_deployment { - enabled = true - retain_stacks_on_account_removal = false - } - - # Permission model for organization deployment - permission_model = "SERVICE_MANAGED" - lifecycle { prevent_destroy = true } @@ -111,20 +102,4 @@ resource "aws_cloudformation_stack_set" "jit_integration_org" { ] } -# StackSet instances for organization integration (deploys to all accounts in org) -resource "aws_cloudformation_stack_set_instance" "jit_integration_org_instance" { - count = var.integration_type == "org" ? 1 : 0 - - stack_set_name = aws_cloudformation_stack_set.jit_integration_org[0].name - deployment_targets { - organizational_unit_ids = [var.organization_root_id] - } - - operation_preferences { - region_concurrency_type = "PARALLEL" - max_concurrent_percentage = 100 - failure_tolerance_percentage = 10 - } - - depends_on = [aws_cloudformation_stack_set.jit_integration_org] -} + From 047c5d097d3de813d7be6055df06967ed05146e3 Mon Sep 17 00:00:00 2001 From: arielb135 Date: Sun, 8 Jun 2025 15:37:03 +0300 Subject: [PATCH 6/7] Update AWS integration examples with variable configurations - Changed `resource_name_prefix` in `organization_integration.tf` to use a variable instead of a hardcoded value. - Added new variables for `regions_to_monitor`, `aws_region`, and `resource_name_prefix` in `variables.tf` for both organization and single account examples. - Updated `terraform.tfvars` files to include the new variables and their default values for better configurability. --- .../aws_organization/organization_integration.tf | 2 +- .../examples/aws_organization/terraform.tfvars | 9 +++++++++ .../examples/aws_organization/variables.tf | 8 ++++++-- .../examples/single_account/terraform.tfvars | 12 ++++++++++++ .../examples/single_account/variables.tf | 3 --- 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/integrations/aws_integration_automation/examples/aws_organization/organization_integration.tf b/src/integrations/aws_integration_automation/examples/aws_organization/organization_integration.tf index 5e409ee..daf0913 100644 --- a/src/integrations/aws_integration_automation/examples/aws_organization/organization_integration.tf +++ b/src/integrations/aws_integration_automation/examples/aws_organization/organization_integration.tf @@ -36,7 +36,7 @@ module "jit_aws_org_integration" { # Stack Configuration stack_name = "JitOrgIntegration" - resource_name_prefix = "JitOrg" # Optional: Prefix for CloudFormation resources + resource_name_prefix = var.resource_name_prefix # Optional: Prefix for CloudFormation resources # CloudFormation Configuration capabilities = ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM", "CAPABILITY_AUTO_EXPAND"] diff --git a/src/integrations/aws_integration_automation/examples/aws_organization/terraform.tfvars b/src/integrations/aws_integration_automation/examples/aws_organization/terraform.tfvars index 35baae7..1a92548 100644 --- a/src/integrations/aws_integration_automation/examples/aws_organization/terraform.tfvars +++ b/src/integrations/aws_integration_automation/examples/aws_organization/terraform.tfvars @@ -9,3 +9,12 @@ should_include_root_account = true # The organization's root ID - can be obtained under AWS Organizations -> AWS Accounts organization_root_id = "r-xxxx" + +# AWS regions to monitor using Jit +regions_to_monitor = ["us-east-1", "us-west-2"] + +# AWS region to deploy the integration to +aws_region = "us-east-1" + +# Prefix for the resource name +resource_name_prefix = "JitOrg" \ No newline at end of file diff --git a/src/integrations/aws_integration_automation/examples/aws_organization/variables.tf b/src/integrations/aws_integration_automation/examples/aws_organization/variables.tf index d33ef91..2512c5a 100644 --- a/src/integrations/aws_integration_automation/examples/aws_organization/variables.tf +++ b/src/integrations/aws_integration_automation/examples/aws_organization/variables.tf @@ -27,11 +27,15 @@ variable "should_include_root_account" { variable "regions_to_monitor" { description = "AWS regions to monitor using Jit" type = list(string) - default = ["us-east-1", "us-west-2"] } variable "aws_region" { description = "AWS region to deploy the integration to" type = string - default = "us-east-1" +} + +variable "resource_name_prefix" { + description = "Prefix for the resource name" + type = string + default = "JitOrg" } \ No newline at end of file diff --git a/src/integrations/aws_integration_automation/examples/single_account/terraform.tfvars b/src/integrations/aws_integration_automation/examples/single_account/terraform.tfvars index 08b279b..e816caa 100644 --- a/src/integrations/aws_integration_automation/examples/single_account/terraform.tfvars +++ b/src/integrations/aws_integration_automation/examples/single_account/terraform.tfvars @@ -3,3 +3,15 @@ # Create creds using "Engineering Manager" role jit_client_id = "JIT_API_KEY_CLIENT_ID" jit_secret = "JIT_API_KEY_SECRET" + +# AWS regions to monitor using Jit +regions_to_monitor = ["us-east-1", "us-west-2"] + +# AWS region to deploy the integration to +aws_region = "us-east-1" + +# Prefix for the resource name +resource_name_prefix = "JitProd" + +# Name of the account to monitor +account_name = "My AWS Account" \ No newline at end of file diff --git a/src/integrations/aws_integration_automation/examples/single_account/variables.tf b/src/integrations/aws_integration_automation/examples/single_account/variables.tf index e5f5879..466138b 100644 --- a/src/integrations/aws_integration_automation/examples/single_account/variables.tf +++ b/src/integrations/aws_integration_automation/examples/single_account/variables.tf @@ -15,19 +15,16 @@ variable "jit_secret" { variable "regions_to_monitor" { description = "AWS regions to monitor using Jit" type = list(string) - default = ["us-east-1", "us-west-2"] } variable "aws_region" { description = "AWS region to deploy the integration to" type = string - default = "us-east-1" } variable "account_name" { description = "Name of the account to monitor" type = string - default = "Production Account" } variable "resource_name_prefix" { From 0170e261282968ff0f6c12ba40b2ce72b4a1144b Mon Sep 17 00:00:00 2001 From: arielb135 Date: Sun, 8 Jun 2025 15:54:43 +0300 Subject: [PATCH 7/7] Enhance AWS integration documentation and examples - Updated README.md to clarify the organization integration process, specifying that it creates a CloudFormation stack with internal StackSets. - Revised module names in examples for better clarity, changing `jit_aws_integration` to `jit_aws_account_integration`. - Added detailed explanations for required CloudFormation capabilities for both single account and organization integrations. - Improved validation notes for parameters and added comprehensive examples for both integration types, ensuring users have clear guidance on usage. --- .../aws_integration_automation/README.md | 169 +++++++++++------- 1 file changed, 106 insertions(+), 63 deletions(-) diff --git a/src/integrations/aws_integration_automation/README.md b/src/integrations/aws_integration_automation/README.md index 9461f15..a2933ab 100644 --- a/src/integrations/aws_integration_automation/README.md +++ b/src/integrations/aws_integration_automation/README.md @@ -5,12 +5,9 @@ A Terraform module for automating AWS integration with JIT (Just-in-Time) securi ## Features - **Dual Integration Types**: Support for both single account and organization-wide deployments -- **Native Terraform**: Pure Terraform implementation using `data "http"` resources -- **Create-Only State Token**: Implements proper create-only behavior for JIT oauth/state-token using Terraform state - **Multi-Region Support**: Monitor multiple AWS regions simultaneously - **US/EU API Support**: Compatible with both US and EU JIT API endpoints - **Error Handling**: Built-in validation and error handling with postconditions -- **State Management**: Pure Terraform state management without local file dependencies ## Integration Types @@ -18,7 +15,7 @@ A Terraform module for automating AWS integration with JIT (Just-in-Time) securi Deploys JIT integration to a single AWS account using CloudFormation stack. ### Organization Integration -Deploys JIT integration across an entire AWS Organization using CloudFormation StackSet, automatically including all current and future accounts in the organization. +Deploys JIT integration across an entire AWS Organization using a CloudFormation stack that creates internal StackSets, automatically including all current and future accounts in the organization. ## Prerequisites @@ -32,29 +29,27 @@ Deploys JIT integration across an entire AWS Organization using CloudFormation S ### Single Account Integration ```hcl -module "jit_aws_integration" { +module "jit_aws_account_integration" { source = "path/to/aws_integration_automation" # JIT Configuration jit_client_id = var.jit_client_id jit_secret = var.jit_secret - jit_region = "us" + jit_region = "us" # Use "eu" for European API endpoint # Integration Type integration_type = "account" - # AWS Regions to Monitor + # AWS Configuration aws_regions_to_monitor = ["us-east-1", "us-west-2"] - # Optional Configuration + # Stack Configuration stack_name = "JitAccountIntegration" account_name = "Production Account" resource_name_prefix = "JitProd" - tags = { - Environment = "production" - Owner = "security-team" - } + # CloudFormation Configuration + capabilities = ["CAPABILITY_NAMED_IAM"] } ``` @@ -67,7 +62,7 @@ module "jit_aws_org_integration" { # JIT Configuration jit_client_id = var.jit_client_id jit_secret = var.jit_secret - jit_region = "us" + jit_region = "us" # Use "eu" for European API endpoint # Integration Type integration_type = "org" @@ -76,11 +71,15 @@ module "jit_aws_org_integration" { organization_root_id = "r-xxxxxxxxxxxx" should_include_root_account = true - # AWS Regions to Monitor + # AWS Configuration aws_regions_to_monitor = ["us-east-1", "us-west-2", "eu-west-1"] - # Optional Configuration + # Stack Configuration + stack_name = "JitOrgIntegration" resource_name_prefix = "JitOrg" + + # CloudFormation Configuration + capabilities = ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM", "CAPABILITY_AUTO_EXPAND"] } ``` @@ -93,55 +92,36 @@ module "jit_aws_org_integration" { | `integration_type` | Type of AWS integration (`account` or `org`) | `string` | n/a | yes | | `jit_region` | Jit API region (`us` or `eu`) | `string` | `"us"` | no | | `aws_regions_to_monitor` | List of AWS regions to monitor | `list(string)` | `["us-east-1"]` | no | -| `stack_name` | Name for the CloudFormation stack or stackset | `string` | `"JitIntegrationStack"` | no | +| `stack_name` | Name for the CloudFormation stack | `string` | `"JitIntegrationStack"` | no | | `account_name` | Optional account name alias for Jit platform | `string` | `""` | no | -| `resource_name_prefix` | Prefix for CloudFormation resources | `string` | `null` (auto: "Jit" for account, "JitOrg" for org) | no | -| `organization_root_id` | AWS Organization Root ID (required for org type) | `string` | `""` | no | +| `resource_name_prefix` | Prefix for CloudFormation resources (1-40 chars, alphanumeric, hyphens, underscores) | `string` | `null` (auto: "Jit" for account, "JitOrg" for org) | no | +| `organization_root_id` | AWS Organization Root ID (required for org type, format: `r-xxxxxxxxxx`) | `string` | `""` | no | | `should_include_root_account` | Include root account in organization integration | `bool` | `false` | no | | `capabilities` | CloudFormation capabilities required | `list(string)` | `["CAPABILITY_NAMED_IAM"]` | no | -| `tags` | Tags to apply to CloudFormation resources | `map(string)` | `{}` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| `integration_type` | Type of integration deployed | -| `jit_api_endpoint` | JIT API endpoint used | -| `cloudformation_template_url` | CloudFormation template URL used | -| `stack_name` | CloudFormation stack/stackset name | -| `account_name` | Account name alias used in Jit platform | -| `resource_name_prefix` | Prefix used for CloudFormation resources | -| `aws_regions_monitored` | List of AWS regions being monitored | -| `cloudformation_stack_id` | Stack ID (account integration only) | -| `cloudformation_stack_arn` | Stack ARN (account integration only) | -| `cloudformation_stackset_id` | StackSet ID (organization integration only) | -| `cloudformation_stackset_arn` | StackSet ARN (organization integration only) | -| `organization_root_id` | Organization Root ID (organization integration only) | -| `state_token_created` | Whether a new state token was created | -| `state_token_flag_created` | Whether state token flag is initialized in Terraform state | -| `state_token_stored` | Whether state token is stored in Terraform state | ## State Token Management -This module implements a **create-only** behavior for the JIT oauth/state-token endpoint using Terraform state management: +This module implements a **create-only** behavior for the JIT oauth/state-token endpoint using the REST API provider: -1. **First Run**: Creates a new state token and stores it in Terraform state using `terraform_data` resource +1. **First Run**: Creates a new state token via JIT API 2. **Subsequent Runs**: Reuses the existing state token from Terraform state -3. **No Updates**: The state token is never updated or regenerated unless the Terraform state is manually modified +3. **No Updates**: The state token is never updated or regenerated unless explicitly recreated ### State Token Implementation -The module uses three key resources for state token management: +The module uses the `restapi_object` resource for state token management: -- `terraform_data.state_token_flag`: Tracks whether a token has been created -- `data.http.jit_state_token`: Only executes when the flag indicates first creation -- `terraform_data.state_token_storage`: Stores the actual token value in Terraform state +- Creates state token via JIT API endpoint `/oauth/state-token` +- Stores token in Terraform state automatically +- Uses `ignore_changes` lifecycle rule to prevent updates ### Important Notes -- State token is managed entirely within Terraform state - no local files required +- State token is managed entirely within Terraform state - Token persists across Terraform runs and is only created once -- To regenerate a state token, you must manually modify or destroy the relevant Terraform state resources +- To regenerate a state token, you must manually destroy and recreate the `restapi_object.jit_state_token` resource along with the created AWS stack. +- **External ID Persistence**: The state token (external_id) should be created only once. Changing AWS regions, account configurations, or other integration parameters will not affect the existing integration's configuration or regenerate the token +- **External ID Uniqueness**: The external_id is generated by JIT and cannot be reused across integrations. After a successful integration, changing or regenerating the external_id value will cause issues with the existing integration and may break the connection between JIT and your AWS environment ## CloudFormation Templates @@ -150,6 +130,23 @@ The module automatically selects the appropriate CloudFormation template: - **Account Integration**: `https://jit-aws-prod.s3.amazonaws.com/jit_aws_integration_stack.json` - **Organization Integration**: `https://jit-aws-prod.s3.amazonaws.com/jit_aws_org_integration_stack.json` +## Required Capabilities + +### Single Account Integration +```terraform +capabilities = ["CAPABILITY_NAMED_IAM"] +``` + +### Organization Integration +```terraform +capabilities = ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM", "CAPABILITY_AUTO_EXPAND"] +``` + +The organization integration requires additional capabilities because: +- `CAPABILITY_AUTO_EXPAND`: For creating nested stacks and StackSets within the CloudFormation template +- `CAPABILITY_IAM`: For creating IAM resources +- `CAPABILITY_NAMED_IAM`: For creating IAM resources with custom names + ## Resource Name Prefix The `resource_name_prefix` parameter controls the prefix used for CloudFormation resources: @@ -157,6 +154,7 @@ The `resource_name_prefix` parameter controls the prefix used for CloudFormation - **Default for Account Integration**: "Jit" - **Default for Organization Integration**: "JitOrg" - **Custom Prefix**: Provide your own prefix (1-40 characters, alphanumeric, hyphens, underscores only) +- **Validation**: The module validates the prefix format automatically ## API Endpoints @@ -169,21 +167,62 @@ The module supports both JIT API regions: The module includes comprehensive error handling: -- **Authentication Validation**: Ensures JIT API authentication succeeds -- **State Token Validation**: Verifies state token creation -- **Input Validation**: Validates required parameters and formats +- **Authentication Validation**: Ensures JIT API authentication succeeds with postconditions +- **Input Validation**: Validates required parameters and formats using Terraform validation blocks +- **Integration Type Validation**: Ensures integration_type is either "account" or "org" +- **Region Validation**: Ensures jit_region is either "us" or "eu" +- **Organization Root ID Validation**: Validates proper format for organization root IDs - **Lifecycle Management**: Prevents accidental destruction of resources -## Examples +## Complete Working Examples + +The `examples/` directory contains complete working examples organized by integration type: + +### Single Account Integration +- **Directory**: [`examples/single_account/`](examples/single_account/) +- **Main File**: `account_integration.tf` +- **Variables**: `variables.tf` +- **Configuration**: `terraform.tfvars` + +To use the single account example: +```bash +cd examples/single_account/ +cp terraform.tfvars.example terraform.tfvars +# Edit terraform.tfvars with your values +terraform init +terraform plan +terraform apply +``` + +### Organization Integration +- **Directory**: [`examples/aws_organization/`](examples/aws_organization/) +- **Main File**: `organization_integration.tf` +- **Variables**: `variables.tf` +- **Configuration**: `terraform.tfvars` + +To use the organization example: +```bash +cd examples/aws_organization/ +cp terraform.tfvars.example terraform.tfvars +# Edit terraform.tfvars with your values +terraform init +terraform plan +terraform apply +``` + +## How Organization Integration Works + +The organization integration creates a **single CloudFormation stack** that internally: -See the `examples/` directory for complete working examples: +1. **Creates an IAM Role** for JIT to assume across the organization +2. **Creates a StackSet** (`JitOrganizationsStacksSetRocket`) that automatically deploys to all accounts in the organization +3. **Optionally creates a stack** in the root account if `should_include_root_account = true` -- [`examples/account_integration.tf`](examples/account_integration.tf) - Single account integration -- [`examples/organization_integration.tf`](examples/organization_integration.tf) - Organization integration +The CloudFormation template handles the StackSet creation and deployment automatically using AWS Organizations' `SERVICE_MANAGED` permission model. ## Security Considerations -1. **Sensitive Variables**: `jit_client_id` and `jit_secret` are marked as sensitive +1. **Sensitive Variables**: `jit_client_id`, `jit_secret`, and `organization_root_id` are marked as sensitive 2. **State Files**: Ensure Terraform state files are stored securely (e.g., S3 with encryption) 3. **State Token**: Stored in Terraform state - secure your state backend appropriately 4. **IAM Permissions**: Follow principle of least privilege for AWS IAM permissions @@ -200,17 +239,20 @@ See the `examples/` directory for complete working examples: - Ensure the organization root ID is in the correct format (`r-xxxxxxxxxxxx`) - Verify AWS Organizations is enabled and accessible -3. **State Token Issues** - - Check Terraform state for `terraform_data.state_token_storage` resource - - Use `terraform state show` to inspect state token resources +3. **CloudFormation Capabilities Error** + - For organization integration, ensure you have all three capabilities: `["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM", "CAPABILITY_AUTO_EXPAND"]` + - For single account integration, use: `["CAPABILITY_NAMED_IAM"]` -4. **CloudFormation Failures** +4. **CloudFormation Stack Errors** - Verify AWS permissions for CloudFormation operations - Check CloudFormation events in the AWS console for detailed error messages + - For organization integration, ensure AWS Organizations service is properly configured 5. **Parameter Validation Errors** - - Verify `resource_name_prefix` meets length and character requirements - - Check that `organization_root_id` follows the correct pattern for org integration + - Verify `resource_name_prefix` meets length and character requirements (1-40 chars, alphanumeric, hyphens, underscores) + - Check that `organization_root_id` follows the correct pattern (`r-` followed by 4-32 alphanumeric characters) + - Ensure `integration_type` is exactly "account" or "org" + - Ensure `jit_region` is exactly "us" or "eu" ### Debug Information @@ -229,11 +271,12 @@ terraform apply | aws | >= 5.0 | | http | >= 3.0 | | local | >= 2.0 | +| restapi | >= 1.19.1 | ## Contributing 1. Follow existing code patterns and documentation standards -2. Test changes with both integration types +2. Test changes with both integration types using the provided examples 3. Update examples and documentation as needed 4. Ensure all variables have proper validation where applicable