AWS
Follow this guide to provision Supermetal BYOC (Bring Your Own Cloud), which deploys Supermetal's data replication agents in your own virtual cloud environment, maintaining full control and ownership over your data. This provides an additional layer of security and isolation. Supermetal handles provisioning, operations, and maintenance.
Overview
- Data Sovereignty: Your primary data (being replicated) is processed entirely within your AWS VPC and never leaves your environment to the Supermetal Control Plane.
- Secure Communication: All metadata communication between Supermetal agents in your VPC and the Supermetal Control Plane occurs securely over AWS PrivateLink via an Interface VPC Endpoint. This traffic is encrypted in transit using TLS 1.2+.
- Credential Protection: Sensitive connector credentials provided to Supermetal are encrypted using your AWS KMS Customer Managed Key (
CredentialsKeyArn) before being stored by the Supermetal Control Plane. Only the Supermetal Agents decrypt these credentials at runtime within your environment. Supermetal Control Plane cannot decrypt these credentials. - IAM Security: The cross-account role (
SupermetalControlPlaneRole) assumed by Supermetal utilizes anExternalIdto protect against the "confused deputy" problem. All IAM roles are designed with the principle of least privilege to perform only their required functions.
Prerequisites
To successfully deploy Supermetal BYOC in your AWS environment, please ensure you have the following:
Supermetal Account Identifier
Supermetal account identifier (obtainable from the Supermetal Console under Settings).
SUPERMETAL_ACCOUNT_ID
AWS Account & Permissions
- IAM User or Role with sufficient permissions (e.g.,
AWSAdministratorAccess) to bootstrap the Supermetal BYOC environment using either the CloudFormation or the Terraform.
Deployment Parameters
- AWS Account ID: The AWS Account ID of the target deployment account (e.g.,
123456789012). - AWS Region(s): The AWS Region(s) of the target deployment account (e.g.,
us-east-1,eu-west-2). - VPC ID (
VpcId): The ID of an existing VPC within your chosen AWS Region where the bootstrap infrastructure and Supermetal agents will be deployed to on connector creation. - Subnet IDs (
SubnetIds): A list of (private) subnet IDs within your specifiedVpcId. Supermetal agents will be deployed across these subnets for high availability. Ensure these subnets belong to the same availability zones as your source (and target) databases. - (Optional) KMS Key for Credentials (
CredentialsKeyArn): Decide if you'll use an existing AWS KMS Customer Managed Key (CMK) for encrypting sensitive connector credentials. If you have an existing CMK, have its ARN ready. If left blank, the bootstrap process will create a new KMS key specifically for Supermetal.
Setup
A two phase process, one is to bootstrap the environment and the other is to create a connector to deploy the data plane / agents in your VPC.
Environment Bootstrap
The bootstrap process uses an AWS CloudFormation template or a Terraform script provided by Supermetal to provision the necessary resources, including IAM roles and private endpoint. These IAM roles are used by Supermetal to automatically register your cloud environment with the Supermetal control plane over the private endpoint, and to automatically deploy Supermetal data plane / agents in your VPC.
Environment Bootstrap
The initial environment bootstrap process does not provision any compute resources or the data plane / agents. Once the environment bootstrap is complete, you can create connectors to deploy Supermetal data plane / agents on-demand in your VPC.
You can use the AWS Management Console or the AWS CLI to deploy the bootstrap CloudFormation template.
Obtain CloudFormation Template
- Obtain the latest CloudFormation template file from the Supermetal Console, the template from appendix is for reference.
Deploy CloudFormation Template
Deploy with AWS Management Console
Prepare and Initiate Stack Creation
- Navigate to the AWS CloudFormation console in your AWS account.
- Ensure you are in the correct AWS Region for your deployment.
- Click "Create stack" and select "With new resources (standard)".
- Under "Prerequisite - Prepare template", choose "Template is ready".
- Under "Specify template", select "Upload a template file".
- Click "Choose file" and upload the CloudFormation template file you obtained.
- Click "Next".
Specify Stack Details
- On the "Specify stack details" page, enter a "Stack name" (e.g.,
supermetal-bootstrap). - In the "Parameters" section, provide values for each parameter.
- Click "Next".
Parameters
Refer to the Deployment Parameters section above for detailed explanations of each.
SupermetalAccountIdVpcIdSubnetIdsCredentialsKeyArn
Review and Create
- On the "Review" page, carefully review all your settings and parameter values.
- Scroll to the bottom and find the "Capabilities" section.
- Important: You must check the box that says: "I acknowledge that AWS CloudFormation might create IAM resources with custom names." (The exact wording might vary slightly).
- Click "Create stack" (or "Submit" depending on the AWS console version).
Deploy with AWS CLI
- AWS variables necessary to authenticate. Use either:
AWS_PROFILEAWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEY
- Verify access, run
aws sts get-caller-identity --region <region>. See the AWS CLI reference.
Deploy Stack
Use the aws cloudformation deploy command to create the stack.
aws cloudformation deploy \
--template-file /path/to/your/supermetal-bootstrap-cf-template.yaml \
--stack-name supermetal-bootstrap \
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
--parameter-overrides \
SupermetalAccountId="$SUPERMETAL_ACCOUNT_ID" \
VpcId="$VPC_ID" \
SubnetIds="$SUBNET_IDS" \
CredentialsKeyArn="$CREDENTIALS_KEY_ARN" # Optional: Provide a Customer Managed Key (CMK) ARN for encrypting connector credentialsParameters
Refer to the Deployment Parameters section above for detailed explanations of each.
SUPERMETAL_ACCOUNT_IDVPC_IDSUBNET_IDSCREDENTIALS_KEY_ARN
Verification
- Monitor the CloudFormation stack creation process in the AWS CloudFormation console. It may take several minutes to complete.
- Once the CloudFormation stack status shows
CREATE_COMPLETE, Supermetal's Control Plane automatically performs a registration process for the new environment post stack creation. - Monitor the Environments page in your Supermetal Console. The environment should show up with a "Ready" status within a few minutes after the CloudFormation stack completes.
You can use the Terraform CLI to deploy the bootstrap module.
Obtain Terraform Script
- Obtain the latest Terraform script from the Supermetal Console. The Terraform code in the appendix can be used as a reference or saved as local files (e.g.,
main.tf,variables.tf,outputs.tf).
Configure Terraform
- Ensure you have Terraform installed (version 1.0 or later).
- Set up your AWS credentials for Terraform. This typically involves configuring the AWS CLI and ensuring your environment variables (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN) or AWS profile (AWS_PROFILE) are correctly set. - Verify access by running
aws sts get-caller-identity --region <your-aws-region>. Refer to the AWS provider documentation for more details.
Prepare Terraform Files
- Create a new directory for your Terraform configuration.
- If using the appendix script, save the Terraform code into files within this directory (e.g.,
main.tf). - Create a
terraform.tfvarsfile in the same directory to specify the required variable values. Alternatively, you can pass variables via the command line.
Example terraform.tfvars:
vpc_id = "vpc-xxxxxxxxxxxxxxxxx"
private_subnet_ids = ["subnet-xxxxxxxxxxxxxxxxx", "subnet-yyyyyyyyyyyyyyyyy"]
external_id = "your-supermetal-provided-external-id"
supermetal_control_plane_aws_account_id = "supermetal-aws-account-id" # Provided by Supermetal
supermetal_metadata_service_endpoint_name = "com.amazonaws.vpce.your-region.vpce-svc-xxxxxxxxxxxxxxxxx" # Provided by Supermetal
# credentials_key_arn = "arn:aws:kms:your-region:your-account-id:key/your-key-id" # Optional: Only if using an existing KMS CMKParameters
Refer to the Deployment Parameters section and the variable descriptions within the Terraform script for detailed explanations of each.
vpc_idprivate_subnet_idsexternal_idsupermetal_control_plane_aws_account_idsupermetal_metadata_service_endpoint_namecredentials_key_arn(Optional)
Deploy with Terraform CLI
- Navigate to your Terraform configuration directory in your terminal.
- Initialize Terraform:
terraform init - Review the execution plan:
terraform plan - Apply the configuration:
Confirm the action by typingterraform applyyeswhen prompted.
Variable Input
If you didn't use a .tfvars file, you can pass variables directly with the apply command. Ensure you provide values for all required variables:
terraform apply \
-var="vpc_id=vpc-xxxxxxxxxxxxxxxxx" \
-var="private_subnet_ids=[\"subnet-xxxxxxxxxxxxxxxxx\", \"subnet-yyyyyyyyyyyyyyyyy\"]" \
-var="external_id=YOUR_EXTERNAL_ID_FROM_SUPERMETAL_CONSOLE" \
-var="supermetal_control_plane_aws_account_id=SUPERMETAL_CONTROL_PLANE_AWS_ACCOUNT_ID" \
-var="supermetal_metadata_service_endpoint_name=COM_AMAZONAWS_VPCE_YOUR_REGION_VPCE_SVC_XXXXXXXXXXXXXX" \
# Optional: Provide this if you are using an existing KMS CMK.
# If omitted and 'credentials_key_arn' is not set in a .tfvars file, a new KMS key will be created.
# -var="credentials_key_arn=arn:aws:kms:your-region:your-account-id:key/your-key-id"Replace placeholder values (e.g., vpc-xxxxxxxxxxxxxxxxx, YOUR_EXTERNAL_ID_FROM_SUPERMETAL_CONSOLE) with your actual values.
Verification
- Monitor the
terraform applycommand output. It will indicate when the resources have been successfully created. - Once the Terraform apply is complete, Supermetal's Control Plane automatically performs a registration process for the new environment.
- Monitor the Environments page in your Supermetal Console. The environment should show up with a "Ready" status within a few minutes after Terraform completes.
Create Connector(s) to deploy Supermetal agents
Once the environment bootstrap is complete, we are now ready to create a connector to deploy Supermetal agents in your VPC.
Select source database
Follow the connector setup instructions in the Supermetal Console to select a source database, supermetal can optionally auto discover available source databases.
Create Security Group and Configure Source Database Access
To give Supermetal access to your source database and to validate network reachability and connection credentials, you need to create a security group and configure the source database access.
Create Security Group
Supermetal Console will list pre-filled steps to create the security group and configure the database access as part of the prerequisite steps similar to the steps listed below.
# Create the security group
SG_ID=$(aws ec2 create-security-group \
--description "Security group for Supermetal" \
--vpc-id $VPC_ID \
--output text --query 'GroupId')
# Add required outbound rules
# 1. For metadata VPC endpoint (created during environment bootstrap)
aws ec2 authorize-security-group-egress \
--group-id $SG_ID \
--protocol tcp \
--port 443 \
--source-group $(aws ec2 describe-security-groups \
--filters Name=group-name,Values=supermetal-metadata-sg Name=vpc-id,Values=$VPC_ID \
--query 'SecurityGroups[0].GroupId' --output text)
# 2. For database access
aws ec2 authorize-security-group-egress \
--group-id $SG_ID \
--protocol tcp \
--port $DATABASE_PORT \
--source-group $DATABASE_SECURITY_GROUP_ID
# Add database access rule to your database's security group
aws ec2 authorize-security-group-ingress \
--group-id $DATABASE_SECURITY_GROUP_ID \
--protocol tcp \
--port $DATABASE_PORT \
--source-group $SG_IDParameters
VPC_ID: The VPC ID of your environment. Refer to the Deployment Parameters sectionDATABASE_SECURITY_GROUP_ID: The security group ID of your databaseDATABASE_PORT: The port number your database listens on (e.g., 5432 for PostgreSQL)- The metadata endpoint security group (
supermetal-metadata-sg) is automatically looked up using the VPC ID
Create Security Group and Configure Database Access
- Navigate to the AWS EC2 Console > Security Groups
- Click Create security group
- Configure the basic details:
- Description: "Security group for Supermetal agents "
- VPC: Select the same VPC used for your Supermetal environment
- Leave Inbound rules empty (no rules needed)
- Outbound rules (required):
- Add rule for metadata access:
- Type: HTTPS
- Port: 443
- Destination: Security group named
supermetal-metadata-sg(created during environment bootstrap)
- Add rule for database access:
- Type: Custom TCP
- Port: Your database port
- Destination: Your database's security group
- Add rule for metadata access:
- Click Create security group and note the Security Group ID
Configure Database Access
- Locate your database's security group
- Edit its inbound rules:
- Type: Custom TCP
- Port: Your database port (e.g., 5432 for PostgreSQL)
- Source: Select the
supermetal-agent-sgsecurity group
- Save the rules
CloudFormation Template for Security Groups
Resources:
# Create the Supermetal Agent Security Group
SupermetalAgentSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for Supermetal agents
VpcId: !Ref VpcId
SecurityGroupEgress:
# Required outbound rules
- Description: Allow access to metadata service
IpProtocol: tcp
FromPort: 443
ToPort: 443
SourceSecurityGroupId: !Ref MetadataSecurityGroup
- Description: Allow access to database
IpProtocol: tcp
FromPort: !Ref DatabasePort
ToPort: !Ref DatabasePort
SourceSecurityGroupId: !Ref DatabaseSecurityGroupId
# Look up the existing metadata security group
MetadataSecurityGroup:
Type: AWS::EC2::SecurityGroup::Id
Properties:
Filters:
- Name: group-name
Values: [supermetal-metadata-sg]
- Name: vpc-id
Values: [!Ref VpcId]
# Add the ingress rule to the database security group
DatabaseSecurityGroupIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref DatabaseSecurityGroupId
IpProtocol: tcp
FromPort: !Ref DatabasePort
ToPort: !Ref DatabasePort
SourceSecurityGroupId: !Ref SupermetalAgentSecurityGroup
Parameters:
VpcId:
Type: AWS::EC2::VPC::Id
Description: VPC where the security groups will be created
DatabaseSecurityGroupId:
Type: AWS::EC2::SecurityGroup::Id
Description: Security Group ID of your database
DatabasePort:
Type: Number
Description: Port number your database listens on (e.g., 5432 for PostgreSQL)
MinValue: 1
MaxValue: 65535
Outputs:
AgentSecurityGroupId:
Description: ID of the Supermetal Agent Security Group
Value: !Ref SupermetalAgentSecurityGroupThis template creates both the agent security group and configures the database access in one deployment.
Parameters
VpcId: The VPC ID of your environment. Refer to the Deployment Parameters sectionDatabaseSecurityGroupId: The security group ID of your databaseDatabasePort: The port number your database listens on (e.g., 5432 for PostgreSQL)- The metadata endpoint security group (
supermetal-metadata-sg) is automatically looked up using the VPC ID
Create Security Group and Configure Database Access
# Create the agent security group
resource "aws_security_group" "supermetal_agent_sg" {
description = "Security group for Supermetal agents"
vpc_id = var.vpc_id
# Required outbound rules
egress {
description = "Allow access to metadata service"
from_port = 443
to_port = 443
protocol = "tcp"
security_group_ids = [data.aws_security_group.metadata_endpoint.id]
}
# Look up the metadata endpoint security group
data "aws_security_group" "metadata_endpoint" {
name = "supermetal-metadata-sg"
vpc_id = var.vpc_id
}
egress {
description = "Allow access to database"
from_port = var.database_port
to_port = var.database_port
protocol = "tcp"
security_group_ids = [var.database_security_group_id]
}
}
# Add database access rule
resource "aws_security_group_rule" "allow_supermetal_agent_to_db" {
type = "ingress"
from_port = var.database_port
to_port = var.database_port
protocol = "tcp"
source_security_group_id = aws_security_group.supermetal_agent_sg.id
security_group_id = var.database_security_group_id
}
# Output the security group ID for reference
output "supermetal_agent_sg_id" {
value = aws_security_group.supermetal_agent_sg.id
}Parameters
vpc_id: The VPC ID of your environment. Refer to the Deployment Parameters sectiondatabase_security_group_id: The security group ID of your databasedatabase_port: The port number your database listens on (e.g., 5432 for PostgreSQL)- The metadata endpoint security group (
supermetal-metadata-sg) is automatically looked up using the VPC ID
Validate Source database
Follow the setup instructions in the Supermetal Console to input the created security group ID for the source database and validate the network reachability and connection credentials.
Repeat for Target database
Repeat the same steps for the target database.
Finalize
Once the source and target database connections are validated, you can finalize the connector setup which will launch the Supermetal agents in your VPC.
Appendix
Environment Bootstrap Scripts
Resources used to bootstrap the environment
AWSTemplateFormatVersion: '2010-09-09'
Description: >-
CloudFormation template to set up the Supermetal BYOC environment in your AWS account.
This template creates IAM roles, a VPC Endpoint for communication with the Supermetal Control Plane and an optional KMS key.
Parameters:
VpcId:
Type: AWS::EC2::VPC::Id
Description: (Required) The ID of your existing VPC for agent deployment.
PrivateSubnetIds:
Type: List<AWS::EC2::Subnet::Id>
Description: >-
(Required) Comma-separated list of private subnet IDs within the VpcId
where agents and the VPC endpoint will be deployed.
CredentialsKeyArn:
Type: String
Description: >-
(Optional) ARN of your existing KMS CMK for encrypting connector credentials.
If left blank, a new KMS key will be created.
Default: ""
ExternalId:
Type: String
Description: >-
(Required) A unique ID provided by the Supermetal Console.
Used for securing the cross-account IAM role.
SupermetalControlPlaneAWSAccountId:
Type: String
Description: The AWS Account ID of the Supermetal Control Plane.
NoEcho: true
SupermetalMetadataServiceEndpointName:
Type: String
Description: >-
The VPC Endpoint Service Name for the Supermetal Metadata Service (e.g., com.amazonaws.vpce.us-east-1.vpce-svc-xxxxxxxxxxxxxxxxx).
Conditions:
CreateKMSKey: !Equals [!Ref CredentialsKeyArn, ""]
Resources:
# IAM Role for Supermetal Control Plane
SupermetalControlPlaneRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "SupermetalControlPlaneRole-${VpcId}"
Tags:
- Key: Name
Value: !Sub "SupermetalControlPlaneRole-${VpcId}"
- Key: supermetal:byoc:vpc-id
Value: !Ref VpcId
- Key: supermetal:byoc:stack-name
Value: !Ref AWS::StackName
- Key: supermetal:byoc:external-id
Value: !Ref ExternalId
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${SupermetalControlPlaneAWSAccountId}:root
Action: sts:AssumeRole
Condition:
StringEquals:
sts:ExternalId: !Ref ExternalId
ManagedPolicyArns:
- !Ref SupermetalControlPlanePolicy
# Managed Policy for SupermetalControlPlaneRole
SupermetalControlPlanePolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: !Sub "SupermetalControlPlanePolicy-${VpcId}"
PolicyDocument:
Version: '2012-10-17'
Statement:
# ECS Cluster Management
- Effect: Allow
Action:
- ecs:CreateCluster
- ecs:DeleteCluster
- ecs:DescribeClusters
Resource: "*" # Scoped to clusters with name prefix 'supermetal-' via condition
# Note: Conditions like 'ecs:cluster' on CreateCluster primarily apply to actions on existing resources.
# They do not strictly restrict naming during creation itself due to IAM evaluation timing.
Condition:
StringLike:
ecs:cluster: "supermetal-*"
# ECS Task and Service Management
- Effect: Allow
Action:
- ecs:CreateService
- ecs:UpdateService
- ecs:DeleteService
- ecs:RegisterTaskDefinition
- ecs:DeregisterTaskDefinition
- ecs:DescribeServices
- ecs:DescribeTasks
- ecs:RunTask
- ecs:StopTask
- ecs:ListTasks
- ecs:ListServices
- ecs:DescribeTaskDefinition
- ecs:ListContainerInstances
- ecs:DescribeContainerInstances
Resource: "*" # Scoped to clusters with name prefix 'supermetal-' via condition
Condition:
StringLike:
ecs:cluster: "supermetal-*"
# ECS Capacity Provider Management
- Effect: Allow
Action:
- ecs:CreateCapacityProvider
- ecs:DeleteCapacityProvider
- ecs:DescribeCapacityProviders
- ecs:UpdateCapacityProvider
- ecs:PutClusterCapacityProviders
Resource: "*" # Scoped to capacity providers with name prefix 'supermetal-' via condition
# Note: Conditions like 'ecs:capacity-provider' on CreateCapacityProvider primarily apply to actions on existing resources.
# They do not strictly restrict naming during creation itself due to IAM evaluation timing.
Condition:
StringLike:
ecs:capacity-provider: "supermetal-*"
# EC2 Capacity Management
- Effect: Allow
Action:
- autoscaling:CreateAutoScalingGroup
- autoscaling:UpdateAutoScalingGroup
- autoscaling:DeleteAutoScalingGroup
- autoscaling:DescribeAutoScalingGroups
- autoscaling:DescribeScalingActivities
- autoscaling:SetDesiredCapacity
- ec2:CreateLaunchTemplate
- ec2:DeleteLaunchTemplate
- ec2:DescribeLaunchTemplates
- ec2:DescribeLaunchTemplateVersions
- ec2:CreateTags
- ec2:DescribeInstances
Resource: "*"
Condition:
StringLike:
aws:ResourceTag/supermetal: "*"
# Lambda for Connector Validation
- Effect: Allow
Action:
- lambda:InvokeFunction
- lambda:CreateFunction
- lambda:DeleteFunction
- lambda:GetFunctionConfiguration
- lambda:UpdateFunctionConfiguration
- lambda:AddPermission
- lambda:RemovePermission
Resource: !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:supermetal-validation-*
# Lambda VPC Access
- Effect: Allow
Action:
- ec2:CreateNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DeleteNetworkInterface
- ec2:AssignPrivateIpAddresses
- ec2:UnassignPrivateIpAddresses
Resource: "*" # Scoped to specified VPC via condition
Condition:
StringEquals:
ec2:VpcId: !Ref VpcId
# Database Discovery
- Effect: Allow
Action:
- rds:DescribeDBInstances
- rds:DescribeDBClusters
- redshift:DescribeClusters
Resource: "*" # Scoped to specified VPC via condition
# Note: While ec2:VpcId is the correct key, RDS/Redshift Describe actions may not fully support VPC-based IAM filtering.
# The control plane might list all regional resources; further filtering may be needed application-side.
Condition:
StringEquals:
ec2:VpcId: !Ref VpcId
# S3 Buffer Management
- Effect: Allow
Action:
- s3:CreateBucket
- s3:DeleteBucket
- s3:PutBucketTagging
- s3:GetBucketTagging
- s3:PutEncryptionConfiguration
- s3:GetEncryptionConfiguration
Resource: !Sub arn:aws:s3:::supermetal-*
# Security Group Management
- Effect: Allow
Action:
- ec2:DescribeSecurityGroups
Resource: "*"
Condition:
StringEquals:
ec2:VpcId: !Ref VpcId
# Secrets Management
- Effect: Allow
Action:
- secretsmanager:CreateSecret
- secretsmanager:DeleteSecret
- secretsmanager:GetSecretValue
- secretsmanager:PutSecretValue
- secretsmanager:UpdateSecret
- secretsmanager:TagResource
Resource: !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:supermetal-*
# KMS Encryption
- Effect: Allow
Action: kms:Encrypt
Resource: !If [CreateKMSKey, !GetAtt SupermetalKMSKey.Arn, !Ref CredentialsKeyArn]
# IAM Role Management
- Effect: Allow
Action: iam:PassRole
Resource:
- !GetAtt SupermetalAgentTaskRole.Arn
- !GetAtt SupermetalValidationLambdaRole.Arn
- !GetAtt SupermetalTaskExecutionRole.Arn
- !GetAtt SupermetalAgentEC2InstanceRole.Arn
Condition:
StringEquals:
iam:PassedToService:
- ecs-tasks.amazonaws.com
- lambda.amazonaws.com
- ec2.amazonaws.com
# IAM Role for Lambda Validation Function
SupermetalValidationLambdaRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "SupermetalValidationLambdaRole-${VpcId}"
Tags:
- Key: Name
Value: !Sub "SupermetalValidationLambdaRole-${VpcId}"
- Key: supermetal:byoc:vpc-id
Value: !Ref VpcId
- Key: supermetal:byoc:stack-name
Value: !Ref AWS::StackName
- Key: supermetal:byoc:external-id
Value: !Ref ExternalId
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
Path: /
# IAM Role for Supermetal Agent Tasks
SupermetalAgentTaskRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "SupermetalAgentTaskRole-${VpcId}"
Tags:
- Key: Name
Value: !Sub "SupermetalAgentTaskRole-${VpcId}"
- Key: supermetal:byoc:vpc-id
Value: !Ref VpcId
- Key: supermetal:byoc:stack-name
Value: !Ref AWS::StackName
- Key: supermetal:byoc:external-id
Value: !Ref ExternalId
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
Condition:
ArnLike:
aws:SourceArn: !Sub arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:task/supermetal-*
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId
ManagedPolicyArns:
- !Ref SupermetalAgentTaskPolicy
Path: /
# IAM Role for ECS Task Execution
SupermetalTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "SupermetalTaskExecutionRole-${VpcId}"
Tags:
- Key: Name
Value: !Sub "SupermetalTaskExecutionRole-${VpcId}"
- Key: supermetal:byoc:vpc-id
Value: !Ref VpcId
- Key: supermetal:byoc:stack-name
Value: !Ref AWS::StackName
- Key: supermetal:byoc:external-id
Value: !Ref ExternalId
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
Condition:
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId
ArnLike:
aws:SourceArn: !Sub arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:task-definition/supermetal*:*
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
- !Ref SupermetalTaskExecutionPolicy
Path: /
# Managed Policy for SupermetalAgentTaskRole
SupermetalAgentTaskPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: !Sub "SupermetalAgentTaskPolicy-${VpcId}"
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- kms:Decrypt
- kms:Encrypt
Resource: !If [CreateKMSKey, !GetAtt SupermetalKMSKey.Arn, !Ref CredentialsKeyArn]
# Secrets Access
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:supermetal-*
# S3 Buffer Access
- Effect: Allow
Action:
- s3:PutObject
- s3:GetObject
- s3:DeleteObject
- s3:ListBucket
Resource:
- !Sub arn:aws:s3:::supermetal-*
- !Sub arn:aws:s3:::supermetal-*/*
# Managed Policy for SupermetalTaskExecutionRole
SupermetalTaskExecutionPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: !Sub "SupermetalTaskExecutionPolicy-${VpcId}"
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:supermetal-*
- Effect: Allow
Action:
- kms:Decrypt
Resource: !If [CreateKMSKey, !GetAtt SupermetalKMSKey.Arn, !Ref CredentialsKeyArn]
# KMS Key for Connector Credentials
SupermetalKMSKey:
Type: AWS::KMS::Key
Condition: CreateKMSKey
Properties:
Description: KMS key for encrypting Supermetal connector credentials
Tags:
- Key: Name
Value: !Sub "SupermetalKMSKey-${VpcId}"
- Key: supermetal:byoc:vpc-id
Value: !Ref VpcId
- Key: supermetal:byoc:stack-name
Value: !Ref AWS::StackName
- Key: supermetal:byoc:external-id
Value: !Ref ExternalId
EnableKeyRotation: true
KeyPolicy:
Version: '2012-10-17'
Statement:
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
Action: kms:*
Resource: '*' # Key policy applies to this key only
- Sid: Allow Supermetal Control Plane to Encrypt
Effect: Allow
Principal:
AWS: !GetAtt SupermetalControlPlaneRole.Arn
Action: kms:Encrypt
Resource: '*' # Key policy applies to this key only
- Sid: Allow Supermetal Agents to Decrypt/Encrypt
Effect: Allow
Principal:
AWS: !GetAtt SupermetalAgentTaskRole.Arn
Action:
- kms:Decrypt
- kms:Encrypt
Resource: '*' # Key policy applies to this key only
SupermetalKMSKeyAlias:
Type: AWS::KMS::Alias
Condition: CreateKMSKey
Properties:
AliasName: !Sub "alias/supermetal-key-${VpcId}"
TargetKeyId: !Ref SupermetalKMSKey
SupermetalAgentEC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: !Sub "SupermetalAgentEC2InstanceProfile-${VpcId}"
Tags:
- Key: Name
Value: !Sub "SupermetalAgentEC2InstanceProfile-${VpcId}"
- Key: supermetal:byoc:vpc-id
Value: !Ref VpcId
- Key: supermetal:byoc:stack-name
Value: !Ref AWS::StackName
- Key: supermetal:byoc:external-id
Value: !Ref ExternalId
Path: /
Roles:
- !Ref SupermetalAgentEC2InstanceRole
SupermetalAgentEC2InstanceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "SupermetalAgentEC2InstanceRole-${VpcId}"
Tags:
- Key: Name
Value: !Sub "SupermetalAgentEC2InstanceRole-${VpcId}"
- Key: supermetal:byoc:vpc-id
Value: !Ref VpcId
- Key: supermetal:byoc:stack-name
Value: !Ref AWS::StackName
- Key: supermetal:byoc:external-id
Value: !Ref ExternalId
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role
- !Ref SupermetalAgentEC2InstancePolicy
Path: /
# Managed Policy for EC2 Instance Role
SupermetalAgentEC2InstancePolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: !Sub "SupermetalAgentEC2InstancePolicy-${VpcId}"
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ecr:GetAuthorizationToken
Resource: "*"
- Effect: Allow
Action:
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
Resource: !Sub arn:aws:ecr:${AWS::Region}:${SupermetalControlPlaneAWSAccountId}:repository/agent/*
SupermetalAgentMetadataSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "supermetal-agent-metadata-sg-${VpcId}"
GroupDescription: Security group for Supermetal Agents
VpcId: !Ref VpcId
Tags:
- Key: Name
Value: !Sub "supermetal-agent-metadata-sg-${VpcId}"
- Key: supermetal:byoc:vpc-id
Value: !Ref VpcId
- Key: supermetal:byoc:stack-name
Value: !Ref AWS::StackName
- Key: supermetal:byoc:external-id
Value: !Ref ExternalId
# VPC Endpoint for Supermetal Metadata Service
MetadataVPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Ref SupermetalMetadataServiceEndpointName
VpcId: !Ref VpcId
SubnetIds: !Ref PrivateSubnetIds
VpcEndpointType: Interface
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref MetadataVPCEndpointSecurityGroup
Tags:
- Key: Name
Value: !Sub "MetadataVPCEndpoint-${VpcId}"
- Key: supermetal:byoc:vpc-id
Value: !Ref VpcId
- Key: supermetal:byoc:stack-name
Value: !Ref AWS::StackName
- Key: supermetal:byoc:external-id
Value: !Ref ExternalId
MetadataVPCEndpointSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "supermetal-metadata-vpce-sg-${VpcId}"
GroupDescription: Security group for Supermetal metadata VPC Endpoint
VpcId: !Ref VpcId
SecurityGroupIngress: # Allow agents to connect to the endpoint
- IpProtocol: tcp
FromPort: 443 # HTTPS
ToPort: 443
SourceSecurityGroupId: !Ref SupermetalAgentMetadataSecurityGroup
Tags:
- Key: Name
Value: !Sub "supermetal-metadata-vpce-sg-${VpcId}"
- Key: supermetal:byoc:vpc-id
Value: !Ref VpcId
- Key: supermetal:byoc:stack-name
Value: !Ref AWS::StackName
- Key: supermetal:byoc:external-id
Value: !Ref ExternalId
Outputs:
SupermetalControlPlaneRoleArn:
Description: ARN of the IAM Role for Supermetal Control Plane
Value: !GetAtt SupermetalControlPlaneRole.Arn
SupermetalAgentTaskRoleArn:
Description: ARN of the IAM Role for Supermetal Agent Tasks
Value: !GetAtt SupermetalAgentTaskRole.Arn
SupermetalKMSKeyArnOutput:
Description: ARN of the KMS Key for encrypting credentials
Value: !If [CreateKMSKey, !GetAtt SupermetalKMSKey.Arn, !Ref CredentialsKeyArn]
SupermetalTaskExecutionRoleArn:
Description: ARN of the IAM Role for ECS Task Execution
Value: !GetAtt SupermetalTaskExecutionRole.Arn
MetadataVPCEndpointId:
Description: ID of the VPC Endpoint for Supermetal Metadata Service
Value: !Ref MetadataVPCEndpoint
SupermetalAgentMetadataSecurityGroupId:
Description: ID of the Security Group for Supermetal Agents (for Metadata Endpoint communication)
Value: !GetAtt SupermetalAgentMetadataSecurityGroup.GroupId
SupermetalValidationLambdaRoleArn:
Description: ARN of the IAM Role for Supermetal Validation Lambda functions
Value: !GetAtt SupermetalValidationLambdaRole.Arn
SupermetalAgentEC2InstanceRoleArn:
Description: ARN of the IAM Role for Supermetal Agent EC2 Instances
Value: !GetAtt SupermetalAgentEC2InstanceRole.Arn
SupermetalAgentEC2InstanceProfileArn:
Description: ARN of the IAM Instance Profile for Supermetal Agent EC2 Instances
Value: !GetAtt SupermetalAgentEC2InstanceProfile.Arn
# This sets up the Supermetal BYOC environment in your AWS account.
# It creates IAM roles, a VPC Endpoint for communication with the Supermetal Control Plane,
# and an optional KMS key.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # Specify a compatible AWS provider version
}
}
}
# ------------------------------------------------------------------------------
# Input Variables
# ------------------------------------------------------------------------------
variable "vpc_id" {
description = "(Required) The ID of your existing VPC for agent deployment."
type = string
}
variable "private_subnet_ids" {
description = "(Required) List of private subnet IDs within the VpcId where agents and the VPC endpoint will be deployed."
type = list(string)
}
variable "credentials_key_arn" {
description = "(Optional) ARN of your existing KMS CMK for encrypting connector credentials. If left blank, a new KMS key will be created."
type = string
default = ""
}
variable "external_id" {
description = "(Required) A unique ID provided by the Supermetal Console. Used for securing the cross-account IAM role."
type = string
}
variable "supermetal_control_plane_aws_account_id" {
description = "The AWS Account ID of the Supermetal Control Plane."
type = string
}
variable "supermetal_metadata_service_endpoint_name" {
description = "The VPC Endpoint Service Name for the Supermetal Metadata Service (e.g., com.amazonaws.vpce.us-east-1.vpce-svc-xxxxxxxxxxxxxxxxx)."
type = string
}
locals {
create_kms_key = var.credentials_key_arn == ""
common_tags = {
"supermetal:byoc:vpc-id" = var.vpc_id
"supermetal:byoc:stack-name" = "terraform-supermetal-byoc" # Terraform doesn't have a direct AWS::StackName equivalent, using a placeholder
"supermetal:byoc:external-id" = var.external_id
}
}
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
# ------------------------------------------------------------------------------
# Resources
# ------------------------------------------------------------------------------
# IAM Role for Supermetal Control Plane
resource "aws_iam_role" "supermetal_control_plane_role" {
name = "SupermetalControlPlaneRole-${var.vpc_id}"
tags = merge(local.common_tags, {
Name = "SupermetalControlPlaneRole-${var.vpc_id}"
})
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
AWS = "arn:aws:iam::${var.supermetal_control_plane_aws_account_id}:root"
},
Action = "sts:AssumeRole",
Condition = {
StringEquals = {
"sts:ExternalId" = var.external_id
}
}
}
]
})
}
# Managed Policy for SupermetalControlPlaneRole
resource "aws_iam_policy" "supermetal_control_plane_policy" {
name = "SupermetalControlPlanePolicy-${var.vpc_id}"
description = "Policy for Supermetal Control Plane Role"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
# ECS Cluster Management
{
Effect = "Allow",
Action = [
"ecs:CreateCluster",
"ecs:DeleteCluster",
"ecs:DescribeClusters"
],
Resource = "*", # Scoped to clusters with name prefix 'supermetal-' via condition
Condition = {
StringLike = {
"ecs:cluster" = "supermetal-*"
}
}
},
# ECS Task and Service Management
{
Effect = "Allow",
Action = [
"ecs:CreateService",
"ecs:UpdateService",
"ecs:DeleteService",
"ecs:RegisterTaskDefinition",
"ecs:DeregisterTaskDefinition",
"ecs:DescribeServices",
"ecs:DescribeTasks",
"ecs:RunTask",
"ecs:StopTask",
"ecs:ListTasks",
"ecs:ListServices",
"ecs:DescribeTaskDefinition",
"ecs:ListContainerInstances",
"ecs:DescribeContainerInstances"
],
Resource = "*", # Scoped to clusters with name prefix 'supermetal-' via condition
Condition = {
StringLike = {
"ecs:cluster" = "supermetal-*"
}
}
},
# ECS Capacity Provider Management
{
Effect = "Allow",
Action = [
"ecs:CreateCapacityProvider",
"ecs:DeleteCapacityProvider",
"ecs:DescribeCapacityProviders",
"ecs:UpdateCapacityProvider",
"ecs:PutClusterCapacityProviders"
],
Resource = "*", # Scoped to capacity providers with name prefix 'supermetal-' via condition
Condition = {
StringLike = {
"ecs:capacity-provider" = "supermetal-*"
}
}
},
# EC2 Capacity Management
{
Effect = "Allow",
Action = [
"autoscaling:CreateAutoScalingGroup",
"autoscaling:UpdateAutoScalingGroup",
"autoscaling:DeleteAutoScalingGroup",
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:DescribeScalingActivities",
"autoscaling:SetDesiredCapacity",
"ec2:CreateLaunchTemplate",
"ec2:DeleteLaunchTemplate",
"ec2:DescribeLaunchTemplates",
"ec2:DescribeLaunchTemplateVersions",
"ec2:CreateTags",
"ec2:DescribeInstances"
],
Resource = "*",
Condition = {
StringLike = {
"aws:ResourceTag/supermetal" = "*"
}
}
},
# Lambda for Connector Validation
{
Effect = "Allow",
Action = [
"lambda:InvokeFunction",
"lambda:CreateFunction",
"lambda:DeleteFunction",
"lambda:GetFunctionConfiguration",
"lambda:UpdateFunctionConfiguration",
"lambda:AddPermission",
"lambda:RemovePermission"
],
Resource = "arn:aws:lambda:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:function:supermetal-validation-*"
},
# Lambda VPC Access
{
Effect = "Allow",
Action = [
"ec2:CreateNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface",
"ec2:AssignPrivateIpAddresses",
"ec2:UnassignPrivateIpAddresses"
],
Resource = "*", # Scoped to specified VPC via condition
Condition = {
StringEquals = {
"ec2:VpcId" = var.vpc_id
}
}
},
# Database Discovery
{
Effect = "Allow",
Action = [
"rds:DescribeDBInstances",
"rds:DescribeDBClusters",
"redshift:DescribeClusters"
],
Resource = "*", # Scoped to specified VPC via condition
Condition = {
StringEquals = {
"ec2:VpcId" = var.vpc_id
}
}
},
# S3 Buffer Management
{
Effect = "Allow",
Action = [
"s3:CreateBucket",
"s3:DeleteBucket",
"s3:PutBucketTagging",
"s3:GetBucketTagging",
"s3:PutEncryptionConfiguration",
"s3:GetEncryptionConfiguration"
],
Resource = "arn:aws:s3:::supermetal-*"
},
# Security Group Management
{
Effect = "Allow",
Action = [
"ec2:DescribeSecurityGroups"
],
Resource = "*",
Condition = {
StringEquals = {
"ec2:VpcId" = var.vpc_id
}
}
},
# Secrets Management
{
Effect = "Allow",
Action = [
"secretsmanager:CreateSecret",
"secretsmanager:DeleteSecret",
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue",
"secretsmanager:UpdateSecret",
"secretsmanager:TagResource"
],
Resource = "arn:aws:secretsmanager:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:secret:supermetal-*"
},
# KMS Encryption
{
Effect = "Allow",
Action = "kms:Encrypt",
Resource = local.create_kms_key ? aws_kms_key.supermetal_kms_key[0].arn : var.credentials_key_arn
},
# IAM Role Management
{
Effect = "Allow",
Action = "iam:PassRole",
Resource = [
aws_iam_role.supermetal_agent_task_role.arn,
aws_iam_role.supermetal_validation_lambda_role.arn,
aws_iam_role.supermetal_task_execution_role.arn,
aws_iam_role.supermetal_agent_ec2_instance_role.arn
],
Condition = {
StringEquals = {
"iam:PassedToService" = [
"ecs-tasks.amazonaws.com",
"lambda.amazonaws.com",
"ec2.amazonaws.com"
]
}
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "supermetal_control_plane_role_policy_attach" {
role = aws_iam_role.supermetal_control_plane_role.name
policy_arn = aws_iam_policy.supermetal_control_plane_policy.arn
}
# IAM Role for Lambda Validation Function
resource "aws_iam_role" "supermetal_validation_lambda_role" {
name = "SupermetalValidationLambdaRole-${var.vpc_id}"
path = "/"
tags = merge(local.common_tags, {
Name = "SupermetalValidationLambdaRole-${var.vpc_id}"
})
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
Service = "lambda.amazonaws.com"
},
Action = "sts:AssumeRole"
}
]
})
}
resource "aws_iam_role_policy_attachment" "supermetal_validation_lambda_role_vpc_access_policy_attach" {
role = aws_iam_role.supermetal_validation_lambda_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
}
# IAM Role for Supermetal Agent Tasks
resource "aws_iam_role" "supermetal_agent_task_role" {
name = "SupermetalAgentTaskRole-${var.vpc_id}"
path = "/"
tags = merge(local.common_tags, {
Name = "SupermetalAgentTaskRole-${var.vpc_id}"
})
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
Service = "ecs-tasks.amazonaws.com"
},
Action = "sts:AssumeRole",
Condition = {
ArnLike = {
"aws:SourceArn" = "arn:aws:ecs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:task/supermetal-*"
},
StringEquals = {
"aws:SourceAccount" = data.aws_caller_identity.current.account_id
}
}
}
]
})
}
# Managed Policy for SupermetalAgentTaskRole
resource "aws_iam_policy" "supermetal_agent_task_policy" {
name = "SupermetalAgentTaskPolicy-${var.vpc_id}"
description = "Policy for Supermetal Agent Task Role"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = [
"kms:Decrypt",
"kms:Encrypt"
],
Resource = local.create_kms_key ? aws_kms_key.supermetal_kms_key[0].arn : var.credentials_key_arn
},
# Secrets Access
{
Effect = "Allow",
Action = [
"secretsmanager:GetSecretValue"
],
Resource = "arn:aws:secretsmanager:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:secret:supermetal-*"
},
# S3 Buffer Access
{
Effect = "Allow",
Action = [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:ListBucket"
],
Resource = [
"arn:aws:s3:::supermetal-*",
"arn:aws:s3:::supermetal-*/*"
]
}
]
})
}
resource "aws_iam_role_policy_attachment" "supermetal_agent_task_role_policy_attach" {
role = aws_iam_role.supermetal_agent_task_role.name
policy_arn = aws_iam_policy.supermetal_agent_task_policy.arn
}
# IAM Role for ECS Task Execution
resource "aws_iam_role" "supermetal_task_execution_role" {
name = "SupermetalTaskExecutionRole-${var.vpc_id}"
path = "/"
tags = merge(local.common_tags, {
Name = "SupermetalTaskExecutionRole-${var.vpc_id}"
})
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
Service = "ecs-tasks.amazonaws.com"
},
Action = "sts:AssumeRole",
Condition = {
StringEquals = {
"aws:SourceAccount" = data.aws_caller_identity.current.account_id
},
ArnLike = {
"aws:SourceArn" = "arn:aws:ecs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:task-definition/supermetal*:*"
}
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "supermetal_task_execution_role_ecs_policy_attach" {
role = aws_iam_role.supermetal_task_execution_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
# Managed Policy for SupermetalTaskExecutionRole
resource "aws_iam_policy" "supermetal_task_execution_policy" {
name = "SupermetalTaskExecutionPolicy-${var.vpc_id}"
description = "Policy for Supermetal Task Execution Role"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = [
"secretsmanager:GetSecretValue"
],
Resource = "arn:aws:secretsmanager:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:secret:supermetal-*"
},
{
Effect = "Allow",
Action = [
"kms:Decrypt"
],
Resource = local.create_kms_key ? aws_kms_key.supermetal_kms_key[0].arn : var.credentials_key_arn
}
]
})
}
resource "aws_iam_role_policy_attachment" "supermetal_task_execution_role_custom_policy_attach" {
role = aws_iam_role.supermetal_task_execution_role.name
policy_arn = aws_iam_policy.supermetal_task_execution_policy.arn
}
# KMS Key for Connector Credentials
resource "aws_kms_key" "supermetal_kms_key" {
count = local.create_kms_key ? 1 : 0
description = "KMS key for encrypting Supermetal connector credentials"
enable_key_rotation = true
tags = merge(local.common_tags, {
Name = "SupermetalKMSKey-${var.vpc_id}"
})
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Sid = "Enable IAM User Permissions",
Effect = "Allow",
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
},
Action = "kms:*",
Resource = "*"
},
{
Sid = "Allow Supermetal Control Plane to Encrypt",
Effect = "Allow",
Principal = {
AWS = aws_iam_role.supermetal_control_plane_role.arn
},
Action = "kms:Encrypt",
Resource = "*"
},
{
Sid = "Allow Supermetal Agents to Decrypt/Encrypt",
Effect = "Allow",
Principal = {
AWS = aws_iam_role.supermetal_agent_task_role.arn
},
Action = [
"kms:Decrypt",
"kms:Encrypt"
],
Resource = "*"
}
]
})
}
resource "aws_kms_alias" "supermetal_kms_key_alias" {
count = local.create_kms_key ? 1 : 0
name = "alias/supermetal-key-${var.vpc_id}"
target_key_id = aws_kms_key.supermetal_kms_key[0].key_id
}
# IAM Role for Supermetal Agent EC2 Instances
resource "aws_iam_role" "supermetal_agent_ec2_instance_role" {
name = "SupermetalAgentEC2InstanceRole-${var.vpc_id}"
path = "/"
tags = merge(local.common_tags, {
Name = "SupermetalAgentEC2InstanceRole-${var.vpc_id}"
})
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
Service = "ec2.amazonaws.com"
},
Action = "sts:AssumeRole"
}
]
})
}
# Managed Policy for EC2 Instance Role
resource "aws_iam_policy" "supermetal_agent_ec2_instance_policy" {
name = "SupermetalAgentEC2InstancePolicy-${var.vpc_id}"
description = "Policy for Supermetal EC2 Instance Role"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = [
"ecr:GetAuthorizationToken"
],
Resource = "*"
},
{
Effect = "Allow",
Action = [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
],
Resource = "arn:aws:ecr:${data.aws_region.current.name}:${var.supermetal_control_plane_aws_account_id}:repository/agent/*"
}
]
})
}
resource "aws_iam_role_policy_attachment" "supermetal_agent_ec2_instance_role_ecs_policy_attach" {
role = aws_iam_role.supermetal_agent_ec2_instance_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
}
resource "aws_iam_role_policy_attachment" "supermetal_agent_ec2_instance_role_custom_policy_attach" {
role = aws_iam_role.supermetal_agent_ec2_instance_role.name
policy_arn = aws_iam_policy.supermetal_agent_ec2_instance_policy.arn
}
resource "aws_iam_instance_profile" "supermetal_agent_ec2_instance_profile" {
name = "SupermetalAgentEC2InstanceProfile-${var.vpc_id}"
role = aws_iam_role.supermetal_agent_ec2_instance_role.name
path = "/"
tags = merge(local.common_tags, {
Name = "SupermetalAgentEC2InstanceProfile-${var.vpc_id}"
})
}
# Security Group for Supermetal Agents (for Metadata Endpoint communication)
resource "aws_security_group" "supermetal_agent_metadata_sg" {
name = "supermetal-agent-metadata-sg-${var.vpc_id}"
description = "Security group for Supermetal Agents"
vpc_id = var.vpc_id
tags = merge(local.common_tags, {
Name = "supermetal-agent-metadata-sg-${var.vpc_id}"
})
}
# Security Group for Metadata VPC Endpoint
resource "aws_security_group" "metadata_vpc_endpoint_sg" {
name = "supermetal-metadata-vpce-sg-${var.vpc_id}"
description = "Security group for Supermetal metadata VPC Endpoint"
vpc_id = var.vpc_id
tags = merge(local.common_tags, {
Name = "supermetal-metadata-vpce-sg-${var.vpc_id}"
})
ingress {
description = "Allow agents to connect to the endpoint (HTTPS)"
from_port = 443
to_port = 443
protocol = "tcp"
security_groups = [aws_security_group.supermetal_agent_metadata_sg.id]
}
}
# VPC Endpoint for Supermetal Metadata Service
resource "aws_vpc_endpoint" "metadata_vpc_endpoint" {
vpc_id = var.vpc_id
service_name = var.supermetal_metadata_service_endpoint_name
vpc_endpoint_type = "Interface"
subnet_ids = var.private_subnet_ids
private_dns_enabled = true
security_group_ids = [
aws_security_group.metadata_vpc_endpoint_sg.id
]
tags = merge(local.common_tags, {
Name = "MetadataVPCEndpoint-${var.vpc_id}"
})
}
# ------------------------------------------------------------------------------
# Outputs
# ------------------------------------------------------------------------------
output "supermetal_control_plane_role_arn" {
description = "ARN of the IAM Role for Supermetal Control Plane"
value = aws_iam_role.supermetal_control_plane_role.arn
}
output "supermetal_agent_task_role_arn" {
description = "ARN of the IAM Role for Supermetal Agent Tasks"
value = aws_iam_role.supermetal_agent_task_role.arn
}
output "supermetal_kms_key_arn_output" {
description = "ARN of the KMS Key for encrypting credentials"
value = local.create_kms_key ? aws_kms_key.supermetal_kms_key[0].arn : var.credentials_key_arn
}
output "supermetal_task_execution_role_arn" {
description = "ARN of the IAM Role for ECS Task Execution"
value = aws_iam_role.supermetal_task_execution_role.arn
}
output "metadata_vpc_endpoint_id" {
description = "ID of the VPC Endpoint for Supermetal Metadata Service"
value = aws_vpc_endpoint.metadata_vpc_endpoint.id
}
output "supermetal_agent_metadata_security_group_id" {
description = "ID of the Security Group for Supermetal Agents (for Metadata Endpoint communication)"
value = aws_security_group.supermetal_agent_metadata_sg.id
}
output "supermetal_validation_lambda_role_arn" {
description = "ARN of the IAM Role for Supermetal Validation Lambda functions"
value = aws_iam_role.supermetal_validation_lambda_role.arn
}
output "supermetal_agent_ec2_instance_role_arn" {
description = "ARN of the IAM Role for Supermetal Agent EC2 Instances"
value = aws_iam_role.supermetal_agent_ec2_instance_role.arn
}
output "supermetal_agent_ec2_instance_profile_arn" {
description = "ARN of the IAM Instance Profile for Supermetal Agent EC2 Instances"
value = aws_iam_instance_profile.supermetal_agent_ec2_instance_profile.arn
}IAM Roles & Policies
Bootstrap creates several IAM roles and policies with specific permissions necessary for Supermetal BYOC to function securely. These roles ensure that the Supermetal Control Plane can manage resources in your AWS account on your behalf, and that the Supermetal agents have the necessary permissions to operate. The principle of least privilege is applied to scope down permissions as much as possible.
Key roles created and their purposes:
SupermetalControlPlaneRole
This role is assumed by the Supermetal Control Plane. It is granted permissions to orchestrate and manage the lifecycle of resources required for the Supermetal data plane within your AWS account.
-
ECS Resources
- Create and manage ECS clusters, services, tasks, and capacity providers (e.g.,
ecs:CreateCluster,ecs:DeleteCluster,ecs:CreateService,ecs:RunTask) - Resources are restricted to those tagged or named with
supermetal-*prefix
- Create and manage ECS clusters, services, tasks, and capacity providers (e.g.,
-
EC2 and Auto Scaling
- Manage EC2 instances and launch templates (
ec2:CreateLaunchTemplate,ec2:DescribeInstances,ec2:CreateTags) - Control Auto Scaling groups (
autoscaling:CreateAutoScalingGroup,autoscaling:UpdateAutoScalingGroup) - Resources are typically conditioned on tags like
supermetal:* - Network interface management for VPC access (
ec2:CreateNetworkInterface,ec2:DescribeNetworkInterfaces) - Security group discovery (
ec2:DescribeSecurityGroups) for network configuration
- Manage EC2 instances and launch templates (
-
Lambda Functions
- Deploy and manage validation functions named
supermetal-validation-*(lambda:CreateFunction,lambda:InvokeFunction,lambda:DeleteFunction) - VPC access permissions for Lambda functions
- Pre-deployment validation and auxiliary tasks within your VPC
- Deploy and manage validation functions named
-
Database Access
-
Storage and Data Management
- S3 bucket management (
s3:CreateBucket,s3:DeleteBucket,s3:PutBucketTagging) for buckets prefixed withsupermetal-* - Used for data buffering and staging per connector
- S3 bucket management (
-
Security and Access Control
- Secrets Manager: Full control over secrets prefixed with
supermetal-*(secretsmanager:CreateSecret,secretsmanager:GetSecretValue) - KMS: Encryption permissions (
kms:Encrypt) on specified KMS keys - IAM: Pass roles to AWS services (
iam:PassRole) for ECS tasks, Lambda functions, and EC2 instances
- Secrets Manager: Full control over secrets prefixed with
SupermetalAgentRole
This IAM role is assumed by the Supermetal agent tasks running on ECS within your AWS account. It grants the agent the necessary permissions to perform its data processing duties:
-
Data Security and Encryption
- KMS: Encrypt and decrypt operations (
kms:Decrypt,kms:Encrypt) on specified KMS key - Used for connector credential decryption
- KMS: Encrypt and decrypt operations (
-
Configuration Management
- Secrets Manager: Access to secrets (
secretsmanager:GetSecretValue) prefixed withsupermetal-* - Retrieves necessary API keys for Supermetal control plane and to authenticate to ECR repository for agent images
- Secrets Manager: Access to secrets (
-
Data Processing
- S3: Full object operations on
supermetal-*buckets (s3:PutObject,s3:GetObject,s3:DeleteObject,s3:ListBucket) - Used to create buckets per connector to buffer data
- S3: Full object operations on
SupermetalTaskExecutionRole
This role is used by Amazon ECS to launch your agent tasks. It has two sets of permissions:
-
Core ECS Operations
- Uses AWS managed policy
arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy - Pull container images from Amazon ECR
- Uses AWS managed policy
-
Agent Configuration
- Custom policy
SupermetalTaskExecutionPolicyprovides:- Access to Secrets Manager (
secretsmanager:GetSecretValue) for fetching Control Plane API secrets - KMS decryption (
kms:Decrypt) to decrypt connector credentials
- Access to Secrets Manager (
- Makes configuration available to agent containers via environment variables
- Custom policy
SupermetalAgentEC2InstanceRole
This role is attached to EC2 instances that form your ECS cluster's capacity when using the EC2 launch type. It has two sets of permissions:
-
Core ECS Instance Operations
- Uses AWS managed policy
arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role - Register with ECS cluster
- Pull common container images
- Send instance logs
- Uses AWS managed policy
-
Supermetal Agent Access
- Custom policy
SupermetalAgentEC2InstancePolicyprovides:- ECR authentication (
ecr:GetAuthorizationToken) - Image pulling from Supermetal's ECR repository (
ecr:BatchGetImage,ecr:GetDownloadUrlForLayer)
- ECR authentication (
- Enables instances to pull agent container images from Supermetal's control plane
- Custom policy
SupermetalValidationLambdaRole
This role is assumed by Lambda functions that perform pre-deployment validation tasks. It provides:
- VPC Access and Networking
- Uses AWS managed policy
arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole - Create and manage elastic network interfaces
- Access resources within your VPC
- Send function logs to CloudWatch
- Uses AWS managed policy
Last updated on