diff --git a/README.md b/README.md index 0a42739..6142fd8 100644 --- a/README.md +++ b/README.md @@ -1 +1,10 @@ -# Dev cluster on AWS via AWS CDK and Cloudformation \ No newline at end of file +# Dev cluster on AWS via AWS CDK and Cloudformation + +## Inputs + +* domain e.g. dev.marketplace.sys.garden +* hosted zone id +``` +aws route53 list-hosted-zones-by-name --dns-name dev.marketplace.sys.garden +``` +* user arns or role arn to add to the aws-auth configmap \ No newline at end of file diff --git a/cdk.json b/cdk.json index 6594824..66e7f43 100644 --- a/cdk.json +++ b/cdk.json @@ -1,4 +1,9 @@ { "app": "npx ts-node main.ts", - "context": {} + "context": { + "hostedZoneId": "Z028702323WOQ31QJAJJP", + "subdomainName": "dev.marketplace.sys.garden", + "iamUsers": "", + "iamRole": "arn:aws:sts::049586690729:assumed-role/AWSReservedSSO_AdministratorAccess_b3c1cae6dc09120a" + } } \ No newline at end of file diff --git a/cluster.ts b/cluster.ts index d324351..f7bb176 100644 --- a/cluster.ts +++ b/cluster.ts @@ -1,21 +1,17 @@ import * as blueprints from "@aws-quickstart/eks-blueprints"; import { GlobalResources, - utils, ImportHostedZoneProvider, } from "@aws-quickstart/eks-blueprints"; import { Construct } from "constructs"; import { TeamPlatform } from "./teams"; import * as cdk from "aws-cdk-lib"; -import * as eks from "aws-cdk-lib/aws-eks"; import * as iam from "aws-cdk-lib/aws-iam"; import { ECRRepository } from "./ecr"; import { KubernetesVersion } from "aws-cdk-lib/aws-eks"; import { DeployImagePullSecret } from "./pullsecret"; - -//const burnhamManifestDir = './lib/teams/team-burnham/' -//const rikerManifestDir = './lib/teams/team-riker/' -//const teamManifestDirList = [burnhamManifestDir, rikerManifestDir] +import { ArnPrincipal } from "aws-cdk-lib/aws-iam"; +import { StackProps } from "aws-cdk-lib"; const accountID = process.env.CDK_DEFAULT_ACCOUNT!; const gitUrl = "https://github.com/aws-samples/eks-blueprints-workloads.git"; @@ -24,34 +20,61 @@ const gitUrl = "https://github.com/aws-samples/eks-blueprints-workloads.git"; * See docs/patterns/nginx.md for mode details on the setup. */ export default class DevCluster extends cdk.Stack { - async eksCluster(scope: Construct, id: string) { - const repoNames: string[] = ["api", "result", "vote", "worker"]; - // new ECRRepository(repoNames, this, 'garden-repo') + async eksCluster(scope: Construct, id: string, props: StackProps) { + const accountId = cdk.Stack.of(this).account + const region = cdk.Stack.of(this).region - const teams: Array = [new TeamPlatform(accountID)]; - const subdomain: string = utils.valueFromContext( - scope, - "dev.marketplace", - "sys.garden" - ); + const subdomainName = new cdk.CfnParameter(this, "subdomain", { + type: "String", + description: "The subdomain that can be used for ingress to the development environments\ + e.g. garden.mycompany.com. \ + Needs to be a hosted domain in Route53RecordTarget."}).valueAsString; - blueprints.HelmAddOn.validateHelmVersions = false; + //unfortunately we can't use this lookup for env agnostic stacks (need to specify region) + // https://docs.aws.amazon.com/cdk/v2/guide/resources.html#resources_external + // const domainID = route53.HostedZone.fromLookup(this, id, {domainName: subdomainName}) + + const hostedZoneId = new cdk.CfnParameter(this, "hostedZoneId", { + type: "AWS::Route53::HostedZone::Id", + description: "The ID of the Route53 hosted zone with the domain that can be used for ingress\ + to the development environments"}).valueAsString; + + const iamUsers = new cdk.CfnParameter(this, "iamUsers", { + type: "CommaDelimitedList", + default: "", + description: "Comma delimited list of IAM users principal ARNs that should get access to the dev cluster\ + e.g. \"arn:aws:iam::123456789012:user/JohnDoe,arn:aws:iam::123456789012:user/Alice\""}).valueAsList; + const iamRole = new cdk.CfnParameter(this, "iamRole", { + type: "String", + default: "arn:aws:sts::049586690729:assumed-role/AWSReservedSSO_AdministratorAccess_b3c1cae6dc09120a", + description: "A role that should get access to the dev cluster e.g.\ + \"arn:aws:iam::123456789023:role/AWSReservedSSO_PlatformEngineers_4ed12acae0543\""}).valueAsString; + + // use context variables for now because we cannot use CfnParameters + // for these values since we are manipulating the values in the code + const iamUsersArnsCtx = this.node.tryGetContext('iamUsers').split(",").map((item: string) => new ArnPrincipal(item)) + const iamRoleArnCtx = this.node.tryGetContext('iamRole') + const subdomainNameCtx = this.node.tryGetContext('subdomainName') + const hostedZoneIdCtx = this.node.tryGetContext('hostedZoneId') + const teams: Array = [new TeamPlatform({userRoleArn: iamRoleArnCtx, users: iamUsersArnsCtx})]; + + blueprints.HelmAddOn.validateHelmVersions = false; const cluster = await blueprints.EksBlueprint.builder() - .account("049586690729") - .region("eu-central-1") + .account(accountId) + .region(region) .version(KubernetesVersion.V1_24) .teams(...teams) .resourceProvider( GlobalResources.HostedZone, - new ImportHostedZoneProvider("Z028702323WOQ31QJAJJP", subdomain) + new ImportHostedZoneProvider(hostedZoneIdCtx, subdomainNameCtx) ) .resourceProvider( GlobalResources.Certificate, new blueprints.CreateCertificateProvider( "wildcard-cert", - "*.dev.marketplace.sys.garden", + `*${subdomainNameCtx}`, GlobalResources.HostedZone ) ) @@ -61,19 +84,19 @@ export default class DevCluster extends cdk.Stack { new blueprints.CertManagerAddOn(), new blueprints.AwsLoadBalancerControllerAddOn(), new blueprints.ExternalDnsAddOn({ - hostedZoneResources: [blueprints.GlobalResources.HostedZone], // you can add more if you register resource providers + hostedZoneResources: [blueprints.GlobalResources.HostedZone], }), new blueprints.NginxAddOn({ version: "0.15.2", internetFacing: true, backendProtocol: "tcp", - externalDnsHostname: subdomain, + externalDnsHostname: subdomainNameCtx, crossZoneEnabled: false, certificateResourceName: GlobalResources.Certificate, }), new blueprints.SecretsStoreAddOn({ rotationPollInterval: "120s" }), new blueprints.ClusterAutoScalerAddOn(), - new DeployImagePullSecret(), + new DeployImagePullSecret({accountId: accountId, region: region}), new blueprints.NestedStackAddOn({ builder: ECRRepository.builder(), id: "ecr-nested-stack" diff --git a/ecr.ts b/ecr.ts index 06380ac..1edf581 100644 --- a/ecr.ts +++ b/ecr.ts @@ -23,11 +23,6 @@ export class ECRRepository extends cdk.NestedStack { encryption: ecr.RepositoryEncryption.KMS, imageScanOnPush: true, })); - this.ecrRepos.push(new ecr.Repository(this, `${repo}/cache`, { - repositoryName: `garden-demo/${repo}/cache`, - encryption: ecr.RepositoryEncryption.KMS, - imageScanOnPush: true, - })); } } } \ No newline at end of file diff --git a/main.ts b/main.ts index 322393a..2453b1b 100644 --- a/main.ts +++ b/main.ts @@ -4,11 +4,8 @@ import { HelmAddOn } from "@aws-quickstart/eks-blueprints"; import DevCluster from "./cluster"; const app = new cdk.App(); -const account = "049586690729"; -const region = "eu-central-1"; -const env: cdk.Environment = { account: account, region: region }; HelmAddOn.validateHelmVersions = false; -new DevCluster().eksCluster(app, `dev-cluster`).catch(() => { +new DevCluster().eksCluster(app, `dev-cluster`, {crossRegionReferences: true}).catch(() => { logger.info("Error setting up dev cluster"); }); diff --git a/pullsecret.ts b/pullsecret.ts index 5dee0a3..c2fd0a3 100644 --- a/pullsecret.ts +++ b/pullsecret.ts @@ -1,13 +1,18 @@ -import { KubernetesManifest } from "aws-cdk-lib/aws-eks"; import { ClusterAddOn, ClusterInfo } from "@aws-quickstart/eks-blueprints"; -import * as blueprints from "@aws-quickstart/eks-blueprints"; + +export interface ImagePullSecretAddOnProps{ + readonly accountId: string + readonly region: string +} export class DeployImagePullSecret implements ClusterAddOn { + readonly imagePullSecretAddOnProps: ImagePullSecretAddOnProps; + constructor(imagePullSecretAddOnProps: ImagePullSecretAddOnProps){ + this.imagePullSecretAddOnProps = imagePullSecretAddOnProps + }; deploy(clusterInfo: ClusterInfo): void { - const accountID = process.env.CDK_DEFAULT_ACCOUNT!; const cluster = clusterInfo.cluster; - const region = "eu-central-1" - const ecrURL = `${accountID}.dkr.ecr.${region}.amazonaws.com` + const ecrURL = `${this.imagePullSecretAddOnProps.accountId}.dkr.ecr.${this.imagePullSecretAddOnProps.region}.amazonaws.com` let secretValue = { credHelpers: { [ecrURL]: "ecr-login" diff --git a/teams.ts b/teams.ts index 913f4d6..50882f2 100644 --- a/teams.ts +++ b/teams.ts @@ -1,10 +1,30 @@ -import { PlatformTeam } from "@aws-quickstart/eks-blueprints"; +import { PlatformTeam, TeamProps } from "@aws-quickstart/eks-blueprints"; +import { ArnPrincipal } from "aws-cdk-lib/aws-iam"; +interface TeamPlatformProps { + userRoleArn?: string + users?: ArnPrincipal[] +} export class TeamPlatform extends PlatformTeam { - constructor(accountID: string) { - super({ - name: "platform", - userRoleArn: `arn:aws:iam::${accountID}:role/AWSReservedSSO_AdministratorAccess_b3c1cae6dc09120a`, - }); + constructor(teamPlatformProps: TeamPlatformProps) { + if (teamPlatformProps.userRoleArn && teamPlatformProps.users){ + super({ + name: "platform", + userRoleArn: teamPlatformProps.userRoleArn, + users: teamPlatformProps.users + }); + } + if (!teamPlatformProps.userRoleArn && teamPlatformProps.users){ + super({ + name: "platform", + users: teamPlatformProps.users + }); + } + if (teamPlatformProps.userRoleArn && !teamPlatformProps.users){ + super({ + name: "platform", + userRoleArn: teamPlatformProps.userRoleArn + }); + } } }