internal Database(CdkStack stack, Vpc vpc, SecurityGroup asgSecurityGroup) { var dbSecurityGroup = new SecurityGroup(vpc, "DBSecurityGroup", new SecurityGroupProps { Vpc = vpc, Description = "Allows database access to the specified." }); dbSecurityGroup.AddIngressRule(asgSecurityGroup, Port.Tcp(1433), "Allow SQL Server"); var db = new DatabaseInstance(stack, $"{stack.StackName}-DatabaseCluster", new DatabaseInstanceProps { Vpc = vpc, VpcPlacement = new SubnetSelection { SubnetType = SubnetType.PRIVATE }, SecurityGroups = new[] { dbSecurityGroup }, Engine = DatabaseInstanceEngine.SQL_SERVER_EX, MasterUsername = "******", AllocatedStorage = 20, MultiAz = false, InstanceType = InstanceType.Of(InstanceClass.BURSTABLE3, InstanceSize.SMALL), DeletionProtection = false }); DatabaseResource = db; ServerAddress = db.DbInstanceEndpointAddress; Password = db.Secret; }
internal CdkFargateStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { var vpc = new Vpc(this, "SatellytesVpc", new VpcProps { Cidr = "10.0.0.0/16", MaxAzs = 1, SubnetConfiguration = new[] { new SubnetConfiguration() { Name = "Satellytes/public", SubnetType = SubnetType.PUBLIC, } }, }); var cluster = new Cluster(this, "SatellytesCluster", new ClusterProps { Vpc = vpc, }); var taskDefinition = new TaskDefinition(this, "SatellytesWebTask", new TaskDefinitionProps { TaskRole = Role.FromRoleArn(this, "taskRole", "arn:aws:iam::576853867587:role/ecsTaskExecutionRole", new FromRoleArnOptions() { Mutable = false }), ExecutionRole = Role.FromRoleArn(this, "taskExecutionRole", "arn:aws:iam::576853867587:role/ecsTaskExecutionRole", new FromRoleArnOptions() { Mutable = false }), Compatibility = Compatibility.FARGATE, Cpu = "256", MemoryMiB = "512", }); var inboundSecurityGrp = new SecurityGroup(this, "satellytesSecurityGrpInboundInet", new SecurityGroupProps { Vpc = vpc }); inboundSecurityGrp.AddIngressRule(Peer.AnyIpv4(), Port.Tcp(8080), "inbound http"); taskDefinition.AddContainer("satellytesWebImage", new ContainerDefinitionProps { Image = ContainerImage.FromEcrRepository(Repository.FromRepositoryName(this, "repo", "satellytes-website/backend"), "1cfb651f73fcd20895fc44c06f7bb180ca0e8322"), }); new FargateService(this, "SatellytesWebService", new FargateServiceProps { Cluster = cluster, TaskDefinition = taskDefinition, VpcSubnets = new SubnetSelection { SubnetType = SubnetType.PUBLIC }, AssignPublicIp = true, }); }
public static void AddResourceAccessToRds(SecurityGroup securityGroup, SecurityGroup resource) { var ports = new Port[] { new Port(Utils.Ports.GetPortProps(3306, 3306, "MySQL/Auora")) }; foreach (var port in ports) { securityGroup.AddIngressRule(resource, port); } }
public ISecurityGroup Create(string identification, string groupName, bool allowGroupAllOutbound, IVpc vpc, IPeer ingressPeer, Port ingressPort, IPeer egressPeer, Port egressPort) { var securityGroup = new SecurityGroup(Scope, identification, new SecurityGroupProps { AllowAllOutbound = allowGroupAllOutbound, SecurityGroupName = groupName, Vpc = vpc }); securityGroup.AddIngressRule(ingressPeer, ingressPort); securityGroup.AddEgressRule(egressPeer, egressPort); return(securityGroup); }
public static SecurityGroup WithSecurityGroupRule(this SecurityGroup group, SecurityGroupRule rule) { if (rule is IngressRule) { group.AddIngressRule(rule.Peer, rule.Port, rule.Description, rule.RemoteRule); } else if (rule is EgressRule) { group.AddEgressRule(rule.Peer, rule.Port, rule.Description, rule.RemoteRule); } else { throw new SecurityGroupRuleException(rule); } return(group); }
internal AuroraDatabaseStack(Construct scope, string id, AuroraDatabaseStackProps props) : base(scope, id, props) { var defaultVpc = Vpc.FromLookup(this, "default", new VpcLookupOptions { IsDefault = true }); var securityGroup = new SecurityGroup(this, "aurora-sg", new SecurityGroupProps { AllowAllOutbound = true, Description = "Primary Aurora Serverless SG. Used by Lambdas", Vpc = defaultVpc }); securityGroup.AddIngressRule(securityGroup, Port.AllTraffic()); new CfnDBCluster(this, "aurora-cluster", new CfnDBClusterProps { Engine = "aurora", EngineMode = "serverless", Port = 3306, MasterUsername = "******", MasterUserPassword = "******", DeletionProtection = false, StorageEncrypted = true, VpcSecurityGroupIds = new string[] { securityGroup.SecurityGroupId }, ScalingConfiguration = new ScalingConfigurationProperty { AutoPause = true, MinCapacity = 1, MaxCapacity = 1, SecondsUntilAutoPause = 300 } }); new BastionHostLinux(this, "bastion-host", new BastionHostLinuxProps { Vpc = defaultVpc, SecurityGroup = securityGroup }); }
internal TargetInstanceStack(Construct scope, string id, Vpc vpc, string keyPairName, IStackProps props = null) : base(scope, id, props) { SecurityGroup = new SecurityGroup(this, "TargetInstance-Security-Group", new SecurityGroupProps { Vpc = vpc, AllowAllOutbound = true, Description = "TargetInstance-Security-Group", SecurityGroupName = "secgroup-" + id }); Role = new Role(this, "ec2-targetinstance-role", new RoleProps { AssumedBy = new ServicePrincipal("ec2.amazonaws.com") }); Role.AddManagedPolicy(ManagedPolicy.FromAwsManagedPolicyName("SecretsManagerReadWrite")); TargetInstance = new Instance_(this, id, new InstanceProps { InstanceType = InstanceType.Of(InstanceClass.BURSTABLE3, InstanceSize.MICRO), MachineImage = new WindowsImage(WindowsVersion.WINDOWS_SERVER_2019_ENGLISH_FULL_BASE), Vpc = vpc, UserData = UserData.Custom(Utils.GetResource("target_instance_user_data.ps1")), KeyName = keyPairName, Role = Role, VpcSubnets = new SubnetSelection { SubnetType = SubnetType.PRIVATE }, SecurityGroup = SecurityGroup }); SecurityGroup.AddIngressRule(Peer.AnyIpv4(), Port.AllTraffic(), "Allow all trafic in. In production - change this!"); new CfnOutput(this, "target-instance", new CfnOutputProps { Value = TargetInstance.InstancePrivateIp }); }
internal NorthwindCdkStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { var vpc = new Vpc(this, "LabVpc", new VpcProps { MaxAzs = 2 }); // SQL Server var sg = new SecurityGroup(this, "NorthwindDatabaseSecurityGroup", new SecurityGroupProps { Vpc = vpc, SecurityGroupName = "Northwind-DB-SG", AllowAllOutbound = false }); // !!!!!!!!!! replace IP according to the instructions above sg.AddIngressRule(Peer.Ipv4("35.171.193.180/32"), Port.Tcp(1433)); // SQL Server // !!!!!!!!!! var sql = new DatabaseInstance(this, "NorthwindSQLServer", new DatabaseInstanceProps { Vpc = vpc, InstanceIdentifier = "northwind-sqlserver", Engine = DatabaseInstanceEngine.SqlServerEx(new SqlServerExInstanceEngineProps { Version = SqlServerEngineVersion.VER_14 }), // SQL Server Express Credentials = Credentials.FromUsername("adminuser", new CredentialsFromUsernameOptions() { Password = new SecretValue("Admin12345?") }), //MasterUsername = "******", //MasterUserPassword = new SecretValue("Admin12345?"), InstanceType = InstanceType.Of(InstanceClass.BURSTABLE3, InstanceSize.SMALL), // t3.small SecurityGroups = new ISecurityGroup[] { sg }, MultiAz = false, VpcSubnets = new SubnetSelection() { SubnetType = SubnetType.PUBLIC }, // public subnet DeletionProtection = false, // you need to be able to delete database DeleteAutomatedBackups = true, BackupRetention = Duration.Days(0), RemovalPolicy = RemovalPolicy.DESTROY // you need to be able to delete database });; new CfnOutput(this, "SQLServerEndpointAddress", new CfnOutputProps { Value = sql.DbInstanceEndpointAddress }); // SQL Server connection string in Systems Manager Parameter Store new StringParameter(this, "NorthwindDatabaseConnectionString", new StringParameterProps { ParameterName = "/Northwind/ConnectionStrings/NorthwindDatabase", Type = ParameterType.STRING, Description = "SQL Server connection string", StringValue = string.Format("Server={0},1433;Integrated Security=false;User ID=adminuser;Password=Admin12345?;Initial Catalog=NorthwindTraders;", sql.DbInstanceEndpointAddress) }); // PostgreSQL setup // !!!!!!!!!! add 2 rules when you use provided VM, add 1 rule when you use your computer sg.AddIngressRule(Peer.Ipv4("35.171.193.180/32"), Port.Tcp(5432)); // PostgreSQL sg.AddIngressRule(Peer.Ipv4("3.238.53.13/32"), Port.Tcp(5432)); // PostgreSQL // !!!!!!!!!! var postgreSql = new DatabaseCluster(this, "NorthwindPostgreSQL", new DatabaseClusterProps { InstanceProps = new Amazon.CDK.AWS.RDS.InstanceProps { Vpc = vpc, InstanceType = InstanceType.Of(InstanceClass.BURSTABLE3, InstanceSize.MEDIUM), // t3.medium SecurityGroups = new ISecurityGroup[] { sg }, VpcSubnets = new SubnetSelection() { SubnetType = SubnetType.PUBLIC }, // you need to access database from your developer PC ParameterGroup = ParameterGroup.FromParameterGroupName(this, "DBInstanceParameterGroup", "default.aurora-postgresql11"), }, ParameterGroup = ParameterGroup.FromParameterGroupName(this, "DBClusterParameterGroup", "default.aurora-postgresql11"), ClusterIdentifier = "northwind-postgresql", Engine = DatabaseClusterEngine.AuroraPostgres(new AuroraPostgresClusterEngineProps { Version = AuroraPostgresEngineVersion.VER_11_6 }), // Aurora PostgreSQL Credentials = Credentials.FromUsername("adminuser", new CredentialsFromUsernameOptions { Password = new SecretValue("Admin12345?") }), //MasterUser = new Login //{ // Username = "******", // Password = new SecretValue("Admin12345?") //}, Instances = 1, Port = 5432, Backup = new BackupProps { Retention = Duration.Days(1) // minimum is 1 }, DefaultDatabaseName = "NorthwindTraders", InstanceIdentifierBase = "northwind-postgresql-instance", RemovalPolicy = RemovalPolicy.DESTROY // you need to be able to delete database, });; new CfnOutput(this, "PostgreSQLEndpointAddress", new CfnOutputProps { Value = postgreSql.ClusterEndpoint.Hostname }); // Aurora PostgreSQL connection string in Systems Manager Parameter Store new StringParameter(this, "NorthwindPostgreSQLDatabaseConnectionString", new StringParameterProps { ParameterName = "/Northwind/ConnectionStrings/NorthwindPostgreDatabase", Type = ParameterType.STRING, Description = "PostgreSQL connection string", StringValue = string.Format("Server={0};Database=NorthwindTraders;Username=adminuser;Password=Admin12345?", postgreSql.ClusterEndpoint.Hostname) }); }
internal InfraStack(Construct scope, string id, CustomStackProps props = null) : base(scope, id, props) { var vpc = props.Vpc; var cluster = new Cluster(this, "Cluster", new ClusterProps { Vpc = vpc, ClusterName = Globals.GetDeployEnvironment(this).PutEnvNamePrefixWithDash("Cluster") }); var albSecurityGroup = new SecurityGroup(this, "AlbSecurityGroup", new SecurityGroupProps { Vpc = vpc, AllowAllOutbound = true }); albSecurityGroup.AddIngressRule(Peer.AnyIpv4(), Port.Tcp(80)); var alb = new ApplicationLoadBalancer(this, "ALB", new ApplicationLoadBalancerProps { Vpc = vpc, InternetFacing = true, Http2Enabled = true, IdleTimeout = Duration.Seconds(60), IpAddressType = IpAddressType.IPV4, SecurityGroup = albSecurityGroup }); var webApiServiceSecurityGroup = new SecurityGroup(this, "WebApiServiceSecurityGroup", new SecurityGroupProps { Vpc = vpc, AllowAllOutbound = true }); webApiServiceSecurityGroup.AddIngressRule(albSecurityGroup, Port.Tcp(80)); var appListener = alb.AddListener("AppListener", new BaseApplicationListenerProps { Port = 80, Protocol = ApplicationProtocol.HTTP, DefaultAction = ListenerAction.FixedResponse(404, new FixedResponseOptions { ContentType = "text/plain", MessageBody = "This is not here..." }) }); new CfnOutput(this, "ClusterName", new CfnOutputProps { ExportName = Globals.GetDeployEnvironment(this).PutEnvNamePrefixWithDash("ClusterName"), Value = cluster.ClusterName }); new CfnOutput(this, "WebApiServiceSecurityGroupId", new CfnOutputProps { ExportName = Globals.GetDeployEnvironment(this).PutEnvNamePrefixWithDash("WebApiServiceSecurityGroupId"), Value = albSecurityGroup.SecurityGroupId }); new CfnOutput(this, "AppListenerArn", new CfnOutputProps { ExportName = Globals.GetDeployEnvironment(this).PutEnvNamePrefixWithDash("AppListenerArn"), Value = appListener.ListenerArn }); }
private (FargateService, Amazon.CDK.AWS.ServiceDiscovery.IService) CreateService(string id, string serviceName, string branch, IVpc vpc, Cluster cluster, PrivateDnsNamespace cloudMapNamespace, string appMeshVirtualNodeName, Mesh mesh, IRole taskExecutionRole, RepositoryImage envoyImage, ContainerImage containerImage) { var taskDefinition = new TaskDefinition(this, $"{id}_{serviceName}-{branch}-task-definiton", new TaskDefinitionProps { Compatibility = Compatibility.FARGATE, MemoryMiB = "512", Cpu = "256", ProxyConfiguration = new AppMeshProxyConfiguration(new AppMeshProxyConfigurationConfigProps() { ContainerName = "envoy", Properties = new AppMeshProxyConfigurationProps() { AppPorts = new double[1] { 5000 }, ProxyIngressPort = 15000, ProxyEgressPort = 15001, IgnoredUID = 1337, EgressIgnoredIPs = new string[2] { "169.254.170.2", "169.254.169.254" } } }), Family = $"{id}_{serviceName}-task-definition", ExecutionRole = taskExecutionRole }); var envoyContainer = taskDefinition.AddContainer("envoy", new ContainerDefinitionOptions { User = "******", Image = envoyImage, Essential = true, Environment = new Dictionary <string, string> { { "APPMESH_VIRTUAL_NODE_NAME", $"mesh/{mesh.MeshName}/virtualNode/{appMeshVirtualNodeName}" } }, HealthCheck = new Amazon.CDK.AWS.ECS.HealthCheck() { Command = new string[] { "CMD-SHELL", "curl -s http://localhost:9901/server_info | grep state | grep -q LIVE" }, Interval = Duration.Seconds(5), Timeout = Duration.Seconds(2), StartPeriod = Duration.Seconds(10), Retries = 3 }, MemoryLimitMiB = 500, Logging = new AwsLogDriver(new AwsLogDriverProps() { StreamPrefix = $"{id}_{serviceName}-{branch}-envoy", LogRetention = RetentionDays.ONE_DAY }), }); var container = taskDefinition.AddContainer($"{serviceName}-container", new ContainerDefinitionOptions() { Image = containerImage, Logging = new AwsLogDriver(new AwsLogDriverProps() { StreamPrefix = $"{id}_{serviceName}-{branch}-service", LogRetention = RetentionDays.ONE_DAY }), Essential = true, Environment = new Dictionary <string, string> { { "BRANCH", branch }, { "APPMESH_NAMESPACE", cloudMapNamespace.PrivateDnsNamespaceName } } }); container.AddPortMappings(new Amazon.CDK.AWS.ECS.PortMapping() { ContainerPort = 5000 }); container.AddContainerDependencies(new ContainerDependency() { Condition = ContainerDependencyCondition.HEALTHY, Container = envoyContainer }); // Cloudmap will append the namespace to the dns entry in R53. // We're explicitly checking for master here because for service to service lookups to go via the envoy proxy, the DNS name must resolve. // see https://github.com/aws/aws-app-mesh-roadmap/issues/65 // i.e I want the ping service to call http://pong-service.{namespace}:5000/ and for this to be routed correctly by the proxy. // If you create the fargate task with cloudmap service integration with a more specific (branched) DNS name then pong-service.{namespace} r53 entry will never be created // and routing doesn't work through envoy. var dnsName = $"{serviceName}-service{(branch == "master" ? "" : "-" + branch)}"; var sg = new SecurityGroup(this, $"{id}_{serviceName}-{branch}-sg", new SecurityGroupProps() { AllowAllOutbound = true, SecurityGroupName = $"{id}_{serviceName}-{branch}-sg", Vpc = vpc, }); sg.AddIngressRule(Peer.AnyIpv4(), new Port(new PortProps() { Protocol = Amazon.CDK.AWS.EC2.Protocol.TCP, FromPort = 5000, ToPort = 5000, StringRepresentation = "tcp:5000:5000" }), "allow access from outside."); var fargateService = new Amazon.CDK.AWS.ECS.FargateService(this, $"{serviceName}-{branch}-service", new FargateServiceProps { ServiceName = $"{serviceName}-{branch}-service", AssignPublicIp = true, Cluster = cluster, TaskDefinition = taskDefinition, VpcSubnets = new SubnetSelection() { Subnets = vpc.PublicSubnets }, CloudMapOptions = new CloudMapOptions() { Name = dnsName, DnsRecordType = DnsRecordType.A, CloudMapNamespace = cloudMapNamespace }, SecurityGroup = sg }); return(fargateService, fargateService.CloudMapService); }
internal EksCdkStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { var clusterAdmin = new Role(this, Constants.ADMIN_ROLE, new RoleProps { AssumedBy = new AccountRootPrincipal() }); IVpc vpc = new Vpc(this, Constants.VPC_ID, new VpcProps { Cidr = Constants.VPC_CIDR }); var cluster = new Cluster(this, Constants.CLUSTER_ID, new ClusterProps { MastersRole = clusterAdmin, Version = KubernetesVersion.V1_16, KubectlEnabled = true, DefaultCapacity = 0, Vpc = vpc }); var tags = new Dictionary <string, string>(); tags.Add("name", Constants.CDK8s); var eksEC2sNodeGroup = cluster.AddNodegroup(Constants.CLUSTER_NODE_GRP_ID, new NodegroupOptions { InstanceType = new InstanceType(Constants.EC2_INSTANCE_TYPE), MinSize = 2, Subnets = new SubnetSelection { Subnets = vpc.PrivateSubnets }, Tags = tags }); string[] ManagedPolicyArns = GetNodeRoleManagedPolicyARNs(); foreach (string arn in ManagedPolicyArns) { eksEC2sNodeGroup.Role.AddManagedPolicy(ManagedPolicy.FromAwsManagedPolicyName(arn)); } var repository = new ecr.Repository(this, Constants.ECR_REPOSITORY_ID, new ecr.RepositoryProps { RepositoryName = Constants.ECR_REPOSITORY_NAME }); #region Aurora Database var secGrp = new SecurityGroup(this, Constants.DATABASE_SECURITY_GRP, new SecurityGroupProps { Vpc = vpc }); var eksSecGrp = ec2.SecurityGroup.FromSecurityGroupId(this, Constants.EKS_SECURITY_GRP, cluster.ClusterSecurityGroupId); secGrp.AddIngressRule(eksSecGrp, ec2.Port.Tcp(3306), description: Constants.EC2_INGRESS_DESCRIPTION); var privateSubnets = new List <string>(); foreach (Subnet subnet in vpc.PrivateSubnets) { privateSubnets.Add(subnet.SubnetId); } var dbsubnetGroup = new rds.CfnDBSubnetGroup(this, Constants.AURORA_DB_SUBNET_ID, new rds.CfnDBSubnetGroupProps { DbSubnetGroupDescription = Constants.AURORA_DB_SUBNET_DESCRIPTION, DbSubnetGroupName = Constants.AURORA_DB_SUBNET_GROUP_NAME, SubnetIds = privateSubnets.ToArray() }); List <CfnTag> cfnDbSecurityGroupTag = new List <CfnTag>(); CfnTag tagName = new CfnTag() { Key = "Name", Value = Constants.APP_NAME }; cfnDbSecurityGroupTag.Add(tagName); var dbSecurityGroup = new CfnSecurityGroup(this, Constants.AURORA_CFN_SG_ID, new CfnSecurityGroupProps { VpcId = vpc.VpcId, GroupName = Constants.AURORA_GROUP_NAME, GroupDescription = "Access to the RDS", Tags = cfnDbSecurityGroupTag.ToArray() } ); var cfnSecurityGroupIngress = new ec2.CfnSecurityGroupIngress( this, Constants.AURORA_SG_INGRESS, new ec2.CfnSecurityGroupIngressProps { Description = Constants.AURORA_SG_INGRESS_DESCRIPTION, FromPort = Constants.AURORA_PORT, ToPort = Constants.AURORA_PORT, IpProtocol = Constants.CONTAINER_PROTOCOL, SourceSecurityGroupId = eksSecGrp.SecurityGroupId, GroupId = dbSecurityGroup.AttrGroupId }); var dbcluster = new rds.CfnDBCluster(this, Constants.AURORA_TODO_DATABASE, new rds.CfnDBClusterProps { Engine = Constants.AURORA_DB_ENGINE, EngineMode = Constants.AURORA_ENGINE_MODE, Port = Constants.AURORA_PORT, MasterUsername = Constants.DB_USER_VALUE, MasterUserPassword = Constants.DB_PASSWORD_VALUE, DbSubnetGroupName = Constants.AURORA_DB_SUBNET_GROUP_NAME, DatabaseName = Constants.DB_NAME_VALUE, VpcSecurityGroupIds = new string[] { dbSecurityGroup.AttrGroupId } }); dbcluster.DbClusterIdentifier = Constants.AURORA_TODO_DATABASE; dbcluster.AddDependsOn(dbsubnetGroup); dbcluster.CfnOptions.DeletionPolicy = CfnDeletionPolicy.DELETE; #endregion #region SSM StringBuilder connString = new StringBuilder(); connString.AppendFormat("server={0}", dbcluster.AttrEndpointAddress); connString.AppendFormat(";port={0}", Constants.AURORA_PORT); connString.AppendFormat(";database={0}", Constants.DB_NAME_VALUE); connString.AppendFormat(";user={0}", Constants.DB_USER_VALUE); connString.AppendFormat(";password={0}", Constants.DB_PASSWORD_VALUE); new ssm.StringParameter(this, "Parameter", new ssm.StringParameterProps { Description = "Maintains the Aurora Database Connection String", ParameterName = Constants.SSM_DB_CONN_STRING, StringValue = connString.ToString(), Tier = ssm.ParameterTier.ADVANCED }); #endregion }
internal CdkStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { var targetPlatform = new CfnParameter(this, "TargetPlatform", new CfnParameterProps { AllowedValues = new[] { "Linux", "Windows" }, Type = "String", Default = "Linux" }); var vpc = new Vpc(this, "VPC", new VpcProps { Cidr = "10.0.0.0/16", MaxAzs = 2, NatGatewayProvider = NatProvider.Gateway(), NatGateways = 2, NatGatewaySubnets = new SubnetSelection { SubnetType = SubnetType.PUBLIC, OnePerAz = true }, SubnetConfiguration = new[] { new SubnetConfiguration { CidrMask = 24, Name = "PublicSubnet", SubnetType = SubnetType.PUBLIC }, new SubnetConfiguration { CidrMask = 24, Name = "PrivateSubnet", SubnetType = SubnetType.PRIVATE }, } }); var externalLoadBalancerSecurityGroup = new SecurityGroup(vpc, "ExternalLoadBalancerSecurityGroup", new SecurityGroupProps { Vpc = vpc, Description = "Allows HTTP access to the application." }); externalLoadBalancerSecurityGroup.AddIngressRule(externalLoadBalancerSecurityGroup, Port.Tcp(80), "Allow HTTP"); var frontEndSecurityGroup = new SecurityGroup(vpc, "UISecurityGroup", new SecurityGroupProps { Vpc = vpc, Description = "Allows HTTP access to the UI." }); frontEndSecurityGroup.AddIngressRule(externalLoadBalancerSecurityGroup, Port.Tcp(80), "Allow HTTP"); var internalLoadBalancerSecurityGroup = new SecurityGroup(vpc, "InternalLoadBalancerSecurityGroup", new SecurityGroupProps { Vpc = vpc, Description = "Allows HTTP access to the REST API." }); internalLoadBalancerSecurityGroup.AddIngressRule(frontEndSecurityGroup, Port.Tcp(80)); var restApiSecurityGroup = new SecurityGroup(vpc, "ApiSecurityGroup", new SecurityGroupProps { Vpc = vpc, Description = "Allows HTTP access to the Rest API." }); restApiSecurityGroup.AddIngressRule(internalLoadBalancerSecurityGroup, Port.Tcp(80)); var db = new Database(this, vpc, restApiSecurityGroup); var policy = new Amazon.CDK.AWS.IAM.Policy(this, "DBPasswordSecretAccess", new Amazon.CDK.AWS.IAM.PolicyProps { PolicyName = "AllowPasswordAccess", Statements = new[] { new Amazon.CDK.AWS.IAM.PolicyStatement(new Amazon.CDK.AWS.IAM.PolicyStatementProps { Effect = Amazon.CDK.AWS.IAM.Effect.ALLOW, Actions = new [] { "secretsmanager:GetSecretValue" }, Resources = new [] { db.Password.SecretArn } }) } }); var restApiInstances = new AutoScaledInstances(this, targetPlatform, vpc, false, internalLoadBalancerSecurityGroup, restApiSecurityGroup, db, policy: policy); var frontEndInstances = new AutoScaledInstances(this, targetPlatform, vpc, true, externalLoadBalancerSecurityGroup, frontEndSecurityGroup, restApiLoadBalancer: restApiInstances.Result.LoadBalancer); //var instances = new AutoScaledInstances(this, targetPlatform, vpc, appSecurityGroup); new CICD(this, targetPlatform, frontEndInstances.Result, restApiInstances.Result); }
internal CdkStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { var stackProps = ReportStackProps.ParseOrDefault(props); var dframeWorkerLogGroup = "MagicOnionBenchWorkerLogGroup"; var dframeMasterLogGroup = "MagicOnionBenchMasterLogGroup"; var benchNetwork = stackProps.GetBenchNetwork(); var recreateMagicOnionTrigger = stackProps.GetBenchmarkServerBinariesHash(); // s3 var s3 = new Bucket(this, "Bucket", new BucketProps { AutoDeleteObjects = true, RemovalPolicy = RemovalPolicy.DESTROY, AccessControl = BucketAccessControl.PRIVATE, }); var lifecycleRule = new LifecycleRule { Enabled = true, Prefix = "reports/", Expiration = Duration.Days(stackProps.DaysKeepReports), AbortIncompleteMultipartUploadAfter = Duration.Days(1), }; s3.AddLifecycleRule(lifecycleRule); s3.AddToResourcePolicy(new PolicyStatement(new PolicyStatementProps { Sid = "AllowPublicRead", Effect = Effect.ALLOW, Principals = new[] { new AnyPrincipal() }, Actions = new[] { "s3:GetObject*" }, Resources = new[] { $"{s3.BucketArn}/html/*" }, })); s3.AddToResourcePolicy(new PolicyStatement(new PolicyStatementProps { Sid = "AllowAwsAccountAccess", Effect = Effect.ALLOW, Principals = new[] { new AccountRootPrincipal() }, Actions = new[] { "s3:*" }, Resources = new[] { $"{s3.BucketArn}/*" }, })); // s3 deploy var masterDllDeployment = new BucketDeployment(this, "DeployMasterDll", new BucketDeploymentProps { DestinationBucket = s3, Sources = new[] { Source.Asset(Path.Combine(Directory.GetCurrentDirectory(), $"out/linux/server")) }, DestinationKeyPrefix = $"assembly/linux/server" }); var userdataDeployment = new BucketDeployment(this, "UserData", new BucketDeploymentProps { DestinationBucket = s3, Sources = new[] { Source.Asset(Path.Combine(Directory.GetCurrentDirectory(), "userdata/")) }, DestinationKeyPrefix = "userdata/" }); // docker deploy var dockerImage = new DockerImageAsset(this, "dframeWorkerImage", new DockerImageAssetProps { Directory = Path.Combine(Directory.GetCurrentDirectory(), "app"), File = "ConsoleAppEcs/Dockerfile.Ecs", }); var dframeImage = ContainerImage.FromDockerImageAsset(dockerImage); // network var vpc = new Vpc(this, "Vpc", new VpcProps { MaxAzs = 2, NatGateways = 0, SubnetConfiguration = new[] { new SubnetConfiguration { Name = "public", SubnetType = SubnetType.PUBLIC } }, }); var allsubnets = new SubnetSelection { Subnets = vpc.PublicSubnets }; var singleSubnets = new SubnetSelection { Subnets = new[] { vpc.PublicSubnets.First() } }; var sg = new SecurityGroup(this, "MasterSg", new SecurityGroupProps { AllowAllOutbound = true, Vpc = vpc, }); foreach (var subnet in vpc.PublicSubnets) { sg.AddIngressRule(Peer.Ipv4(vpc.VpcCidrBlock), Port.AllTcp(), "VPC", true); } // service discovery var serviceDiscoveryDomain = "local"; var serverMapName = "server"; var dframeMapName = "dframe-master"; var ns = new PrivateDnsNamespace(this, "Namespace", new PrivateDnsNamespaceProps { Vpc = vpc, Name = serviceDiscoveryDomain, }); var serviceDiscoveryServer = ns.CreateService("server", new DnsServiceProps { Name = serverMapName, DnsRecordType = DnsRecordType.A, RoutingPolicy = RoutingPolicy.MULTIVALUE, }); // alb var albDnsName = "benchmark-alb"; var benchToMagicOnionDnsName = benchNetwork.RequireAlb ? $"{benchNetwork.EndpointScheme}://{albDnsName}.{stackProps.AlbDomain.domain}" : $"{benchNetwork.EndpointScheme}://{serverMapName}.{serviceDiscoveryDomain}"; IApplicationTargetGroup grpcTargetGroup = null; IApplicationTargetGroup httpsTargetGroup = null; if (benchNetwork.RequireAlb) { // route53 var hostedZone = HostedZone.FromHostedZoneAttributes(this, "HostedZone", new HostedZoneAttributes { HostedZoneId = stackProps.AlbDomain.zoneId, ZoneName = stackProps.AlbDomain.domain, }); // acm var certificate = new DnsValidatedCertificate(this, "certificate", new DnsValidatedCertificateProps { DomainName = $"{albDnsName}.{hostedZone.ZoneName}", HostedZone = hostedZone, }); // alb var lb = new ApplicationLoadBalancer(this, "LB", new ApplicationLoadBalancerProps { Vpc = vpc, VpcSubnets = allsubnets, SecurityGroup = new SecurityGroup(this, "AlbSg", new SecurityGroupProps { AllowAllOutbound = true, Vpc = vpc, }), InternetFacing = false, Http2Enabled = true, }); grpcTargetGroup = AddGrpcTargetGroup(benchNetwork, vpc, certificate, lb); httpsTargetGroup = AddHttpsTargetGroup(benchNetwork, vpc, certificate, lb); // Dns Record _ = new CnameRecord(this, "alb-alias-record", new CnameRecordProps { RecordName = $"{albDnsName}.{stackProps.AlbDomain.domain}", Ttl = Duration.Seconds(60), Zone = hostedZone, DomainName = lb.LoadBalancerDnsName, }); } // iam var iamEc2MagicOnionRole = GetIamEc2MagicOnionRole(s3, serviceDiscoveryServer); var iamEcsTaskExecuteRole = GetIamEcsTaskExecuteRole(new[] { dframeWorkerLogGroup, dframeMasterLogGroup }); var iamDFrameTaskDefRole = GetIamEcsDframeTaskDefRole(s3); var iamWorkerTaskDefRole = GetIamEcsWorkerTaskDefRole(s3); // secrets var ddToken = stackProps.UseEc2DatadogAgentProfiler || stackProps.UseFargateDatadogAgentProfiler ? Amazon.CDK.AWS.SecretsManager.Secret.FromSecretNameV2(this, "dd-token", "magiconion-benchmark-datadog-token") : null; // MagicOnion var asg = new AutoScalingGroup(this, "MagicOnionAsg", new AutoScalingGroupProps { // Monitoring is default DETAILED. SpotPrice = "1.0", // 0.0096 for spot price average for m3.medium Vpc = vpc, SecurityGroup = sg, VpcSubnets = singleSubnets, InstanceType = stackProps.MagicOnionInstanceType, DesiredCapacity = 1, MaxCapacity = 1, MinCapacity = 0, AssociatePublicIpAddress = true, MachineImage = new AmazonLinuxImage(new AmazonLinuxImageProps { CpuType = AmazonLinuxCpuType.X86_64, Generation = AmazonLinuxGeneration.AMAZON_LINUX_2, Storage = AmazonLinuxStorage.GENERAL_PURPOSE, Virtualization = AmazonLinuxVirt.HVM, }), AllowAllOutbound = true, GroupMetrics = new[] { GroupMetrics.All() }, Role = iamEc2MagicOnionRole, UpdatePolicy = UpdatePolicy.ReplacingUpdate(), Signals = Signals.WaitForCount(1, new SignalsOptions { Timeout = Duration.Minutes(10), }), }); asg.AddSecretsReadGrant(ddToken, () => stackProps.UseEc2DatadogAgentProfiler); var userdata = GetUserData(recreateMagicOnionTrigger, s3.BucketName, stackProps.BenchmarkBinaryNames, serviceDiscoveryServer.ServiceId, stackProps.UseEc2CloudWatchAgentProfiler, stackProps.UseEc2DatadogAgentProfiler); asg.AddUserData(userdata); asg.UserData.AddSignalOnExitCommand(asg); asg.Node.AddDependency(masterDllDeployment); asg.Node.AddDependency(userdataDeployment); if (stackProps.EnableMagicOnionScaleInCron) { asg.ScaleOnSchedule("ScheduleOut", new BasicScheduledActionProps { DesiredCapacity = 1, MaxCapacity = 1, // AM9:00 (JST+9) on Monday to Wednesday Schedule = Schedule.Expression("0 0 * 1-3 *"), }); asg.ScaleOnSchedule("ScheduleIn", new BasicScheduledActionProps { DesiredCapacity = 0, MaxCapacity = 0, // PM9:00 (JST+9) on Everyday Schedule = Schedule.Expression("0 12 * 1-7 *"), }); } if (benchNetwork.RequireAlb) { asg.AttachToApplicationTargetGroup(grpcTargetGroup); asg.AttachToApplicationTargetGroup(httpsTargetGroup); } // ECS var cluster = new Cluster(this, "WorkerCluster", new ClusterProps { Vpc = vpc, }); cluster.Node.AddDependency(asg); // wait until asg is up // dframe-worker var dframeWorkerContainerName = "worker"; var dframeWorkerTaskDef = new FargateTaskDefinition(this, "DFrameWorkerTaskDef", new FargateTaskDefinitionProps { ExecutionRole = iamEcsTaskExecuteRole, TaskRole = iamWorkerTaskDefRole, Cpu = stackProps.WorkerFargate.CpuSize, MemoryLimitMiB = stackProps.WorkerFargate.MemorySize, }); dframeWorkerTaskDef.AddContainer(dframeWorkerContainerName, new ContainerDefinitionOptions { Image = dframeImage, Command = new[] { "--worker-flag" }, Environment = new Dictionary <string, string> { { "DFRAME_MASTER_CONNECT_TO_HOST", $"{dframeMapName}.{serviceDiscoveryDomain}" }, { "DFRAME_MASTER_CONNECT_TO_PORT", "12345" }, { "BENCH_SERVER_HOST", benchToMagicOnionDnsName }, { "BENCH_REPORTID", stackProps.ReportId }, { "BENCH_S3BUCKET", s3.BucketName }, }, Logging = LogDriver.AwsLogs(new AwsLogDriverProps { LogGroup = new LogGroup(this, "WorkerLogGroup", new LogGroupProps { LogGroupName = dframeWorkerLogGroup, RemovalPolicy = RemovalPolicy.DESTROY, Retention = RetentionDays.TWO_WEEKS, }), StreamPrefix = dframeWorkerLogGroup, }), }); dframeWorkerTaskDef.AddDatadogContainer($"{dframeWorkerContainerName}-datadog", ddToken, () => stackProps.UseFargateDatadogAgentProfiler); var dframeWorkerService = new FargateService(this, "DFrameWorkerService", new FargateServiceProps { ServiceName = "DFrameWorkerService", DesiredCount = 0, Cluster = cluster, TaskDefinition = dframeWorkerTaskDef, VpcSubnets = singleSubnets, SecurityGroups = new[] { sg }, PlatformVersion = FargatePlatformVersion.VERSION1_4, MinHealthyPercent = 0, AssignPublicIp = true, }); // dframe-master var dframeMasterTaskDef = new FargateTaskDefinition(this, "DFrameMasterTaskDef", new FargateTaskDefinitionProps { ExecutionRole = iamEcsTaskExecuteRole, TaskRole = iamDFrameTaskDefRole, Cpu = stackProps.MasterFargate.CpuSize, MemoryLimitMiB = stackProps.MasterFargate.MemorySize, }); dframeMasterTaskDef.AddContainer("dframe", new ContainerDefinitionOptions { Image = dframeImage, Environment = new Dictionary <string, string> { { "DFRAME_CLUSTER_NAME", cluster.ClusterName }, { "DFRAME_MASTER_SERVICE_NAME", "DFrameMasterService" }, { "DFRAME_WORKER_CONTAINER_NAME", dframeWorkerContainerName }, { "DFRAME_WORKER_SERVICE_NAME", dframeWorkerService.ServiceName }, { "DFRAME_WORKER_TASK_NAME", Fn.Select(1, Fn.Split("/", dframeWorkerTaskDef.TaskDefinitionArn)) }, { "DFRAME_WORKER_IMAGE", dockerImage.ImageUri }, { "BENCH_REPORTID", stackProps.ReportId }, { "BENCH_S3BUCKET", s3.BucketName }, }, Logging = LogDriver.AwsLogs(new AwsLogDriverProps { LogGroup = new LogGroup(this, "MasterLogGroup", new LogGroupProps { LogGroupName = dframeMasterLogGroup, RemovalPolicy = RemovalPolicy.DESTROY, Retention = RetentionDays.TWO_WEEKS, }), StreamPrefix = dframeMasterLogGroup, }), }); dframeMasterTaskDef.AddDatadogContainer($"dframe-datadog", ddToken, () => stackProps.UseFargateDatadogAgentProfiler); var dframeMasterService = new FargateService(this, "DFrameMasterService", new FargateServiceProps { ServiceName = "DFrameMasterService", DesiredCount = 1, Cluster = cluster, TaskDefinition = dframeMasterTaskDef, VpcSubnets = singleSubnets, SecurityGroups = new[] { sg }, PlatformVersion = FargatePlatformVersion.VERSION1_4, MinHealthyPercent = 0, AssignPublicIp = true, }); dframeMasterService.EnableCloudMap(new CloudMapOptions { CloudMapNamespace = ns, Name = dframeMapName, DnsRecordType = DnsRecordType.A, DnsTtl = Duration.Seconds(300), }); // output new CfnOutput(this, "ReportUrl", new CfnOutputProps { Value = $"https://{s3.BucketRegionalDomainName}/html/{stackProps.ReportId}/index.html" }); new CfnOutput(this, "EndPointStyle", new CfnOutputProps { Value = stackProps.BenchmarkEndpoint.ToString() }); new CfnOutput(this, "AsgName", new CfnOutputProps { Value = asg.AutoScalingGroupName }); new CfnOutput(this, "EcsClusterName", new CfnOutputProps { Value = cluster.ClusterName }); new CfnOutput(this, "DFrameWorkerEcsTaskdefImage", new CfnOutputProps { Value = dockerImage.ImageUri }); }