Skip to content

Instantly share code, notes, and snippets.

@brysontyrrell
Last active March 26, 2022 21:52
Show Gist options
  • Save brysontyrrell/d84b88ee7361365192a629ce3c034b1b to your computer and use it in GitHub Desktop.
Save brysontyrrell/d84b88ee7361365192a629ce3c034b1b to your computer and use it in GitHub Desktop.
Deploy Jamf Pro using Fargate and Aurora Serverless (non production)
AWSTemplateFormatVersion: 2010-09-09
Parameters:
AvailabilityZone1:
Type: String
Default: a
AvailabilityZone2:
Type: String
Default: b
NatImageId:
Type: String
AllowedPattern: 'ami-[0-9a-fA-f]+'
DatabaseMasterUsername:
Type: String
DatabaseMasterPassword:
Type: String
NoEcho: true
CertificateArn:
Type: String
Default: ""
JamfProImageURI:
Type: String
Conditions:
UseHTTP: !Equals [ !Ref CertificateArn, '' ]
UseHTTPS: !Not [ !Equals [ !Ref CertificateArn, '' ] ]
Resources:
# VPC
JamfVpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Sub '10.0.0.0/24'
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}'
VpcInternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-igw'
VpcInternetGatewayAttach:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref VpcInternetGateway
VpcId: !Ref JamfVpc
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref JamfVpc
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-public-route-table'
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref JamfVpc
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-private-route-table'
VpcInternetGatewayRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref VpcInternetGateway
DependsOn:
- VpcInternetGatewayAttach
# NAT Gateway Public Subnet
NatSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Sub "${AWS::Region}${AvailabilityZone1}"
VpcId: !Ref JamfVpc
CidrBlock: !Sub '10.0.0.0/27'
# 10.0.0.0 - 10.0.0.31
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-public-nat'
NatSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref NatSubnet
# Commented out resources are for using a NAT Gateway. Far more scalable, less
# management, but _much_ more expensive than a t3a.nano instance.
# ElasticIP:
# Type: AWS::EC2::EIP
# Properties:
# Domain: vpc
# NatGateway:
# Type: AWS::EC2::NatGateway
# Properties:
# AllocationId: !GetAtt ElasticIP.AllocationId
# SubnetId: !Ref NatSubnet
# Tags:
# - Key: Name
# Value: !Sub '${AWS::StackName}-nat-gw'
# NatGatewayRoute:
# Type: AWS::EC2::Route
# Properties:
# RouteTableId: !Ref PrivateRouteTable
# DestinationCidrBlock: 0.0.0.0/0
# NatGatewayId: !Ref NatGateway
NatInstanceSercurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref JamfVpc
GroupDescription: NAT Access
SecurityGroupIngress:
# Allowing all traffic from the Web App subnet
- SourceSecurityGroupId: !Ref WebAppSecurityGroup
IpProtocol: -1
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-nat"
NatInstance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t3a.nano
ImageId: !Ref NatImageId
SourceDestCheck: false
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-nat-ins"
NetworkInterfaces:
- SubnetId: !Ref NatSubnet
GroupSet:
- !Ref NatInstanceSercurityGroup
AssociatePublicIpAddress: true
DeviceIndex: 0
NatInstanceRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
InstanceId: !Ref NatInstance
# Load Balancer Public Subnet
PublicLoadBalancerSubnet1:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Sub "${AWS::Region}${AvailabilityZone1}"
VpcId: !Ref JamfVpc
CidrBlock: !Sub '10.0.0.32/27'
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-public-load-balancer'
PublicLoadBalancerSubnet2:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Sub "${AWS::Region}${AvailabilityZone2}"
VpcId: !Ref JamfVpc
CidrBlock: !Sub '10.0.0.64/27'
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-public-load-balancer'
LoadBalancerSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicLoadBalancerSubnet1
LoadBalancerSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicLoadBalancerSubnet2
LoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Port 443 access to load balancers
SecurityGroupIngress:
- !If
- UseHTTP
- IpProtocol: TCP
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: TCP
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
VpcId: !Ref JamfVpc
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-load-balancers'
# Web App Private Subnet
WebAppSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Sub "${AWS::Region}${AvailabilityZone1}"
VpcId: !Ref JamfVpc
CidrBlock: !Sub '10.0.0.192/27'
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-private-webapp'
WebAppSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref WebAppSubnet
WebAppSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Default security group for web app containers
SecurityGroupIngress:
- IpProtocol: TCP
FromPort: 8080
ToPort: 8080
SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup
VpcId: !Ref JamfVpc
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-webapps'
# Database Private Subnet
DatabaseSubnet1:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Sub "${AWS::Region}${AvailabilityZone1}"
VpcId: !Ref JamfVpc
CidrBlock: !Sub '10.0.0.128/27'
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-private-database-1'
DatabaseSubnet2:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Sub "${AWS::Region}${AvailabilityZone2}"
VpcId: !Ref JamfVpc
CidrBlock: !Sub '10.0.0.160/27'
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-private-database-2'
DatabaseSubnetRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref DatabaseSubnet1
DatabaseSubnetRouteTableAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref DatabaseSubnet2
DatabaseSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: RDS Subnet Group for Aurora databases
SubnetIds:
- !Ref DatabaseSubnet1
- !Ref DatabaseSubnet2
DatabaseSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Default security group for web app containers
SecurityGroupIngress:
- IpProtocol: TCP
FromPort: 3306
ToPort: 3306
SourceSecurityGroupId: !Ref WebAppSecurityGroup
VpcId: !Ref JamfVpc
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-databases'
# Fargate Cluster
FargateCluster:
Type: AWS::ECS::Cluster
FargateCloudWatchLogsGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "/fargate/${AWS::StackName}"
RetentionInDays: 30
TaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy'
# Aurora Database
JamfDatabase:
Type: AWS::RDS::DBCluster
DeletionPolicy: Delete
Properties:
DBSubnetGroupName: !Ref DatabaseSubnetGroup
VpcSecurityGroupIds:
- !Ref DatabaseSecurityGroup
Engine: aurora
EngineVersion: 5.6.10a
EngineMode: serverless
StorageEncrypted: true
Port: 3306
ScalingConfiguration:
MinCapacity: 2
MaxCapacity: 2
AutoPause: true
SecondsUntilAutoPause: 1800
# EnableHttpEndpoint: true
DatabaseName: jamfsoftware
MasterUsername: !Ref DatabaseMasterUsername
MasterUserPassword: !Ref DatabaseMasterPassword
# Load Balancer
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Type: application
IpAddressType: ipv4
Scheme: internet-facing
Subnets:
- !Ref PublicLoadBalancerSubnet1
- !Ref PublicLoadBalancerSubnet2
SecurityGroups:
- !Ref LoadBalancerSecurityGroup
JamfProTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
VpcId: !Ref JamfVpc
Protocol: HTTP
Port: 8080
TargetType: ip
HealthCheckPath: "/healthCheck.html"
HealthCheckIntervalSeconds: 300
HealthCheckTimeoutSeconds: 30
JamfProHTTPListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Condition: UseHTTP
Properties:
LoadBalancerArn: !Ref LoadBalancer
Protocol: HTTP
Port: 80
DefaultActions:
- Type: forward
TargetGroupArn: !Ref JamfProTargetGroup
JamfProHTTPSListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Condition: UseHTTPS
Properties:
LoadBalancerArn: !Ref LoadBalancer
Protocol: HTTPS
Port: 443
SslPolicy: "ELBSecurityPolicy-TLS-1-1-2017-01"
Certificates:
- CertificateArn: !Ref CertificateArn
DefaultActions:
- Type: forward
TargetGroupArn: !Ref JamfProTargetGroup
# Jamf Pro Web App
JamfProTaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: jamfpro
ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: 512
Memory: 2048
ContainerDefinitions:
- Name: jamfpro-webapp
Image: !Ref JamfProImageURI
Essential: true
PortMappings:
- ContainerPort: 8080
HostPort: 8080
Protocol: tcp
Environment:
- Name: DATABASE_HOST
Value: !GetAtt JamfDatabase.Endpoint.Address
- Name: DATABASE_USERNAME
Value: !Ref DatabaseMasterUsername
- Name: DATABASE_PASSWORD
Value: !Ref DatabaseMasterPassword
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Sub "/fargate/${AWS::StackName}"
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: jamfpro
JamfProService:
Type: AWS::ECS::Service
Properties:
LaunchType: FARGATE
Cluster: !Ref FargateCluster
TaskDefinition: !Ref JamfProTaskDefinition
DesiredCount: 1
SchedulingStrategy: REPLICA
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
NetworkConfiguration:
AwsvpcConfiguration:
Subnets:
- !Ref WebAppSubnet
SecurityGroups:
- !Ref WebAppSecurityGroup
AssignPublicIp: DISABLED
HealthCheckGracePeriodSeconds: 300
LoadBalancers:
- TargetGroupArn: !Ref JamfProTargetGroup
ContainerName: jamfpro-webapp
ContainerPort: 8080
# Helpful CloudFormation outputs
Outputs:
LoadBalancerDNS:
Value: !GetAtt LoadBalancer.DNSName
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment