Created
May 9, 2023 14:47
-
-
Save Frans/bfe186f74be3f169f2159be9d05068c8 to your computer and use it in GitHub Desktop.
Mozilla Hubs 1.1.5 Template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AWSTemplateFormatVersion: '2010-09-09' | |
Description: | | |
Hubs Cloud: Private Social VR in your web browser. Your own self-hosted hub powered by Hubs by Mozilla. Full documentation: https://github.com/mozilla/hubs-cloud | |
Transform: AWS::Serverless-2016-10-31 | |
Metadata: | |
AWS::CloudFormation::Interface: | |
ParameterGroups: | |
- Label: | |
default: Account Configuration | |
Parameters: | |
- AdminEmailAddress | |
- Label: | |
default: Domain Configuration | |
Parameters: | |
- DomainName | |
- IsDomainOnRoute53 | |
- InternalZone | |
- ShortlinkZone | |
- Label: | |
default: Email Configuration | |
Parameters: | |
- EmailZone | |
- EmailSubdomain | |
- Label: | |
default: Server Configuration | |
Parameters: | |
- SpecifyInboundCidr | |
- KeyPair | |
- SpecifyInboundSSHCidr | |
- AppInstanceType | |
- StreamInstanceType | |
- Label: | |
default: Cost Management | |
Parameters: | |
- DatabaseMonthlyBudget | |
- MaxStorage | |
- Label: | |
default: Offline Mode | |
Parameters: | |
- StackOffline | |
- StackOfflineRedirectUrl | |
- Label: | |
default: Database Configuration | |
Parameters: | |
- AutoPauseDb | |
- DbBackupRetentionPeriod | |
- DbMaxCapacity | |
- Label: | |
default: Restore from Backup | |
Parameters: | |
- RestoreStackName | |
- RestoreDbSnapshotIdentifier | |
- RestoreAppDbSecretArn | |
- RestoreBackupVaultName | |
- RestoreRecoveryPointArn | |
- Label: | |
default: SSL Certificates | |
Parameters: | |
- UnmanagedDomainCertArn | |
- UnmanagedDomainEastCertArn | |
- LetsEncryptEmailAddress | |
- Label: | |
default: Advanced - Leave these alone unless you know what you're doing! | |
Parameters: | |
- InboundCidrOverride | |
- InboundSSHCidrOverride | |
- LoadBalancingMethod | |
- NewCMKForDiskEncryption | |
- SubnetAZs | |
- ClassB | |
- AppPlacementGroupStrategy | |
ParameterLabels: | |
DomainName: | |
default: Domain Name | |
IsDomainOnRoute53: | |
default: Is your domain set up on Route 53? | |
ShortlinkZone: | |
default: Short link domain name | |
InternalZone: | |
default: Internal domain name | |
EmailZone: | |
default: Outgoing Email Domain | |
EmailSubdomain: | |
default: Outgoing Email Subdomain Prefix | |
AdminEmailAddress: | |
default: Administrator Email Address | |
LetsEncryptEmailAddress: | |
default: Let's Encrypt Contact Email Address (Optional) | |
UnmanagedDomainCertArn: | |
default: Non-Route 53 Domain SSL Certificate ARN | |
UnmanagedDomainEastCertArn: | |
default: Non-Route 53 Domain US-East-1 (N. Virginia) SSL Certificate ARN | |
AppInstanceType: | |
default: App server EC2 instance type | |
ClassB: | |
default: Class B IP Block | |
NewCMKForDiskEncryption: | |
default: Create a dedicated CMK for database and backup encryption? | |
AppPlacementGroupStrategy: | |
default: App server placement group strategy | |
DbBackupRetentionPeriod: | |
default: Database backup retention period (in days) | |
DbMaxCapacity: | |
default: Max database capacity (in ACUs) | |
RestoreDbSnapshotIdentifier: | |
default: Restore From Database Snapshot Identifier | |
RestoreAppDbSecretArn: | |
default: Restore From Database Secret ARN | |
RestoreStackName: | |
default: Restore From Stack Name | |
RestoreBackupVaultName: | |
default: Restore From Vault Name | |
RestoreRecoveryPointArn: | |
default: Restore From Recovery Point ARN | |
LoadBalancingMethod: | |
default: Load Balancing Method | |
AutoPauseDb: | |
default: Auto-Pause Database | |
SubnetAZs: | |
default: Subnet Availability Zones | |
StackOffline: | |
default: Stack Mode | |
StackOfflineRedirectUrl: | |
default: Offline Redirect URL (Optional) | |
DatabaseMonthlyBudget: | |
default: Account Monthly Database Budget (US dollars, no $ and no cents) | |
MaxStorage: | |
default: Storage Limit (in GB) | |
InboundCidrOverride: | |
default: Inbound Site Access CIDR | |
InboundSSHCidrOverride: | |
default: Inbound SSH CIDR | |
SpecifyInboundCidr: | |
default: Restrict Site Access | |
SpecifyInboundSSHCidr: | |
default: Restrict SSH Access | |
Parameters: | |
DomainName: | |
Type: String | |
Description: Domain name your hub will be hosted on. (eg myhub.com or hub.mydomain.com.) | |
This should be a domain you already own, either on a third party provider (eg | |
GoDaddy or Namecheap) or on AWS Route 53. | |
AllowedPattern: ^((?:([a-z0-9]\.|[a-z0-9][a-z0-9\-]{0,61}[a-z0-9])\.)+)([a-z0-9]{2,63}|(?:[a-z0-9][a-z0-9\-]{0,61}[a-z0-9]))\.?$ | |
ConstraintDescription: Must be a valid domain name (eg myhub.com or hub.mycompany.com) | |
IsDomainOnRoute53: | |
Type: String | |
Description: We recommended you use Route 53 to manage your domain. Set to Yes | |
if your domain is set up on Route 53, and you can move onto the next item. Set | |
to No if your domain is on a third party service such as GoDaddy or Namecheap. | |
If you choose 'No', you'll need to set the fields in 'Custom SSL Certificates' | |
below. Also, if you choose 'No', once your stack is created you'll need to update | |
your DNS provider using the 'AddressForRootDomain' information in the 'Outputs' | |
tab. See the 'Outputs' tab once the stack is created for more information on | |
what to do next. | |
AllowedValues: | |
- Yes - My domain is set up on Route 53 | |
- No - My domain is not on Route 53 and I will set up my SSL certificate below | |
in the 'SSL Certificates' section | |
ShortlinkZone: | |
Type: AWS::Route53::HostedZone::Id | |
Description: You'll need a Route 53 domain name for short room permalinks, entry | |
codes, and device linking. (eg myhub.link) It should *not* be the same domain | |
as the any of the domain names that you specified above. This domain will be | |
used by visitors to share room links, and ideally should be short and easy to | |
type since it will be typed by users on mobile devices and in VR. | |
AllowedPattern: .+ | |
ConstraintDescription: You didn't specify a short link Route 53 Zone. Please choose | |
one or create a new domain name on AWS on Route 53, ideally one short and easy | |
to type. | |
InternalZone: | |
Type: AWS::Route53::HostedZone::Id | |
Description: Your hub needs a domain name to use internally for server management. | |
If your primary domain is on Route 53 and you're not using it for anything else, | |
you can choose it here. Otherwise, you'll need to register a new domain name | |
on Route 53 for this (for example, myhub-internal.com). This domain name will | |
not be seen by users. | |
AllowedPattern: .+ | |
ConstraintDescription: You didn't specify an internal Route 53 Zone. Please choose | |
one or create a new domain name on AWS on Route 53. It can be anything you want | |
and is used internally. It will not be seen by your visitors. | |
EmailZone: | |
Type: AWS::Route53::HostedZone::Id | |
Description: To verify email addresses, your hub needs to be able to send email. | |
If you do not have an existing email SMTP provider, choose a Route 53 domain | |
you'd like to send email from. It will be set up automatically using AWS Simple | |
Email Service. If you have an existing email provider you'd like to send email | |
from (eg Mailchimp) or already have a verified domain in AWS Simple Email Service | |
select the same zone you chose above for 'Internal Route 53 Zone' and you'll | |
be able to enter the login credentials from your email provider later. | |
AllowedPattern: .+ | |
ConstraintDescription: You didn't specify an Route 53 Zone to send email from. | |
Please choose one. If you already have email set up, just choose your Internal | |
Route 53 Zone. | |
EmailSubdomain: | |
Type: String | |
Description: The Subdomain prefix to use for outgoing emails. For example, if | |
your Outgoing Email Route 53 Zone is set to myhub.com, setting this field to | |
"mail" will send email from the domain mail.myhub.com. If you have a third party | |
email provider (eg Mailchimp) you can ignore this field. | |
Default: mail | |
AdminEmailAddress: | |
Type: String | |
Description: Email address for your administrator account. Individuals with access | |
to this email address will have full administrative control of your hub. Before | |
you can log in, you will need to verify this email address - you will get a | |
verification email from AWS after the stack is ready. | |
AllowedPattern: .+ | |
ConstraintDescription: You must enter a contact email address. | |
LetsEncryptEmailAddress: | |
Type: String | |
Description: Your servers will use Let's Encrypt to automatically manage SSL certificates. | |
To receive urgent email notifications regarding your certificates from Let's | |
Encrypt, specify a contact email here. This field is optional. By deploying | |
this stack you agree to the LetsEncrypt TOS. (https://letsencrypt.org/repository/) | |
Default: None | |
UnmanagedDomainCertArn: | |
Type: String | |
Description: You can skip this if your domain is on Route 53 and you chose 'Yes' | |
above to 'Is your domain set up on Route 53?'. Otherwise, if you chose 'No' | |
and you are using a domain name managed by another provider like GoDaddy or | |
Namecheap you will need to create an SSL certificate in the same region as your | |
stack in AWS Certificate Manager before completing this form. Instructions can | |
be found at https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html. | |
*Note* - make sure you create your SSL certificate in the AWS region you'd like | |
to use for your hub's stack. (The region can be found in the top right corner | |
of the AWS console.) Once you've created the certificate, paste the ARN for | |
the certificate here (eg arn:aws:acm:<region>:<account>:certificate/<id>). You | |
can find the ARN by clicking on the certificate in the AWS Certificate Manager | |
console. | |
ConstraintDescription: You must point to an ARN in AWS Certificate Manager for | |
your domain. | |
UnmanagedDomainEastCertArn: | |
Type: String | |
Description: You can skip this if your domain is on Route 53 and you chose 'Yes' | |
above to 'Is your domain set up on Route 53?'. Otherwise, you'll need to create | |
a SSL certificate for your external domain in US-East-1 (N. Virginia). Instructions | |
can be found at https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html. | |
*Note* - make sure you provide a SSL certificate in US-East-1 regardless of | |
what region you are creating the stack in. If you are creating your stack in | |
US-East-1 (N. Virginia) and so already pasted a certificate ARN in US-East-1 | |
for the above field, you can paste the same certificate ARN here. Otherwise, | |
you'll need to create a new SSL certificate in US-East-1 and paste its ARN here. | |
(eg arn:aws:acm:us-east-1:<account>:certificate/<id>) Be sure to change the | |
region in the top right of the console to 'US East (N. Virginia) before creating | |
the certificate. | |
ConstraintDescription: You must point to an ARN in US-East-1 (N. Virginia) AWS | |
Certificate Manager for your domain. | |
AppInstanceType: | |
Type: String | |
Description: The EC2 instance type for your app server(s). Choose a "t" instance | |
type if you expect bursty use of the service, and the "c" types if you expect | |
steady usage. | |
Default: t3.micro | |
AllowedValues: | |
- t3.micro | |
- t3.small | |
- t3.medium | |
- t3.large | |
- t3.xlarge | |
- t3.2xlarge | |
- c4.large | |
- c5.large | |
- c5.xlarge | |
- c5.2xlarge | |
- c5.4xlarge | |
- c5.9xlarge | |
- c5.12xlarge | |
- c5.18xlarge | |
- c5.24xlarge | |
KeyPair: | |
Type: AWS::EC2::KeyPair::KeyName | |
Description: 'SSH Keypair for server SSH access. If there no keypairs in this | |
list, you''ll need to add a SSH keypair via the EC2 console. See: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html#having-ec2-create-your-key-pair.' | |
AllowedPattern: .+ | |
ConstraintDescription: 'You must choose an SSH keypair. To create one, see: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html#having-ec2-create-your-key-pair.' | |
ClassB: | |
Type: String | |
Description: VPC ClassB (10.XX.0.0/16) block to use for internal server IPs. | |
Default: 0 | |
AppPlacementGroupStrategy: | |
Type: String | |
Description: Placement group strategy for app servers. If you only have one app | |
server, this setting has no effect. If you have more than one app server, choose | |
'cluster' to increase connectivity between servers at the cost of reliability. | |
When using 'cluster', you will have lower latency between users but your service | |
will go down if there is an AZ-wide outage. Choose 'spread' if you would like | |
to ensure higher availability at the cost of latency or maximum throughput between | |
app servers. Choose 'none' if you do not have a preference, will be running | |
more than 7 app servers, or if AWS has been failing to provision servers for | |
you in previous attempts to create the stack. App servers communicate with one | |
another extensively, so 'cluster' is the preferred setting if you can tolerate | |
some risk of downtime if there are AZ-wide outages, which are rare. | |
Default: cluster | |
AllowedValues: | |
- cluster | |
- spread | |
- none | |
DbBackupRetentionPeriod: | |
Type: Number | |
Description: Number of days your AWS Aurora database backups will be retained | |
for. | |
Default: 14 | |
DbMaxCapacity: | |
Type: Number | |
Description: Maximum ACU Capacity for the database. If you are unsure, leave this | |
at the default. The database will be a serverless Aurora PostgreSQL-compatible | |
cluster. Increasing the Maximum ACUs will increase the available resources to | |
your hub for database queries, but will cost more. This needs to be set to at | |
least 2. Pricing info for ACUs can be found at https://aws.amazon.com/rds/aurora/serverless/ | |
MinValue: 2 | |
MaxValue: 384 | |
Default: 2 | |
RestoreDbSnapshotIdentifier: | |
Type: String | |
Description: 'Leave blank if you are not restoring from a backup. To restore from | |
an existing backup, specify the AWS Aurora database snapshot identifier to restore | |
from. (eg mystack-snapshot-mystack-app-db-xxxxx) WARNING: do *not* change or | |
blank out this value after your stack is created, otherwise you will replace | |
your existing database!' | |
RestoreAppDbSecretArn: | |
Type: String | |
Description: 'Leave blank if you are not restoring from a backup. To restore from | |
an existing backup, specify the ARN for the database secret from the hub being | |
restored from. (eg arn:aws:secretsmanager:<region>:<account-id>:secret:AppDbSecret-xxxxxxxxx) | |
WARNING: do *not* change or blank out this value after your stack is created, | |
otherwise you will replace your existing database!' | |
RestoreStackName: | |
Type: String | |
Description: 'Leave blank if you are not restoring from a backup. To restore from | |
an existing backup, specify the stack name being restored from. To restore, | |
the secrets stored in Parameter Store must still be available from the stack | |
being restored from. These parameters should still exist even if you deleted | |
the old stack, unless you manually removed them. WARNING: do *not* change or | |
blank out this value after your stack is created, otherwise you will replace | |
your existing database!' | |
RestoreBackupVaultName: | |
Type: String | |
Description: 'Leave blank if you are not restoring from a backup. To restore from | |
an existing backup, specify the AWS Backup Vault Name to restore storage from. | |
(eg mystack-daily-backup-xxxxxxx) WARNING: do *not* change or blank out this | |
value after your stack is created, otherwise you will replace your existing | |
database!' | |
RestoreRecoveryPointArn: | |
Type: String | |
Description: 'Leave blank if you are not restoring from a backup. To restore from | |
an existing backup, specify the AWS Backup Vault''s Recovery Point ARN to restore | |
storage from. (eg arn:aws:backup:<region>:<account id>:recovery-point:xxxxxxxx) | |
WARNING: do *not* change or blank out this value after your stack is created, | |
otherwise you will replace your existing database!' | |
NewCMKForDiskEncryption: | |
Type: String | |
Description: Select 'Yes' if you'd like to create a dedicated CMK in KMS for database | |
and backup encryption. KMS charges for a single managed key will apply. | |
Default: No - Use the AWS Managed Key | |
AllowedValues: | |
- No - Use the AWS Managed Key | |
- Yes - Create a new CMK | |
SubnetAZs: | |
Type: String | |
Description: 'If your stack is failing to create, and the ''Events'' tab shows | |
the error ''Value (xxx) for parameter availabilityZone is invalid. Subnets can | |
currently only be created in the following availability zones: X, Y'', you will | |
need to select an alternative subnet configuration here. This is an issue with | |
AWS: https://github.com/widdix/aws-cf-templates/issues/36' | |
Default: Use zone a and b | |
AllowedValues: | |
- Use zone a and b | |
- Use zone b and c | |
- Use zone a and c | |
- Use zone b and d | |
- Use zone c and d | |
LoadBalancingMethod: | |
Type: String | |
Description: Method for load balancing requests to app servers. If you only have | |
one or a few app servers, and would like to save on AWS costs, choose DNS Round | |
Robin. If you have several app servers or would like improved load balancing, | |
you can also use an Application Load Balancer, which will incur additional costs. | |
If you are using a domain not on Route 53, be sure to note the "AddressForRootDomain" | |
in the Outputs tab after the stack update to set your DNS to point to the ALB. | |
Default: DNS Round Robin | |
AllowedValues: | |
- DNS Round Robin | |
- Application Load Balancer | |
AutoPauseDb: | |
Type: String | |
Description: Your hub uses the Aurora Serverless database, which can greatly save | |
costs by pausing the database when your hub is not in use. However, when the | |
database is paused, it can take up to 20 seconds for your site to come back | |
up when you visit it. You can turn off database pausing if you'd like to avoid | |
any delay in loading your site. Turning off pausing will incur continuous hourly | |
database costs. Pricing info can be found at https://aws.amazon.com/rds/aurora/serverless/ | |
Default: Yes - Pause database when not in use | |
AllowedValues: | |
- Yes - Pause database when not in use | |
- No - Pay for database continuously to prevent loading delays | |
StackOffline: | |
Type: String | |
Description: To save costs, you can switch your stack from being Online to Offline | |
when it is not needed. This will shut down the servers while keeping all of | |
your data. Once your stack is switched back to Online, it will come back up | |
just how you left it. | |
AllowedValues: | |
- Online | |
- Offline - Temporarily shut off servers | |
Default: Online | |
StackOfflineRedirectUrl: | |
Type: String | |
Description: (Optional) When your stack is set to Offline, visitors will be redirected | |
to this URL. | |
DatabaseMonthlyBudget: | |
Type: Number | |
Description: If you do not want a monthly database budget, set this to zero. To | |
save costs, your AWS Aurora Serverless database will be shut down when nobody | |
is hitting your site. When visitors are using your site, you'll incur database | |
costs. To avoid unexpected charges, you can set a monthly budget. If you are | |
running multiple hubs, this budget should reflect your maximum allowed cost | |
across *all* your hubs in this region. This hub will automatically switch into | |
Offline mode if the Aurora Serverless database charges across all your hubs | |
in this region exceed this budget. A minimum $20 budget is recommended and will | |
provide more than 40 hours a week of connectivity for a hub. You only pay for | |
what you use. Leave this field blank to fall back to your "Max database capacity" | |
setting in "Database Configuration" to limit your database cost. Pricing info | |
can be found at https://aws.amazon.com/rds/aurora/serverless/ | |
Default: 0 | |
MaxStorage: | |
Type: Number | |
Description: When users upload scenes, avatars, or files, they will use disk storage. | |
You only pay for the storage you use. You can set a maximum storage amount here | |
(in GB) to ensure there are no unexpected charges. Pricing info can be found | |
at https://aws.amazon.com/efs/pricing/ | |
MinValue: 5 | |
MaxValue: 1048576 | |
Default: 128 | |
InboundCidrOverride: | |
Type: String | |
Description: IP CDIR which will be granted HTTPS and WebRTC access to your site. | |
Set to 0.0.0.0/0 to allow anyone on the Internet to access your site. | |
InboundSSHCidrOverride: | |
Type: String | |
Description: IP CDIR which will be granted SSH port access to your servers. Connecting | |
users will still need valid SSH keys to access your server. Set to 0.0.0.0/0 | |
to allow any IP with the SSH key + 2FA token to access your servers via SSH. | |
SpecifyInboundCidr: | |
Type: String | |
Description: You can restrict which IP addresses can access your site. To allow | |
anyone on the Internet to load your site, choose 'Allow anyone to access my | |
site.' | |
AllowedValues: | |
- Allow anyone to access my site | |
- I will specify the 'Inbound Site Access CDIR' in 'Advanced' below to restrict | |
IP addresses to my site | |
Default: Allow anyone to access my site | |
SpecifyInboundSSHCidr: | |
Type: String | |
Description: You can restrict the IP addresses that can access your servers over | |
SSH. SSH access will always require the SSH private key file specified above | |
and the 2FA token regardless of IP. | |
AllowedValues: | |
- Allow anyone with the SSH secret key and 2FA token to access my servers | |
- I will specify the 'Inbound SSH CDIR' in 'Advanced' below to restrict SSH IP | |
addresses to my servers | |
AllowedPattern: .+ | |
ConstraintDescription: You must select if you'd like any IP SSH restrictions. | |
Mappings: | |
Regions: | |
us-east-1: | |
Abbreviation: USE1 | |
ImageId: ami-0a81bac87065ce464 | |
us-west-2: | |
Abbreviation: USW2 | |
ImageId: ami-0bd077dc2cb632f73 | |
us-east-2: | |
Abbreviation: USE2 | |
ImageId: ami-0168e6c7a32422283 | |
ap-northeast-1: | |
Abbreviation: APN1 | |
ImageId: ami-08fb6cdf9bdf950d0 | |
eu-west-1: | |
Abbreviation: EU | |
ImageId: ami-0c16e27df6da08592 | |
ServicesMeta: | |
RetExternal: | |
Port: 443 | |
RetInternal: | |
Port: 4000 | |
RetTurnExternal: | |
Port: 5349 | |
DialogInternal: | |
Port: 8443 | |
DialogExternal: | |
AppPort: 8443 | |
StreamPort: 443 | |
DialogTurnExternal: | |
Port: 5349 | |
DialogAdmin: | |
Port: 7000 | |
DialogWebRTCFrom: | |
Port: 51610 | |
DialogWebRTCTo: | |
Port: 60999 | |
YTDL: | |
Port: 8080 | |
BioCensus: | |
Port: 9631 | |
Ssh: | |
Port: 22 | |
PostgreSQL: | |
Port: 5432 | |
InstanceTypeMeta: | |
t3.micro: | |
PlacementForCluster: spread | |
t3.small: | |
PlacementForCluster: spread | |
t3.medium: | |
PlacementForCluster: spread | |
t3.large: | |
PlacementForCluster: spread | |
t3.xlarge: | |
PlacementForCluster: spread | |
t3.2xlarge: | |
PlacementForCluster: spread | |
c4.large: | |
PlacementForCluster: cluster | |
c5.large: | |
PlacementForCluster: cluster | |
c5.xlarge: | |
PlacementForCluster: cluster | |
c5.2xlarge: | |
PlacementForCluster: cluster | |
c5.4xlarge: | |
PlacementForCluster: cluster | |
c5.9xlarge: | |
PlacementForCluster: cluster | |
c5.12xlarge: | |
PlacementForCluster: cluster | |
c5.18xlarge: | |
PlacementForCluster: cluster | |
c5.24xlarge: | |
PlacementForCluster: cluster | |
AWSMPServerlessResourcesRegionalizedMappings: | |
us-east-1: | |
photomnemonic5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:us-east-1:865964754613:applications/photomnemonic-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
speelycaptor5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:us-east-1:865964754613:applications/speelycaptor-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
nearspark5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:us-east-1:865964754613:applications/nearspark-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
awscfnsesdomain5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:us-east-1:865964754613:applications/aws-cfn-ses-domain-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
keymasterpublic5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:us-east-1:771996922080:applications/keymaster-public-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
eu-west-1: | |
photomnemonic5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:eu-west-1:118096126957:applications/photomnemonic-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
speelycaptor5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:eu-west-1:118096126957:applications/speelycaptor-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
nearspark5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:eu-west-1:118096126957:applications/nearspark-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
awscfnsesdomain5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:eu-west-1:118096126957:applications/aws-cfn-ses-domain-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
keymasterpublic5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:eu-west-1:753951066699:applications/keymaster-public-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
ap-northeast-1: | |
photomnemonic5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:ap-northeast-1:006377987546:applications/photomnemonic-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
speelycaptor5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:ap-northeast-1:006377987546:applications/speelycaptor-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
nearspark5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:ap-northeast-1:006377987546:applications/nearspark-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
awscfnsesdomain5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:ap-northeast-1:006377987546:applications/aws-cfn-ses-domain-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
keymasterpublic5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:ap-northeast-1:954919168245:applications/keymaster-public-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
us-east-2: | |
photomnemonic5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:us-east-2:093084136110:applications/photomnemonic-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
speelycaptor5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:us-east-2:093084136110:applications/speelycaptor-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
nearspark5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:us-east-2:093084136110:applications/nearspark-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
awscfnsesdomain5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:us-east-2:093084136110:applications/aws-cfn-ses-domain-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
keymasterpublic5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:us-east-2:024540230436:applications/keymaster-public-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
us-west-2: | |
photomnemonic5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:us-west-2:382795170440:applications/photomnemonic-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
speelycaptor5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:us-west-2:382795170440:applications/speelycaptor-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
nearspark5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:us-west-2:382795170440:applications/nearspark-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
awscfnsesdomain5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:us-west-2:382795170440:applications/aws-cfn-ses-domain-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
keymasterpublic5f452d36bfff4c02a4175c663b64fd08: arn:aws:serverlessrepo:us-west-2:623131709813:applications/keymaster-public-5f452d36-bfff-4c02-a417-5c663b64fd08 | |
Conditions: | |
HasManagedDomain: | |
Fn::Equals: | |
- Ref: IsDomainOnRoute53 | |
- Yes - My domain is set up on Route 53 | |
HasInboundCidrOverride: | |
Fn::Equals: | |
- Ref: SpecifyInboundCidr | |
- I will specify the 'Inbound Site Access CDIR' in 'Advanced' below to restrict | |
IP addresses to my site | |
HasInboundSSHCidrOverride: | |
Fn::Equals: | |
- Ref: SpecifyInboundSSHCidr | |
- I will specify the 'Inbound SSH CDIR' in 'Advanced' below to restrict SSH IP | |
addresses to my servers | |
HasUnmangedDomain: | |
Fn::Not: | |
- Condition: HasManagedDomain | |
HasLetsEncryptEmail: | |
Fn::Not: | |
- Fn::Equals: | |
- Ref: LetsEncryptEmailAddress | |
- None | |
HasStreamingServers: | |
Fn::And: | |
- Fn::Equals: | |
- Ref: StackOffline | |
- Online | |
- Fn::Not: | |
- Fn::Equals: | |
- 0 | |
- 0 | |
AppIsNoPlacement: | |
Fn::Equals: | |
- Ref: AppPlacementGroupStrategy | |
- none | |
AppIsClusterPlacement: | |
Fn::Equals: | |
- Ref: AppPlacementGroupStrategy | |
- cluster | |
HasEmailSubdomain: | |
Fn::Not: | |
- Fn::Equals: | |
- Ref: EmailSubdomain | |
- '' | |
CreateDiskEncryptionKey: | |
Fn::Equals: | |
- Ref: NewCMKForDiskEncryption | |
- Yes - Create a new CMK | |
SubnetChoiceA: | |
Fn::Equals: | |
- Ref: SubnetAZs | |
- Use zone a and b | |
SubnetChoiceB: | |
Fn::Equals: | |
- Ref: SubnetAZs | |
- Use zone b and c | |
SubnetChoiceC: | |
Fn::Equals: | |
- Ref: SubnetAZs | |
- Use zone a and c | |
SubnetChoiceD: | |
Fn::Equals: | |
- Ref: SubnetAZs | |
- Use zone b and d | |
IsOnline: | |
Fn::Equals: | |
- Ref: StackOffline | |
- Online | |
IsOffline: | |
Fn::Not: | |
- Condition: IsOnline | |
HasOfflineRedirectUrl: | |
Fn::Not: | |
- Fn::Equals: | |
- Ref: StackOfflineRedirectUrl | |
- '' | |
PerformOfflineRedirect: | |
Fn::And: | |
- Condition: IsOffline | |
- Condition: HasOfflineRedirectUrl | |
PerformOfflineRedirectWithUnmanagedDomain: | |
Fn::And: | |
- Condition: PerformOfflineRedirect | |
- Condition: HasUnmangedDomain | |
EnableDbAutoPause: | |
Fn::Equals: | |
- Ref: AutoPauseDb | |
- Yes - Pause database when not in use | |
HasDbMonthlyBudget: | |
Fn::Not: | |
- Fn::Equals: | |
- Ref: DatabaseMonthlyBudget | |
- 0 | |
IsNotRestore: | |
Fn::Equals: | |
- Ref: RestoreDbSnapshotIdentifier | |
- '' | |
IsRestore: | |
Fn::Not: | |
- Condition: IsNotRestore | |
IsEast: | |
Fn::Equals: | |
- Fn::Sub: ${AWS::Region} | |
- us-east-1 | |
IsNonEast: | |
Fn::Not: | |
- Condition: IsEast | |
HasManagedDomainNonEast: | |
Fn::And: | |
- Condition: IsNonEast | |
- Condition: HasManagedDomain | |
HasManagedDomainEast: | |
Fn::And: | |
- Condition: IsEast | |
- Condition: HasManagedDomain | |
RequestedALB: | |
Fn::Equals: | |
- Ref: LoadBalancingMethod | |
- Application Load Balancer | |
HasALB: | |
Fn::And: | |
- Fn::Or: | |
- Condition: IsOnline | |
- Condition: PerformOfflineRedirectWithUnmanagedDomain | |
- Condition: RequestedALB | |
Resources: | |
VPC: | |
Type: AWS::EC2::VPC | |
Properties: | |
CidrBlock: | |
Fn::Sub: 10.${ClassB}.0.0/16 | |
EnableDnsSupport: true | |
EnableDnsHostnames: true | |
InstanceTenancy: default | |
Tags: | |
- Key: Name | |
Value: | |
Fn::Sub: ${AWS::StackName} 10.${ClassB}.0.0/16 | |
InternetGateway: | |
Type: AWS::EC2::InternetGateway | |
Properties: | |
Tags: | |
- Key: Name | |
Value: | |
Fn::Sub: ${AWS::StackName} 10.${ClassB}.0.0/16 | |
VPCGatewayAttachment: | |
Type: AWS::EC2::VPCGatewayAttachment | |
Properties: | |
VpcId: | |
Ref: VPC | |
InternetGatewayId: | |
Ref: InternetGateway | |
SubnetAPublic: | |
Type: AWS::EC2::Subnet | |
Properties: | |
AvailabilityZone: | |
Fn::Select: | |
- Fn::If: | |
- SubnetChoiceA | |
- 0 | |
- Fn::If: | |
- SubnetChoiceB | |
- 1 | |
- Fn::If: | |
- SubnetChoiceC | |
- 0 | |
- Fn::If: | |
- SubnetChoiceD | |
- 1 | |
- 2 | |
- Fn::GetAZs: '' | |
CidrBlock: | |
Fn::Sub: 10.${ClassB}.0.0/20 | |
MapPublicIpOnLaunch: true | |
VpcId: | |
Ref: VPC | |
Tags: | |
- Key: Name | |
Value: | |
Fn::Sub: ${AWS::StackName} A public | |
- Key: Reach | |
Value: public | |
SubnetAPrivate: | |
Type: AWS::EC2::Subnet | |
Properties: | |
AvailabilityZone: | |
Fn::Select: | |
- Fn::If: | |
- SubnetChoiceA | |
- 0 | |
- Fn::If: | |
- SubnetChoiceB | |
- 1 | |
- Fn::If: | |
- SubnetChoiceC | |
- 0 | |
- Fn::If: | |
- SubnetChoiceD | |
- 1 | |
- 2 | |
- Fn::GetAZs: '' | |
CidrBlock: | |
Fn::Sub: 10.${ClassB}.16.0/20 | |
VpcId: | |
Ref: VPC | |
Tags: | |
- Key: Name | |
Value: | |
Fn::Sub: ${AWS::StackName} A private | |
- Key: Reach | |
Value: private | |
SubnetBPublic: | |
Type: AWS::EC2::Subnet | |
Properties: | |
AvailabilityZone: | |
Fn::Select: | |
- Fn::If: | |
- SubnetChoiceA | |
- 1 | |
- Fn::If: | |
- SubnetChoiceB | |
- 2 | |
- Fn::If: | |
- SubnetChoiceC | |
- 2 | |
- Fn::If: | |
- SubnetChoiceD | |
- 3 | |
- 3 | |
- Fn::GetAZs: '' | |
CidrBlock: | |
Fn::Sub: 10.${ClassB}.32.0/20 | |
MapPublicIpOnLaunch: true | |
VpcId: | |
Ref: VPC | |
Tags: | |
- Key: Name | |
Value: | |
Fn::Sub: ${AWS::StackName} B public | |
- Key: Reach | |
Value: public | |
SubnetBPrivate: | |
Type: AWS::EC2::Subnet | |
Properties: | |
AvailabilityZone: | |
Fn::Select: | |
- Fn::If: | |
- SubnetChoiceA | |
- 1 | |
- Fn::If: | |
- SubnetChoiceB | |
- 2 | |
- Fn::If: | |
- SubnetChoiceC | |
- 2 | |
- Fn::If: | |
- SubnetChoiceD | |
- 3 | |
- 3 | |
- Fn::GetAZs: '' | |
CidrBlock: | |
Fn::Sub: 10.${ClassB}.48.0/20 | |
VpcId: | |
Ref: VPC | |
Tags: | |
- Key: Name | |
Value: | |
Fn::Sub: ${AWS::StackName} B private | |
- Key: Reach | |
Value: private | |
RouteTableAPublic: | |
Type: AWS::EC2::RouteTable | |
Properties: | |
VpcId: | |
Ref: VPC | |
Tags: | |
- Key: Name | |
Value: | |
Fn::Sub: ${AWS::StackName} A public | |
RouteTableAPrivate: | |
Type: AWS::EC2::RouteTable | |
Properties: | |
VpcId: | |
Ref: VPC | |
Tags: | |
- Key: Name | |
Value: | |
Fn::Sub: ${AWS::StackName} A private | |
RouteTableBPublic: | |
Type: AWS::EC2::RouteTable | |
Properties: | |
VpcId: | |
Ref: VPC | |
Tags: | |
- Key: Name | |
Value: | |
Fn::Sub: ${AWS::StackName} B public | |
RouteTableBPrivate: | |
Type: AWS::EC2::RouteTable | |
Properties: | |
VpcId: | |
Ref: VPC | |
Tags: | |
- Key: Name | |
Value: | |
Fn::Sub: ${AWS::StackName} B private | |
RouteTableAssociationAPublic: | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
SubnetId: | |
Ref: SubnetAPublic | |
RouteTableId: | |
Ref: RouteTableAPublic | |
RouteTableAssociationAPrivate: | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
SubnetId: | |
Ref: SubnetAPrivate | |
RouteTableId: | |
Ref: RouteTableAPrivate | |
RouteTableAssociationBPublic: | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
SubnetId: | |
Ref: SubnetBPublic | |
RouteTableId: | |
Ref: RouteTableBPublic | |
RouteTableAssociationBPrivate: | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
SubnetId: | |
Ref: SubnetBPrivate | |
RouteTableId: | |
Ref: RouteTableBPrivate | |
RouteTablePublicAInternetRoute: | |
Type: AWS::EC2::Route | |
DependsOn: VPCGatewayAttachment | |
Properties: | |
RouteTableId: | |
Ref: RouteTableAPublic | |
DestinationCidrBlock: 0.0.0.0/0 | |
GatewayId: | |
Ref: InternetGateway | |
RouteTablePublicBInternetRoute: | |
Type: AWS::EC2::Route | |
DependsOn: VPCGatewayAttachment | |
Properties: | |
RouteTableId: | |
Ref: RouteTableBPublic | |
DestinationCidrBlock: 0.0.0.0/0 | |
GatewayId: | |
Ref: InternetGateway | |
NetworkAclPublic: | |
Type: AWS::EC2::NetworkAcl | |
Properties: | |
VpcId: | |
Ref: VPC | |
Tags: | |
- Key: Name | |
Value: | |
Fn::Sub: ${AWS::StackName} Public | |
NetworkAclPrivate: | |
Type: AWS::EC2::NetworkAcl | |
Properties: | |
VpcId: | |
Ref: VPC | |
Tags: | |
- Key: Name | |
Value: | |
Fn::Sub: ${AWS::StackName} Private | |
SubnetNetworkAclAssociationAPublic: | |
Type: AWS::EC2::SubnetNetworkAclAssociation | |
Properties: | |
SubnetId: | |
Ref: SubnetAPublic | |
NetworkAclId: | |
Ref: NetworkAclPublic | |
SubnetNetworkAclAssociationAPrivate: | |
Type: AWS::EC2::SubnetNetworkAclAssociation | |
Properties: | |
SubnetId: | |
Ref: SubnetAPrivate | |
NetworkAclId: | |
Ref: NetworkAclPrivate | |
SubnetNetworkAclAssociationBPublic: | |
Type: AWS::EC2::SubnetNetworkAclAssociation | |
Properties: | |
SubnetId: | |
Ref: SubnetBPublic | |
NetworkAclId: | |
Ref: NetworkAclPublic | |
SubnetNetworkAclAssociationBPrivate: | |
Type: AWS::EC2::SubnetNetworkAclAssociation | |
Properties: | |
SubnetId: | |
Ref: SubnetBPrivate | |
NetworkAclId: | |
Ref: NetworkAclPrivate | |
NetworkAclEntryInPublicAllowAll: | |
Type: AWS::EC2::NetworkAclEntry | |
Properties: | |
NetworkAclId: | |
Ref: NetworkAclPublic | |
RuleNumber: 99 | |
Protocol: -1 | |
RuleAction: allow | |
Egress: false | |
CidrBlock: 0.0.0.0/0 | |
NetworkAclEntryOutPublicAllowAll: | |
Type: AWS::EC2::NetworkAclEntry | |
Properties: | |
NetworkAclId: | |
Ref: NetworkAclPublic | |
RuleNumber: 99 | |
Protocol: -1 | |
RuleAction: allow | |
Egress: true | |
CidrBlock: 0.0.0.0/0 | |
NetworkAclEntryInPrivateAllowVPC: | |
Type: AWS::EC2::NetworkAclEntry | |
Properties: | |
NetworkAclId: | |
Ref: NetworkAclPrivate | |
RuleNumber: 99 | |
Protocol: -1 | |
RuleAction: allow | |
Egress: false | |
CidrBlock: 0.0.0.0/0 | |
NetworkAclEntryOutPrivateAllowVPC: | |
Type: AWS::EC2::NetworkAclEntry | |
Properties: | |
NetworkAclId: | |
Ref: NetworkAclPrivate | |
RuleNumber: 99 | |
Protocol: -1 | |
RuleAction: allow | |
Egress: true | |
CidrBlock: 0.0.0.0/0 | |
Route53DependencyLambda: | |
Metadata: | |
Source: https://github.com/sonyxperiadev/amazon-custom-resources/tree/master/route53-dependency | |
Version: 1.0.0 | |
Type: AWS::Lambda::Function | |
Properties: | |
FunctionName: | |
Fn::Sub: ${AWS::StackName}-route53-dependency | |
Description: Lookup Route 53 info | |
Handler: index.handler | |
Role: | |
Fn::GetAtt: Route53DependencyRole.Arn | |
Runtime: nodejs14.x | |
Timeout: 900 | |
Code: | |
ZipFile: | | |
'use strict'; | |
function route53Dependency(properties, callback) { | |
if (!properties.Id && !properties.Domain) | |
callback("Zone id or domain not specified"); | |
var aws = require("aws-sdk"); | |
var route53 = new aws.Route53(); | |
var responseData = {}; | |
console.log('route53Dependency', properties); | |
route53.listHostedZones({}, function(err, data) { | |
console.log('listHostedZones', err, data); | |
if (err) | |
return callback(err); | |
var zones = data.HostedZones; | |
var matching = zones.filter(function(zone) { | |
if (properties.Id) { | |
return zone.Id === "/hostedzone/" + properties.Id; | |
} else { | |
var tldParts = properties.Domain.split("."); | |
var tld = tldParts[tldParts.length - 2] + "." + tldParts[tldParts.length - 1]; | |
return zone.Name === tld + "."; | |
} | |
}); | |
if (matching.length != 1) | |
return callback('Exactly one matching zone is allowed ' + zones); | |
var match = matching[0]; | |
delete match.Config; | |
delete match.CallerReference; | |
match.Id = match.Id.split('/')[2]; | |
match.Name = match.Name.substring(0, match.Name.length-1); | |
return callback(null, match); | |
}); | |
} | |
route53Dependency.handler = function(event, context) { | |
console.log(JSON.stringify(event, null, ' ')); | |
if (event.RequestType == 'Delete') { | |
return sendResponse(event, context, "SUCCESS"); | |
} | |
route53Dependency(event.ResourceProperties, function(err, result) { | |
var status = err ? 'FAILED' : 'SUCCESS'; | |
return sendResponse(event, context, status, result, err); | |
}); | |
}; | |
function getReason(err) { | |
if (err) | |
return err.message; | |
else | |
return ''; | |
} | |
function sendResponse(event, context, status, data, err) { | |
var responseBody = { | |
StackId: event.StackId, | |
RequestId: event.RequestId, | |
LogicalResourceId: event.LogicalResourceId, | |
PhysicalResourceId: 'route53Dependency-' + (event.ResourceProperties.Domain || event.ResourceProperties.Id), | |
Status: status, | |
Reason: getReason(err) + " See details in CloudWatch Log: " + context.logStreamName, | |
Data: data | |
}; | |
console.log("RESPONSE:\n", responseBody); | |
var json = JSON.stringify(responseBody); | |
var https = require("https"); | |
var url = require("url"); | |
var parsedUrl = url.parse(event.ResponseURL); | |
var options = { | |
hostname: parsedUrl.hostname, | |
port: 443, | |
path: parsedUrl.path, | |
method: "PUT", | |
headers: { | |
"content-type": "", | |
"content-length": json.length | |
} | |
}; | |
var request = https.request(options, function(response) { | |
console.log("STATUS: " + response.statusCode); | |
console.log("HEADERS: " + JSON.stringify(response.headers)); | |
context.done(null, data); | |
}); | |
request.on("error", function(error) { | |
console.log("sendResponse Error:\n", error); | |
context.done(error); | |
}); | |
request.on("end", function() { | |
console.log("end"); | |
}); | |
request.write(json); | |
request.end(); | |
} | |
module.exports = route53Dependency; | |
Route53DependencyRole: | |
Properties: | |
RoleName: | |
Fn::Sub: ${AWS::StackName}-Route53Dependency | |
AssumeRolePolicyDocument: | |
Statement: | |
- Action: | |
- sts:AssumeRole | |
Effect: Allow | |
Principal: | |
Service: lambda.amazonaws.com | |
Version: '2012-10-17' | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole | |
- arn:aws:iam::aws:policy/service-role/AWSLambdaRole | |
Policies: | |
- PolicyDocument: | |
Statement: | |
- Action: | |
- route53:ListHostedZones | |
Effect: Allow | |
Resource: '*' | |
Version: '2012-10-17' | |
PolicyName: | |
Fn::Sub: ${AWS::StackName}Route53DependencyCustomPolicy | |
Type: AWS::IAM::Role | |
ParseURL: | |
Type: AWS::Lambda::Function | |
Properties: | |
FunctionName: | |
Fn::Sub: ${AWS::StackName}-parse-url | |
Description: Parse a URL into its constituent parts for redirector | |
Handler: index.handler | |
Role: | |
Fn::GetAtt: ParseURLRole.Arn | |
Runtime: nodejs14.x | |
Timeout: 900 | |
Code: | |
ZipFile: | | |
const URL = require('url'); | |
const parseURL = {}; | |
function btoa(s) { | |
return Buffer.from(s).toString('base64'); | |
} | |
parseURL.handler = function(event, context) { | |
const url = event.ResourceProperties.URL || "https://en.wikipedia.org/wiki/Rubber_duck"; | |
try { | |
const parsed = URL.parse(url); | |
const pathname = parsed.pathname; | |
return sendResponse(event, context, "SUCCESS", { | |
S3ReplaceKeyPrefixWith: `${parsed.pathname.substring(1)}${parsed.search || ""}${parsed.hash || ""}`, | |
S3Protocol: parsed.protocol.replace(":", ""), | |
S3Hostname: parsed.host, | |
ALBProtocol: parsed.protocol.replace(":", "").toUpperCase(), | |
ALBPort: parsed.port, | |
ALBHost: parsed.host, | |
ALBPath: parsed.pathname, | |
ALBQuery: `${parsed.search || ""}${parsed.hash || ""}`.replace(/^\?/, "") | |
}); | |
} catch (e) { | |
return sendResponse(event, context, "FAILED", null, `Invalid URL specified: ${url}`); | |
} | |
}; | |
function getReason(err) { | |
if (err) | |
return err.message; | |
else | |
return ''; | |
} | |
function sendResponse(event, context, status, data, err) { | |
var responseBody = { | |
StackId: event.StackId, | |
RequestId: event.RequestId, | |
LogicalResourceId: event.LogicalResourceId, | |
PhysicalResourceId: 'parseUrl-' + btoa(event.ResourceProperties.URL), | |
Status: status, | |
Reason: getReason(err) + " See details in CloudWatch Log: " + context.logStreamName, | |
Data: data | |
}; | |
console.log("RESPONSE:\n", responseBody); | |
var json = JSON.stringify(responseBody); | |
var https = require("https"); | |
var url = require("url"); | |
var parsedUrl = url.parse(event.ResponseURL); | |
var options = { | |
hostname: parsedUrl.hostname, | |
port: 443, | |
path: parsedUrl.path, | |
method: "PUT", | |
headers: { | |
"content-type": "", | |
"content-length": json.length | |
} | |
}; | |
var request = https.request(options, function(response) { | |
console.log("STATUS: " + response.statusCode); | |
console.log("HEADERS: " + JSON.stringify(response.headers)); | |
context.done(null, data); | |
}); | |
request.on("error", function(error) { | |
console.log("sendResponse Error:\n", error); | |
context.done(error); | |
}); | |
request.on("end", function() { | |
console.log("end"); | |
}); | |
request.write(json); | |
request.end(); | |
} | |
module.exports = parseURL; | |
ParseURLRole: | |
Properties: | |
RoleName: | |
Fn::Sub: ${AWS::StackName}-ParseURLRole | |
AssumeRolePolicyDocument: | |
Statement: | |
- Action: | |
- sts:AssumeRole | |
Effect: Allow | |
Principal: | |
Service: lambda.amazonaws.com | |
Version: '2012-10-17' | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole | |
- arn:aws:iam::aws:policy/service-role/AWSLambdaRole | |
Type: AWS::IAM::Role | |
ToLower: | |
Type: AWS::Lambda::Function | |
Properties: | |
FunctionName: | |
Fn::Sub: ${AWS::StackName}-to-lower | |
Description: Convert a string to lower | |
Handler: index.handler | |
Role: | |
Fn::GetAtt: ToLowerRole.Arn | |
Runtime: nodejs14.x | |
Timeout: 900 | |
Code: | |
ZipFile: | | |
const toLower = {}; | |
function btoa(s) { | |
return Buffer.from(s).toString('base64'); | |
}; | |
function getReason(err) { | |
if (err) | |
return err.message; | |
else | |
return ''; | |
}; | |
toLower.handler = function(event, context) { | |
return sendResponse(event, context, "SUCCESS", { Value: event.ResourceProperties.String.toLowerCase() }); | |
}; | |
function sendResponse(event, context, status, data, err) { | |
var responseBody = { | |
StackId: event.StackId, | |
RequestId: event.RequestId, | |
LogicalResourceId: event.LogicalResourceId, | |
PhysicalResourceId: 'toLower-' + btoa(event.ResourceProperties.String), | |
Status: status, | |
Reason: getReason(err) + " See details in CloudWatch Log: " + context.logStreamName, | |
Data: data | |
}; | |
console.log("RESPONSE:\n", responseBody); | |
var json = JSON.stringify(responseBody); | |
var https = require("https"); | |
var url = require("url"); | |
var parsedUrl = url.parse(event.ResponseURL); | |
var options = { | |
hostname: parsedUrl.hostname, | |
port: 443, | |
path: parsedUrl.path, | |
method: "PUT", | |
headers: { | |
"content-type": "", | |
"content-length": json.length | |
} | |
}; | |
var request = https.request(options, function(response) { | |
console.log("STATUS: " + response.statusCode); | |
console.log("HEADERS: " + JSON.stringify(response.headers)); | |
context.done(null, data); | |
}); | |
request.on("error", function(error) { | |
console.log("sendResponse Error:\n", error); | |
context.done(error); | |
}); | |
request.on("end", function() { | |
console.log("end"); | |
}); | |
request.write(json); | |
request.end(); | |
} | |
module.exports = toLower; | |
ToLowerRole: | |
Properties: | |
RoleName: | |
Fn::Sub: ${AWS::StackName}-ToLowerRole | |
AssumeRolePolicyDocument: | |
Statement: | |
- Action: | |
- sts:AssumeRole | |
Effect: Allow | |
Principal: | |
Service: lambda.amazonaws.com | |
Version: '2012-10-17' | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole | |
- arn:aws:iam::aws:policy/service-role/AWSLambdaRole | |
Type: AWS::IAM::Role | |
CustomAcmCertificateLambda: | |
Metadata: | |
Source: https://github.com/dflook/cloudformation-dns-certificate | |
Version: 1.7.2 | |
Properties: | |
Code: | |
ZipFile: "y=Exception\nU=RuntimeError\nM=True\nimport copy,hashlib as t,json,logging\ | |
\ as B,time\nfrom boto3 import client as J\nfrom botocore.exceptions import\ | |
\ ClientError as u,ParamValidationError as v\nfrom urllib.request import\ | |
\ Request as w,urlopen as x\nA=B.getLogger()\nA.setLevel(B.INFO)\nC=A.info\n\ | |
R=A.exception\nL=copy.copy\nS=time.sleep\nT=lambda j:json.dumps(j,sort_keys=M).encode()\n\ | |
def handler(e,c):\n\tAB='OldResourceProperties';AA='Update';A9='Delete';A8='None';A7='acm';A6='FAILED';A5='properties';A4='stack-id';A3='logical-id';A2='DNS';s='Old';r='Certificate';q='LogicalResourceId';p='DomainName';o='ValidationMethod';n='Route53RoleArn';m='Region';d='RequestType';b='Reinvoked';a='StackId';Z=None;Q='Status';P='Key';O='';N='DomainValidationOptions';K=False;I='ResourceProperties';H='cloudformation:';G='Value';F='CertificateArn';E='Tags';B='PhysicalResourceId';f=c.get_remaining_time_in_millis;C(e)\n\ | |
\tdef g():\n\t\tC=L(A)\n\t\tfor G in ['ServiceToken',m,E,n]:C.pop(G,Z)\n\ | |
\t\tif o in A:\n\t\t\tif A[o]==A2:\n\t\t\t\tfor H in set([A[p]]+A.get('SubjectAlternativeNames',[])):k(H)\n\ | |
\t\t\t\tdel C[N]\n\t\te[B]=D.request_certificate(IdempotencyToken=A0,**C)[F];l()\n\ | |
\tdef V(a):\n\t\twhile M:\n\t\t\ttry:D.delete_certificate(**{F:a});return\n\ | |
\t\t\texcept u as B:\n\t\t\t\tR(O);A=B.response['Error']['Code']\n\t\t\t\ | |
\tif A=='ResourceInUseException':\n\t\t\t\t\tif f()/1000<30:raise\n\t\t\t\ | |
\t\tS(5);continue\n\t\t\t\tif A in['ResourceNotFoundException','ValidationException']:return\n\ | |
\t\t\t\traise\n\t\t\texcept v:return\n\tdef W(p):\n\t\tfor I in D.get_paginator('list_certificates').paginate():\n\ | |
\t\t\tfor A in I['CertificateSummaryList']:\n\t\t\t\tC(A);B={B[P]:B[G]for\ | |
\ B in D.list_tags_for_certificate(**{F:A[F]})[E]}\n\t\t\t\tif B.get(H+A3)==e[q]and\ | |
\ B.get(H+A4)==e[a]and B.get(H+A5)==X(p):return A[F]\n\tdef h():\n\t\tif\ | |
\ e.get(b,K):raise U('Certificate not issued in time')\n\t\te[b]=M;C(e);J('lambda').invoke(FunctionName=c.invoked_function_arn,InvocationType='Event',Payload=T(e))\n\ | |
\tdef i():\n\t\twhile f()/1000>30:\n\t\t\tA=D.describe_certificate(**{F:e[B]})[r];C(A)\n\ | |
\t\t\tif A[Q]=='ISSUED':return M\n\t\t\telif A[Q]==A6:raise U(A.get('FailureReason',O))\n\ | |
\t\t\tS(5)\n\t\treturn K\n\tdef z():A=L(e[s+I]);A.pop(E,Z);B=L(e[I]);B.pop(E,Z);return\ | |
\ A!=B\n\tdef j():\n\t\tW='Type';V='Name';U='HostedZoneId';T='ValidationStatus';R='PENDING_VALIDATION';K='ResourceRecord'\n\ | |
\t\tif A.get(o)!=A2:return\n\t\twhile M:\n\t\t\tH=D.describe_certificate(**{F:e[B]})[r];C(H)\n\ | |
\t\t\tif H[Q]!=R:return\n\t\t\tif not[A for A in H.get(N,[{}])if T not in\ | |
\ A or K not in A]:break\n\t\t\tS(1)\n\t\tfor E in H[N]:\n\t\t\tif E[T]==R:L=k(E[p]);O=L.get(n,A.get(n));I=J('sts').assume_role(RoleArn=O,RoleSessionName=(r+e[q])[:64],DurationSeconds=900)['Credentials']if\ | |
\ O is not Z else{};P=J('route53',aws_access_key_id=I.get('AccessKeyId'),aws_secret_access_key=I.get('SecretAccessKey'),aws_session_token=I.get('SessionToken')).change_resource_record_sets(**{U:L[U],'ChangeBatch':{'Comment':'Domain\ | |
\ validation for '+e[B],'Changes':[{'Action':'UPSERT','ResourceRecordSet':{V:E[K][V],W:E[K][W],'TTL':60,'ResourceRecords':[{G:E[K][G]}]}}]}});C(P)\n\ | |
\tdef k(n):\n\t\tC='.';n=n.rstrip(C);D={B[p].rstrip(C):B for B in A[N]};B=n.split(C)\n\ | |
\t\twhile len(B):\n\t\t\tif C.join(B)in D:return D[C.join(B)]\n\t\t\tB=B[1:]\n\ | |
\t\traise U(N+' missing for '+n)\n\tX=lambda v:t.new('md5',T(v)).hexdigest()\n\ | |
\tdef l():A=L(e[I].get(E,[]));A+=[{P:H+A3,G:e[q]},{P:H+A4,G:e[a]},{P:H+'stack-name',G:e[a].split('/')[1]},{P:H+A5,G:X(e[I])}];D.add_tags_to_certificate(**{F:e[B],E:A})\n\ | |
\tdef Y():\n\t\tC(e);A=x(w(e['ResponseURL'],T(e),{'content-type':O},method='PUT'))\n\ | |
\t\tif A.status!=200:raise y(A)\n\ttry:\n\t\tA0=X(e['RequestId']+e[a]);A=e[I];D=J(A7,region_name=A.get(m));e[Q]='SUCCESS'\n\ | |
\t\tif e[d]=='Create':\n\t\t\tif e.get(b,K)is K:e[B]=A8;g()\n\t\t\tj()\n\ | |
\t\t\tif not i():return h()\n\t\telif e[d]==A9:\n\t\t\tif e[B]!=A8:\n\t\t\ | |
\t\tif e[B].startswith('arn:'):V(e[B])\n\t\t\t\telse:V(W(A))\n\t\telif e[d]==AA:\n\ | |
\t\t\tif z():\n\t\t\t\tC(AA)\n\t\t\t\tif W(A)==e[B]:\n\t\t\t\t\ttry:D=J(A7,region_name=e[AB].get(m));C(A9);V(W(e[AB]))\n\ | |
\t\t\t\t\texcept:R(O)\n\t\t\t\t\treturn Y()\n\t\t\t\tif e.get(b,K)is K:g()\n\ | |
\t\t\t\tj()\n\t\t\t\tif not i():return h()\n\t\t\telse:\n\t\t\t\tif E in\ | |
\ e[s+I]:D.remove_tags_from_certificate(**{F:e[B],E:e[s+I][E]})\n\t\t\t\t\ | |
l()\n\t\telse:raise U(e[d])\n\t\treturn Y()\n\texcept y as A1:R(O);e[Q]=A6;e['Reason']=str(A1);return\ | |
\ Y()" | |
Description: Cloudformation custom resource for DNS validated certificates | |
Handler: index.handler | |
Role: | |
Fn::GetAtt: CustomAcmCertificateLambdaExecutionRole.Arn | |
Runtime: python3.9 | |
Timeout: 900 | |
Type: AWS::Lambda::Function | |
CustomAcmCertificateLambdaExecutionRole: | |
Properties: | |
RoleName: | |
Fn::Sub: ${AWS::StackName}-CustomAcmCertificateLambdaExecution | |
AssumeRolePolicyDocument: | |
Statement: | |
- Action: | |
- sts:AssumeRole | |
Effect: Allow | |
Principal: | |
Service: lambda.amazonaws.com | |
Version: '2012-10-17' | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole | |
- arn:aws:iam::aws:policy/service-role/AWSLambdaRole | |
Policies: | |
- PolicyDocument: | |
Statement: | |
- Action: | |
- acm:AddTagsToCertificate | |
- acm:DeleteCertificate | |
- acm:DescribeCertificate | |
- acm:RemoveTagsFromCertificate | |
Effect: Allow | |
Resource: | |
- Fn::Sub: arn:aws:acm:*:${AWS::AccountId}:certificate/* | |
- Action: | |
- acm:RequestCertificate | |
- acm:ListTagsForCertificate | |
- acm:ListCertificates | |
Effect: Allow | |
Resource: | |
- '*' | |
- Action: | |
- route53:ChangeResourceRecordSets | |
Effect: Allow | |
Resource: | |
- arn:aws:route53:::hostedzone/* | |
Version: '2012-10-17' | |
PolicyName: | |
Fn::Sub: ${AWS::StackName}CustomAcmCertificateLambdaExecutionPolicy | |
Type: AWS::IAM::Role | |
ExternalZoneInfo: | |
Type: Custom::Route53 | |
Condition: HasManagedDomain | |
Properties: | |
Domain: | |
Ref: DomainName | |
ServiceToken: | |
Fn::GetAtt: Route53DependencyLambda.Arn | |
ShortlinkZoneInfo: | |
Type: Custom::Route53 | |
Properties: | |
Id: | |
Ref: ShortlinkZone | |
ServiceToken: | |
Fn::GetAtt: Route53DependencyLambda.Arn | |
InternalZoneInfo: | |
Type: Custom::Route53 | |
Properties: | |
Id: | |
Ref: InternalZone | |
ServiceToken: | |
Fn::GetAtt: Route53DependencyLambda.Arn | |
EmailZoneInfo: | |
Type: Custom::Route53 | |
Properties: | |
Id: | |
Ref: EmailZone | |
ServiceToken: | |
Fn::GetAtt: Route53DependencyLambda.Arn | |
ExternalZoneSSLCertLocalIfNonEast: | |
Type: Custom::DNSCertificate | |
Condition: HasManagedDomainNonEast | |
Properties: | |
DomainName: | |
Ref: DomainName | |
DomainValidationOptions: | |
- DomainName: | |
Ref: DomainName | |
HostedZoneId: | |
Fn::GetAtt: ExternalZoneInfo.Id | |
ValidationMethod: DNS | |
Region: | |
Fn::Sub: ${AWS::Region} | |
ServiceToken: | |
Fn::GetAtt: CustomAcmCertificateLambda.Arn | |
DependsOn: ExternalZoneSSLCertEast | |
ExternalZoneSSLCertLocalIfEast: | |
Type: Custom::DNSCertificate | |
Condition: HasManagedDomainEast | |
Properties: | |
DomainName: | |
Ref: DomainName | |
DomainValidationOptions: | |
- DomainName: | |
Ref: DomainName | |
HostedZoneId: | |
Fn::GetAtt: ExternalZoneInfo.Id | |
ValidationMethod: DNS | |
Region: | |
Fn::Sub: ${AWS::Region} | |
ServiceToken: | |
Fn::GetAtt: CustomAcmCertificateLambda.Arn | |
DependsOn: ShortlinkZoneSSLCertEast | |
ExternalZoneSSLCertEast: | |
Type: Custom::DNSCertificate | |
Condition: HasManagedDomainNonEast | |
Properties: | |
DomainName: | |
Ref: DomainName | |
DomainValidationOptions: | |
- DomainName: | |
Ref: DomainName | |
HostedZoneId: | |
Fn::GetAtt: ExternalZoneInfo.Id | |
ValidationMethod: DNS | |
Region: us-east-1 | |
ServiceToken: | |
Fn::GetAtt: CustomAcmCertificateLambda.Arn | |
DependsOn: InternalZoneSSLCertEast | |
ShortlinkZoneSSLCertEast: | |
Type: Custom::DNSCertificate | |
Properties: | |
DomainName: | |
Fn::GetAtt: ShortlinkZoneInfo.Name | |
DomainValidationOptions: | |
- DomainName: | |
Fn::GetAtt: ShortlinkZoneInfo.Name | |
HostedZoneId: | |
Fn::GetAtt: ShortlinkZoneInfo.Id | |
ValidationMethod: DNS | |
Region: us-east-1 | |
ServiceToken: | |
Fn::GetAtt: CustomAcmCertificateLambda.Arn | |
DependsOn: InternalZoneSSLCert | |
InternalZoneSSLCert: | |
Type: Custom::DNSCertificate | |
Properties: | |
DomainName: | |
Fn::Sub: '*.${InternalZoneInfo.Name}' | |
SubjectAlternativeNames: | |
- Fn::Sub: ${LowerStackName.Value}-cors-proxy.${InternalZoneInfo.Name} | |
DomainValidationOptions: | |
- DomainName: | |
Fn::GetAtt: InternalZoneInfo.Name | |
HostedZoneId: | |
Ref: InternalZone | |
ValidationMethod: DNS | |
Region: | |
Fn::Sub: ${AWS::Region} | |
ServiceToken: | |
Fn::GetAtt: CustomAcmCertificateLambda.Arn | |
InternalZoneSSLCertEast: | |
Type: Custom::DNSCertificate | |
Condition: IsNonEast | |
Properties: | |
DomainName: | |
Fn::Sub: '*.${InternalZoneInfo.Name}' | |
SubjectAlternativeNames: | |
- Fn::Sub: ${LowerStackName.Value}-cors-proxy.${InternalZoneInfo.Name} | |
DomainValidationOptions: | |
- DomainName: | |
Fn::GetAtt: InternalZoneInfo.Name | |
HostedZoneId: | |
Ref: InternalZone | |
ValidationMethod: DNS | |
Region: us-east-1 | |
ServiceToken: | |
Fn::GetAtt: CustomAcmCertificateLambda.Arn | |
DependsOn: ShortlinkZoneSSLCertEast | |
SESDomainRole: | |
Type: AWS::IAM::Role | |
Properties: | |
RoleName: | |
Fn::Sub: ${AWS::StackName}-SES | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: lambda.amazonaws.com | |
Action: sts:AssumeRole | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole | |
Policies: | |
- PolicyName: provision-ses | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- ses:DeleteIdentity | |
- ses:GetIdentityDkimAttributes | |
- ses:GetIdentityMailFromDomainAttributes | |
- ses:GetIdentityVerificationAttributes | |
- ses:SetIdentityDomainDkim | |
- ses:SetIdentityMailFromDomain | |
- ses:VerifyDomainDkim | |
- ses:VerifyDomainIdentity | |
- ses:VerifyEmailIdentity | |
Resource: | |
- '*' | |
SESDomainRecords: | |
Type: AWS::Route53::RecordSetGroup | |
Properties: | |
HostedZoneName: | |
Fn::Sub: ${EmailZoneInfo.Name}. | |
RecordSets: | |
Fn::GetAtt: SESDomain.Route53RecordSets | |
SESDomain: | |
Type: Custom::SES_Domain | |
Properties: | |
ServiceToken: | |
Fn::GetAtt: SESDomainApplication.Outputs.SESDomainFunctionArn | |
Domain: | |
Fn::GetAtt: EmailZoneInfo.Name | |
EnableReceive: false | |
EnableSend: true | |
MailFromSubdomain: | |
Ref: EmailSubdomain | |
TTL: 1800 | |
CustomDMARC: '"v=DMARC1;p=reject;pct=100;aspf=r;"' | |
Region: us-east-1 | |
EmailAddress: | |
Ref: AdminEmailAddress | |
SendEmailUser: | |
Type: AWS::IAM::User | |
Properties: | |
UserName: | |
Fn::Sub: ${AWS::StackName}-send-email | |
Policies: | |
- PolicyName: send-email | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- ses:SendRawEmail | |
Resource: '*' | |
Condition: | |
StringEquals: | |
ses:FromAddress: | |
Fn::Sub: noreply@${EmailSubdomain}.${SESDomain} | |
- Effect: Allow | |
Action: | |
- ses:SendRawEmail | |
Resource: '*' | |
Condition: | |
StringEquals: | |
ses:FromAddress: | |
Fn::Sub: noreply@${SESDomain} | |
SendEmailAccessKey: | |
Type: AWS::IAM::AccessKey | |
DependsOn: | |
- SendEmailUser | |
Properties: | |
UserName: | |
Fn::Sub: ${AWS::StackName}-send-email | |
KeymasterRole: | |
Type: AWS::IAM::Role | |
Properties: | |
RoleName: | |
Fn::Sub: ${AWS::StackName}-KeymasterRole | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: lambda.amazonaws.com | |
Action: sts:AssumeRole | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole | |
Path: / | |
Policies: | |
- PolicyName: keymaster | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- s3:PutObject | |
Resource: | |
- Fn::Sub: ${BoxKeysBucket.Arn}/* | |
- Effect: Allow | |
Action: | |
- secretsmanager:GetSecretValue | |
Resource: | |
Fn::If: | |
- IsRestore | |
- Ref: RestoreAppDbSecretArn | |
- Fn::Sub: arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:NONE | |
- Effect: Allow | |
Action: | |
- ssm:PutParameter | |
- ssm:DeleteParameter | |
Resource: | |
Fn::Sub: arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/keymaster/${AWS::StackName}/* | |
- Effect: Allow | |
Action: | |
- ssm:GetParameter | |
Resource: | |
Fn::If: | |
- IsRestore | |
- Fn::Sub: arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/keymaster/${RestoreStackName}/* | |
- Fn::Sub: arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/keymaster/${AWS::StackName}/* | |
- Effect: Allow | |
Action: | |
- ssm:DescribeParameters | |
Resource: | |
- Fn::Sub: arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:* | |
- Effect: Allow | |
Action: | |
- ssm:DeleteParameters | |
Resource: | |
- Fn::Sub: arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/ita/${AWS::StackName}/* | |
DeleteItaParameters: | |
Type: Custom::DeleteParameters | |
Properties: | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.DeleteParametersFunctionArn | |
Region: | |
Fn::Sub: ${AWS::Region} | |
Prefix: | |
Fn::Sub: /ita/${AWS::StackName}/ | |
EmptyBucketRole: | |
Type: AWS::IAM::Role | |
Properties: | |
RoleName: | |
Fn::Sub: ${AWS::StackName}-EmptyBucket | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: lambda.amazonaws.com | |
Action: sts:AssumeRole | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole | |
Path: / | |
Policies: | |
- PolicyName: delete-s3-files | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- s3:DeleteObject | |
Resource: | |
- Fn::Sub: ${AssetsBucket.Arn}/* | |
- Fn::Sub: ${LinkRedirectorBucket.Arn}/* | |
- Fn::Sub: ${SpeelycaptorScratchBucket.Arn}/* | |
- Fn::Sub: ${BoxKeysBucket.Arn}/* | |
- Effect: Allow | |
Action: | |
- s3:ListBucket | |
Resource: | |
- Fn::Sub: ${AssetsBucket.Arn} | |
- Fn::Sub: ${LinkRedirectorBucket.Arn} | |
- Fn::Sub: ${BoxKeysBucket.Arn} | |
- Fn::Sub: ${SpeelycaptorScratchBucket.Arn} | |
EmptyBucketFunction: | |
Type: AWS::Lambda::Function | |
Properties: | |
Handler: index.handler | |
Runtime: python3.9 | |
Role: | |
Fn::GetAtt: EmptyBucketRole.Arn | |
Timeout: 240 | |
Code: | |
ZipFile: | | |
import json | |
import boto3 | |
import urllib3 | |
from botocore.exceptions import ClientError | |
http = urllib3.PoolManager() | |
def handler(event, context): | |
try: | |
bucketNames = event['ResourceProperties']['BucketNames'] | |
if event['RequestType'] == 'Delete': | |
for bucketName in bucketNames: | |
s3 = boto3.resource('s3') | |
try: | |
s3.meta.client.head_bucket(Bucket=bucketName) | |
bucket = s3.Bucket(bucketName) | |
for obj in bucket.objects.filter(): | |
s3.Object(bucket.name, obj.key).delete() | |
except ClientError: | |
# Bucket already removed/never existed | |
pass | |
sendResponseCfn(event, context, "SUCCESS") | |
except Exception as e: | |
print(e) | |
sendResponseCfn(event, context, "FAILED") | |
def sendResponseCfn(event, context, responseStatus): | |
response_body = {'Status': responseStatus, | |
'Reason': 'Log stream name: ' + context.log_stream_name, | |
'PhysicalResourceId': context.log_stream_name, | |
'StackId': event['StackId'], | |
'RequestId': event['RequestId'], | |
'LogicalResourceId': event['LogicalResourceId'], | |
'Data': json.loads("{}")} | |
http.request('PUT', event['ResponseURL'], body=json.dumps(response_body)) | |
SESPasswordRole: | |
Type: AWS::IAM::Role | |
Properties: | |
RoleName: | |
Fn::Sub: ${AWS::StackName}-SESPassword | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: lambda.amazonaws.com | |
Action: sts:AssumeRole | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole | |
Path: / | |
Policies: [] | |
SESPasswordFunction: | |
Type: AWS::Lambda::Function | |
Properties: | |
FunctionName: | |
Fn::Sub: ${AWS::StackName}-ses-password | |
Handler: index.handler | |
Runtime: python3.9 | |
Role: | |
Fn::GetAtt: SESPasswordRole.Arn | |
Timeout: 240 | |
Code: | |
ZipFile: | | |
import json | |
import boto3 | |
import hmac | |
import hashlib | |
import base64 | |
import argparse | |
import urllib3 | |
DATE = "11111111" | |
SERVICE = "ses" | |
MESSAGE = "SendRawEmail" | |
TERMINAL = "aws4_request" | |
VERSION = 0x04 | |
http = urllib3.PoolManager() | |
def sign(key, msg): | |
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest() | |
def calculateKey(secretAccessKey, region): | |
signature = sign(("AWS4" + secretAccessKey).encode('utf-8'), DATE) | |
signature = sign(signature, region) | |
signature = sign(signature, SERVICE) | |
signature = sign(signature, TERMINAL) | |
signature = sign(signature, MESSAGE) | |
signatureAndVersion = bytes([VERSION]) + signature | |
smtpPassword = base64.b64encode(signatureAndVersion) | |
return smtpPassword.decode('utf-8') | |
def handler(event, context): | |
try: | |
if event['RequestType'] == 'Delete': | |
sendResponseCfn(event, context, "SUCCESS", {}) | |
return | |
secretAccessKey = event['ResourceProperties']['SecretAccessKey'] | |
region = event['ResourceProperties']['Region'] | |
key = calculateKey(secretAccessKey, region) | |
sendResponseCfn(event, context, "SUCCESS", { "Key": key }) | |
except Exception as e: | |
print(e) | |
sendResponseCfn(event, context, "FAILED", {}) | |
def sendResponseCfn(event, context, responseStatus, data): | |
response_body = {'Status': responseStatus, | |
'Reason': 'Log stream name: ' + context.log_stream_name, | |
'PhysicalResourceId': context.log_stream_name, | |
'StackId': event['StackId'], | |
'RequestId': event['RequestId'], | |
'LogicalResourceId': event['LogicalResourceId'], | |
'Data': data} | |
http.request('PUT', event['ResponseURL'], body=json.dumps(response_body)) | |
StackTopicHandlerRole: | |
Type: AWS::IAM::Role | |
Properties: | |
RoleName: | |
Fn::Sub: ${AWS::StackName}-StackTopicHandler | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: lambda.amazonaws.com | |
Action: sts:AssumeRole | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole | |
Path: / | |
Policies: | |
- PolicyName: stack-topic-handler-role | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- autoscaling:DescribeAutoScalingGroups | |
- ec2:DescribeInstances | |
Resource: | |
- '*' | |
- Effect: Allow | |
Action: | |
- route53:ChangeResourceRecordSets | |
- route53:ListResourceRecordSets | |
Resource: | |
Fn::Sub: arn:aws:route53:::hostedzone/${InternalZoneInfo.Id} | |
- Effect: Allow | |
Action: | |
- route53:CreateHealthCheck | |
- route53:ListHealthChecks | |
- route53:DeleteHealthCheck | |
- route53:GetHostedZone | |
Resource: '*' | |
- Effect: Allow | |
Action: | |
- route53:* | |
Resource: | |
- Fn::Sub: arn:aws:route53:::hostedzone/${InternalZoneInfo.Id} | |
- Fn::If: | |
- HasManagedDomain | |
- Fn::Sub: arn:aws:route53:::hostedzone/${ExternalZoneInfo.Id} | |
- arn:aws:route53:::hostedzone/DEADZONE* | |
- Effect: Allow | |
Action: | |
- cloudformation:DescribeStacks | |
- cloudformation:GetTemplate | |
- cloudformation:UpdateStack | |
Resource: | |
Ref: AWS::StackId | |
- Effect: Allow | |
Action: | |
- sns:ListTagsForResource | |
Resource: '*' | |
- Effect: Allow | |
Action: | |
- autoscaling:* | |
Resource: | |
- Fn::Sub: arn:aws:autoscaling:${AWS::Region}:${AWS::AccountId}:autoScalingGroup:*:autoScalingGroupName/${AWS::StackName}-app | |
- Fn::Sub: arn:aws:autoscaling:${AWS::Region}:${AWS::AccountId}:autoScalingGroup:*:autoScalingGroupName/${AWS::StackName}-stream | |
- Effect: Allow | |
Action: | |
- elasticloadbalancing:* | |
Resource: | |
- Fn::Sub: arn:aws:elasticloadbalancing:${AWS::Region}:${AWS::AccountId}:loadbalancer/app/${AWS::StackName}-app/* | |
- Fn::Sub: arn:aws:elasticloadbalancing:${AWS::Region}:${AWS::AccountId}:loadbalancer/app/${AWS::StackName}-app | |
- Fn::Sub: arn:aws:elasticloadbalancing:${AWS::Region}:${AWS::AccountId}:targetgroup/${AWS::StackName}-ret/* | |
- Fn::Sub: arn:aws:elasticloadbalancing:${AWS::Region}:${AWS::AccountId}:listener/app/${AWS::StackName}-app/* | |
- Fn::Sub: arn:aws:elasticloadbalancing:${AWS::Region}:${AWS::AccountId}:listener-rule/app/${AWS::StackName}-app/* | |
- Effect: Allow | |
Action: | |
- ec2:* | |
Resource: | |
- Fn::Sub: arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:security-group/* | |
- Effect: Allow | |
Action: | |
- ec2:Describe* | |
- ec2:Get* | |
- route53:Get* | |
- autoscaling:Describe* | |
- rds:Describe* | |
- elasticloadbalancing:Describe* | |
- elasticloadbalancingv2:Describe* | |
Resource: | |
- '*' | |
StackTopicCloudfrontPolicy: | |
Type: AWS::IAM::Policy | |
Properties: | |
Roles: | |
- Ref: StackTopicHandlerRole | |
PolicyName: | |
Fn::Sub: ${AWS::StackName}-stack-topic-cf-policy | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- cloudfront:* | |
Resource: | |
Fn::Sub: arn:aws:cloudfront::${AWS::AccountId}:distribution/${AppCloudfrontDistribution} | |
StackTopicHandler: | |
Type: AWS::Lambda::Function | |
Properties: | |
FunctionName: | |
Fn::Sub: ${AWS::StackName}-stack-topic-handler | |
Description: Registers Node IPs into Round Robin DNS and manages stack if budgets | |
are hit | |
Handler: index.handler | |
Runtime: nodejs14.x | |
Role: | |
Fn::GetAtt: StackTopicHandlerRole.Arn | |
Timeout: 900 | |
Code: | |
ZipFile: | |
Fn::Sub: | | |
const AWS=require("aws-sdk"),promisify=e=>t=>new Promise((a,n)=>e(t,(e,t)=>{e?(console.log(e),n(e)):a(t)})),OFFLINE_SETTING="Offline - Temporarily shut off servers";async function handleBudgetAlert(e,t){const a=e.Records[0].Sns.TopicArn,n=new AWS.SNS,s=(await promisify(n.listTagsForResource.bind(n))({ResourceArn:a})).Tags,r=s.find(e=>"stack-name"===e.Key).Value,o=s.find(e=>"stack-region"===e.Key).Value,i=new AWS.CloudFormation({region:o});await new Promise(async e=>{let t;const a=async()=>{const a=await promisify(i.describeStacks.bind(i))({StackName:r});if(a){const n=a.Stacks[0].StackStatus;if(n.endsWith("_COMPLETE")||n.endsWith("_FAILED"))return t&&clearInterval(t),e(),!0}return!1};await a()||(t=setInterval(a,3e4))});const c=(await promisify(i.describeStacks.bind(i))({StackName:r})).Stacks[0].Parameters,d=[];for(const e of c)"StackOffline"===e.ParameterKey?d.push({ParameterKey:e.ParameterKey,ParameterValue:OFFLINE_SETTING}):d.push({ParameterKey:e.ParameterKey,UsePreviousValue:!0});await promisify(i.updateStack.bind(i))({StackName:r,UsePreviousTemplate:!0,Parameters:d,Capabilities:["CAPABILITY_IAM","CAPABILITY_NAMED_IAM"]})}async function handleASGMessage(e,t){const a=e.AutoScalingGroupName,n=e.Event,s="${LowerStackName.Value}-app.${InternalZoneInfo.Name}.";if("autoscaling:EC2_INSTANCE_LAUNCH"===n||"autoscaling:EC2_INSTANCE_TERMINATE"===n||"INSTANCE_REBOOT"===n){const e=new AWS.AutoScaling({region:"${AWS::Region}"}),t=new AWS.EC2({region:"${AWS::Region}"}),n=new AWS.Route53,r=await promisify(e.describeAutoScalingGroups.bind(e))({AutoScalingGroupNames:[a],MaxRecords:1}),o=(await promisify(n.listResourceRecordSets.bind(n))({StartRecordName:s,StartRecordType:"A",HostedZoneId:"${InternalZoneInfo.Id}",MaxItems:"100"})).ResourceRecordSets,i=r.AutoScalingGroups[0].Instances.map(e=>e.InstanceId),c=await promisify(t.describeInstances.bind(t))({DryRun:!1,InstanceIds:i}),d=[];for(let e=0;e<c.Reservations.length;e++){const t=c.Reservations[e];for(let e=0;e<t.Instances.length;e++){const a=t.Instances[e].NetworkInterfaces[0].Association.PublicIp;a&&d.indexOf(a)<0&&d.push(a)}}for(let e=0,t=o.length;e<t;e++){const t=o[e];if(t.Name!==s||"A"!==t.Type)continue;const a=t.ResourceRecords.length>0&&t.ResourceRecords[0].Value;if(a&&!d.find(e=>e===a))try{await promisify(n.changeResourceRecordSets.bind(n))({ChangeBatch:{Changes:[{Action:"DELETE",ResourceRecordSet:{MultiValueAnswer:!0,Name:t.Name,Type:t.Type,TTL:t.TTL,SetIdentifier:t.SetIdentifier,ResourceRecords:t.ResourceRecords,HealthCheckId:t.HealthCheckId}}]},HostedZoneId:"${InternalZoneInfo.Id}"})}catch(e){}}let l=(await promisify(n.listHealthChecks.bind(n))({MaxItems:"100"})).HealthChecks;if(d.length>1)for(let e=0,t=d.length;e<t;e++){const t=d[e];if(!l.find(e=>e.HealthCheckConfig.IPAddress===t))try{await promisify(n.createHealthCheck.bind(n))({CallerReference:Math.floor(1e9*Math.random()).toString(),HealthCheckConfig:{EnableSNI:!0,FailureThreshold:2,FullyQualifiedDomainName:s,IPAddress:t,Port:443,RequestInterval:10,ResourcePath:"/health",Type:"HTTPS"}})}catch(e){}}l=(await promisify(n.listHealthChecks.bind(n))({})).HealthChecks;for(let e=0,t=d.length;e<t;e++){const t=d[e];if(!o.find(e=>e.Name===s&&e.ResourceRecords&&e.ResourceRecords.length&&e.ResourceRecords[0].Value===t)){const e=l.find(e=>e.HealthCheckConfig.IPAddress===t);try{await promisify(n.changeResourceRecordSets.bind(n))({ChangeBatch:{Changes:[{Action:"UPSERT",ResourceRecordSet:{MultiValueAnswer:!0,Name:s,Type:"A",HealthCheckId:e?e.Id:null,TTL:15,SetIdentifier:t,ResourceRecords:[{Value:t}]}}]},HostedZoneId:"${InternalZoneInfo.Id}"})}catch(e){}}}for(let e=0,t=l.length;e<t;e++){const t=l[e];if(1===d.length||!d.find(e=>t.HealthCheckConfig.IPAddress===e))try{await promisify(n.deleteHealthCheck.bind(n))({HealthCheckId:t.Id})}catch(e){}}}else console.log("Unsupported ASG event: "+a+" "+n),t.done("Unsupported ASG event: "+a+" "+n)}exports.handler=async function(e,t){if(e.Records[0].Sns.Message.indexOf("Budget Name")>=0)return handleBudgetAlert(e,t);return handleASGMessage(JSON.parse(e.Records[0].Sns.Message),t)}; | |
InvokeStackTopicHandlerPermission: | |
Type: AWS::Lambda::Permission | |
Properties: | |
Action: lambda:InvokeFunction | |
FunctionName: | |
Ref: StackTopicHandler | |
Principal: sns.amazonaws.com | |
StackTopicHandlerTopicPolicy: | |
Type: AWS::SNS::TopicPolicy | |
Properties: | |
Topics: | |
- Ref: StackTopic | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
Fn::If: | |
- HasDbMonthlyBudget | |
- - Effect: Allow | |
Sid: | |
Fn::Sub: ${AWS::StackName}-topic-policy-stmt-asg | |
Action: sns:Publish | |
Resource: | |
Ref: StackTopic | |
Principal: | |
AWS: '*' | |
Condition: | |
ArnLike: | |
AWS:SourceArn: | |
Ref: AppASG | |
- Effect: Allow | |
Sid: | |
Fn::Sub: ${AWS::StackName}-topic-policy-stmt-budget | |
Action: sns:Publish | |
Resource: | |
Ref: StackTopic | |
Principal: | |
Service: budgets.amazonaws.com | |
- - Effect: Allow | |
Sid: | |
Fn::Sub: ${AWS::StackName}-topic-policy-stmt-asg | |
Action: sns:Publish | |
Resource: | |
Ref: StackTopic | |
Principal: | |
AWS: '*' | |
Condition: | |
ArnLike: | |
AWS:SourceArn: | |
Ref: AppASG | |
EFSCreateOrRestoreRole: | |
Type: AWS::IAM::Role | |
Properties: | |
RoleName: | |
Fn::Sub: ${AWS::StackName}-EFSCreateOrRestore | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: lambda.amazonaws.com | |
Action: sts:AssumeRole | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole | |
Path: / | |
Policies: | |
- PolicyName: efs-create-or-restore-role | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- elasticfilesystem:* | |
- kms:Get* | |
- kms:Describe* | |
- kms:Decrypt | |
- backup:StartRestoreJob | |
- backup:Get* | |
- backup:Describe* | |
- backup:List* | |
Resource: | |
- '*' | |
- Effect: Allow | |
Action: | |
- iam:PassRole | |
Resource: | |
- Fn::GetAtt: DailyBackupRole.Arn | |
EFSCreateOrRestore: | |
Type: AWS::Lambda::Function | |
Properties: | |
FunctionName: | |
Fn::Sub: ${AWS::StackName}-efs-create-or-restore | |
Description: Creates or restores EFS volumes | |
Handler: index.handler | |
Runtime: nodejs14.x | |
Role: | |
Fn::GetAtt: EFSCreateOrRestoreRole.Arn | |
Timeout: 900 | |
Code: | |
ZipFile: | |
Fn::Sub: | | |
const https=require("https"),url=require("url"),AWS=require("aws-sdk"),promisify=e=>t=>new Promise((s,o)=>e(t,(e,t)=>{e?(console.log(e),o(e)):s(t)}));function getReason(e){return e?e.message:""}async function sendResponse(e,t,s,o,r,i){const n={StackId:t.StackId,RequestId:t.RequestId,LogicalResourceId:t.LogicalResourceId,PhysicalResourceId:e,Status:o,Reason:getReason(i)+" See details in CloudWatch Log: "+s.logStreamName,Data:r},a=JSON.stringify(n),c=url.parse(t.ResponseURL),u={hostname:c.hostname,port:443,path:c.path,method:"PUT",headers:{"content-type":"","content-length":a.length}};await new Promise((e,t)=>{const o=https.request(u,()=>{s.done(null,r),e()});o.on("error",e=>{console.log("sendResponse Error:\n",e),s.done(e),t()}),o.write(a),o.end()})}exports.handler=async function(e,t){const s=new AWS.EFS;if("Create"==e.RequestType||"Update"==e.RequestType){const o=e.ResourceProperties.PerformanceMode||"generalPurpose",r=e.ResourceProperties.ThroughputMode||"bursting",i=e.RequestId;let n=null;"provisioned"===r&&(n=e.ResourceProperties.ProvisionedThroughputInMibps);const a=e.ResourceProperties.Encrypted||!1,c=e.ResourceProperties.FileSystemTags||[],u=e.ResourceProperties.KmsKeyId||null;let d;if("Create"===e.RequestType){const l=e.ResourceProperties.RestoreBackupVaultName,p=e.ResourceProperties.RestoreRecoveryPointArn,y=e.ResourceProperties.RestoreIamRoleArn;if(p&&l){const R=new AWS.Backup,m=(await promisify(R.getRecoveryPointRestoreMetadata.bind(R))({BackupVaultName:l,RecoveryPointArn:p})).RestoreMetadata,S=(await promisify(R.startRestoreJob.bind(R))({RecoveryPointArn:p,Metadata:{"file-system-id":m["file-system-id"],PerformanceMode:o,CreationToken:i,Encrypted:a?"true":"false",KmsKeyId:u,newFileSystem:"true"},ResourceType:"EFS",IdempotencyToken:i,IamRoleArn:y})).RestoreJobId,I=await new Promise(async(s,o)=>{let r;const i=async()=>{const i=await promisify(R.describeRestoreJob.bind(R))({RestoreJobId:S});return("COMPLETED"===i.Status||"FAILED"===i.Status)&&(r&&clearInterval(r),"COMPLETED"===i.Status?s(i.CreatedResourceArn):(await sendResponse(null,e,t,"FAILED",{},"Restore job failed."),o()),!0)};await i()||(r=setInterval(i,1e4))});d=I.split("/")[1],await promisify(s.createTags.bind(s))({FileSystemId:d,Tags:c}),"bursting"!==r&&await promisify(s.updateFileSystem.bind(s))({FileSystemId:d,ThroughputMode:r,ProvisionedThroughputInMibps:n})}else d=(await promisify(s.createFileSystem.bind(s))({PerformanceMode:o,CreationToken:i,ThroughputMode:r,Encrypted:a,Tags:c,KmsKeyId:u,ProvisionedThroughputInMibps:n})).FileSystemId}else d=e.PhysicalResourceId,await promisify(s.updateFileSystem.bind(s))({FileSystemId:d,ThroughputMode:r,ProvisionedThroughputInMibps:n});await new Promise(async e=>{let t;const o=async()=>{return"available"===(await promisify(s.describeFileSystems.bind(s))({FileSystemId:d})).FileSystems[0].LifeCycleState&&(t&&clearInterval(t),e(),!0)};await o()||(t=setInterval(o,1e4))});const l=e.ResourceProperties.LifecyclePolicies;return l&&await promisify(s.putLifecycleConfiguration.bind(s))({FileSystemId:d,LifecyclePolicies:l}),void await sendResponse(d,e,t,"SUCCESS")}if("Delete"==e.RequestType){const o=e.PhysicalResourceId;return await promisify(s.deleteFileSystem.bind(s))({FileSystemId:o}),void await sendResponse(o,e,t,"SUCCESS")}}; | |
SendEmailPassword: | |
Type: Custom::SendEmailPassword | |
Properties: | |
ServiceToken: | |
Fn::GetAtt: SESPasswordFunction.Arn | |
SecretAccessKey: | |
Fn::GetAtt: SendEmailAccessKey.SecretAccessKey | |
Region: us-east-1 | |
DependsOn: | |
- SendEmailUser | |
BoxKeysBucket: | |
Type: AWS::S3::Bucket | |
Properties: | |
AccessControl: Private | |
BucketName: | |
Fn::Join: | |
- '-' | |
- - Fn::Sub: ${LowerStackName.Value}-box-keys | |
- Fn::Select: | |
- 0 | |
- Fn::Split: | |
- '-' | |
- Fn::Select: | |
- 2 | |
- Fn::Split: | |
- / | |
- Ref: AWS::StackId | |
EmptyBuckets: | |
Type: Custom::EmptyBuckets | |
Properties: | |
ServiceToken: | |
Fn::GetAtt: EmptyBucketFunction.Arn | |
BucketNames: | |
- Ref: BoxKeysBucket | |
- Ref: SpeelycaptorScratchBucket | |
- Ref: AssetsBucket | |
- Ref: LinkRedirectorBucket | |
DependsOn: | |
- EmptyBucketRole | |
SshTOTP: | |
Type: Custom::SshTOTP | |
Properties: | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.GenerateTOTPFunctionArn | |
BucketName: | |
Ref: BoxKeysBucket | |
BucketRegion: | |
Ref: AWS::Region | |
StackName: | |
Ref: AWS::StackName | |
JWTKeys: | |
Type: Custom::JWTKeys | |
Properties: | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.GenerateJWTKeysFunctionArn | |
BucketName: | |
Ref: BoxKeysBucket | |
BucketRegion: | |
Ref: AWS::Region | |
VapidKeys: | |
Type: Custom::VapidKeys | |
Properties: | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.GenerateVapidKeysFunctionArn | |
BucketName: | |
Ref: BoxKeysBucket | |
BucketRegion: | |
Ref: AWS::Region | |
BioRingKey: | |
Type: Custom::BioRingKey | |
Properties: | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.GenerateRingKeyFunctionArn | |
BucketName: | |
Ref: BoxKeysBucket | |
BucketRegion: | |
Ref: AWS::Region | |
KeyName: | |
Ref: AWS::StackName | |
BioServiceKeys: | |
Type: Custom::BioServiceKeys | |
Properties: | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.GenerateServiceKeyFunctionArn | |
BucketName: | |
Ref: BoxKeysBucket | |
BucketRegion: | |
Ref: AWS::Region | |
ServiceNames: | |
- reticulum.default | |
- janus-gateway.default | |
- postgrest.default | |
- coturn.default | |
- certbot.default | |
- ita.default | |
- speelycaptor.default | |
- photomnemonic.default | |
- youtube-dl-api-server.default | |
- pgbouncer.default | |
- imgproxy.default | |
- hubs.default | |
- spoke.default | |
- polycosm-static-assets.default | |
Org: | |
Ref: AWS::StackName | |
BioUserKey: | |
Type: Custom::BioUserKey | |
Properties: | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.GenerateUserKeyFunctionArn | |
BucketName: | |
Ref: BoxKeysBucket | |
BucketRegion: | |
Ref: AWS::Region | |
UserName: polycosm-config-user | |
SpeelycaptorScratchBucket: | |
Type: AWS::S3::Bucket | |
Properties: | |
AccessControl: Private | |
BucketName: | |
Fn::Join: | |
- '-' | |
- - Fn::Sub: ${LowerStackName.Value}-speelycaptor-scratch | |
- Fn::Select: | |
- 0 | |
- Fn::Split: | |
- '-' | |
- Fn::Select: | |
- 2 | |
- Fn::Split: | |
- / | |
- Ref: AWS::StackId | |
SpeelycaptorRole: | |
Type: AWS::IAM::Role | |
Properties: | |
RoleName: | |
Fn::Sub: ${AWS::StackName}-Speelycaptor | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: | |
- lambda.amazonaws.com | |
Action: | |
- sts:AssumeRole | |
Sid: '' | |
Path: / | |
SpeelycaptorRolePolicy: | |
Type: AWS::IAM::Policy | |
Properties: | |
PolicyName: speelycaptor-policy | |
Roles: | |
- Ref: SpeelycaptorRole | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: s3:GetObject | |
Resource: | |
Fn::Sub: ${SpeelycaptorScratchBucket.Arn}/* | |
- Effect: Allow | |
Action: s3:PutObject | |
Resource: | |
Fn::Sub: ${SpeelycaptorScratchBucket.Arn}/* | |
- Effect: Allow | |
Action: s3:PutObjectAcl | |
Resource: | |
Fn::Sub: ${SpeelycaptorScratchBucket.Arn}/* | |
- Effect: Allow | |
Action: s3:GetObject | |
Resource: | |
Fn::Sub: ${SpeelycaptorScratchBucket.Arn}/* | |
- Effect: Allow | |
Action: s3:ListBucket | |
Resource: | |
Fn::Sub: ${SpeelycaptorScratchBucket.Arn} | |
- Effect: Allow | |
Action: | |
- ec2:DescribeInstances | |
- ec2:CreateNetworkInterface | |
- ec2:AttachNetworkInterface | |
- ec2:DescribeNetworkInterfaces | |
- ec2:DeleteNetworkInterface | |
- autoscaling:CompleteLifecycleAction | |
Resource: '*' | |
- Effect: Allow | |
Action: | |
- logs:CreateLogGroup | |
- logs:CreateLogStream | |
- logs:PutLogEvents | |
Resource: | |
Fn::Sub: arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*:*:* | |
SpeelycaptorApiGatewayRestApi: | |
Type: AWS::ApiGateway::RestApi | |
Properties: | |
Name: speelycaptor | |
EndpointConfiguration: | |
Types: | |
- EDGE | |
SpeelycaptorApiGatewayResourceInit: | |
Type: AWS::ApiGateway::Resource | |
Properties: | |
ParentId: | |
Fn::GetAtt: SpeelycaptorApiGatewayRestApi.RootResourceId | |
PathPart: init | |
RestApiId: | |
Ref: SpeelycaptorApiGatewayRestApi | |
SpeelycaptorApiGatewayResourceConvert: | |
Type: AWS::ApiGateway::Resource | |
Properties: | |
ParentId: | |
Fn::GetAtt: SpeelycaptorApiGatewayRestApi.RootResourceId | |
PathPart: convert | |
RestApiId: | |
Ref: SpeelycaptorApiGatewayRestApi | |
SpeelycaptorApiGatewayMethodInitGet: | |
Type: AWS::ApiGateway::Method | |
Properties: | |
HttpMethod: GET | |
ResourceId: | |
Ref: SpeelycaptorApiGatewayResourceInit | |
RestApiId: | |
Ref: SpeelycaptorApiGatewayRestApi | |
ApiKeyRequired: false | |
AuthorizationType: NONE | |
Integration: | |
IntegrationHttpMethod: POST | |
Type: AWS_PROXY | |
Uri: | |
Fn::Sub: arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${SpeelycaptorApplication.Outputs.SpeelycaptorInitFunctionArn}/invocations | |
MethodResponses: [] | |
SpeelycaptorApiGatewayMethodConvertGet: | |
Type: AWS::ApiGateway::Method | |
Properties: | |
HttpMethod: GET | |
ResourceId: | |
Ref: SpeelycaptorApiGatewayResourceConvert | |
RestApiId: | |
Ref: SpeelycaptorApiGatewayRestApi | |
ApiKeyRequired: false | |
AuthorizationType: NONE | |
Integration: | |
IntegrationHttpMethod: POST | |
Type: AWS_PROXY | |
Uri: | |
Fn::Sub: arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${SpeelycaptorApplication.Outputs.SpeelycaptorConvertFunctionArn}/invocations | |
MethodResponses: [] | |
SpeelycaptorApiGatewayDeployment: | |
Type: AWS::ApiGateway::Deployment | |
Properties: | |
RestApiId: | |
Ref: SpeelycaptorApiGatewayRestApi | |
StageName: prod | |
DependsOn: | |
- SpeelycaptorApiGatewayMethodInitGet | |
- SpeelycaptorApiGatewayMethodConvertGet | |
SpeelycaptorInitLambdaPermissionApiGateway: | |
Type: AWS::Lambda::Permission | |
Properties: | |
FunctionName: | |
Fn::GetAtt: SpeelycaptorApplication.Outputs.SpeelycaptorInitFunctionArn | |
Action: lambda:InvokeFunction | |
Principal: | |
Fn::Sub: apigateway.${AWS::URLSuffix} | |
SourceArn: | |
Fn::Sub: arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${SpeelycaptorApiGatewayRestApi}/*/* | |
SpeelycaptorConvertLambdaPermissionApiGateway: | |
Type: AWS::Lambda::Permission | |
Properties: | |
FunctionName: | |
Fn::GetAtt: SpeelycaptorApplication.Outputs.SpeelycaptorConvertFunctionArn | |
Action: lambda:InvokeFunction | |
Principal: | |
Fn::Sub: apigateway.${AWS::URLSuffix} | |
SourceArn: | |
Fn::Sub: arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${SpeelycaptorApiGatewayRestApi}/*/* | |
PhotomnemonicRole: | |
Type: AWS::IAM::Role | |
Properties: | |
RoleName: | |
Fn::Sub: ${AWS::StackName}-Photomnemonic | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: | |
- lambda.amazonaws.com | |
Action: | |
- sts:AssumeRole | |
Sid: '' | |
Path: / | |
PhotomnemonicRolePolicy: | |
Type: AWS::IAM::Policy | |
Properties: | |
PolicyName: photomnemonic-policy | |
Roles: | |
- Ref: PhotomnemonicRole | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- ec2:DescribeInstances | |
- ec2:CreateNetworkInterface | |
- ec2:AttachNetworkInterface | |
- ec2:DescribeNetworkInterfaces | |
- ec2:DeleteNetworkInterface | |
- autoscaling:CompleteLifecycleAction | |
Resource: '*' | |
- Effect: Allow | |
Action: | |
- logs:CreateLogGroup | |
- logs:CreateLogStream | |
- logs:PutLogEvents | |
Resource: | |
Fn::Sub: arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*:*:* | |
PhotomnemonicApiGatewayRestApi: | |
Type: AWS::ApiGateway::RestApi | |
Properties: | |
Name: photomnemonic | |
BinaryMediaTypes: | |
- '*/*' | |
EndpointConfiguration: | |
Types: | |
- EDGE | |
Body: | |
swagger: 2 | |
paths: | |
/screenshot: | |
get: | |
parameters: | |
- name: url | |
in: query | |
required: true | |
x-amazon-apigateway-binary-media-types: | |
- '*/*' | |
x-amazon-apigateway-integration: | |
httpMethod: POST | |
responses: | |
contentHandling: CONVERT_TO_BINARY, | |
type: aws_proxy | |
contentHandling: CONVERT_TO_BINARY | |
uri: | |
Fn::Sub: arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PhotomnemonicApplication.Outputs.PhotomnemonicScreenshotFunctionArn}/invocations | |
PhotomnemonicApiGatewayDeployment: | |
Type: AWS::ApiGateway::Deployment | |
Properties: | |
RestApiId: | |
Ref: PhotomnemonicApiGatewayRestApi | |
StageName: prod | |
DependsOn: | |
- PhotomnemonicApiGatewayRestApi | |
PhotomnemonicScreenshotLambdaPermissionApiGateway: | |
Type: AWS::Lambda::Permission | |
Properties: | |
FunctionName: | |
Fn::GetAtt: PhotomnemonicApplication.Outputs.PhotomnemonicScreenshotFunctionArn | |
Action: lambda:InvokeFunction | |
Principal: | |
Fn::Sub: apigateway.${AWS::URLSuffix} | |
SourceArn: | |
Fn::Sub: arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${PhotomnemonicApiGatewayRestApi}/*/* | |
NearsparkRole: | |
Type: AWS::IAM::Role | |
Properties: | |
RoleName: | |
Fn::Sub: ${AWS::StackName}-Nearspark | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: | |
- lambda.amazonaws.com | |
Action: | |
- sts:AssumeRole | |
Sid: '' | |
Path: / | |
NearsparkRolePolicy: | |
Type: AWS::IAM::Policy | |
Properties: | |
PolicyName: nearspark-policy | |
Roles: | |
- Ref: NearsparkRole | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- ec2:DescribeInstances | |
- ec2:CreateNetworkInterface | |
- ec2:AttachNetworkInterface | |
- ec2:DescribeNetworkInterfaces | |
- ec2:DeleteNetworkInterface | |
- autoscaling:CompleteLifecycleAction | |
Resource: '*' | |
- Effect: Allow | |
Action: | |
- logs:CreateLogGroup | |
- logs:CreateLogStream | |
- logs:PutLogEvents | |
Resource: | |
Fn::Sub: arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*:*:* | |
SESDomainApplication: | |
Type: AWS::Serverless::Application | |
Properties: | |
Location: | |
ApplicationId: | |
Fn::FindInMap: | |
- AWSMPServerlessResourcesRegionalizedMappings | |
- Ref: AWS::Region | |
- awscfnsesdomain5f452d36bfff4c02a4175c663b64fd08 | |
SemanticVersion: 0.4.2 | |
Parameters: | |
SESDomainRoleArn: | |
Fn::GetAtt: SESDomainRole.Arn | |
KeymasterApplication: | |
Type: AWS::Serverless::Application | |
Properties: | |
Location: | |
ApplicationId: | |
Fn::FindInMap: | |
- AWSMPServerlessResourcesRegionalizedMappings | |
- Ref: AWS::Region | |
- keymasterpublic5f452d36bfff4c02a4175c663b64fd08 | |
SemanticVersion: 0.0.1 | |
Parameters: | |
KeymasterRoleArn: | |
Fn::GetAtt: KeymasterRole.Arn | |
NearsparkApplication: | |
Type: AWS::Serverless::Application | |
Properties: | |
Location: | |
ApplicationId: | |
Fn::FindInMap: | |
- AWSMPServerlessResourcesRegionalizedMappings | |
- Ref: AWS::Region | |
- nearspark5f452d36bfff4c02a4175c663b64fd08 | |
SemanticVersion: 0.1.1 | |
Parameters: | |
NearsparkRoleArn: | |
Fn::GetAtt: NearsparkRole.Arn | |
SpeelycaptorApplication: | |
Type: AWS::Serverless::Application | |
Properties: | |
Location: | |
ApplicationId: | |
Fn::FindInMap: | |
- AWSMPServerlessResourcesRegionalizedMappings | |
- Ref: AWS::Region | |
- speelycaptor5f452d36bfff4c02a4175c663b64fd08 | |
SemanticVersion: 0.1.3 | |
Parameters: | |
SpeelycaptorRoleArn: | |
Fn::GetAtt: SpeelycaptorRole.Arn | |
SpeelycaptorScratchBucketId: | |
Ref: SpeelycaptorScratchBucket | |
SpeelycaptorScratchBucketArn: | |
Fn::GetAtt: SpeelycaptorScratchBucket.Arn | |
SpeelycaptorScratchBucketRegion: | |
Ref: AWS::Region | |
PhotomnemonicApplication: | |
Type: AWS::Serverless::Application | |
Properties: | |
Location: | |
ApplicationId: | |
Fn::FindInMap: | |
- AWSMPServerlessResourcesRegionalizedMappings | |
- Ref: AWS::Region | |
- photomnemonic5f452d36bfff4c02a4175c663b64fd08 | |
SemanticVersion: 0.3.0 | |
Parameters: | |
PhotomnemonicRoleArn: | |
Fn::GetAtt: PhotomnemonicRole.Arn | |
NearsparkApiGatewayRestApi: | |
Type: AWS::ApiGateway::RestApi | |
Properties: | |
Name: nearspark | |
BinaryMediaTypes: | |
- '*/*' | |
EndpointConfiguration: | |
Types: | |
- EDGE | |
Body: | |
swagger: 2 | |
paths: | |
/thumbnail/{url}: | |
get: | |
consumes: | |
- application/json | |
parameters: | |
- name: url | |
in: path | |
required: true | |
type: string | |
- name: width | |
in: query | |
required: true | |
type: number | |
- name: height | |
in: query | |
required: true | |
type: number | |
- name: fit | |
in: query | |
required: false | |
type: string | |
- name: position | |
in: query | |
required: false | |
type: string | |
- name: gravity | |
in: query | |
required: false | |
type: string | |
- name: strategy | |
in: query | |
required: false | |
type: string | |
- name: background | |
in: query | |
required: false | |
type: string | |
- name: withoutEnlargement | |
in: query | |
required: false | |
type: boolean | |
requestTemplates: | |
application/json: '{"url": "$input.params(''url'')"}' | |
x-amazon-apigateway-binary-media-types: | |
- '*/*' | |
x-amazon-apigateway-integration: | |
httpMethod: POST | |
responses: | |
contentHandling: CONVERT_TO_BINARY, | |
type: aws_proxy | |
contentHandling: CONVERT_TO_BINARY | |
uri: | |
Fn::Sub: arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${NearsparkApplication.Outputs.NearsparkThumbnailFunctionArn}/invocations | |
NearsparkApiGatewayDeployment: | |
Type: AWS::ApiGateway::Deployment | |
Properties: | |
RestApiId: | |
Ref: NearsparkApiGatewayRestApi | |
StageName: prod | |
DependsOn: | |
- NearsparkApiGatewayRestApi | |
NearsparkThumbnailLambdaPermissionApiGateway: | |
Type: AWS::Lambda::Permission | |
Properties: | |
FunctionName: | |
Fn::GetAtt: NearsparkApplication.Outputs.NearsparkThumbnailFunctionArn | |
Action: lambda:InvokeFunction | |
Principal: | |
Fn::Sub: apigateway.${AWS::URLSuffix} | |
SourceArn: | |
Fn::Sub: arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${NearsparkApiGatewayRestApi}/*/* | |
NearsparkDNS: | |
Type: AWS::Route53::RecordSet | |
Properties: | |
Name: | |
Fn::Sub: ${LowerStackName.Value}-nearspark.${InternalZoneInfo.Name} | |
HostedZoneId: | |
Ref: InternalZone | |
Type: A | |
AliasTarget: | |
DNSName: | |
Fn::GetAtt: NearsparkCloudfrontDistribution.DomainName | |
HostedZoneId: Z2FDTNDATAQYW2 | |
NearsparkCloudfrontDistribution: | |
Type: AWS::CloudFront::Distribution | |
Properties: | |
DistributionConfig: | |
Origins: | |
- Id: ApiGateway | |
DomainName: | |
Fn::Sub: ${NearsparkApiGatewayRestApi}.execute-api.${AWS::Region}.amazonaws.com | |
CustomOriginConfig: | |
HTTPPort: 80 | |
HTTPSPort: 443 | |
OriginProtocolPolicy: https-only | |
OriginSSLProtocols: | |
- SSLv3 | |
- TLSv1 | |
- TLSv1.1 | |
- TLSv1.2 | |
OriginPath: /prod | |
Enabled: true | |
HttpVersion: http2 | |
PriceClass: PriceClass_All | |
Aliases: | |
- Fn::Sub: ${LowerStackName.Value}-nearspark.${InternalZoneInfo.Name} | |
DefaultCacheBehavior: | |
AllowedMethods: | |
- GET | |
- HEAD | |
- OPTIONS | |
CachedMethods: | |
- GET | |
- HEAD | |
TargetOriginId: ApiGateway | |
ForwardedValues: | |
QueryString: true | |
Headers: [] | |
Cookies: | |
Forward: none | |
ViewerProtocolPolicy: redirect-to-https | |
MinTTL: '0' | |
DefaultTTL: '3600' | |
ViewerCertificate: | |
AcmCertificateArn: | |
Fn::If: | |
- IsNonEast | |
- Ref: InternalZoneSSLCertEast | |
- Ref: InternalZoneSSLCert | |
SslSupportMethod: sni-only | |
AssetsBucket: | |
Type: AWS::S3::Bucket | |
Properties: | |
BucketName: | |
Fn::Join: | |
- '-' | |
- - Fn::Sub: ${LowerStackName.Value}-assets | |
- Fn::Select: | |
- 0 | |
- Fn::Split: | |
- '-' | |
- Fn::Select: | |
- 2 | |
- Fn::Split: | |
- / | |
- Ref: AWS::StackId | |
OwnershipControls: | |
Rules: | |
- ObjectOwnership: ObjectWriter | |
PublicAccessBlockConfiguration: | |
BlockPublicAcls: false | |
BlockPublicPolicy: false | |
IgnorePublicAcls: false | |
RestrictPublicBuckets: false | |
CorsConfiguration: | |
CorsRules: | |
- AllowedHeaders: | |
- '*' | |
AllowedMethods: | |
- GET | |
- HEAD | |
AllowedOrigins: | |
- Fn::Sub: https://${DomainName} | |
- Fn::Sub: https://${ShortlinkZoneInfo.Name} | |
- Fn::Sub: https://hubs.local:8080 | |
- Fn::Sub: https://localhost:8080 | |
ExposedHeaders: [] | |
MaxAge: 31536000 | |
AssetsPublicBucketPolicy: | |
Type: AWS::S3::BucketPolicy | |
Properties: | |
PolicyDocument: | |
Id: PublicAssets | |
Version: '2012-10-17' | |
Statement: | |
- Sid: PublicReadForGetBucketObjects | |
Effect: Allow | |
Principal: '*' | |
Action: s3:GetObject | |
Resource: | |
Fn::Sub: ${AssetsBucket.Arn}/* | |
Bucket: | |
Ref: AssetsBucket | |
RootRedirectorBucket: | |
Type: AWS::S3::Bucket | |
Properties: | |
BucketName: | |
Fn::Join: | |
- '-' | |
- - Fn::Sub: ${LowerStackName.Value}-root-redirector | |
- Fn::Select: | |
- 0 | |
- Fn::Split: | |
- '-' | |
- Fn::Select: | |
- 2 | |
- Fn::Split: | |
- / | |
- Ref: AWS::StackId | |
OwnershipControls: | |
Rules: | |
- ObjectOwnership: ObjectWriter | |
PublicAccessBlockConfiguration: | |
BlockPublicAcls: false | |
BlockPublicPolicy: false | |
IgnorePublicAcls: false | |
RestrictPublicBuckets: false | |
CorsConfiguration: | |
CorsRules: | |
- AllowedHeaders: | |
- '*' | |
AllowedMethods: | |
- GET | |
- HEAD | |
AllowedOrigins: | |
- '*' | |
ExposedHeaders: | |
- Date | |
- ETag | |
MaxAge: 31536000 | |
WebsiteConfiguration: | |
IndexDocument: index.html | |
ErrorDocument: error.html | |
RoutingRules: | |
- RedirectRule: | |
ReplaceKeyPrefixWith: | |
Fn::GetAtt: ParsedOfflineRedirectUrl.S3ReplaceKeyPrefixWith | |
Protocol: | |
Fn::GetAtt: ParsedOfflineRedirectUrl.S3Protocol | |
HostName: | |
Fn::GetAtt: ParsedOfflineRedirectUrl.S3Hostname | |
HttpRedirectCode: 307 | |
LinkRedirectorBucket: | |
Type: AWS::S3::Bucket | |
Properties: | |
BucketName: | |
Fn::Join: | |
- '-' | |
- - Fn::Sub: ${LowerStackName.Value}-link-redirector | |
- Fn::Select: | |
- 0 | |
- Fn::Split: | |
- '-' | |
- Fn::Select: | |
- 2 | |
- Fn::Split: | |
- / | |
- Ref: AWS::StackId | |
OwnershipControls: | |
Rules: | |
- ObjectOwnership: ObjectWriter | |
PublicAccessBlockConfiguration: | |
BlockPublicAcls: false | |
BlockPublicPolicy: false | |
IgnorePublicAcls: false | |
RestrictPublicBuckets: false | |
CorsConfiguration: | |
CorsRules: | |
- AllowedHeaders: | |
- '*' | |
AllowedMethods: | |
- GET | |
- HEAD | |
AllowedOrigins: | |
- '*' | |
ExposedHeaders: | |
- Date | |
- ETag | |
MaxAge: 31536000 | |
WebsiteConfiguration: | |
IndexDocument: link-redirector-index.html | |
ErrorDocument: link-redirector-error.html | |
RoutingRules: | |
- RedirectRule: | |
ReplaceKeyPrefixWith: link/ | |
Protocol: https | |
HostName: | |
Ref: DomainName | |
LinkRedirectorPublicBucketPolicy: | |
Type: AWS::S3::BucketPolicy | |
Properties: | |
PolicyDocument: | |
Id: PublicLinkRedirector | |
Version: '2012-10-17' | |
Statement: | |
- Sid: PublicReadForGetBucketObjects | |
Effect: Allow | |
Principal: '*' | |
Action: s3:GetObject | |
Resource: | |
Fn::Sub: ${LinkRedirectorBucket.Arn}/* | |
Bucket: | |
Ref: LinkRedirectorBucket | |
LinkRedirectorDNS: | |
Type: AWS::Route53::RecordSet | |
Properties: | |
Name: | |
Fn::GetAtt: ShortlinkZoneInfo.Name | |
HostedZoneId: | |
Ref: ShortlinkZone | |
Type: A | |
AliasTarget: | |
DNSName: | |
Fn::GetAtt: LinkRedirectorCloudfrontDistribution.DomainName | |
HostedZoneId: Z2FDTNDATAQYW2 | |
LinkRedirectorCloudfrontDistribution: | |
Type: AWS::CloudFront::Distribution | |
Properties: | |
DistributionConfig: | |
Origins: | |
- Id: | |
Fn::Sub: ${AWS::StackName}-link-redirector | |
DomainName: | |
Fn::Select: | |
- 2 | |
- Fn::Split: | |
- / | |
- Fn::GetAtt: LinkRedirectorBucket.WebsiteURL | |
CustomOriginConfig: | |
HTTPPort: 80 | |
HTTPSPort: 443 | |
OriginProtocolPolicy: http-only | |
OriginSSLProtocols: | |
- SSLv3 | |
- TLSv1 | |
- TLSv1.1 | |
- TLSv1.2 | |
Enabled: true | |
PriceClass: PriceClass_All | |
Aliases: | |
- Fn::GetAtt: ShortlinkZoneInfo.Name | |
DefaultCacheBehavior: | |
AllowedMethods: | |
- GET | |
- HEAD | |
- OPTIONS | |
CachedMethods: | |
- GET | |
- HEAD | |
TargetOriginId: | |
Fn::Sub: ${AWS::StackName}-link-redirector | |
ForwardedValues: | |
QueryString: false | |
Headers: | |
- Origin | |
- Access-Control-Request-Method | |
- Access-Control-Request-Headers | |
- Accept | |
Cookies: | |
Forward: none | |
ViewerProtocolPolicy: allow-all | |
MinTTL: 86400 | |
DefaultTTL: 86400 | |
MaxTTL: 86400 | |
Restrictions: | |
GeoRestriction: | |
RestrictionType: none | |
Locations: [] | |
ViewerCertificate: | |
AcmCertificateArn: | |
Ref: ShortlinkZoneSSLCertEast | |
SslSupportMethod: sni-only | |
MinimumProtocolVersion: TLSv1 | |
AppALBSecurityGroup: | |
Type: AWS::EC2::SecurityGroup | |
Condition: HasALB | |
Properties: | |
GroupName: | |
Fn::Sub: ${AWS::StackName}-app-alb | |
GroupDescription: App ALB | |
VpcId: | |
Ref: VPC | |
SecurityGroupIngress: | |
- IpProtocol: tcp | |
FromPort: 80 | |
ToPort: 80 | |
CidrIp: | |
Fn::If: | |
- HasInboundCidrOverride | |
- Ref: InboundCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: 443 | |
ToPort: 443 | |
CidrIp: | |
Fn::If: | |
- HasInboundCidrOverride | |
- Ref: InboundCidrOverride | |
- 0.0.0.0/0 | |
BioRingSecurityGroup: | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupName: | |
Fn::Sub: ${AWS::StackName}-bio-ring | |
GroupDescription: Bio | |
VpcId: | |
Ref: VPC | |
BioRingRPCIngress: | |
Type: AWS::EC2::SecurityGroupIngress | |
Properties: | |
GroupId: | |
Ref: BioRingSecurityGroup | |
IpProtocol: tcp | |
FromPort: 9638 | |
ToPort: 9638 | |
SourceSecurityGroupId: | |
Ref: BioRingSecurityGroup | |
BioRingRPCEgress: | |
Type: AWS::EC2::SecurityGroupEgress | |
Properties: | |
GroupId: | |
Ref: BioRingSecurityGroup | |
IpProtocol: tcp | |
FromPort: 9638 | |
ToPort: 9638 | |
DestinationSecurityGroupId: | |
Ref: BioRingSecurityGroup | |
BioRingRPCUDPIngress: | |
Type: AWS::EC2::SecurityGroupIngress | |
Properties: | |
GroupId: | |
Ref: BioRingSecurityGroup | |
IpProtocol: udp | |
FromPort: 9638 | |
ToPort: 9638 | |
SourceSecurityGroupId: | |
Ref: BioRingSecurityGroup | |
BioRingRPCUDPEgress: | |
Type: AWS::EC2::SecurityGroupEgress | |
Properties: | |
GroupId: | |
Ref: BioRingSecurityGroup | |
IpProtocol: udp | |
FromPort: 9638 | |
ToPort: 9638 | |
DestinationSecurityGroupId: | |
Ref: BioRingSecurityGroup | |
AppSecurityGroup: | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupName: | |
Fn::Sub: ${AWS::StackName}-app | |
GroupDescription: App | |
VpcId: | |
Ref: VPC | |
SecurityGroupIngress: | |
- IpProtocol: tcp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- RetExternal | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- RetExternal | |
- Port | |
CidrIp: | |
Fn::If: | |
- HasInboundCidrOverride | |
- Ref: InboundCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- RetTurnExternal | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- RetTurnExternal | |
- Port | |
CidrIp: | |
Fn::If: | |
- HasInboundCidrOverride | |
- Ref: InboundCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: udp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- RetTurnExternal | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- RetTurnExternal | |
- Port | |
CidrIp: | |
Fn::If: | |
- HasInboundCidrOverride | |
- Ref: InboundCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogExternal | |
- AppPort | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogExternal | |
- AppPort | |
CidrIp: | |
Fn::If: | |
- HasInboundCidrOverride | |
- Ref: InboundCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogTurnExternal | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogTurnExternal | |
- Port | |
CidrIp: | |
Fn::If: | |
- HasInboundCidrOverride | |
- Ref: InboundCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: udp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogTurnExternal | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogTurnExternal | |
- Port | |
CidrIp: | |
Fn::If: | |
- HasInboundCidrOverride | |
- Ref: InboundCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: udp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogWebRTCFrom | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogWebRTCTo | |
- Port | |
CidrIp: | |
Fn::If: | |
- HasInboundCidrOverride | |
- Ref: InboundCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogWebRTCFrom | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogWebRTCTo | |
- Port | |
CidrIp: | |
Fn::If: | |
- HasInboundCidrOverride | |
- Ref: InboundCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- Ssh | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- Ssh | |
- Port | |
CidrIp: | |
Fn::If: | |
- HasInboundSSHCidrOverride | |
- Ref: InboundSSHCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: icmp | |
FromPort: -1 | |
ToPort: -1 | |
CidrIp: | |
Fn::If: | |
- HasInboundSSHCidrOverride | |
- Ref: InboundSSHCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: icmpv6 | |
FromPort: -1 | |
ToPort: -1 | |
CidrIp: | |
Fn::If: | |
- HasInboundSSHCidrOverride | |
- Ref: InboundSSHCidrOverride | |
- 0.0.0.0/0 | |
SecurityGroupEgress: | |
- IpProtocol: tcp | |
FromPort: 80 | |
ToPort: 80 | |
CidrIp: 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: 443 | |
ToPort: 443 | |
CidrIp: 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: 587 | |
ToPort: 587 | |
CidrIp: 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: 25 | |
ToPort: 25 | |
CidrIp: 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: 2525 | |
ToPort: 2525 | |
CidrIp: 0.0.0.0/0 | |
- IpProtocol: udp | |
FromPort: 123 | |
ToPort: 123 | |
CidrIp: 0.0.0.0/0 | |
- IpProtocol: udp | |
FromPort: 0 | |
ToPort: 65535 | |
CidrIp: 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: 53 | |
ToPort: 53 | |
CidrIp: 0.0.0.0/0 | |
AppALBReticulumIngress: | |
Type: AWS::EC2::SecurityGroupIngress | |
Condition: HasALB | |
Properties: | |
GroupId: | |
Ref: AppSecurityGroup | |
IpProtocol: tcp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- RetInternal | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- RetInternal | |
- Port | |
SourceSecurityGroupId: | |
Ref: AppALBSecurityGroup | |
AppALBReticulumEgress: | |
Type: AWS::EC2::SecurityGroupEgress | |
Condition: HasALB | |
Properties: | |
GroupId: | |
Ref: AppALBSecurityGroup | |
IpProtocol: tcp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- RetInternal | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- RetInternal | |
- Port | |
DestinationSecurityGroupId: | |
Ref: AppSecurityGroup | |
AppJanusAdminAppIngress: | |
Type: AWS::EC2::SecurityGroupIngress | |
Properties: | |
GroupId: | |
Ref: AppSecurityGroup | |
IpProtocol: tcp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogAdmin | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogAdmin | |
- Port | |
SourceSecurityGroupId: | |
Ref: AppSecurityGroup | |
AppJanusAdminStreamEgress: | |
Type: AWS::EC2::SecurityGroupEgress | |
Properties: | |
GroupId: | |
Ref: AppSecurityGroup | |
IpProtocol: tcp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogAdmin | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogAdmin | |
- Port | |
DestinationSecurityGroupId: | |
Ref: StreamSecurityGroup | |
AppEPMDAppIngress: | |
Type: AWS::EC2::SecurityGroupIngress | |
Properties: | |
GroupId: | |
Ref: AppSecurityGroup | |
IpProtocol: tcp | |
FromPort: 4369 | |
ToPort: 4369 | |
SourceSecurityGroupId: | |
Ref: AppSecurityGroup | |
AppErlangAppIngress: | |
Type: AWS::EC2::SecurityGroupIngress | |
Properties: | |
GroupId: | |
Ref: AppSecurityGroup | |
IpProtocol: tcp | |
FromPort: 9000 | |
ToPort: 9100 | |
SourceSecurityGroupId: | |
Ref: AppSecurityGroup | |
AppDbEgress: | |
Type: AWS::EC2::SecurityGroupEgress | |
Properties: | |
GroupId: | |
Ref: AppSecurityGroup | |
IpProtocol: tcp | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- PostgreSQL | |
- Port | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- PostgreSQL | |
- Port | |
DestinationSecurityGroupId: | |
Ref: AppDbSecurityGroup | |
StreamDbEgress: | |
Type: AWS::EC2::SecurityGroupEgress | |
Properties: | |
GroupId: | |
Ref: StreamSecurityGroup | |
IpProtocol: tcp | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- PostgreSQL | |
- Port | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- PostgreSQL | |
- Port | |
DestinationSecurityGroupId: | |
Ref: AppDbSecurityGroup | |
AppFullSelfEgress: | |
Type: AWS::EC2::SecurityGroupEgress | |
Properties: | |
GroupId: | |
Ref: AppSecurityGroup | |
IpProtocol: tcp | |
FromPort: 0 | |
ToPort: 65535 | |
DestinationSecurityGroupId: | |
Ref: AppSecurityGroup | |
AppRole: | |
Type: AWS::IAM::Role | |
Properties: | |
RoleName: | |
Fn::Sub: ${AWS::StackName}-app | |
AssumeRolePolicyDocument: | |
Statement: | |
- Action: | |
- sts:AssumeRole | |
Effect: Allow | |
Principal: | |
Service: ec2.amazonaws.com | |
Version: '2012-10-17' | |
ManagedPolicyArns: | |
- Ref: BasePolicy | |
- Ref: CleanupRecordsEC2AppPolicy | |
StreamRole: | |
Type: AWS::IAM::Role | |
Properties: | |
RoleName: | |
Fn::Sub: ${AWS::StackName}-stream | |
AssumeRolePolicyDocument: | |
Statement: | |
- Action: | |
- sts:AssumeRole | |
Effect: Allow | |
Principal: | |
Service: ec2.amazonaws.com | |
Version: '2012-10-17' | |
ManagedPolicyArns: | |
- Ref: BasePolicy | |
CleanupRecordsEC2AppPolicy: | |
Type: AWS::IAM::ManagedPolicy | |
Properties: | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- autoscaling:DescribeAutoScalingGroups | |
- ec2:DescribeInstances | |
Resource: | |
- '*' | |
- Effect: Allow | |
Action: | |
- route53:ChangeResourceRecordSets | |
- route53:ListResourceRecordSets | |
Resource: | |
Fn::Sub: arn:aws:route53:::hostedzone/${InternalZoneInfo.Id} | |
- Effect: Allow | |
Action: | |
- route53:* | |
Resource: | |
- Fn::Sub: arn:aws:route53:::hostedzone/${InternalZoneInfo.Id} | |
- Fn::If: | |
- HasManagedDomain | |
- Fn::Sub: arn:aws:route53:::hostedzone/${ExternalZoneInfo.Id} | |
- arn:aws:route53:::hostedzone/DEADZONE* | |
- Effect: Allow | |
Action: | |
- autoscaling:DescribeAutoScalingGroups | |
- ec2:DescribeInstances | |
Resource: | |
- '*' | |
- Effect: Allow | |
Action: | |
- route53:ChangeResourceRecordSets | |
- route53:ListResourceRecordSets | |
Resource: | |
Fn::Sub: arn:aws:route53:::hostedzone/${InternalZoneInfo.Id} | |
BasePolicy: | |
Type: AWS::IAM::ManagedPolicy | |
Properties: | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- ec2:DescribeInstances | |
Resource: '*' | |
- Effect: Allow | |
Action: | |
- ec2:CreateTags | |
Resource: '*' | |
- Effect: Allow | |
Action: | |
- ec2:DescribeTags | |
Resource: '*' | |
- Effect: Allow | |
Action: | |
- ssm:DescribeAssociation | |
- ssm:GetDeployablePatchSnapshotForInstance | |
- ssm:GetDocument | |
- ssm:DescribeDocument | |
- ssm:GetManifest | |
- ssm:GetParameters | |
- ssm:ListAssociations | |
- ssm:ListInstanceAssociations | |
- ssm:PutInventory | |
- ssm:PutComplianceItems | |
- ssm:PutConfigurePackageResult | |
- ssm:UpdateAssociationStatus | |
- ssm:UpdateInstanceAssociationStatus | |
- ssm:UpdateInstanceInformation | |
Resource: '*' | |
- Effect: Allow | |
Action: | |
- ssmmessages:CreateControlChannel | |
- ssmmessages:CreateDataChannel | |
- ssmmessages:OpenControlChannel | |
- ssmmessages:OpenDataChannel | |
Resource: '*' | |
- Effect: Allow | |
Action: | |
- sns:Publish | |
Resource: | |
Ref: StackTopic | |
- Effect: Allow | |
Action: | |
- ec2messages:AcknowledgeMessage | |
- ec2messages:DeleteMessage | |
- ec2messages:FailMessage | |
- ec2messages:GetEndpoint | |
- ec2messages:GetMessages | |
- ec2messages:SendReply | |
Resource: '*' | |
- Effect: Allow | |
Action: | |
- route53:ChangeResourceRecordSets | |
Resource: | |
Fn::Sub: arn:aws:route53:::hostedzone/${InternalZoneInfo.Id} | |
- Effect: Allow | |
Action: | |
- route53:ListHostedZones | |
- route53:GetChange | |
Resource: '*' | |
- Effect: Allow | |
Action: | |
- iam:GetUser | |
- iam:ListAccessKeys | |
Resource: | |
Fn::GetAtt: SendEmailUser.Arn | |
- Effect: Allow | |
Action: s3:GetObject | |
Resource: | |
Fn::Sub: ${BoxKeysBucket.Arn}/* | |
- Effect: Allow | |
Action: | |
- s3:ListBucket | |
Resource: | |
- Fn::Sub: ${BoxKeysBucket.Arn} | |
- Effect: Allow | |
Action: | |
- s3:GetObject | |
- s3:DeleteObject | |
- s3:PutObject | |
- s3:PutObjectAcl | |
Resource: | |
- Fn::Sub: ${AssetsBucket.Arn}/hubs/* | |
- Fn::Sub: ${AssetsBucket.Arn}/spoke/* | |
- Fn::Sub: ${AssetsBucket.Arn}/assets/* | |
- Fn::Sub: ${LinkRedirectorBucket.Arn}/* | |
- Effect: Allow | |
Action: | |
- s3:ListBucket | |
Resource: | |
- Fn::Sub: ${AssetsBucket.Arn} | |
- Fn::Sub: ${LinkRedirectorBucket.Arn} | |
- Effect: Allow | |
Action: cloudformation:DescribeStacks | |
Resource: | |
Ref: AWS::StackId | |
- Effect: Allow | |
Action: | |
- ssm:PutParameter | |
- ssm:DeleteParameter | |
- ssm:GetParameterHistory | |
- ssm:GetParametersByPath | |
- ssm:GetParameters | |
- ssm:GetParameter | |
- ssm:DeleteParameters | |
Resource: | |
- Fn::Sub: arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/ita/${AWS::StackName}/* | |
- Fn::Sub: arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/keymaster/${AWS::StackName}/* | |
- Fn::Sub: arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/keymaster/${AWS::StackName} | |
- Effect: Allow | |
Action: | |
- ses:GetSendQuota | |
Resource: '*' | |
- Effect: Allow | |
Action: | |
- lambda:ListFunctions | |
- lambda:ListTags | |
Resource: '*' | |
AppALB: | |
Type: AWS::ElasticLoadBalancingV2::LoadBalancer | |
Condition: HasALB | |
Properties: | |
Name: | |
Fn::Sub: ${AWS::StackName}-app | |
LoadBalancerAttributes: | |
- Key: idle_timeout.timeout_seconds | |
Value: 120 | |
SecurityGroups: | |
- Ref: AppALBSecurityGroup | |
Subnets: | |
- Ref: SubnetAPublic | |
- Ref: SubnetBPublic | |
DependsOn: | |
- AppCloudfrontDistribution | |
- AppAssetsCloudfrontDistribution | |
- NearsparkCloudfrontDistribution | |
- LinkRedirectorCloudfrontDistribution | |
AppALBDNSInternal: | |
Type: AWS::Route53::RecordSet | |
Condition: HasALB | |
Properties: | |
Name: | |
Fn::Sub: ${LowerStackName.Value}-app-alb.${InternalZoneInfo.Name} | |
HostedZoneId: | |
Ref: InternalZone | |
Type: A | |
AliasTarget: | |
DNSName: | |
Fn::GetAtt: AppALB.DNSName | |
HostedZoneId: | |
Fn::GetAtt: AppALB.CanonicalHostedZoneID | |
AppALBRetTargetGroup: | |
Type: AWS::ElasticLoadBalancingV2::TargetGroup | |
Condition: HasALB | |
Properties: | |
Name: | |
Fn::Sub: ${AWS::StackName}-ret | |
VpcId: | |
Ref: VPC | |
Port: | |
Fn::FindInMap: | |
- ServicesMeta | |
- RetInternal | |
- Port | |
Protocol: HTTPS | |
TargetGroupAttributes: | |
- Key: deregistration_delay.timeout_seconds | |
Value: 0 | |
HealthCheckPath: /health | |
HealthCheckProtocol: HTTPS | |
HealthyThresholdCount: 2 | |
UnhealthyThresholdCount: 2 | |
HealthCheckIntervalSeconds: 10 | |
HealthCheckTimeoutSeconds: 5 | |
AppALBRetSSLListener: | |
Type: AWS::ElasticLoadBalancingV2::Listener | |
Condition: HasALB | |
Properties: | |
LoadBalancerArn: | |
Ref: AppALB | |
Port: 443 | |
Protocol: HTTPS | |
SslPolicy: ELBSecurityPolicy-2015-05 | |
Certificates: | |
- CertificateArn: | |
Fn::If: | |
- HasManagedDomain | |
- Fn::If: | |
- IsEast | |
- Ref: ExternalZoneSSLCertLocalIfEast | |
- Ref: ExternalZoneSSLCertLocalIfNonEast | |
- Ref: UnmanagedDomainCertArn | |
DefaultActions: | |
- TargetGroupArn: | |
Fn::If: | |
- PerformOfflineRedirect | |
- Ref: AWS::NoValue | |
- Ref: AppALBRetTargetGroup | |
Type: | |
Fn::If: | |
- PerformOfflineRedirect | |
- redirect | |
- forward | |
RedirectConfig: | |
Fn::If: | |
- PerformOfflineRedirect | |
- Protocol: | |
Fn::GetAtt: ParsedOfflineRedirectUrl.ALBProtocol | |
Port: 443 | |
Host: | |
Fn::GetAtt: ParsedOfflineRedirectUrl.ALBHost | |
Path: | |
Fn::GetAtt: ParsedOfflineRedirectUrl.ALBPath | |
Query: | |
Fn::GetAtt: ParsedOfflineRedirectUrl.ALBQuery | |
StatusCode: HTTP_302 | |
- Ref: AWS::NoValue | |
AppALBRetSSLListenerInternalCert: | |
Type: AWS::ElasticLoadBalancingV2::ListenerCertificate | |
Condition: HasALB | |
Properties: | |
ListenerArn: | |
Ref: AppALBRetSSLListener | |
Certificates: | |
- CertificateArn: | |
Ref: InternalZoneSSLCert | |
AppALBRetClearListener: | |
Type: AWS::ElasticLoadBalancingV2::Listener | |
Condition: HasALB | |
Properties: | |
LoadBalancerArn: | |
Ref: AppALB | |
Port: 80 | |
Protocol: HTTP | |
DefaultActions: | |
- Type: redirect | |
RedirectConfig: | |
Port: '443' | |
Protocol: HTTPS | |
StatusCode: HTTP_301 | |
AppALBRetListenerRule: | |
Type: AWS::ElasticLoadBalancingV2::ListenerRule | |
Condition: HasALB | |
Properties: | |
ListenerArn: | |
Ref: AppALBRetSSLListener | |
Priority: 1 | |
Actions: | |
- Type: forward | |
TargetGroupArn: | |
Ref: AppALBRetTargetGroup | |
Conditions: | |
- Field: path-pattern | |
Values: | |
- / | |
AppInstanceProfile: | |
Type: AWS::IAM::InstanceProfile | |
Properties: | |
InstanceProfileName: | |
Fn::Sub: ${AWS::StackName}-app | |
Roles: | |
- Ref: AppRole | |
StreamInstanceProfile: | |
Type: AWS::IAM::InstanceProfile | |
Properties: | |
InstanceProfileName: | |
Fn::Sub: ${AWS::StackName}-stream | |
Roles: | |
- Ref: StreamRole | |
DomainDNS: | |
Type: AWS::Route53::RecordSet | |
Condition: HasManagedDomain | |
Properties: | |
Name: | |
Ref: DomainName | |
HostedZoneId: | |
Fn::GetAtt: ExternalZoneInfo.Id | |
Type: A | |
AliasTarget: | |
DNSName: | |
Fn::If: | |
- HasALB | |
- Fn::GetAtt: AppALB.DNSName | |
- Fn::GetAtt: AppCloudfrontDistribution.DomainName | |
HostedZoneId: | |
Fn::If: | |
- HasALB | |
- Fn::GetAtt: AppALB.CanonicalHostedZoneID | |
- Z2FDTNDATAQYW2 | |
ParsedOfflineRedirectUrl: | |
Type: Custom::ParsedURL | |
Properties: | |
URL: | |
Ref: StackOfflineRedirectUrl | |
ServiceToken: | |
Fn::GetAtt: ParseURL.Arn | |
LowerStackName: | |
Type: Custom::ToLower | |
Properties: | |
String: | |
Fn::Sub: ${AWS::StackName} | |
ServiceToken: | |
Fn::GetAtt: ToLower.Arn | |
AppCloudfrontDistribution: | |
Type: AWS::CloudFront::Distribution | |
Properties: | |
DistributionConfig: | |
Enabled: true | |
Origins: | |
- Id: | |
Fn::Sub: ${AWS::StackName}-app | |
DomainName: | |
Fn::If: | |
- RequestedALB | |
- Fn::Sub: ${LowerStackName.Value}-app-alb.${InternalZoneInfo.Name} | |
- Fn::Sub: ${LowerStackName.Value}-app.${InternalZoneInfo.Name} | |
CustomOriginConfig: | |
HTTPPort: 80 | |
HTTPSPort: 443 | |
OriginProtocolPolicy: https-only | |
OriginSSLProtocols: | |
- SSLv3 | |
- TLSv1 | |
- TLSv1.1 | |
- TLSv1.2 | |
OriginReadTimeout: 60 | |
- Id: | |
Fn::Sub: ${AWS::StackName}-redirector | |
DomainName: | |
Fn::Select: | |
- 2 | |
- Fn::Split: | |
- / | |
- Fn::GetAtt: RootRedirectorBucket.WebsiteURL | |
CustomOriginConfig: | |
HTTPPort: 80 | |
HTTPSPort: 443 | |
OriginProtocolPolicy: http-only | |
OriginSSLProtocols: | |
- SSLv3 | |
- TLSv1 | |
- TLSv1.1 | |
- TLSv1.2 | |
- Id: | |
Fn::Sub: ${AWS::StackName}-app-assets | |
DomainName: | |
Fn::GetAtt: AssetsBucket.RegionalDomainName | |
S3OriginConfig: | |
OriginAccessIdentity: '' | |
Restrictions: | |
GeoRestriction: | |
RestrictionType: none | |
Locations: [] | |
Aliases: | |
- Ref: DomainName | |
HttpVersion: http2 | |
PriceClass: PriceClass_All | |
CustomErrorResponses: | |
- ErrorCode: 403 | |
ErrorCachingMinTTL: 0 | |
- ErrorCode: 404 | |
ErrorCachingMinTTL: 0 | |
- ErrorCode: 500 | |
ErrorCachingMinTTL: 0 | |
- ErrorCode: 502 | |
ResponseCode: 502 | |
ErrorCachingMinTTL: 0 | |
ResponsePagePath: | |
Fn::Sub: /assets/pages/unavailable.html | |
- ErrorCode: 503 | |
ResponseCode: 503 | |
ErrorCachingMinTTL: 0 | |
ResponsePagePath: | |
Fn::Sub: /assets/pages/unavailable.html | |
- ErrorCode: 504 | |
ResponseCode: 504 | |
ErrorCachingMinTTL: 0 | |
ResponsePagePath: | |
Fn::Sub: /assets/pages/unavailable.html | |
CacheBehaviors: | |
- PathPattern: /assets/* | |
Compress: true | |
AllowedMethods: | |
- GET | |
- HEAD | |
- OPTIONS | |
CachedMethods: | |
- GET | |
- HEAD | |
TargetOriginId: | |
Fn::Sub: ${AWS::StackName}-app-assets | |
ForwardedValues: | |
QueryString: true | |
Headers: | |
- Origin | |
- Content-Type | |
- Access-Control-Request-Method | |
- Access-Control-Request-Headers | |
- Accept | |
Cookies: | |
Forward: none | |
ViewerProtocolPolicy: https-only | |
DefaultCacheBehavior: | |
Compress: true | |
AllowedMethods: | |
- GET | |
- HEAD | |
- POST | |
- PATCH | |
- PUT | |
- DELETE | |
- OPTIONS | |
CachedMethods: | |
- GET | |
- HEAD | |
TargetOriginId: | |
Fn::If: | |
- PerformOfflineRedirect | |
- Fn::Sub: ${AWS::StackName}-redirector | |
- Fn::Sub: ${AWS::StackName}-app | |
ForwardedValues: | |
QueryString: | |
Fn::If: | |
- PerformOfflineRedirect | |
- false | |
- true | |
Headers: | |
Fn::If: | |
- PerformOfflineRedirect | |
- [] | |
- - Origin | |
- Content-Type | |
- Range | |
- Host | |
- Authorization | |
- Access-Control-Request-Method | |
- Access-Control-Request-Headers | |
- Accept | |
Cookies: | |
Forward: none | |
ViewerProtocolPolicy: redirect-to-https | |
MinTTL: 0 | |
DefaultTTL: 3600 | |
MaxTTL: 3600 | |
ViewerCertificate: | |
AcmCertificateArn: | |
Fn::If: | |
- HasManagedDomain | |
- Fn::If: | |
- IsNonEast | |
- Ref: ExternalZoneSSLCertEast | |
- Ref: ExternalZoneSSLCertLocalIfEast | |
- Ref: UnmanagedDomainEastCertArn | |
SslSupportMethod: sni-only | |
MinimumProtocolVersion: TLSv1 | |
AppAssetsCloudfrontDistribution: | |
Type: AWS::CloudFront::Distribution | |
Properties: | |
DistributionConfig: | |
Enabled: true | |
Origins: | |
- Id: | |
Fn::Sub: ${AWS::StackName}-app-assets | |
DomainName: | |
Fn::GetAtt: AssetsBucket.RegionalDomainName | |
S3OriginConfig: | |
OriginAccessIdentity: '' | |
- Id: | |
Fn::Sub: ${AWS::StackName}-app | |
DomainName: | |
Fn::If: | |
- RequestedALB | |
- Fn::Sub: ${LowerStackName.Value}-app-alb.${InternalZoneInfo.Name} | |
- Fn::Sub: ${LowerStackName.Value}-app.${InternalZoneInfo.Name} | |
CustomOriginConfig: | |
HTTPPort: 80 | |
HTTPSPort: 443 | |
OriginProtocolPolicy: https-only | |
OriginSSLProtocols: | |
- SSLv3 | |
- TLSv1 | |
- TLSv1.1 | |
- TLSv1.2 | |
Restrictions: | |
GeoRestriction: | |
RestrictionType: none | |
Locations: [] | |
Aliases: | |
- Fn::Sub: ${LowerStackName.Value}-assets.${InternalZoneInfo.Name} | |
- Fn::Sub: ${LowerStackName.Value}-cors-proxy.${InternalZoneInfo.Name} | |
HttpVersion: http2 | |
PriceClass: PriceClass_All | |
CustomErrorResponses: | |
- ErrorCode: 403 | |
ErrorCachingMinTTL: 0 | |
- ErrorCode: 404 | |
ErrorCachingMinTTL: 0 | |
- ErrorCode: 500 | |
ErrorCachingMinTTL: 0 | |
- ErrorCode: 502 | |
ErrorCachingMinTTL: 0 | |
- ErrorCode: 503 | |
ErrorCachingMinTTL: 0 | |
- ErrorCode: 504 | |
ErrorCachingMinTTL: 0 | |
CacheBehaviors: | |
- PathPattern: /files/* | |
Compress: true | |
AllowedMethods: | |
- GET | |
- HEAD | |
- OPTIONS | |
CachedMethods: | |
- GET | |
- HEAD | |
TargetOriginId: | |
Fn::Sub: ${AWS::StackName}-app | |
ForwardedValues: | |
QueryString: true | |
Headers: | |
- Origin | |
- Content-Type | |
- Host | |
- Authorization | |
- Access-Control-Request-Method | |
- Access-Control-Request-Headers | |
- Accept | |
- Range | |
Cookies: | |
Forward: none | |
ViewerProtocolPolicy: https-only | |
MinTTL: 0 | |
DefaultTTL: 3600 | |
MaxTTL: 3600 | |
- PathPattern: /http* | |
Compress: true | |
AllowedMethods: | |
- GET | |
- HEAD | |
- POST | |
- PATCH | |
- PUT | |
- DELETE | |
- OPTIONS | |
CachedMethods: | |
- GET | |
- HEAD | |
TargetOriginId: | |
Fn::Sub: ${AWS::StackName}-app | |
ForwardedValues: | |
QueryString: true | |
Headers: | |
- Origin | |
- Content-Type | |
- Range | |
- Host | |
- Authorization | |
- Access-Control-Request-Method | |
- Access-Control-Request-Headers | |
- Accept | |
Cookies: | |
Forward: none | |
ViewerProtocolPolicy: https-only | |
MinTTL: 0 | |
DefaultTTL: 3600 | |
MaxTTL: 3600 | |
DefaultCacheBehavior: | |
Compress: true | |
AllowedMethods: | |
- GET | |
- HEAD | |
- OPTIONS | |
CachedMethods: | |
- GET | |
- HEAD | |
TargetOriginId: | |
Fn::Sub: ${AWS::StackName}-app-assets | |
ForwardedValues: | |
QueryString: true | |
Headers: | |
- Origin | |
- Content-Type | |
- Access-Control-Request-Method | |
- Access-Control-Request-Headers | |
- Accept | |
Cookies: | |
Forward: none | |
ViewerProtocolPolicy: https-only | |
MinTTL: 0 | |
DefaultTTL: 3600 | |
MaxTTL: 3600 | |
ViewerCertificate: | |
AcmCertificateArn: | |
Fn::If: | |
- IsNonEast | |
- Ref: InternalZoneSSLCertEast | |
- Ref: InternalZoneSSLCert | |
SslSupportMethod: sni-only | |
MinimumProtocolVersion: TLSv1 | |
AppAssetsDNS: | |
Type: AWS::Route53::RecordSet | |
Properties: | |
Name: | |
Fn::Sub: ${LowerStackName.Value}-assets.${InternalZoneInfo.Name} | |
HostedZoneId: | |
Ref: InternalZone | |
Type: A | |
AliasTarget: | |
DNSName: | |
Fn::GetAtt: AppAssetsCloudfrontDistribution.DomainName | |
HostedZoneId: Z2FDTNDATAQYW2 | |
StreamSecurityGroup: | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupName: | |
Fn::Sub: ${AWS::StackName}-stream | |
GroupDescription: Stream | |
VpcId: | |
Ref: VPC | |
SecurityGroupIngress: | |
- IpProtocol: tcp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogExternal | |
- StreamPort | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogExternal | |
- StreamPort | |
CidrIp: | |
Fn::If: | |
- HasInboundCidrOverride | |
- Ref: InboundCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogTurnExternal | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogTurnExternal | |
- Port | |
CidrIp: | |
Fn::If: | |
- HasInboundCidrOverride | |
- Ref: InboundCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: udp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogTurnExternal | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogTurnExternal | |
- Port | |
CidrIp: | |
Fn::If: | |
- HasInboundCidrOverride | |
- Ref: InboundCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: udp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogWebRTCFrom | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogWebRTCTo | |
- Port | |
CidrIp: | |
Fn::If: | |
- HasInboundCidrOverride | |
- Ref: InboundCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogWebRTCFrom | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogWebRTCTo | |
- Port | |
CidrIp: | |
Fn::If: | |
- HasInboundCidrOverride | |
- Ref: InboundCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- Ssh | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- Ssh | |
- Port | |
CidrIp: | |
Fn::If: | |
- HasInboundSSHCidrOverride | |
- Ref: InboundSSHCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: icmp | |
FromPort: -1 | |
ToPort: -1 | |
CidrIp: | |
Fn::If: | |
- HasInboundSSHCidrOverride | |
- Ref: InboundSSHCidrOverride | |
- 0.0.0.0/0 | |
- IpProtocol: icmpv6 | |
FromPort: -1 | |
ToPort: -1 | |
CidrIp: | |
Fn::If: | |
- HasInboundSSHCidrOverride | |
- Ref: InboundSSHCidrOverride | |
- 0.0.0.0/0 | |
SecurityGroupEgress: | |
- IpProtocol: tcp | |
FromPort: 80 | |
ToPort: 80 | |
CidrIp: 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: 443 | |
ToPort: 443 | |
CidrIp: 0.0.0.0/0 | |
- IpProtocol: udp | |
FromPort: 123 | |
ToPort: 123 | |
CidrIp: 0.0.0.0/0 | |
- IpProtocol: udp | |
FromPort: 0 | |
ToPort: 65535 | |
CidrIp: 0.0.0.0/0 | |
- IpProtocol: tcp | |
FromPort: 53 | |
ToPort: 53 | |
CidrIp: 0.0.0.0/0 | |
StreamJanusAdminAppIngress: | |
Type: AWS::EC2::SecurityGroupIngress | |
Properties: | |
GroupId: | |
Ref: StreamSecurityGroup | |
IpProtocol: tcp | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogAdmin | |
- Port | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogAdmin | |
- Port | |
SourceSecurityGroupId: | |
Ref: AppSecurityGroup | |
StreamFullSelfEgress: | |
Type: AWS::EC2::SecurityGroupEgress | |
Properties: | |
GroupId: | |
Ref: StreamSecurityGroup | |
IpProtocol: tcp | |
FromPort: 0 | |
ToPort: 65535 | |
DestinationSecurityGroupId: | |
Ref: StreamSecurityGroup | |
CorsProxyDNS: | |
Type: AWS::Route53::RecordSet | |
Properties: | |
Name: | |
Fn::Sub: ${LowerStackName.Value}-cors-proxy.${InternalZoneInfo.Name} | |
HostedZoneId: | |
Ref: InternalZone | |
Type: A | |
AliasTarget: | |
DNSName: | |
Fn::GetAtt: AppAssetsCloudfrontDistribution.DomainName | |
HostedZoneId: Z2FDTNDATAQYW2 | |
AppLaunchConfiguration: | |
Type: AWS::AutoScaling::LaunchConfiguration | |
Properties: | |
AssociatePublicIpAddress: true | |
BlockDeviceMappings: | |
- DeviceName: /dev/sda1 | |
Ebs: | |
VolumeSize: 8 | |
IamInstanceProfile: | |
Ref: AppInstanceProfile | |
InstanceType: | |
Ref: AppInstanceType | |
SecurityGroups: | |
- Ref: AppSecurityGroup | |
- Ref: BioRingSecurityGroup | |
- Ref: StorageConnectSecurityGroup | |
ImageId: | |
Fn::FindInMap: | |
- Regions | |
- Ref: AWS::Region | |
- ImageId | |
KeyName: | |
Ref: KeyPair | |
UserData: | |
Fn::Base64: | | |
#!/usr/bin/env bash | |
/opt/polycosm/polycosm_boot.sh aws aws | |
AppPlacementGroup: | |
Type: AWS::EC2::PlacementGroup | |
Properties: | |
Strategy: | |
Fn::If: | |
- AppIsNoPlacement | |
- cluster | |
- Fn::If: | |
- AppIsClusterPlacement | |
- Fn::FindInMap: | |
- InstanceTypeMeta | |
- Ref: AppInstanceType | |
- PlacementForCluster | |
- Ref: AppPlacementGroupStrategy | |
StackTopic: | |
Type: AWS::SNS::Topic | |
Properties: | |
TopicName: | |
Fn::Sub: ${AWS::StackName}-topic | |
DisplayName: Topic for handling scaling events and budget triggers | |
Subscription: | |
- Protocol: lambda | |
Endpoint: | |
Fn::GetAtt: StackTopicHandler.Arn | |
Tags: | |
- Key: stack-name | |
Value: | |
Fn::Sub: ${AWS::StackName} | |
- Key: stack-region | |
Value: | |
Fn::Sub: ${AWS::Region} | |
AppASG: | |
Type: AWS::AutoScaling::AutoScalingGroup | |
UpdatePolicy: | |
AutoScalingRollingUpdate: | |
MaxBatchSize: 10 | |
PauseTime: PT30M | |
SuspendProcesses: | |
- HealthCheck | |
- ReplaceUnhealthy | |
- AZRebalance | |
- AlarmNotification | |
- ScheduledActions | |
MinInstancesInService: | |
Fn::If: | |
- IsOffline | |
- 0 | |
- 1 | |
WaitOnResourceSignals: true | |
Properties: | |
AutoScalingGroupName: | |
Fn::Sub: ${AWS::StackName}-app | |
AvailabilityZones: | |
Fn::If: | |
- AppIsClusterPlacement | |
- - Fn::GetAtt: SubnetAPublic.AvailabilityZone | |
- - Fn::GetAtt: SubnetAPublic.AvailabilityZone | |
- Fn::GetAtt: SubnetBPublic.AvailabilityZone | |
VPCZoneIdentifier: | |
Fn::If: | |
- AppIsClusterPlacement | |
- - Ref: SubnetAPublic | |
- - Ref: SubnetAPublic | |
- Ref: SubnetBPublic | |
TargetGroupARNs: | |
Fn::If: | |
- HasALB | |
- - Ref: AppALBRetTargetGroup | |
- Ref: AWS::NoValue | |
MinSize: 0 | |
MaxSize: 64 | |
NotificationConfigurations: | |
- NotificationTypes: | |
- autoscaling:EC2_INSTANCE_LAUNCH | |
- autoscaling:EC2_INSTANCE_TERMINATE | |
TopicARN: | |
Ref: StackTopic | |
DesiredCapacity: | |
Fn::If: | |
- IsOffline | |
- 0 | |
- 1 | |
PlacementGroup: | |
Fn::If: | |
- AppIsNoPlacement | |
- Ref: AWS::NoValue | |
- Ref: AppPlacementGroup | |
LaunchConfigurationName: | |
Ref: AppLaunchConfiguration | |
Tags: | |
- Key: polycosm-roles | |
Value: | |
Fn::If: | |
- HasStreamingServers | |
- app | |
- app,stream | |
PropagateAtLaunch: true | |
- Key: polycosm-type | |
Value: app | |
PropagateAtLaunch: true | |
- Key: bio-ring | |
Value: | |
Fn::Sub: ${AWS::StackName} | |
PropagateAtLaunch: true | |
DependsOn: | |
- SshTOTP | |
- JWTKeys | |
- VapidKeys | |
- BioRingKey | |
- BioServiceKeys | |
- BioUserKey | |
StreamLaunchConfiguration: | |
Type: AWS::AutoScaling::LaunchConfiguration | |
Properties: | |
AssociatePublicIpAddress: true | |
BlockDeviceMappings: | |
- DeviceName: /dev/sda1 | |
Ebs: | |
VolumeSize: 8 | |
IamInstanceProfile: | |
Ref: StreamInstanceProfile | |
InstanceType: t3.micro | |
SecurityGroups: | |
- Ref: StreamSecurityGroup | |
- Ref: BioRingSecurityGroup | |
- Ref: StorageConnectSecurityGroup | |
ImageId: | |
Fn::FindInMap: | |
- Regions | |
- Ref: AWS::Region | |
- ImageId | |
KeyName: | |
Ref: KeyPair | |
UserData: | |
Fn::Base64: | | |
#!/usr/bin/env bash | |
/opt/polycosm/polycosm_boot.sh aws aws | |
StreamASG: | |
Type: AWS::AutoScaling::AutoScalingGroup | |
UpdatePolicy: | |
AutoScalingRollingUpdate: | |
MaxBatchSize: 10 | |
PauseTime: PT30M | |
SuspendProcesses: | |
- HealthCheck | |
- ReplaceUnhealthy | |
- AZRebalance | |
- AlarmNotification | |
- ScheduledActions | |
MinInstancesInService: | |
Fn::If: | |
- IsOffline | |
- 0 | |
- 0 | |
WaitOnResourceSignals: true | |
Properties: | |
AutoScalingGroupName: | |
Fn::Sub: ${AWS::StackName}-stream | |
AvailabilityZones: | |
- Fn::GetAtt: SubnetAPublic.AvailabilityZone | |
- Fn::GetAtt: SubnetBPublic.AvailabilityZone | |
VPCZoneIdentifier: | |
- Ref: SubnetAPublic | |
- Ref: SubnetBPublic | |
MinSize: 0 | |
MaxSize: 64 | |
DesiredCapacity: | |
Fn::If: | |
- IsOffline | |
- 0 | |
- 0 | |
LaunchConfigurationName: | |
Ref: StreamLaunchConfiguration | |
Tags: | |
- Key: polycosm-roles | |
Value: stream | |
PropagateAtLaunch: true | |
- Key: polycosm-type | |
Value: stream | |
PropagateAtLaunch: true | |
- Key: bio-ring | |
Value: | |
Fn::Sub: ${AWS::StackName} | |
PropagateAtLaunch: true | |
DependsOn: | |
- SshTOTP | |
- JWTKeys | |
- VapidKeys | |
- BioRingKey | |
- BioServiceKeys | |
- BioUserKey | |
StorageEFS: | |
Type: Custom::EFS | |
DeletionPolicy: Retain | |
Properties: | |
PerformanceMode: generalPurpose | |
LifecyclePolicies: | |
- TransitionToIA: AFTER_30_DAYS | |
FileSystemTags: | |
- Key: backup | |
Value: daily | |
- Key: Name | |
Value: | |
Fn::Sub: ${AWS::StackName}-storage | |
RestoreBackupVaultName: | |
Ref: RestoreBackupVaultName | |
RestoreRecoveryPointArn: | |
Ref: RestoreRecoveryPointArn | |
RestoreIamRoleArn: | |
Fn::GetAtt: DailyBackupRole.Arn | |
ServiceToken: | |
Fn::GetAtt: EFSCreateOrRestore.Arn | |
StorageEFSAppMountTargetAPrivate: | |
Type: AWS::EFS::MountTarget | |
Properties: | |
FileSystemId: | |
Ref: StorageEFS | |
SecurityGroups: | |
- Ref: StorageSecurityGroup | |
SubnetId: | |
Ref: SubnetAPrivate | |
StorageEFSAppMountTargetBPrivate: | |
Type: AWS::EFS::MountTarget | |
Properties: | |
FileSystemId: | |
Ref: StorageEFS | |
SecurityGroups: | |
- Ref: StorageSecurityGroup | |
SubnetId: | |
Ref: SubnetBPrivate | |
StorageSecurityGroup: | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupName: | |
Fn::Sub: ${AWS::StackName}-storage-fs | |
GroupDescription: Storage EFS | |
VpcId: | |
Ref: VPC | |
StorageConnectSecurityGroup: | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupName: | |
Fn::Sub: ${AWS::StackName}-storage-fs-connect | |
GroupDescription: Storage EFS Connect | |
VpcId: | |
Ref: VPC | |
SecurityGroupEgress: | |
- IpProtocol: tcp | |
FromPort: 2049 | |
ToPort: 2049 | |
DestinationSecurityGroupId: | |
Ref: StorageSecurityGroup | |
StorageFSConnectIngress: | |
Type: AWS::EC2::SecurityGroupIngress | |
Properties: | |
GroupId: | |
Ref: StorageSecurityGroup | |
IpProtocol: tcp | |
FromPort: 2049 | |
ToPort: 2049 | |
SourceSecurityGroupId: | |
Ref: StorageConnectSecurityGroup | |
DailyBackupVault: | |
Type: AWS::Backup::BackupVault | |
DeletionPolicy: Retain | |
Properties: | |
BackupVaultName: | |
Fn::Join: | |
- '-' | |
- - Fn::Sub: ${AWS::StackName}-daily-backup | |
- Fn::Select: | |
- 0 | |
- Fn::Split: | |
- '-' | |
- Fn::Select: | |
- 2 | |
- Fn::Split: | |
- / | |
- Ref: AWS::StackId | |
EncryptionKeyArn: | |
Fn::If: | |
- CreateDiskEncryptionKey | |
- Fn::GetAtt: DiskKmsKey.Arn | |
- Ref: AWS::NoValue | |
DailyBackupPlan: | |
Type: AWS::Backup::BackupPlan | |
Properties: | |
BackupPlan: | |
BackupPlanName: | |
Fn::Sub: ${AWS::StackName}-daily-backup-plan | |
BackupPlanRule: | |
- RuleName: Daily | |
TargetBackupVault: | |
Ref: DailyBackupVault | |
ScheduleExpression: cron(0 10 ? * * *) | |
DependsOn: DailyBackupVault | |
DailyBackupRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: | |
- backup.amazonaws.com | |
Action: | |
- sts:AssumeRole | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup | |
- arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForRestores | |
DailyBackupSelection: | |
Type: AWS::Backup::BackupSelection | |
Properties: | |
BackupSelection: | |
SelectionName: | |
Fn::Sub: ${AWS::StackName}-daily-backup-selection | |
IamRoleArn: | |
Fn::GetAtt: DailyBackupRole.Arn | |
ListOfTags: | |
- ConditionType: STRINGEQUALS | |
ConditionKey: backup | |
ConditionValue: daily | |
BackupPlanId: | |
Ref: DailyBackupPlan | |
DependsOn: DailyBackupPlan | |
AppDbSecurityGroup: | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupName: | |
Fn::Sub: ${AWS::StackName}-app-db | |
GroupDescription: DB | |
VpcId: | |
Ref: VPC | |
SecurityGroupIngress: | |
- IpProtocol: tcp | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- PostgreSQL | |
- Port | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- PostgreSQL | |
- Port | |
SourceSecurityGroupId: | |
Ref: AppSecurityGroup | |
- IpProtocol: tcp | |
ToPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- PostgreSQL | |
- Port | |
FromPort: | |
Fn::FindInMap: | |
- ServicesMeta | |
- PostgreSQL | |
- Port | |
SourceSecurityGroupId: | |
Ref: StreamSecurityGroup | |
DiskKmsKey: | |
Type: AWS::KMS::Key | |
Condition: CreateDiskEncryptionKey | |
DeletionPolicy: Retain | |
Properties: | |
Description: DB encryption key | |
EnableKeyRotation: true | |
KeyPolicy: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
AWS: | |
Fn::Sub: arn:aws:iam::${AWS::AccountId}:root | |
Action: kms:* | |
Resource: '*' | |
- Effect: Allow | |
Principal: | |
AWS: '*' | |
Action: | |
- kms:Encrypt | |
- kms:Decrypt | |
- kms:ReEncrypt* | |
- kms:GenerateDataKey* | |
- kms:CreateGrant | |
- kms:ListGrants | |
- kms:DescribeKey | |
Resource: '*' | |
Condition: | |
StringEquals: | |
kms:CallerAccount: | |
Ref: AWS::AccountId | |
kms:ViaService: | |
Fn::Sub: rds.${AWS::Region}.amazonaws.com | |
DiskKmsKeyAlias: | |
Type: AWS::KMS::Alias | |
Condition: CreateDiskEncryptionKey | |
DeletionPolicy: Retain | |
DependsOn: AppDb | |
Properties: | |
AliasName: | |
Fn::Join: | |
- '-' | |
- - Fn::Sub: alias/db-app-db-${AWS::StackName} | |
- Fn::Select: | |
- 0 | |
- Fn::Split: | |
- '-' | |
- Fn::Select: | |
- 2 | |
- Fn::Split: | |
- / | |
- Ref: AWS::StackId | |
TargetKeyId: | |
Ref: DiskKmsKey | |
AppDbSubnet: | |
Type: AWS::RDS::DBSubnetGroup | |
Properties: | |
DBSubnetGroupDescription: app-db | |
DBSubnetGroupName: | |
Fn::Sub: ${LowerStackName.Value}-app-db | |
SubnetIds: | |
- Ref: SubnetAPrivate | |
- Ref: SubnetBPrivate | |
RestoreAppDbSecret: | |
Type: Custom::ReadSecret | |
Condition: IsRestore | |
Properties: | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.ReadSecretFunctionArn | |
SecretArn: | |
Ref: RestoreAppDbSecretArn | |
AppDbSecret: | |
Type: AWS::SecretsManager::Secret | |
DeletionPolicy: Retain | |
Properties: | |
Fn::If: | |
- IsRestore | |
- Description: | |
Fn::Sub: ${AWS::StackName} Database Secret | |
SecretString: | |
Fn::GetAtt: RestoreAppDbSecret.SecretString | |
- Description: | |
Fn::Sub: ${AWS::StackName} Database Secret | |
GenerateSecretString: | |
SecretStringTemplate: '{ "username": "postgres" }' | |
GenerateStringKey: password | |
PasswordLength: 64 | |
ExcludePunctuation: true | |
AppDbSecretAttachment: | |
Type: AWS::SecretsManager::SecretTargetAttachment | |
Properties: | |
SecretId: | |
Ref: AppDbSecret | |
TargetId: | |
Ref: AppDb | |
TargetType: AWS::RDS::DBCluster | |
AppDbSecretResourcePolicy: | |
Type: AWS::SecretsManager::ResourcePolicy | |
Properties: | |
SecretId: | |
Ref: AppDbSecret | |
ResourcePolicy: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
AWS: | |
Fn::GetAtt: AppRole.Arn | |
Action: secretsmanager:GetSecretValue | |
Resource: '*' | |
- Effect: Deny | |
Principal: | |
AWS: | |
Fn::Sub: arn:aws:iam::${AWS::AccountId}:root | |
Action: secretsmanager:DeleteSecret | |
Resource: '*' | |
AppPostgrestDbSecret: | |
Type: Custom::GenerateSecret | |
DeletionPolicy: Retain | |
Properties: | |
Description: PostgREST admin db password | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.GenerateSecretFunctionArn | |
Name: postgrest-db-secret | |
Region: | |
Ref: AWS::Region | |
StackName: | |
Ref: AWS::StackName | |
CopyFromStackName: | |
Fn::If: | |
- IsRestore | |
- Ref: RestoreStackName | |
- Ref: AWS::NoValue | |
CopyFromStackRegion: | |
Fn::If: | |
- IsRestore | |
- Ref: AWS::Region | |
- Ref: AWS::NoValue | |
AppDb: | |
Type: AWS::RDS::DBCluster | |
Properties: | |
AvailabilityZones: | |
- Fn::GetAtt: SubnetAPrivate.AvailabilityZone | |
- Fn::GetAtt: SubnetBPrivate.AvailabilityZone | |
BackupRetentionPeriod: | |
Ref: DbBackupRetentionPeriod | |
DatabaseName: polycosm_production | |
DBClusterIdentifier: | |
Fn::Sub: ${AWS::StackName}-app-db | |
DBSubnetGroupName: | |
Ref: AppDbSubnet | |
Engine: aurora-postgresql | |
EngineMode: serverless | |
EngineVersion: 11.13 | |
KmsKeyId: | |
Fn::If: | |
- CreateDiskEncryptionKey | |
- Ref: DiskKmsKey | |
- Ref: AWS::NoValue | |
StorageEncrypted: true | |
MasterUsername: | |
Fn::If: | |
- IsRestore | |
- Ref: AWS::NoValue | |
- Fn::Join: | |
- '' | |
- - '{{resolve:secretsmanager:' | |
- Ref: AppDbSecret | |
- :SecretString:username}} | |
MasterUserPassword: | |
Fn::If: | |
- IsRestore | |
- Ref: AWS::NoValue | |
- Fn::Join: | |
- '' | |
- - '{{resolve:secretsmanager:' | |
- Ref: AppDbSecret | |
- :SecretString:password}} | |
ScalingConfiguration: | |
AutoPause: | |
Fn::If: | |
- IsOffline | |
- true | |
- Fn::If: | |
- EnableDbAutoPause | |
- true | |
- false | |
MaxCapacity: | |
Ref: DbMaxCapacity | |
MinCapacity: 2 | |
SecondsUntilAutoPause: 300 | |
SnapshotIdentifier: | |
Fn::If: | |
- IsRestore | |
- Ref: RestoreDbSnapshotIdentifier | |
- Ref: AWS::NoValue | |
VpcSecurityGroupIds: | |
- Ref: AppDbSecurityGroup | |
Tags: | |
- Key: Name | |
Value: | |
Fn::Sub: app-db | |
AppDbBudget: | |
Type: AWS::Budgets::Budget | |
Condition: HasDbMonthlyBudget | |
DependsOn: StackTopicHandlerTopicPolicy | |
Properties: | |
Budget: | |
BudgetLimit: | |
Amount: | |
Ref: DatabaseMonthlyBudget | |
Unit: USD | |
BudgetType: COST | |
TimeUnit: MONTHLY | |
CostFilters: | |
UsageType: | |
- Fn::Join: | |
- '-' | |
- - Fn::FindInMap: | |
- Regions | |
- Ref: AWS::Region | |
- Abbreviation | |
- Aurora:ServerlessUsage | |
NotificationsWithSubscribers: | |
- Notification: | |
NotificationType: ACTUAL | |
ComparisonOperator: GREATER_THAN | |
Threshold: 100 | |
Subscribers: | |
- SubscriptionType: EMAIL | |
Address: | |
Ref: AdminEmailAddress | |
- SubscriptionType: SNS | |
Address: | |
Ref: StackTopic | |
JanusAndReticulumAdminSecret: | |
Type: Custom::GenerateSecret | |
DependsOn: AppPostgrestDbSecret | |
Properties: | |
Description: Janus and Reticulum Admin Secret | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.GenerateSecretFunctionArn | |
Name: janus-and-reticulum-admin | |
Region: | |
Ref: AWS::Region | |
StackName: | |
Ref: AWS::StackName | |
PhoenixKeySecret: | |
Type: Custom::GenerateSecret | |
DeletionPolicy: Retain | |
DependsOn: JanusAndReticulumAdminSecret | |
Properties: | |
Description: Phoenix secret base key | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.GenerateSecretFunctionArn | |
Name: phoenix-key | |
Region: | |
Ref: AWS::Region | |
StackName: | |
Ref: AWS::StackName | |
CopyFromStackName: | |
Fn::If: | |
- IsRestore | |
- Ref: RestoreStackName | |
- Ref: AWS::NoValue | |
CopyFromStackRegion: | |
Fn::If: | |
- IsRestore | |
- Ref: AWS::Region | |
- Ref: AWS::NoValue | |
GuardianSecretKeySecret: | |
Type: Custom::GenerateSecret | |
DependsOn: PhoenixKeySecret | |
Properties: | |
Description: Reticulum guardian secret key | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.GenerateSecretFunctionArn | |
Name: guardian-secret-key | |
Region: | |
Ref: AWS::Region | |
StackName: | |
Ref: AWS::StackName | |
ReticulumOAuthTokenSecret: | |
Type: Custom::GenerateSecret | |
DependsOn: GuardianSecretKeySecret | |
Properties: | |
Description: Reticulum OAuth Token | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.GenerateSecretFunctionArn | |
Name: reticulum-oauth-token | |
Region: | |
Ref: AWS::Region | |
StackName: | |
Ref: AWS::StackName | |
ReticulumCookieSecret: | |
Type: Custom::GenerateSecret | |
DependsOn: ReticulumOAuthTokenSecret | |
Properties: | |
Description: Reticulum cookie | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.GenerateSecretFunctionArn | |
Name: reticulum-cookie | |
Region: | |
Ref: AWS::Region | |
StackName: | |
Ref: AWS::StackName | |
ReticulumBotAccessKeySecret: | |
Type: Custom::GenerateSecret | |
DependsOn: ReticulumCookieSecret | |
Properties: | |
Description: Reticulum bot access key | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.GenerateSecretFunctionArn | |
Name: reticulum-bot-access-key | |
Region: | |
Ref: AWS::Region | |
StackName: | |
Ref: AWS::StackName | |
SendEmailPasswordSecret: | |
Type: Custom::GenerateSecret | |
DependsOn: ReticulumBotAccessKeySecret | |
Properties: | |
Description: Send email password | |
ServiceToken: | |
Fn::GetAtt: KeymasterApplication.Outputs.GenerateSecretFunctionArn | |
Name: send-email-password | |
Region: | |
Ref: AWS::Region | |
StackName: | |
Ref: AWS::StackName | |
SecretString: | |
Fn::GetAtt: SendEmailPassword.Key | |
Outputs: | |
AddressForRootDomain: | |
Description: Serving domain name for your hub. If you did not set up your domain | |
on Route 53, you should add a CNAME DNS record pointing to this domain name | |
to to start serving your hub from your domain. | |
Value: | |
Fn::If: | |
- HasALB | |
- Fn::GetAtt: AppALB.DNSName | |
- Fn::GetAtt: AppCloudfrontDistribution.DomainName | |
DomainName: | |
Description: Primary hub domain name [reticulum/phx/url_host,reticulum/phx/static_url_host,hubs/general/reticulum_server,spoke/general/reticulum_server,spoke/general/hubs_server,reticulum/turn/realm,coturn/general/realm] | |
Value: | |
Ref: DomainName | |
CorsOrigins: | |
Description: Allowed CORS origins [reticulum/security/cors_origins] | |
Value: | |
Fn::Sub: https://${DomainName},https://${ShortlinkZoneInfo.Name},https://hubs.local:8080,https://localhost:8080 | |
NonCorsProxyDomains: | |
Description: Non-CORS proxied domains [hubs/general/non_cors_proxy_domains,spoke/general/non_cors_proxy_domains] | |
Value: | |
Fn::Sub: ${DomainName},${InternalZoneInfo.Name},${AppAssetsDNS},${LowerStackName.Value}-${AWS::AccountId}-hubs-worker.com | |
JanusAndReticulumAdminSecret: | |
Description: Janus and Reticulum Admin Secret [reticulum/phx/admin_access_key!read-keymaster-secret,janus-gateway/general/admin_secret!read-keymaster-secret,reticulum/janus/admin_secret!read-keymaster-secret] | |
Value: janus-and-reticulum-admin | |
JanusMediaRtpPortRange: | |
Description: Janus WebRTC Port Range [janus-gateway/media/rtp_port_range] | |
Value: | |
Fn::Join: | |
- '-' | |
- - Fn::FindInMap: | |
- ServicesMeta | |
- DialogWebRTCFrom | |
- Port | |
- Fn::FindInMap: | |
- ServicesMeta | |
- DialogWebRTCTo | |
- Port | |
JanusNatIceIgnoreList: | |
Description: Janus ICE Ignore List [janus-gateway/nat/ice_ignore_list] | |
Value: eth0:0 | |
JanusInternalPort: | |
Description: Janus Internal Port [janus-gateway/transports.websockets/wss_port] | |
Value: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogInternal | |
- Port | |
JanusExternalPort: | |
Description: Janus External Port [reticulum/janus/janus_port] | |
Value: | |
Fn::If: | |
- HasStreamingServers | |
- Fn::FindInMap: | |
- ServicesMeta | |
- DialogExternal | |
- StreamPort | |
- Fn::FindInMap: | |
- ServicesMeta | |
- DialogExternal | |
- AppPort | |
JanusAdminPort: | |
Description: Janus Admin Port [janus-gateway/transports.http/admin_port,reticulum/janus/admin_port] | |
Value: | |
Fn::FindInMap: | |
- ServicesMeta | |
- DialogAdmin | |
- Port | |
JanusServiceName: | |
Description: Janus Service Name [reticulum/janus/service_name] | |
Value: janus-gateway | |
RetInternalPort: | |
Description: Reticulum Internal Port [reticulum/phx/port] | |
Value: | |
Fn::FindInMap: | |
- ServicesMeta | |
- RetInternal | |
- Port | |
RetPhoenixSecretKey: | |
Description: Reticulum Phoenix Secret Key [reticulum/phx/secret_key!read-keymaster-secret] | |
Value: phoenix-key | |
RetExternalPort: | |
Description: Reticulum URL Port [reticulum/phx/url_port] | |
Value: | |
Fn::FindInMap: | |
- ServicesMeta | |
- RetExternal | |
- Port | |
CorsProxyDNS: | |
Description: CORS Proxy Domain Name [reticulum/phx/cors_proxy_url_host,hubs/general/cors_proxy_server,spoke/general/cors_proxy_server] | |
Value: | |
Ref: CorsProxyDNS | |
AppDNS: | |
Description: Load balanced app DNS [reticulum/phx/secondary_url_host] | |
Value: | |
Fn::Sub: ${LowerStackName.Value}-app.${InternalZoneInfo.Name} | |
RetHostnameDNSSuffix: | |
Description: Reticulum Hostname DNS Suffix [reticulum/run/hostname_dns_suffix] | |
Value: -local | |
GuardianSecretKeySecret: | |
Description: Guardian Secret Key [reticulum/guardian/secret_key!read-keymaster-secret] | |
Value: guardian-secret-key | |
ReticulumPermsKey: | |
Description: Reticulum Perms Key [reticulum/guardian/perms_key!read-s3-file-escaped] | |
Value: | |
Fn::Sub: s3://${BoxKeysBucket}/jwt-key.pem | |
ReticulumVapidKeys: | |
Description: Reticulum Vapid Keys [reticulum/web_push/public_key!read-s3-file-as-json(publicKey),reticulum/web_push/private_key!read-s3-file-as-json(privateKey)] | |
Value: | |
Fn::Sub: s3://${BoxKeysBucket}/vapid.json | |
ReticulumWebPushSubject: | |
Description: Reticulum Web Push Subject [reticulum/web_push/subject] | |
Value: | |
Fn::Sub: mailto:info@${SESDomain} | |
ReticulumOAuthTokenSecret: | |
Description: Reticulum OAuth Token Secret [reticulum/guardian/oauth_token_key!read-keymaster-secret] | |
Value: reticulum-oauth-token | |
ReticulumCookieSecret: | |
Description: Reticulum Cookie Secret [reticulum/erlang/node_cookie!read-keymaster-secret] | |
Value: reticulum-cookie | |
ReticulumBotAccessKeySecret: | |
Description: Reticulum Bot Access Key Secret [reticulum/ret/bot_access_key!read-keymaster-secret] | |
Value: reticulum-bot-access-key | |
AppDbSecret: | |
Description: App DB Secret [ita/db/password!read-aws-secret] | |
Value: | |
Ref: AppDbSecret | |
AppPostgrestDbSecret: | |
Description: App PostgREST DB Secret [reticulum/db/postgrest_password!read-keymaster-secret] | |
Value: postgrest-db-secret | |
AppDbDatabase: | |
Description: App DB Database [reticulum/db/database,ita/db/database] | |
Value: polycosm_production | |
AppDbHostname: | |
Description: App DB Hostname [ita/db/hostname] | |
Value: | |
Fn::GetAtt: AppDb.Endpoint.Address | |
PostgrestDbURI: | |
Description: Postgrest DB URI [postgrest/db/uri!inject-keymaster-secret] | |
Value: | |
Fn::Sub: postgres://postgrest_authenticator:{postgrest-db-secret}@${AppDb.Endpoint.Address}/polycosm_production | |
PgbouncerDb: | |
Description: Pgbouncer DB Info [pgbouncer/pgbouncer/db!inject-aws-secret] | |
Value: | |
Fn::Sub: |- | |
polycosm_production = host=${AppDb.Endpoint.Address} dbname=polycosm_production user=postgres password={${AppDbSecret}} pool_mode=transaction | |
polycosm_locking = host=${AppDb.Endpoint.Address} dbname=polycosm_production user=postgres password={${AppDbSecret}} pool_mode=session | |
AssetsBucket: | |
Description: Assets bucket name [hubs/deploy/target,spoke/deploy/target,polycosm-static-assets/deploy/assets_target] | |
Value: | |
Ref: AssetsBucket | |
LinkRedirectorBucket: | |
Description: Link redirector bucket name [polycosm-static-assets/deploy/redirector_target] | |
Value: | |
Ref: LinkRedirectorBucket | |
HubsBaseAssetsPath: | |
Description: Base assets path for hubs CDN assets [hubs/general/base_assets_path] | |
Value: | |
Fn::Sub: https://${AppAssetsDNS}/hubs/ | |
HubsPageOrigin: | |
Description: Hubs page origin [reticulum/pages/hubs_page_origin] | |
Value: | |
Fn::Sub: https://${AssetsBucket.RegionalDomainName}/hubs/pages/latest | |
SpokeBaseAssetsPath: | |
Description: Base assets path for spoke CDN assets [spoke/general/base_assets_path] | |
Value: | |
Fn::Sub: https://${AppAssetsDNS}/spoke/ | |
SpokePageOrigin: | |
Description: Spoke page origin [reticulum/pages/spoke_page_origin] | |
Value: | |
Fn::Sub: https://${AssetsBucket.RegionalDomainName}/spoke/pages/latest | |
AssetsDomain: | |
Description: Assets domain for worker | |
Value: | |
Fn::GetAtt: AssetsBucket.RegionalDomainName | |
ClientDeployType: | |
Description: Client deploy type [hubs/deploy/type,spoke/deploy/type,polycosm-static-assets/deploy/type] | |
Value: s3 | |
NearsparkHost: | |
Description: Host of the nearspark service endpoint [hubs/general/thumbnail_server,spoke/general/thumbnail_server,reticulum/phx/thumbnail_url_host] | |
Value: | |
Fn::Sub: ${NearsparkDNS} | |
SpeelycaptorEndpoint: | |
Description: Speelycaptor Service URL [reticulum/speelycaptor/speelycaptor_endpoint] | |
Value: | |
Fn::Sub: https://${SpeelycaptorApiGatewayRestApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/prod | |
PhotomnemonicEndpoint: | |
Description: Photomnemonic Service URL [reticulum/resolver/photomnemonic_endpoint] | |
Value: | |
Fn::Sub: https://${PhotomnemonicApiGatewayRestApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/prod | |
StorageEFSId: | |
Description: Storage EFS volume id | |
Value: | |
Ref: StorageEFS | |
AppAssetsDNS: | |
Description: Storage URL Host [reticulum/uploads/host] | |
Value: | |
Fn::Sub: https://${AppAssetsDNS} | |
AppAssetsHost: | |
Description: Storage Host [hubs/general/uploads_host] | |
Value: | |
Fn::Sub: ${AppAssetsDNS} | |
SmtpSendAccessKey: | |
Description: SMTP send access key for email zone [reticulum/email/username] | |
Value: | |
Ref: SendEmailAccessKey | |
SmtpSendPassword: | |
Description: SMTP send access key for email zone [reticulum/email/password!read-keymaster-secret] | |
Value: send-email-password | |
SmtpSendFromAddress: | |
Description: SMTP send from address [reticulum/email/from] | |
Value: | |
Fn::If: | |
- HasEmailSubdomain | |
- Fn::Sub: noreply@${EmailSubdomain}.${SESDomain} | |
- Fn::Sub: noreply@${SESDomain} | |
StmpServer: | |
Description: SMTP server [reticulum/email/server] | |
Value: email-smtp.us-east-1.amazonaws.com | |
SESRegion: | |
Description: SES region | |
Value: us-east-1 | |
InternalZoneId: | |
Description: Internal Zone Id | |
Value: | |
Fn::GetAtt: InternalZoneInfo.Id | |
InternalZoneDomainName: | |
Description: Internal Zone Domain Name | |
Value: | |
Fn::GetAtt: InternalZoneInfo.Name | |
BoxKeysBucketName: | |
Description: Box Keys Bucket Name | |
Value: | |
Ref: BoxKeysBucket | |
BucketRegion: | |
Description: Bucket Region [hubs/deploy/region,spoke/deploy/region,polycosm-static-assets/deploy/assets_region,polycosm-static-assets/deploy/redirector_region] | |
Value: | |
Ref: AWS::Region | |
ShortlinkDomainName: | |
Description: Shortlink Domain Name [hubs/general/shortlink_domain,reticulum/phx/link_url_host] | |
Value: | |
Fn::GetAtt: ShortlinkZoneInfo.Name | |
AdminEmailAddress: | |
Description: Administrator Email Address [reticulum/accounts/admin_email] | |
Value: | |
Ref: AdminEmailAddress | |
LetsEncryptEmailAddress: | |
Description: Let's Encrypt Email Address [certbot/general/admin_email] | |
Value: | |
Fn::If: | |
- HasLetsEncryptEmail | |
- Ref: LetsEncryptEmailAddress | |
- '' | |
CertBotPlugin: | |
Description: Certbot plugin [certbot/general/plugin] | |
Value: dns-route53 | |
StackName: | |
Description: Stack name [reticulum/ret/pool] | |
Value: | |
Ref: AWS::StackName | |
MaxStorage: | |
Description: Max storage in GB [reticulum/uploads/quota_gb] | |
Value: | |
Ref: MaxStorage | |
StackTopic: | |
Description: Stack SNS Topic | |
Value: | |
Ref: StackTopic |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment