Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ terraform.rc

# Mac Detrtius
.DS_Store
*.zip
351 changes: 39 additions & 312 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/aws/complete-account-onboarding/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ terraform {
required_providers {
aembit = {
source = "aembit/aembit"
version = ">= 1.23.0"
version = "~> 1.25.0"
}
aws = {
source = "hashicorp/aws"
Expand Down
78 changes: 78 additions & 0 deletions examples/aws/ecs-container-salesforce/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Lambda Container Using Aembit to Access Snowflake
Configuration in this directory creates an Aembit Access Policy and AWS Lambda Container that uses the Aembit Lambda Extension to provided dynamic authentication to Snowflake. The function runs a simple Python script that executes a Snowflake query that is pulled from the `SNOWFLAKE_QUERY` environment variable.

## Usage
1. Ensure your [AWS](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration) and [Aembit](https://registry.terraform.io/providers/Aembit/aembit/latest/docs) provider credentials are configured correctly
2. Configure required variables
3. Run `terraform init` and `terraform apply`
4. After the terraform apply completes, update the Callback URL in your Salesforce app to match the Callback URL field of the Aembit Credential Provider
5. Review ECS service logs for successful query output.

<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.5 |
| <a name="requirement_aembit"></a> [aembit](#requirement\_aembit) | ~> 1.25.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.0 |
| <a name="requirement_null"></a> [null](#requirement\_null) | >= 3.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aembit"></a> [aembit](#provider\_aembit) | 1.24.0 |
| <a name="provider_aws"></a> [aws](#provider\_aws) | 6.18.0 |
| <a name="provider_null"></a> [null](#provider\_null) | 3.2.4 |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_aembit-ecs"></a> [aembit-ecs](#module\_aembit-ecs) | Aembit/ecs/aembit | 1.22.1 |
| <a name="module_aembit_ecs_container"></a> [aembit\_ecs\_container](#module\_aembit\_ecs\_container) | ../../../ | n/a |

## Resources

| Name | Type |
|------|------|
| [aembit_agent_controller.ecs](https://registry.terraform.io/providers/aembit/aembit/latest/docs/resources/agent_controller) | resource |
| [aembit_server_workload.salesforce](https://registry.terraform.io/providers/aembit/aembit/latest/docs/resources/server_workload) | resource |
| [aembit_trust_provider.ecs](https://registry.terraform.io/providers/aembit/aembit/latest/docs/resources/trust_provider) | resource |
| [aws_cloudwatch_log_group.salesforce_client](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_ecr_repository.example](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository) | resource |
| [aws_ecs_cluster.example](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster) | resource |
| [aws_ecs_service.salesforce_client](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) | resource |
| [aws_ecs_task_definition.salesforce_client](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) | resource |
| [aws_iam_role.iam_for_ecs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy_attachment.ecs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_security_group.controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.ecs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_vpc_security_group_egress_rule.all_ipv4](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource |
| [aws_vpc_security_group_egress_rule.controller_all_ipv4](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource |
| [aws_vpc_security_group_ingress_rule.controller_http_ipv4](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource |
| [aws_vpc_security_group_ingress_rule.controller_https_ipv4](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource |
| [null_resource.build](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_vpc.selected](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_aembit_agent_log_level"></a> [aembit\_agent\_log\_level](#input\_aembit\_agent\_log\_level) | Log level of Aembit agent proxy Lambda extension. | `string` | `"info"` | no |
| <a name="input_aembit_tenant_id"></a> [aembit\_tenant\_id](#input\_aembit\_tenant\_id) | ID of Aembit tenant. | `string` | n/a | yes |
| <a name="input_aws_account_id"></a> [aws\_account\_id](#input\_aws\_account\_id) | ID of AWS where Aembit edge components will be deployed. | `string` | n/a | yes |
| <a name="input_aws_region"></a> [aws\_region](#input\_aws\_region) | AWS region where Aembit edge components will be deployed. | `string` | n/a | yes |
| <a name="input_salesforce_client_id"></a> [salesforce\_client\_id](#input\_salesforce\_client\_id) | Client ID of Salesforce Oauth app. | `string` | n/a | yes |
| <a name="input_salesforce_client_secret"></a> [salesforce\_client\_secret](#input\_salesforce\_client\_secret) | Client secret of Salesforce Oauth app. | `string` | n/a | yes |
| <a name="input_salesforce_host"></a> [salesforce\_host](#input\_salesforce\_host) | FQDN of Salesforce instance. | `string` | n/a | yes |
| <a name="input_salesforce_oauth_scopes"></a> [salesforce\_oauth\_scopes](#input\_salesforce\_oauth\_scopes) | Set of Salesforce Oauth scopes for Credential Provider. | `string` | `"full offline_access refresh_token openid"` | no |
| <a name="input_subnet_ids"></a> [subnet\_ids](#input\_subnet\_ids) | List of subnet IDs where Aembit edge components will be deployed. | `set(string)` | n/a | yes |
| <a name="input_vpc_id"></a> [vpc\_id](#input\_vpc\_id) | ID of AWS VPC where Aembit edge components will be deployed. | `string` | n/a | yes |

## Outputs

No outputs.
<!-- END_TF_DOCS -->
12 changes: 12 additions & 0 deletions examples/aws/ecs-container-salesforce/container_build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#! /bin/bash

docker_host="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
curl -o src/aembit_root.crt "https://${AEMBIT_TENANT_ID}.aembit.io/api/v1/root-ca"

(cd src && docker build --build-arg AEMBIT_TENANT_ID="${AEMBIT_TENANT_ID}" --platform linux/amd64 --provenance=false -t "${IMAGE_NAME}" .)

aws ecr get-login-password --region "${AWS_REGION}" | docker login --username AWS --password-stdin "${docker_host}"
docker tag "${IMAGE_NAME}:latest" "${docker_host}/${IMAGE_NAME}:latest"
docker push "${docker_host}/${IMAGE_NAME}:latest"

rm src/aembit_root.crt
207 changes: 207 additions & 0 deletions examples/aws/ecs-container-salesforce/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
locals {
ecr_address = format("%v.dkr.ecr.%v.amazonaws.com", var.aws_account_id, var.aws_region)
ecr_image_uri = format("%v/%v:%v", local.ecr_address, aws_ecr_repository.example.id, local.image_tag)
image_tag = "latest"
container_source_hash = base64sha256(join("", [for f in fileset("src", "*") : filebase64sha256("src/${f}")]))
name = "ex-${basename(path.cwd)}"
}


# Create Aembit Server Workload for the salesforce SDK
resource "aembit_server_workload" "salesforce" {
name = var.salesforce_host
description = var.salesforce_host
is_active = true
service_endpoint = {
app_protocol = "HTTP"
host = var.salesforce_host
port = 443
requested_port = 443
tls = true
tls_verification = "full"
requested_tls = true
transport_protocol = "TCP"
authentication_config = {
method = "HTTP Authentication"
scheme = "Bearer"
}
}
}

# Create example ECS task and associated AWS resources
data "aws_iam_policy_document" "assume_role" {
statement {
effect = "Allow"

principals {
type = "Service"
identifiers = ["ecs-tasks.amazonaws.com"]
}

condition {
test = "StringEquals"
variable = "aws:SourceAccount"
values = [var.aws_account_id]
}

actions = ["sts:AssumeRole"]
}
}

resource "aws_iam_role_policy_attachment" "ecs" {
role = aws_iam_role.iam_for_ecs.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}

resource "aws_iam_role" "iam_for_ecs" {
name = local.name
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}

resource "aws_ecr_repository" "example" {
name = local.name
image_tag_mutability = "MUTABLE"
}

resource "null_resource" "build" {
triggers = {
source_hash = local.container_source_hash
}

provisioner "local-exec" {
command = "bash ${path.module}/container_build.sh"
environment = {
"AWS_ACCOUNT_ID" = var.aws_account_id
"AWS_REGION" = var.aws_region
"AEMBIT_TENANT_ID" = var.aembit_tenant_id
"IMAGE_NAME" = local.name
}
}
}

resource "aws_security_group" "ecs" {
name_prefix = local.name
description = "Allow all outbound traffic for ECS task."
vpc_id = var.vpc_id
}

resource "aws_vpc_security_group_egress_rule" "all_ipv4" {
security_group_id = aws_security_group.ecs.id
cidr_ipv4 = "0.0.0.0/0"
ip_protocol = "-1" # semantically equivalent to all ports and protocols
}

# Log Group Resource (Optional)
resource "aws_cloudwatch_log_group" "salesforce_client" {

name = "ecs-${local.name}"
retention_in_days = 30
}

##########################################################################################
# AgentController Task & Service
resource "aws_ecs_task_definition" "salesforce_client" {
depends_on = [null_resource.build]
family = local.name
container_definitions = jsonencode([
jsondecode(module.aembit-ecs.agent_proxy_container),
{
name = local.name
image = local.ecr_image_uri
essential = true
# dependsOn = [
# {
# containerName = jsondecode(module.aembit-ecs.agent_proxy_container).name
# condition = "HEALTHY" # proxy must define a healthCheck
# }
# ]
environment = [
{ "name" : "http_proxy", "value" : module.aembit-ecs.aembit_http_proxy },
{ "name" : "https_proxy", "value" : module.aembit-ecs.aembit_http_proxy },
{ "name" : "REQUESTS_CA_BUNDLE", "value" : "/etc/ssl/certs/ca-certificates.crt" },
{ "name" : "SF_INSTANCE_URL", "value" : "${var.salesforce_host}" }
]
logConfiguration = {
logDriver = "awslogs"
options = {
awslogs-group = aws_cloudwatch_log_group.salesforce_client.name
awslogs-region = var.aws_region
awslogs-stream-prefix = local.name
}
}
}])
task_role_arn = aws_iam_role.iam_for_ecs.arn
execution_role_arn = aws_iam_role.iam_for_ecs.arn
cpu = 256
memory = 512
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
runtime_platform {
operating_system_family = "LINUX"
cpu_architecture = "X86_64"
}
}

resource "aws_ecs_service" "salesforce_client" {
name = local.name
desired_count = 1
launch_type = "FARGATE"
cluster = aws_ecs_cluster.example.id
task_definition = aws_ecs_task_definition.salesforce_client.arn
enable_execute_command = true

network_configuration {
assign_public_ip = true
subnets = var.subnet_ids
security_groups = [aws_security_group.ecs.id]
}
}


# Create Aembit access policy for ECS task
module "aembit_ecs_container" {
depends_on = [aembit_server_workload.salesforce]
source = "../../../"
create_client_workload = true
create_trust_providers = true
create_credential_providers = true
client_workload_identifiers = [
{
type = "awsEcsTaskFamily"
value = local.name
}
]
access_policies = {
salesforce = {
is_active = true
server_workload_name = var.salesforce_host
credential_provider_name = "salesforce"
}
}
trust_providers = {
aws_role = {
type = "aws_role"
aws_role = {
account_id = var.aws_account_id
role_arn = "arn:aws:sts::${var.aws_account_id}:assumed-role/${aws_iam_role.iam_for_ecs.name}/*"
}
}
}
client_workload_name = local.name
credential_providers = {
salesforce = {
is_active = true
type = "oauth_authorization_code"
oauth_authorization_code = {
client_id = var.salesforce_client_id
client_secret = var.salesforce_client_secret
oauth_authorization_url = "https://${var.salesforce_host}/services/oauth2/authorize"
oauth_discovery_url = "https://${var.salesforce_host}/"
oauth_token_url = "https://${var.salesforce_host}/services/oauth2/token"
scopes = var.salesforce_oauth_scopes
is_pkce_required = true
oauth_introspection_url = "https://${var.salesforce_host}/services/oauth2/introspect"
}
}
}
}
Empty file.
71 changes: 71 additions & 0 deletions examples/aws/ecs-container-salesforce/prereqs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Create AWS VPC
data "aws_vpc" "selected" {
id = var.vpc_id
}

# Create AWS ECS cluster to host Aembit Agent Controller
resource "aws_ecs_cluster" "example" {
name = "ecs_aembit_demo"

setting {
name = "containerInsights"
value = "enabled"
}
}

# Create Aembit Agent Controller
resource "aembit_agent_controller" "ecs" {
name = "Agent Controller with ECS Trust Provider"
is_active = true

trust_provider_id = aembit_trust_provider.ecs.id
}

resource "aembit_trust_provider" "ecs" {
name = "AWS Role Trust Provider"
is_active = true
aws_role = {
account_id = var.aws_account_id
}
}

module "aembit-ecs" {
source = "Aembit/ecs/aembit"
version = "1.22.1" # Find the latest version at https://registry.terraform.io/modules/Aembit/ecs/aembit/latest

aembit_tenantid = var.aembit_tenant_id
aembit_agent_controller_id = aembit_agent_controller.ecs.id

ecs_cluster = aws_ecs_cluster.example.id
ecs_vpc_id = var.vpc_id
ecs_subnets = var.subnet_ids
ecs_security_groups = [aws_security_group.controller.id]
}

resource "aws_security_group" "controller" {
name_prefix = "ecs_aembit_controller"
description = "Allow VPC traffic to Aembit controller and outbound internet for controller"
vpc_id = var.vpc_id
}

resource "aws_vpc_security_group_ingress_rule" "controller_https_ipv4" {
security_group_id = aws_security_group.controller.id
cidr_ipv4 = data.aws_vpc.selected.cidr_block
from_port = 443
ip_protocol = "tcp"
to_port = 443
}

resource "aws_vpc_security_group_ingress_rule" "controller_http_ipv4" {
security_group_id = aws_security_group.controller.id
cidr_ipv4 = data.aws_vpc.selected.cidr_block
from_port = 80
ip_protocol = "tcp"
to_port = 80
}

resource "aws_vpc_security_group_egress_rule" "controller_all_ipv4" {
security_group_id = aws_security_group.controller.id
cidr_ipv4 = "0.0.0.0/0"
ip_protocol = "-1" # semantically equivalent to all ports
}
8 changes: 8 additions & 0 deletions examples/aws/ecs-container-salesforce/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
provider "aembit" {
tenant = var.aembit_tenant_id
}

provider "aws" {
region = var.aws_region
allowed_account_ids = [var.aws_account_id]
}
Loading