Skip to content
This repository has been archived by the owner on Dec 6, 2024. It is now read-only.

feat: integrating appstream with rstudio-alb #783

Merged
merged 13 commits into from
Nov 15, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -18,41 +18,53 @@ Parameters:
ACMSSLCertARN:
Type: String
Description: The ARN of the AWS Certificate Manager SSL Certificate to associate with the Load Balancer
IsAppStreamEnabled:
Type: String
AllowedValues: [true, false]
Description: Is AppStream enabled for this workspace

Conditions:
AppStreamEnabled: !Equals [!Ref IsAppStreamEnabled, 'true']
AppStreamDisabled: !Equals [!Ref IsAppStreamEnabled, 'false']

Resources:
ALBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: fixed-response
FixedResponseConfig:
ContentType: "text/plain"
MessageBody: "Forbidden"
StatusCode: "403"
LoadBalancerArn:
Ref: ApplicationLoadBalancer
Port: 443
Protocol: HTTPS
SslPolicy: ELBSecurityPolicy-2016-08
Certificates:
- CertificateArn: !Ref ACMSSLCertARN
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: fixed-response
FixedResponseConfig:
ContentType: 'text/plain'
MessageBody: 'Forbidden'
StatusCode: '403'
LoadBalancerArn:
Ref: ApplicationLoadBalancer
Port: 443
Protocol: HTTPS
SslPolicy: ELBSecurityPolicy-2016-08
Certificates:
- CertificateArn: !Ref ACMSSLCertARN
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Ref Namespace
Scheme: internet-facing # or internal
Scheme: !If [AppStreamEnabled, 'internal', 'internet-facing']
Subnets:
- Ref: Subnet1
- Ref: Subnet2
SecurityGroups:
- Ref: ALBSecurityGroup
- Ref: Subnet1
- Ref: Subnet2
SecurityGroups:
- Ref: ALBSecurityGroup
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
SecurityGroupIngress:
- CidrIp: "0.0.0.0/0"
FromPort: 443
ToPort: 443
IpProtocol: tcp
- !If
- AppStreamEnabled
- !Ref 'AWS::NoValue'
SanketD92 marked this conversation as resolved.
Show resolved Hide resolved
- CidrIp: '0.0.0.0/0'
FromPort: 443
ToPort: 443
IpProtocol: tcp
jn1119 marked this conversation as resolved.
Show resolved Hide resolved
GroupDescription: ALB SecurityGroup
VpcId: !Ref VPC
Outputs:
Expand All @@ -62,9 +74,12 @@ Outputs:
ALBDNSName:
Description: DNS Name of Application Load Balancer
Value: !GetAtt ApplicationLoadBalancer.DNSName
ALBHostedZoneId:
Description: Hosted Zone ID of Application Load Balancer
Value: !GetAtt ApplicationLoadBalancer.HostedZoneId
ListenerArn:
Description: ARN of Application Load Balancer Listener
Value: !Ref ALBListener
ALBSecurityGroupId:
Description: Security Group of Application Load Balancer Listener
Value: !Ref ALBSecurityGroup
Value: !Ref ALBSecurityGroup
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Parameters:
Type: String
Default: 10.0.0.0/19

VpcPublicSubnet2Cidr:
PublicSubnet2Cidr:
Description: Please enter the IP range (CIDR notation) for the public subnet 2 in the 2nd Availability Zone
Type: String
Default: 10.0.32.0/19
Expand All @@ -67,6 +67,12 @@ Parameters:
Description: Please enter the IP range (CIDR notation) for the Workspace subnet. This value is only used if AppStream is enabled.
Type: String
Default: 10.0.64.0/19

# Range from 10.0.96.0 to 10.0.127.255
WorkspaceSubnet2Cidr:
SanketD92 marked this conversation as resolved.
Show resolved Hide resolved
Description: Please enter the IP range (CIDR notation) for the Workspace subnet 2. This value is only used if AppStream is enabled.
Type: String
Default: 10.0.96.0/19

AppStreamFleetDesiredInstances:
Description: The desired number of streaming instances.
Expand Down Expand Up @@ -123,14 +129,15 @@ Metadata:
default: Deployment Configuration
Parameters:
- VpcCidr
- VpcPublicSubnet1Cidr
- VpcPublicSubnet2Cidr
- PublicSubnetCidr
- PublicSubnet2Cidr

Conditions:
isAppStream: !Equals
- !Ref EnableAppStream
- true
isNotAppStream: !Not [Condition: isAppStream]
hasCustomDomain: !Not [!Equals [!Ref "DomainName", ""]]
isAppStreamAndCustomDomain: !And
- !Not [!Equals [!Ref "DomainName", ""]]
- !Condition isAppStream
Expand Down Expand Up @@ -184,8 +191,8 @@ Resources:
- ec2:DescribeImages
- ec2:DescribeInstances
- ec2:DescribeSecurityGroups
- ec2:RevokeSecurityGroupIngress
- ec2:AuthorizeSecurityGroupIngress
- ec2:RevokeSecurityGroup*
- ec2:AuthorizeSecurityGroup*
- ec2-instance-connect:SendSSHPublicKey
Resource: '*'
- PolicyName: cfn-access
Expand Down Expand Up @@ -447,8 +454,8 @@ Resources:
Action:
- ec2:CreateSecurityGroup
- ec2:DeleteSecurityGroup
- ec2:AuthorizeSecurityGroupIngress
- ec2:RevokeSecurityGroupIngress
- ec2:AuthorizeSecurityGroup*
- ec2:RevokeSecurityGroup*
Resource:
- !Sub 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc/*'
- !Sub 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:security-group/*'
Expand Down Expand Up @@ -629,10 +636,11 @@ Resources:

PublicSubnet2:
Type: AWS::EC2::Subnet
Condition: isNotAppStream
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [1, !GetAZs ]
CidrBlock: !Ref VpcPublicSubnet2Cidr
CidrBlock: !Ref PublicSubnet2Cidr
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Expand Down Expand Up @@ -665,6 +673,7 @@ Resources:

PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Condition: isNotAppStream
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet2
Expand Down Expand Up @@ -732,6 +741,19 @@ Resources:
Tags:
- Key: Name
Value: Private Workspace Subnet

# For an ALB - You must specify subnets from at least two Availability Zones.
PrivateWorkspaceSubnet2:
SanketD92 marked this conversation as resolved.
Show resolved Hide resolved
Type: AWS::EC2::Subnet
Condition: isAppStreamAndCustomDomain
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [1, !GetAZs '']
CidrBlock: !Ref WorkspaceSubnet2Cidr
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: Private Workspace Subnet 2

PrivateWorkspaceRouteTable:
Type: 'AWS::EC2::RouteTable'
Expand Down Expand Up @@ -788,6 +810,10 @@ Resources:
Properties:
SubnetIds:
- !Ref PrivateWorkspaceSubnet
- !If
- isAppStreamAndCustomDomain
jn1119 marked this conversation as resolved.
Show resolved Hide resolved
- !Ref PrivateWorkspaceSubnet2
- !Ref 'AWS::NoValue'
PrivateDnsEnabled: true
VpcEndpointType: Interface
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.kms'
Expand All @@ -801,6 +827,10 @@ Resources:
Properties:
SubnetIds:
- !Ref PrivateWorkspaceSubnet
- !If
- isAppStreamAndCustomDomain
- !Ref PrivateWorkspaceSubnet2
SanketD92 marked this conversation as resolved.
Show resolved Hide resolved
- !Ref 'AWS::NoValue'
VpcEndpointType: Interface
PrivateDnsEnabled: true
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.sts'
Expand All @@ -814,6 +844,10 @@ Resources:
Properties:
SubnetIds:
- !Ref PrivateWorkspaceSubnet
- !If
- isAppStreamAndCustomDomain
- !Ref PrivateWorkspaceSubnet2
- !Ref 'AWS::NoValue'
VpcEndpointType: Interface
PrivateDnsEnabled: true
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.ec2'
Expand All @@ -827,6 +861,10 @@ Resources:
Properties:
SubnetIds:
- !Ref PrivateWorkspaceSubnet
- !If
- isAppStreamAndCustomDomain
- !Ref PrivateWorkspaceSubnet2
- !Ref 'AWS::NoValue'
VpcEndpointType: Interface
PrivateDnsEnabled: true
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.cloudformation'
Expand Down Expand Up @@ -883,6 +921,10 @@ Resources:
Properties:
SubnetIds:
- !Ref PrivateWorkspaceSubnet
- !If
- isAppStreamAndCustomDomain
- !Ref PrivateWorkspaceSubnet2
- !Ref 'AWS::NoValue'
VpcEndpointType: Interface
PrivateDnsEnabled: true
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.ssm'
Expand Down Expand Up @@ -951,7 +993,7 @@ Resources:
IpProtocol: '-1'
- DestinationSecurityGroupId: !Ref WorkspaceSecurityGroup
IpProtocol: '-1'

AppStreamSecurityGroupEgress:
Type: AWS::EC2::SecurityGroupEgress
Condition: isAppStream
Expand Down Expand Up @@ -1057,6 +1099,11 @@ Outputs:
Description: Workspace subnet
Condition: isAppStream
Value: !Ref PrivateWorkspaceSubnet

PrivateWorkspaceSubnet2:
Description: Second subnet for ALB
Condition: isAppStreamAndCustomDomain
Value: !Ref PrivateWorkspaceSubnet2

AppStreamSecurityGroup:
Description: AppStream Security Group
Expand All @@ -1069,7 +1116,7 @@ Outputs:
Description: SageMaker Security Group
Condition: isAppStream
Value: !Ref SageMakerSecurityGroup

AppStreamFleet:
Description: AppStream Fleet
Condition: isAppStream
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ describe('ALBService', () => {
};
service.getAlbSdk = jest.fn().mockResolvedValue(albClient);
service.getEc2Sdk = jest.fn().mockResolvedValue(ec2Client);
service.checkIfAppStreamEnabled = jest.fn(() => {
return false;
});
});

afterEach(() => {
Expand Down Expand Up @@ -253,7 +256,10 @@ describe('ALBService', () => {
});

describe('getStackCreationInput', () => {
const resolvedInputParams = [{ Key: 'ACMSSLCertARN', Value: 'Value' }];
const resolvedInputParams = [
{ Key: 'ACMSSLCertARN', Value: 'Value' },
{ Key: 'IsAppStreamEnabled', Value: 'false' },
];
const resolvedVars = { namespace: 'namespace' };
it('should pass and return the stack creation input with success', async () => {
service.findAwsAccountDetails = jest.fn(() => {
Expand Down Expand Up @@ -291,6 +297,10 @@ describe('ALBService', () => {
ParameterKey: 'VPC',
ParameterValue: 'vpc-096b034133955abba',
},
{
ParameterKey: 'IsAppStreamEnabled',
ParameterValue: 'false',
},
],
TemplateBody: ['template'],
Tags: [
Expand Down Expand Up @@ -550,7 +560,6 @@ describe('ALBService', () => {
});
service.getAlbSdk = jest.fn().mockResolvedValue(albClient);
const response = await service.modifyRule({}, resolvedVars);
expect(albClient.modifyRule).toHaveBeenCalledWith(params);
expect(response).toEqual({});
});

Expand Down Expand Up @@ -649,7 +658,7 @@ describe('ALBService', () => {
throw new Error(`Error describing subnet. VPC does not exist`);
});
try {
await service.findSubnet2({}, {}, '');
await service.findSubnet2({}, {}, '', 'true');
} catch (err) {
expect(err.message).toContain('Error describing subnet. VPC does not exist');
}
Expand All @@ -664,7 +673,7 @@ describe('ALBService', () => {
};
});
try {
await service.findSubnet2({}, {}, 'test-vpc');
await service.findSubnet2({}, {}, 'test-vpc', 'false');
} catch (err) {
expect(err.message).toContain(
'Error provisioning environment. Reason: Subnet2 not found for the VPC - test-vpc',
Expand All @@ -680,7 +689,7 @@ describe('ALBService', () => {
},
};
});
const response = await service.findSubnet2({}, {}, 'test-vpc');
const response = await service.findSubnet2({}, {}, 'test-vpc', true);
expect(response).toEqual('test-subnet-id');
});
});
Expand Down
Loading