Skip to content

Commit

Permalink
Add aws integration
Browse files Browse the repository at this point in the history
JadenSimon committed Jun 15, 2024
1 parent b352a24 commit 95f3083
Showing 40 changed files with 4,991 additions and 96 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -51,3 +51,4 @@ Each cloud target also uses their respective SDK + Terraform provider. The AWS t
* [AWS SDK for JavaScript v3](https://github.com/aws/aws-sdk-js-v3)
* [Terraform AWS Provider](https://github.com/hashicorp/terraform-provider-aws)

A few things were also inspired by [Bun](https://github.com/oven-sh/bun). Most notably is the usage of Zig.
1 change: 1 addition & 0 deletions docs/contributing.md
Original file line number Diff line number Diff line change
@@ -54,3 +54,4 @@ The tarballs in this directory contain services (or rather, service stubs) that
1. It's difficult to open-source _live_ services (but Synapse _will_ make it easier!)
2. They may become apart of a strategy to help fund the development of Synapse

Right now only "quotes" does anything. The rest are stubs and will fail if used.
34 changes: 34 additions & 0 deletions integrations/aws/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "synapse-aws",
"dependencies": {
"@aws-sdk/client-cloudfront": "^3.380.0",
"@aws-sdk/client-cloudwatch": "^3.445.0",
"@aws-sdk/client-cloudwatch-logs": "^3.445.0",
"@aws-sdk/client-dynamodb": "^3.379.1",
"@aws-sdk/client-ec2": "^3.379.1",
"@aws-sdk/client-ecr": "^3.379.1",
"@aws-sdk/client-ecr-public": "^3.388.0",
"@aws-sdk/client-ecs": "^3.379.1",
"@aws-sdk/client-eventbridge": "^3.379.1",
"@aws-sdk/client-kinesis": "^3.379.1",
"@aws-sdk/client-lambda": "^3.379.1",
"@aws-sdk/client-organizations": "^3.379.1",
"@aws-sdk/client-s3": "^3.379.1",
"@aws-sdk/client-secrets-manager": "^3.379.1",
"@aws-sdk/client-sqs": "^3.379.1",
"@aws-sdk/s3-request-presigner": "^3.574.0"
},
"exports": {
".": "./src/index.ts",
"./*": "./src/services/*.ts"
},
"types": "./src/index.d.ts",
"synapse": {
"config": {
"sharedLib": true
},
"providers": {
"aws": "*"
}
}
}
110 changes: 110 additions & 0 deletions integrations/aws/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import * as aws from 'synapse-provider:aws'
import * as core from 'synapse:core'
import { Account } from './services/organizations'
import { Role } from './services/iam'
import { Fn } from 'synapse:terraform'
import * as srl from 'synapse:srl'

// TODO: providers in Terraform should be treated as a special kind of
// resource rather than a separate class entirely. This will solve many
// problems as well as enable all sorts of useful functionality.
export class Provider extends aws.AwsProvider {
static readonly [core.contextType] = 'aws'

// We need initializers here so that the keys exist on the instance
private identity?: aws.CallerIdentityData = undefined
private regionData?: aws.RegionData = undefined
private partitionData?: aws.PartitionData = undefined
private _assetBucket?: aws.S3Bucket = undefined

public get arn() {
return (this.identity ??= new aws.CallerIdentityData()).arn
}

public get regionId() {
return (this.regionData ??= new aws.RegionData()).id
}

public get accountId() {
return (this.identity ??= new aws.CallerIdentityData()).accountId
}

public get partition() {
return (this.partitionData ??= new aws.PartitionData()).partition
}

public get roleArn() {
return this.assumeRole?.[0].roleArn
}

public get assetBucket() {
// TODO: only create 1 asset bucket per-account (or possibly per-user)
return this._assetBucket ??= new aws.S3Bucket({
bucketPrefix: 'asset-bucket',
})
}

public static fromAccount(account: Account) {
return this.fromRoleArn(getManagementRoleArn(account))
}

public static fromRoleArn(roleArn: string) {
return new this({ assumeRole: [{ roleArn }] })
}

public static withId(account: Account, id: string) {
const roleName = 'OrganizationAccountAccessRole'
const roleArn = `arn:aws:iam::${account.id}:role/${roleName}`

return new this(...([{ assumeRole: [{ roleArn }] }, id] as any[]))
}
}

// TODO: is this still needed?
Object.assign(Provider, { [core.contextType]: 'aws' })

core.addTarget(srl.Provider, Provider, 'aws')

export function getManagementRoleArn(account: Account) {
// TODO: partitions
return `arn:aws:iam::${account.id}:role/${account.managementRoleName}`
}


export function createCrossAccountRole(target: Account, principal?: Role) {
const awsPrincipal = principal ? principal.resource.arn : core.getContext(Provider).accountId
const roleName = core.getCurrentId().slice(0, 63) // XXX: we need an independent name to avoid circular deps
const role = core.using(Provider.fromAccount(target), () => {
return new Role({
name: roleName,
assumeRolePolicy: Fn.jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Principal": {
"AWS": awsPrincipal
},
}
]
})
})
})

principal?.addPolicy({
name: `Assume-${roleName}`,
policy: Fn.jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": `arn:${core.getContext(Provider).partition}:iam::${target.id}:role/${roleName}`
}
]
})
})

return role
}
Loading

0 comments on commit 95f3083

Please sign in to comment.