8

With Digital Ocean, I'm able to set a fully qualified domain name before starting it and I sorely miss this feature with AWS EC2.

Currently I manually go into a newly started Ubuntu 16.04 LTS instance and:

  1. sudo hostnamectl set-hostname myhost.example.com
  2. sudo vim /etc/hosts and append myhost.example.com to the 127.0.0.1 line
  3. Optionally goto Route 53 panel & set it up if I want it to resolve
  4. And then run the chef scripts.

Am I missing a trick? I can't see any Route 53 / EC2 integration either.

hendry
  • 677
  • 2
  • 10
  • 23

3 Answers3

17

To just set the hostname (without Route53), you can use cloud-init configuration in the user-data

#cloud-config
hostname: mynode
fqdn: mynode.example.com
manage_etc_hosts: true
  • That's a good solution for 1&2 which I tested & confirm it works. Thank you. – hendry Jul 05 '16 at 06:35
  • 1
    No need to set `hostname`: "If both fqdn and hostname are set, fqdn will be used." [cloud-init#set-hostname](https://cloudinit.readthedocs.io/en/latest/topics/modules.html#set-hostname) – ciastek Feb 22 '18 at 01:58
  • Perfect this was driving me crazy, thanks! I had been using user-data to run a script at boot but that's easy enough to accommodate by using a `runcmd:` section in the cloud-config yaml. Luckily my script is pretty basic but could see this being an issue if it were complex. – Diffuser Dec 04 '18 at 21:29
  • Is it possible to add an instance id to the hostname in cloud-config? – kivagant Apr 15 '19 at 16:34
2

You can use the user-data function of the console and/or API to provide a bash script which will be executed on first boot. This script can set the hostname, create a DNS record, and any other actions you desire.

EEAA
  • 109,363
  • 18
  • 175
  • 245
  • 2
    Would be nice to see an example of this script. =) – hendry Jul 05 '16 at 02:39
  • 1
    There are hundreds of bash script examples around the internet. Dig in and give it a try? – EEAA Jul 05 '16 at 02:40
  • i just tried this. a bash script of "hostnamectl set-hostname `curl -s http://169.254.169.254/latest/meta-data/instance-id`" doesn't work. It seems that the user-data runs before the cloud-init sets the hostname ? and using the cloud-config hostname you can't give dynamic info to ? – Sirex Jun 28 '17 at 00:21
0

This is really a reply to hendry's comment that it would be nice to see an example of this kind of script, apparently I need more karma or something to add a comment.

I've just had to make this work so I can set the hostname of an EC2 instance and create a DNS record so I can then run rabbitmq on ECS on top of this using the host networking mode (which means the ECS task has the same networking and hostname as the underlying EC2 instance) so I can preserve the rabbitmq hostname through reboots of the underlying infra (and set kernel parameters which aren't show below)

This is my script, it works for me (I say that, but it's not fully tested yet. I'll report back)

#!/bin/bash

# A script to be copied into the user data of the EC2 instances to UPSERT the CNAME

set -euf -o pipefail

# We first need to install some software and update everything.

yum update -y
yum install jq -y
yum install unzip -y

# install the AWS cli which requires checking against a key.
# https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html
cat > /tmp/aws-public-key << EOF
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQINBF2Cr7UBEADJZHcgusOJl7ENSyumXh85z0TRV0xJorM2B/JL0kHOyigQluUG
ZMLhENaG0bYatdrKP+3H91lvK050pXwnO/R7fB/FSTouki4ciIx5OuLlnJZIxSzx
PqGl0mkxImLNbGWoi6Lto0LYxqHN2iQtzlwTVmq9733zd3XfcXrZ3+LblHAgEt5G
TfNxEKJ8soPLyWmwDH6HWCnjZ/aIQRBTIQ05uVeEoYxSh6wOai7ss/KveoSNBbYz
gbdzoqI2Y8cgH2nbfgp3DSasaLZEdCSsIsK1u05CinE7k2qZ7KgKAUIcT/cR/grk
C6VwsnDU0OUCideXcQ8WeHutqvgZH1JgKDbznoIzeQHJD238GEu+eKhRHcz8/jeG
94zkcgJOz3KbZGYMiTh277Fvj9zzvZsbMBCedV1BTg3TqgvdX4bdkhf5cH+7NtWO
lrFj6UwAsGukBTAOxC0l/dnSmZhJ7Z1KmEWilro/gOrjtOxqRQutlIqG22TaqoPG
fYVN+en3Zwbt97kcgZDwqbuykNt64oZWc4XKCa3mprEGC3IbJTBFqglXmZ7l9ywG
EEUJYOlb2XrSuPWml39beWdKM8kzr1OjnlOm6+lpTRCBfo0wa9F8YZRhHPAkwKkX
XDeOGpWRj4ohOx0d2GWkyV5xyN14p2tQOCdOODmz80yUTgRpPVQUtOEhXQARAQAB
tCFBV1MgQ0xJIFRlYW0gPGF3cy1jbGlAYW1hem9uLmNvbT6JAlQEEwEIAD4WIQT7
Xbd/1cEYuAURraimMQrMRnJHXAUCXYKvtQIbAwUJB4TOAAULCQgHAgYVCgkICwIE
FgIDAQIeAQIXgAAKCRCmMQrMRnJHXJIXEAChLUIkg80uPUkGjE3jejvQSA1aWuAM
yzy6fdpdlRUz6M6nmsUhOExjVIvibEJpzK5mhuSZ4lb0vJ2ZUPgCv4zs2nBd7BGJ
MxKiWgBReGvTdqZ0SzyYH4PYCJSE732x/Fw9hfnh1dMTXNcrQXzwOmmFNNegG0Ox
au+VnpcR5Kz3smiTrIwZbRudo1ijhCYPQ7t5CMp9kjC6bObvy1hSIg2xNbMAN/Do
ikebAl36uA6Y/Uczjj3GxZW4ZWeFirMidKbtqvUz2y0UFszobjiBSqZZHCreC34B
hw9bFNpuWC/0SrXgohdsc6vK50pDGdV5kM2qo9tMQ/izsAwTh/d/GzZv8H4lV9eO
tEis+EpR497PaxKKh9tJf0N6Q1YLRHof5xePZtOIlS3gfvsH5hXA3HJ9yIxb8T0H
QYmVr3aIUes20i6meI3fuV36VFupwfrTKaL7VXnsrK2fq5cRvyJLNzXucg0WAjPF
RrAGLzY7nP1xeg1a0aeP+pdsqjqlPJom8OCWc1+6DWbg0jsC74WoesAqgBItODMB
rsal1y/q+bPzpsnWjzHV8+1/EtZmSc8ZUGSJOPkfC7hObnfkl18h+1QtKTjZme4d
H17gsBJr+opwJw/Zio2LMjQBOqlm3K1A4zFTh7wBC7He6KPQea1p2XAMgtvATtNe
YLZATHZKTJyiqA==
=vYOk
-----END PGP PUBLIC KEY BLOCK-----
EOF

gpg --import /tmp/aws-public-key
# Download the latest x86 cli zip
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "/tmp/awscliv2.zip"
# and the Signature
curl -o "/tmp/awscliv2.sig" "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip.sig"
# and verify
gpg --verify /tmp/awscliv2.sig /tmp/awscliv2.zip
# unzip install and test
cd /tmp
unzip /tmp/awscliv2.zip
./aws/install
aws --version

# The hostname should be set to the right thing, but may need a while to come up
# so set up a retry loop

Hostname='foo' # Initialise this outside the loop
MatchTest='.internal' # facile test we have a valid AWS .internal fqdn
Zone='Z0###1XAOQT59R52D' # Test

for i in {1..7}
do
        Hostname=$(hostname -f)
        [[ "$Hostname" == *"$MatchTest" ]] && break || sleep 3
done

echo "Hostname: $Hostname"

if ! [[ "$Hostname" == *"$MatchTest" ]]
then
        echo "Hostname: $Hostname doesn't match $MatchTest" && exit 1
fi

echo "Hostname: $Hostname"

# so now we need to UPSERT the CNAME

# this next variable needs to be correctly set for each one of the launch templates.
# i.e. rabbita, rabbitb or rabbitc
cnhost='rabbitx'
comment="Updating CNAME record for the underlying EC2 instance for Test-$cnhost"
action='UPSERT'
name="$cnhost.test"
record='CNAME'
ttl=60

jsonCNAME=$( jq -n \
                --arg value "$Hostname" \
                --arg comment "$comment" \
                --arg name "$name" \
                --arg action "$action" \
                --arg record "$record" \
                --arg ttl "$ttl" \
                '{Comment: $comment,
                  Changes: [
                    {  
                       Action: $action,
                       ResourceRecordSet: {
                         Name: $name,
                         Type: $record,
                         TTL: $ttl|tonumber,
                         ResourceRecords: [
                           { 
                             Value: $value
                           }
                         ],
                       }
                     }
                   ]
           }')

echo "JSON: $jsonCNAME"

aws route53 change-resource-record-sets --hosted-zone-id $Zone --change-batch <<<  echo "$jsonCNAME"

# So that's the CNAME set up so now we need to set the hostname to match it
hostname "$cnhost.test"

To successfully run this the role the EC2 instances start with needs access to route53 to insert records into the private ZONE 'test' this is a security issue and should be mitigated in some way.

One of the caveats I learned whilst writing this is that when this script runs not all the ENV is set as you would expect. In particular HOME is not set, and thus you need to be explicit about paths. I chose to use /tmp but I guess /root might be preferable to some.

The loop round the hostname -f and the test to see it matches what we expect may well be redundant.

M. Day
  • 1
  • 1