Last active
December 15, 2023 09:50
-
-
Save daniel-cortez-stevenson/c64ca487f4f93f48b23c0d7910366b61 to your computer and use it in GitHub Desktop.
AWS ElasticSearch Domain with VPC and Bastion Cloudformation 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
# Copyright 2020 Daniel Cortez Stevenson | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# https://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
--- | |
AWSTemplateFormatVersion: 2010-09-09 | |
Description: >- | |
Provisions and deploys AWS Elasticsearch Service (ES) over multiple | |
availability zones in a new AWS Virtual Private Cloud (VPC), along with a | |
bastion server from which Elasticsearch can be accessed. | |
Parameters: | |
ElasticsearchName: | |
Description: >- | |
The name of the AWS Elasticsearch Service deployment. | |
Type: String | |
ConstraintDescription: >- | |
Must be a valid AWS ES domain name prefix. The name must start with a | |
lowercase letter and must be between 3 and 28 characters. Valid characters | |
are a-z (lowercase only), 0-9, and - (hyphen). | |
AllowedPattern: '[a-z][a-z0-9\\-]+' | |
ElasticsearchVersion: | |
Default: 7.4 | |
Type: String | |
ConstraintDescription: >- | |
Must be an allowed AWS ES version (Major.Minor) | |
AllowedValues: | |
- 7.4 | |
- 7.1 | |
- 6.8 | |
- 6.7 | |
- 6.5 | |
- 6.4 | |
- 6.3 | |
- 6.2 | |
- 6.0 | |
- 5.6 | |
- 5.5 | |
- 5.3 | |
- 5.1 | |
- 2.3 | |
- 1.5 | |
ElasticsearchMasterInstanceType: | |
Description: >- | |
Instance type for master nodes. | |
Type: String | |
Default: t2.small.elasticsearch | |
AllowedValues: | |
- r5.large.elasticsearch | |
- r5.xlarge.elasticsearch | |
- r5.2xlarge.elasticsearch | |
- r5.4xlarge.elasticsearch | |
- r5.12xlarge.elasticsearch | |
- t2.small.elasticsearch | |
- t2.medium.elasticsearch | |
- c4.large.elasticsearch | |
- c4.xlarge.elasticsearch | |
- c4.2xlarge.elasticsearch | |
- c4.4xlarge.elasticsearch | |
- c4.8xlarge.elasticsearch | |
- i2.xlarge.elasticsearch | |
- i2.2xlarge.elasticsearch | |
- m4.large.elasticsearch | |
- m4.xlarge.elasticsearch | |
- m4.2xlarge.elasticsearch | |
- m4.4xlarge.elasticsearch | |
- m4.10xlarge.elasticsearch | |
- r4.large.elasticsearch | |
- r4.xlarge.elasticsearch | |
- r4.2xlarge.elasticsearch | |
- r4.4xlarge.elasticsearch | |
- r4.8xlarge.elasticsearch | |
- r4.16xlarge.elasticsearch | |
- m3.medium.elasticsearch | |
- m3.large.elasticsearch | |
- m3.xlarge.elasticsearch | |
- m3.2xlarge.elasticsearch | |
- r3.large.elasticsearch | |
- r3.xlarge.elasticsearch | |
- r3.2xlarge.elasticsearch | |
- r3.4xlarge.elasticsearch | |
- r3.8xlarge.elasticsearch | |
ElasticsearchDataInstanceType: | |
Description: >- | |
Instance type for data nodes. | |
Type: String | |
Default: r5.2xlarge.elasticsearch | |
AllowedValues: | |
- r5.large.elasticsearch | |
- r5.xlarge.elasticsearch | |
- r5.2xlarge.elasticsearch | |
- r5.4xlarge.elasticsearch | |
- r5.12xlarge.elasticsearch | |
- t2.small.elasticsearch | |
- t2.medium.elasticsearch | |
- c4.large.elasticsearch | |
- c4.xlarge.elasticsearch | |
- c4.2xlarge.elasticsearch | |
- c4.4xlarge.elasticsearch | |
- c4.8xlarge.elasticsearch | |
- i2.xlarge.elasticsearch | |
- i2.2xlarge.elasticsearch | |
- m4.large.elasticsearch | |
- m4.xlarge.elasticsearch | |
- m4.2xlarge.elasticsearch | |
- m4.4xlarge.elasticsearch | |
- m4.10xlarge.elasticsearch | |
- r4.large.elasticsearch | |
- r4.xlarge.elasticsearch | |
- r4.2xlarge.elasticsearch | |
- r4.4xlarge.elasticsearch | |
- r4.8xlarge.elasticsearch | |
- r4.16xlarge.elasticsearch | |
- m3.medium.elasticsearch | |
- m3.large.elasticsearch | |
- m3.xlarge.elasticsearch | |
- m3.2xlarge.elasticsearch | |
- r3.large.elasticsearch | |
- r3.xlarge.elasticsearch | |
- r3.2xlarge.elasticsearch | |
- r3.4xlarge.elasticsearch | |
- r3.8xlarge.elasticsearch | |
NumberOfMasterNodes: | |
Description: >- | |
How many dedicated master nodes you want to have. 3 is recommended. | |
Type: Number | |
Default: 3 | |
AllowedValues: | |
- 3 | |
- 5 | |
NumberOfDataNodes: | |
Description: >- | |
How many data nodes you want to have. Multiples of your number of | |
availability zones (2) is recommended. | |
Type: Number | |
Default: 2 | |
MinValue: 1 | |
MaxValue: 20 | |
BastionKeyName: | |
Description: >- | |
Name of the EC2 KeyPair, which enables SSH access to the bastion server. | |
Type: AWS::EC2::KeyPair::KeyName | |
ConstraintDescription: >- | |
Must be the name of an existing EC2 KeyPair. | |
AccessLocation: | |
Description: >- | |
CIDR range from which SSH access to the bastion server is allowed. Default | |
is anywhere. | |
Type: String | |
Default: '0.0.0.0/0' | |
ConstraintDescription: >- | |
Must be a valid CIDR range. | |
MinLength: 9 | |
MaxLength: 18 | |
AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})' | |
AvailabilityZone1: | |
Type: String | |
ConstraintDescription: >- | |
Must be a valid AWS Availability Zone in eu-central-1 or us-east-1. | |
Default: eu-central-1a | |
AllowedValues: | |
- eu-central-1a | |
- eu-central-1b | |
- eu-central-1c | |
- us-east-1a | |
- us-east-1b | |
- us-east-1c | |
- us-east-1d | |
- us-east-1e | |
- us-east-1f | |
AvailabilityZone2: | |
Type: String | |
ConstraintDescription: >- | |
Must be a valid AWS Availability Zone in eu-central-1 or us-east-1. | |
Default: eu-central-1b | |
AllowedValues: | |
- eu-central-1a | |
- eu-central-1b | |
- eu-central-1c | |
- us-east-1a | |
- us-east-1b | |
- us-east-1c | |
- us-east-1d | |
- us-east-1e | |
- us-east-1f | |
Mappings: | |
CidrConfig: | |
VPC: | |
CIDR: 10.192.0.0/16 | |
Public1: | |
CIDR: 10.192.10.0/24 | |
Public2: | |
CIDR: 10.192.11.0/24 | |
AWSRegionArch2AMI: | |
eu-central-1: | |
HVM64: ami-054e21e355db24124 | |
us-east-1: | |
HVM64: ami-0fba9b33b5304d8b4 | |
Resources: | |
VPC: | |
Type: AWS::EC2::VPC | |
Properties: | |
EnableDnsSupport: true | |
EnableDnsHostnames: true | |
InstanceTenancy: default | |
CidrBlock: !FindInMap | |
- CidrConfig | |
- VPC | |
- CIDR | |
InternetGateway: | |
Type: AWS::EC2::InternetGateway | |
InternetGatewayAttachment: | |
Type: AWS::EC2::VPCGatewayAttachment | |
Properties: | |
VpcId: !Ref VPC | |
InternetGatewayId: !Ref InternetGateway | |
PublicSubnet1: | |
Type: AWS::EC2::Subnet | |
Properties: | |
VpcId: !Ref VPC | |
AvailabilityZone: !Ref AvailabilityZone1 | |
CidrBlock: !FindInMap | |
- CidrConfig | |
- Public1 | |
- CIDR | |
MapPublicIpOnLaunch: true | |
PublicSubnet2: | |
Type: AWS::EC2::Subnet | |
Properties: | |
VpcId: !Ref VPC | |
AvailabilityZone: !Ref AvailabilityZone2 | |
CidrBlock: !FindInMap | |
- CidrConfig | |
- Public2 | |
- CIDR | |
MapPublicIpOnLaunch: true | |
PublicRouteTable1: | |
Type: AWS::EC2::RouteTable | |
Properties: | |
VpcId: !Ref VPC | |
PublicRouteTable2: | |
Type: AWS::EC2::RouteTable | |
Properties: | |
VpcId: !Ref VPC | |
PublicSubnetRouteTableAssociation1: | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
SubnetId: !Ref PublicSubnet1 | |
RouteTableId: !Ref PublicRouteTable1 | |
PublicSubnetRouteTableAssociation2: | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
SubnetId: !Ref PublicSubnet2 | |
RouteTableId: !Ref PublicRouteTable2 | |
PublicRoute1: | |
Type: AWS::EC2::Route | |
DependsOn: InternetGatewayAttachment | |
Properties: | |
RouteTableId: !Ref PublicRouteTable1 | |
DestinationCidrBlock: 0.0.0.0/0 | |
GatewayId: !Ref InternetGateway | |
PublicRoute2: | |
Type: AWS::EC2::Route | |
DependsOn: InternetGatewayAttachment | |
Properties: | |
RouteTableId: !Ref PublicRouteTable2 | |
DestinationCidrBlock: 0.0.0.0/0 | |
GatewayId: !Ref InternetGateway | |
BastionElasticIp: | |
Type: AWS::EC2::EIP | |
BastionElasticIPAssociation: | |
Type: AWS::EC2::EIPAssociation | |
Properties: | |
InstanceId: !Ref BastionInstance | |
EIP: !Ref BastionElasticIp | |
BastionInstance: | |
Type: AWS::EC2::Instance | |
DependsOn: InternetGatewayAttachment | |
Properties: | |
InstanceType: t3.nano | |
KeyName: !Ref BastionKeyName | |
SubnetId: !Ref PublicSubnet1 | |
SecurityGroupIds: | |
- !Ref BastionSecurityGroup | |
ImageId: !FindInMap | |
- AWSRegionArch2AMI | |
- !Ref AWS::Region | |
- HVM64 | |
Monitoring: true | |
BlockDeviceMappings: | |
- DeviceName: /dev/sda1 | |
Ebs: | |
VolumeSize: 8 | |
BastionSecurityGroup: | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupDescription: >- | |
Allows all ingress from AccessLocation. | |
VpcId: !Ref VPC | |
SecurityGroupIngress: | |
- IpProtocol: -1 | |
FromPort: -1 | |
ToPort: -1 | |
CidrIp: !Ref AccessLocation | |
ReceiveFromBastionSecurityGroup: | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupDescription: >- | |
Allows all ingress from BastionSecurityGroup | |
VpcId: !Ref VPC | |
SecurityGroupIngress: | |
- IpProtocol: -1 | |
FromPort: -1 | |
ToPort: -1 | |
SourceSecurityGroupId: !Ref BastionSecurityGroup | |
ElasticsearchDomain: | |
Type: AWS::Elasticsearch::Domain | |
Properties: | |
ElasticsearchVersion: !Ref ElasticsearchVersion | |
DomainName: !Ref ElasticsearchName | |
EBSOptions: | |
EBSEnabled: true | |
Iops: 2000 | |
VolumeSize: 196 | |
VolumeType: io1 | |
ElasticsearchClusterConfig: | |
DedicatedMasterEnabled: true | |
DedicatedMasterType: !Ref ElasticsearchMasterInstanceType | |
DedicatedMasterCount: 3 | |
InstanceCount: !Ref NumberOfDataNodes | |
InstanceType: !Ref ElasticsearchDataInstanceType | |
ZoneAwarenessEnabled: true | |
AdvancedOptions: | |
rest.action.multi.allow_explicit_index: 'true' | |
indices.fielddata.cache.size: '40' | |
indices.query.bool.max_clause_count: '1024' | |
SnapshotOptions: | |
AutomatedSnapshotStartHour: 0 | |
VPCOptions: | |
SubnetIds: | |
- !Ref PublicSubnet1 | |
- !Ref PublicSubnet2 | |
SecurityGroupIds: | |
- !GetAtt ElasticsearchSecurityGroup.GroupId | |
- !GetAtt ReceiveFromElasticsearchSecurityGroup.GroupId | |
- !GetAtt ReceiveFromBastionSecurityGroup.GroupId | |
AccessPolicies: | |
Version: 2012-10-17 | |
Statement: | |
- Effect: Allow | |
Principal: '*' | |
Action: es:* | |
Resource: !Sub 'arn:aws:es:${AWS::Region}:${AWS::AccountId}:domain/${ElasticsearchName}/*' | |
Tags: | |
- Key: Name | |
Value: !Sub '${AWS::StackName}-EsDomain' | |
ElasticsearchSecurityGroup: | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupDescription: >- | |
Identifies resources, which should have access to one another. | |
VpcId: !Ref VPC | |
SecurityGroupEgress: | |
- IpProtocol: -1 | |
FromPort: -1 | |
ToPort: -1 | |
CidrIp: 0.0.0.0/0 | |
Tags: | |
- Key: Name | |
Value: !Sub '${AWS::StackName}-EsSg' | |
ReceiveFromElasticsearchSecurityGroup: | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupDescription: >- | |
Allows ingress from ElasticsearchSecurityGroup. | |
VpcId: !Ref VPC | |
SecurityGroupIngress: | |
- IpProtocol: -1 | |
FromPort: -1 | |
ToPort: -1 | |
SourceSecurityGroupId: !GetAtt ElasticsearchSecurityGroup.GroupId | |
Tags: | |
- Key: Name | |
Value: !Sub '${AWS::StackName}-ReceiveFromEsSg' | |
Outputs: | |
DomainArn: | |
Value: !GetAtt ElasticsearchDomain.DomainArn | |
Export: | |
Name: !Sub '${AWS::StackName}-EsDomainArn' | |
DomainEndpoint: | |
Value: !GetAtt ElasticsearchDomain.DomainEndpoint | |
Export: | |
Name: !Sub '${AWS::StackName}-EsDomainEndpoint' | |
BastionIp: | |
Value: !Ref BastionElasticIp | |
Export: | |
Name: !Sub '${AWS::StackName}-BastionIp' | |
ElasticsearchSecurityGroupId: | |
Value: !GetAtt ElasticsearchSecurityGroup.GroupId | |
Export: | |
Name: !Sub '${AWS::StackName}-EsSgId' | |
ReceiveFromElasticsearchSecurityGroupId: | |
Value: !GetAtt ReceiveFromElasticsearchSecurityGroup.GroupId | |
Export: | |
Name: !Sub '${AWS::StackName}-ReceiveFromElasticsearchSgId' | |
BastionSecurityGroupId: | |
Value: !GetAtt BastionSecurityGroup.GroupId | |
Export: | |
Name: !Sub '${AWS::StackName}-BastionSgId' | |
ReceiveFromBastionSecurityGroupId: | |
Value: !GetAtt ReceiveFromBastionSecurityGroup.GroupId | |
Export: | |
Name: !Sub '${AWS::StackName}-ReceiveFromBastionSgId' | |
PublicSubnetId1: | |
Value: !Ref PublicSubnet1 | |
Export: | |
Name: !Sub '${AWS::StackName}-PublicSubnet1' | |
PublicSubnetId2: | |
Value: !Ref PublicSubnet2 | |
Export: | |
Name: !Sub '${AWS::StackName}-PublicSubnet2' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How to deploy this AWS Cloudformation Stack
Create an IAM Service Role
Create an EC2 Key Pair for the bastion server
Deploy the Stack
Ensure your AWS IAM User has sufficient permissions to deploy this stack.
Deploy the Elasticsearch Domain stack with the AWS CLI: