How can i specify an already created subnet and se...
# help
a
How can i specify an already created subnet and security group during the creation of a lambda ?
r
Frank helped me out with this the other day. We do it via setDefaultFunctionProps like this:
Copy code
export default function main(app: <http://sst.App|sst.App>): void {
  const vpcsByStackName: VpcsByStackName = {};
  const securityGroupsByStackName: SecGroupsByStackName = {};

const vpcsByStackName: VpcsByStackName = {};
  const securityGroupsByStackName: SecGroupsByStackName = {};

  app.setDefaultFunctionProps((stack) => {
    // Get already imported VPC and SG
    let vpc = vpcsByStackName[stack.stackName];
    let securityGroup = securityGroupsByStackName[stack.stackName];

    // If VPC and SG have not been imported, then import now
    if (!vpc) {
      vpc = Vpc.fromLookup(stack, 'default-vpc', {
        isDefault: true,
      });
      if (process.env.VPC_RDS_SECURITY_GROUP) {
        securityGroup = SecurityGroup.fromSecurityGroupId(stack, 'default-sg', process.env.VPC_RDS_SECURITY_GROUP);
        securityGroupsByStackName[stack.stackName] = securityGroup;
      }

      vpcsByStackName[stack.stackName] = vpc;
    }
    // Return the function definition{
    const props: DefaultProps = {
      runtime: 'nodejs14.x',
      vpc,
    };
    if (securityGroup) {
      props.securityGroups = [securityGroup];
    }
    return props;
  });

  new OmwOfscBeStack(app, `OmwOfscBeStack-${stage}`);
}
a
@Ross Coundon What is the isDefault flag here ? I am attempting to pass a vpc in the Api like following:
Copy code
const vpc = ec2.Vpc.fromLookup(this, 'qa-cluster-eks', {});
const api = new Api(stack, 'Api', {
    defaultFunctionProps: {
      environment: options.defaultEnvironment || {},
      permissions: [options.commonQueue],
      vpc,
      securityGroup,
    },
    routes: {
      'GET /health': getFunctionDef('health', scope, true), // Lite handler
    },
  });
r
It includes default VPCs in the retrieval, i.e. the VPC that's created automatically with the AWS account
a
Oh. Got it. Thanks a lot. I am able to find the vpc in my code now. 🙂 . Just one more thing, I am not able to figure out how to provide subnet to the lambda function? I have to do that or I can just specify the VPN only ?
r
If you want to specify particular subnets from that retrieved VPCs you can build the VPC property using the ones you need. E.g.
Copy code
vpc = Vpc.fromLookup(stack, 'default-vpc', {
  isDefault: true,
});
const props = {
  runtime: 'nodejs14.x',
  vpc: {
    publicSubnets: vpc.publicSubnets
  }
};
a
ok. Got it. Thanks a lot @Ross Coundon . Will try this.
r
no worries
w
Hello, I just found this post searching for how to associate the function with an existing VPC, Subnets, SG's, etc. Before I get going down this path, I want to make sure I conceptually understand the approach here. You're using CDK to import existing using ...fromLookup, then adding an VPC object to the default props for the function? I'm trying to make sure my deployed function is in a VPC, specific subnets, and associated with specific security groups, so that the peering connection we already have with an atlas VPC will allow traffic from the lambda; it's currently not. Works fine in local dev, as I can easily add my local IP to the atlas network security allow list, but as soon as I've deployed it, the function is now running "outside" of the existing infrastructure and atlas bounces it. Any pointers or suggestions welcome. I've been digging into this all day, and found a lot of good info, just lacking the bits that connect the function to the vpc resources, and this seems like it might be it. Thanks.
s
I'm in a similar boat as @William Hatch. I'm trying to convert an existing Serverless Framework app to SST in a VPC environment. I haven't worked with VPC's in AWS, and there appears to be a bit of a learning curve. Thankfully, the existing Serverless Framework app is not creating the VPC resources, so I only need to import them into my new SST app. This is probably more of a CDK/AWS question than an SST question, but it would be nice to have a complete example of how to do this. Trying to understand how VPC's/Subnets/Security Groups/etc all work together is a doozie 🥴
r
I'm kinda stuck in the middle of something urgent but this is how I've previously done it, hopefully you can pick it apart and I'll try to get back later to help if you need it
s
Thank you @Ross Coundon! I'll dig into this. Good luck with the fire fighting 🔥 🚒
w
Thank you as well, @Ross Coundon! The only other conceptual issue I'm still fuzzy on is what the possible options are when calling setDefaultFunctionProps. @Seth Geoghegan I'll share my results, and happy to help with any conceptual issues on vpc's, subnets and security groups. Thanks all.
s
I've got a basic HTTP API (ApiGatewayV2) running in SST (python). I'm able to connect to an RDS instance running inside my company VPC by providing the DB connection configuration (host, name, port, user, password). Although I am currently connected to my company VPN, I did nothing special in my SST app to make it aware of the VPC. I am confused why this works 🙂
I took the boilerplate rest-api-python example from the SST examples repo and modified an endpoint.
Copy code
import json
import psycopg2

db_host = "MY_HOST_INSIDE_THE_VPC"
db_port = "DB_PORT"
db_name = "DB_NAME"
db_user = "DB_USER"
db_pass = "DB_PASSWORD"
db_table = "DB_TABLE_NAME"

def create_conn():
    conn = None
    try:
        conn = psycopg2.connect("dbname={} user={} host={} password={}".format(db_name,db_user,db_host,db_pass))
    except Exception as e:
        print("Cannot connect. {}".format(e))
    return conn

def fetch(conn, query):
    print("Now executing: {}".format(query))
    cursor = conn.cursor()
    cursor.execute(query)

    raw = cursor.fetchall()
    return [line for line in raw]

def main(event, context):

  print("HEALTHCHECK!")
  # get a connection, if a connect cannot be made an exception will be raised here
  conn = create_conn()
  
  query_cmd = "select count(*) from {}".format(db_table)
  result = fetch(conn, query_cmd)
  conn.close()

  return {
        "statusCode": 200,
        "body": "I'm alive and well! {}".format(result)
  }
Shouldn't I need to take extra steps to deploy this lambda to the VPC? I didn't have to muck with vpc's, subnets and security groups. It worked and I have no idea why! 😆
r
It could be that your RDS instance is in subnet that allows traffic from the internet and is enabled for public access
s
hmm, that's a good point. I'll have to do some digging in the console, as I have no idea how this particular legacy DB was created
w
@Seth Geoghegan yeah, I think Ross is correct. The subnets that the RDS instance is in probably don't have any network ACL's associated that would prevent traffic, and same with the security groups for the rds instance itself. Have you tried connecting directly to the rds instance with another client? If you can, then it's wide open, and that would explain it.
This is what I ended up with, which works perfectly, and with one minor delta I didn't expect, explained below:
import { Vpc, SecurityGroup } from '@aws-cdk/aws-ec2';
import StreamStack from './StreamStack.js';
export default function main(app) {
// Set default runtime for all functions
app.setDefaultFunctionProps((stack) => {
let props = {
runtime: 'nodejs12.x',
environment: {
MONGO_URL: process.env.MONGO_URL,
MONGO_DB: process.env.MONGO_DB,
MONGO_COLLECTION: process.env.MONGO_COLLECTION,
},
};
if (app.stage === 'prod') {
//set the vpc attributes in props
const vpc = Vpc.fromLookup(stack, process.env.VPC_ID, {
isDefault: false,
vpcId: process.env.VPC_ID,
});
const securityGroup = SecurityGroup.fromSecurityGroupId(
stack,
'SG',
process.env.SECURITY_GROUP
);
props.vpc = vpc;
props.subnets = vpc.publicSubnets;
props.securityGroups = [securityGroup];
}
return props;
});
new StreamStack(app, 'stream');
}
The one thing I didn't expect, is setting the subnets, where I'm referencing the public subnets of the vpc. This seems to actually filter those subnets OUT, as I got every other subnet except the public ones. Still works, as the acl's and security groups were enough to allow the traffic to atlas.
s
oh, this is cool! Not only is it useful to see how these are all configured together, I hadn't considered adding the configuration to the
app.setDefaultFunctionProps
before creating the stack.