Skip to content

AWS Service Task Access

This section covers how to connect to Parallel's service tasks through AWS bastion hosts using port forwarding.

Parallel Config

bash
# Configuration
typeset -A _PARALLEL_CONFIG
_PARALLEL_CONFIG=(
  # Bastion instances
  [bastion_prod]="i-0430e03b4e1c1bbdd"
  [bastion_staging]="i-0f052721e4ba2c318"

  # RDS cluster identifiers
  [rds_suffix_prod]="clwscu6m8xeq.eu-west-3.rds.amazonaws.com"
  [rds_suffix_staging]="c5wmseg8am3n.eu-west-3.rds.amazonaws.com"

  # Environment prefixes
  [prefix_prod]="parallel-euw3-prod"
  [prefix_staging]="parallel-euw3-staging"

  # Default local port ranges (prod: 554xx, staging: 654xx)
  [port_base_prod]="55432"
  [port_base_staging]="65432"
)

Connect to a task

bash
# ============================================
# Connect to Generic Task Service
# ============================================

_connect-service() {
  local ENV="$1"
  local SERVICE="$2"
  local LOCAL_PORT="$3"
  local REMOTE_PORT="$4"

  aws-sso-util login --profile "parallel_${ENV}"
  export AWS_PROFILE="parallel_${ENV}"

  local BASTION="${_PARALLEL_CONFIG[bastion_${ENV}]}"
  local PREFIX="${_PARALLEL_CONFIG[prefix_${ENV}]}"
  local CLUSTER="${PREFIX}-ecs-cluster"
  local ECS_SERVICE="${PREFIX}-ecs-${SERVICE}"

  echo "🔍 Finding task for ${SERVICE}..."

  # 1. Get Task ARN
  local TASK_ARN
  TASK_ARN=$(aws ecs list-tasks \
    --cluster "$CLUSTER" \
    --service-name "$ECS_SERVICE" \
    --query 'taskArns[0]' \
    --output text)

  if [[ "$TASK_ARN" == "None" ]] || [[ -z "$TASK_ARN" ]]; then
    echo "❌ No running task found for service: $SERVICE"
    return 1
  fi

  local HOST_IP=""

  # 2. Get Task Details (Raw JSON) to find ENI
  # We grep for the specific pattern "eni-..." inside the task description
  local TASK_JSON
  TASK_JSON=$(aws ecs describe-tasks --cluster "$CLUSTER" --tasks "$TASK_ARN" --output json)

  # Extract ENI ID using grep (looks for "value": "eni-...")
  local ENI_ID
  ENI_ID=$(echo "$TASK_JSON" | grep -o '"value": "eni-[^"]*"' | cut -d'"' -f4 | head -n 1)

  if [[ -n "$ENI_ID" ]]; then
    HOST_IP=$(aws ec2 describe-network-interfaces \
      --network-interface-ids "$ENI_ID" \
      --query 'NetworkInterfaces[0].PrivateIpAddress' \
      --output text)
  fi

  # 3. Fallback: Check for EC2 Instance ID (if grep failed or it's not Fargate)
  if [[ -z "$HOST_IP" || "$HOST_IP" == "None" ]]; then
    # Extract Instance ID using grep
    local INSTANCE_ID
    INSTANCE_ID=$(echo "$TASK_JSON" | grep -o '"ec2InstanceId": "i-[^"]*"' | cut -d'"' -f4 | head -n 1)

    if [[ -n "$INSTANCE_ID" ]]; then
       HOST_IP=$(aws ec2 describe-instances \
          --instance-ids "$INSTANCE_ID" \
          --query 'Reservations[0].Instances[0].PrivateIpAddress' \
          --output text)
    fi
  fi

  if [[ -z "$HOST_IP" || "$HOST_IP" == "None" ]]; then
    echo "❌ Could not resolve Host IP. The task might be pending or the network mode is unsupported."
    return 1
  fi

  echo ""
  echo "🥑 Connecting to ${SERVICE} service (${ENV})..."
  echo "   Task: $TASK_ARN"
  echo "   Host: $HOST_IP"
  echo "   Port: ${REMOTE_PORT} → localhost:${LOCAL_PORT}"
  echo ""

  aws ssm start-session \
    --target "$BASTION" \
    --document-name "AWS-StartPortForwardingSessionToRemoteHost" \
    --parameters "{\"host\":[\"${HOST_IP}\"],\"portNumber\":[\"${REMOTE_PORT}\"],\"localPortNumber\":[\"${LOCAL_PORT}\"]}"
}

# ================== PROD ==================
# Prod usually starts at 9080.
connect-guacamole-prod() { _connect-service "prod" "guacamole" "8080" "9080"; }

# ================ STAGING =================
# Staging usually starts at 8080.
connect-guacamole-staging() { _connect-service "staging" "guacamole" "8080" "8080"; }