Installing a Minecraft Server on AWS and Automating Processes - Windows Server
I've recently gotten into hosting a minecraft server for my friends. Rather than host it on my own computer and open up ports, I've decided to offload the task to AWS for the hell of it and as a learning experience.
This tutorial will assume you have some sort of AWS and networking knowledge. We will being doing this on Windows, rather than Linux, but the methodology should still be roughly the same. There are just different syntax and ways of going about tasks.
This tutorial will create a Minecraft server that automatically:
- Starts the server when the instance boots
- Assigns a given DNS name when the instance boots.
- Backs up server contents to an S3 bucket when shut down
- Shuts down and starts up on user defined time slots
The steps are:
- Install Minecraft Server
- Upload Contents to S3 Bucket
- Create role that has access to EC2 and Route 53
- Create a route 53 hosted zone
- Configure the EC2 instance and start the server
- Configure Windows and testing
Requirements:
- An AWS account
- A domain hosted on Route 53
- A Windows "test" machine that will create a server template
Install Minecraft Server
The first thing you want to do is install a Minecraft server on your local Windows machine. I went with PaperMC. Install Java on your machine, and then run the server from the command line with the following:
java -Xms3G -Xmx3G -jar yourdownloadedfile.jar
It may ask you to agree to the EULA. Go ahead and agree to that by editing the eula.txt file and adding eula=true to it.
Once you do that and re run the above command, your server should start up and start generating a world. Go ahead and install any mods and configurations you want.
Once you are satisfied with the server, go ahead and create a .zip of the entire server. This will serve as your launch template when booting new servers.
Upload Contents to S3 Bucket
Go ahead and launch the AWS Console. You are going to create a new S3 bucket for your minecraft files. Call it whatever you want, but remember the name you chose.
Upload the .zip you created earlier, and call it server.zip. Upload it to the root of your S3 bucket.
We are also going to put some scripts on the root of the S3 Bucket. We'll go over what these scripts do in a later step.
Also, make sure you download Java from their website, and upload the installer to your S3 bucket.
Script 1 - Auto DNS Setup through Route 53 (dns.ps1)
## Gets instance metadata information
$INSTANCE_ID = Invoke-WebRequest -Uri http://169.254.169.254/latest/meta-data/instance-id/ -UseBasicParsing | Select-Object -ExpandProperty Content
$AZ = Invoke-WebRequest -Uri http://169.254.169.254/latest/meta-data/placement/availability-zone/ -UseBasicParsing | Select-Object -ExpandProperty Content
$MYIP = Invoke-WebRequest -Uri http://169.254.169.254/latest/meta-data/public-ipv4/ -UseBasicParsing | Select-Object -ExpandProperty Content
## Gets two tags assigned to the EC2 instance at launch
$ZONE_TAG = get-ec2tag -filter @{Name="resource-id";Value="$INSTANCE_ID"},@{Name="key";Value="AUTO_DNS_ZONE"} | select -expand Value
$NAME_TAG = get-ec2tag -filter @{Name="resource-id";Value="$INSTANCE_ID"},@{Name="key";Value="AUTO_DNS_NAME"} | select -expand Value
$changeRequest = New-Object -TypeName Amazon.Route53.Model.Change
$changeRequest.Action = "UPSERT"
$changeRequest.ResourceRecordSet = New-Object -TypeName Amazon.Route53.Model.ResourceRecordSet
$changeRequest.ResourceRecordSet.Name = "$NAME_TAG"
$changeRequest.ResourceRecordSet.Type = "A"
$changeRequest.ResourceRecordSet.TTL = 60
$changeRequest.ResourceRecordSet.ResourceRecords.Add(@{Value = $MYIP})
Edit-R53ResourceRecordSet `
-HostedZoneId $ZONE_TAG `
-ChangeBatch_Change @($changeRequest)
Script 2 - Gracefully Shutdown and Backup Server (closeandbackup.ps1)
Note: replace "your-bucket-name" with the bucket name you chose.
#C:\Windows\System32\GroupPolicy\Machine\Scripts\Shutdown
$bucket = "your-bucket-name"
#Gracefully shut down server
Get-Process java | Foreach-Object { $_.CloseMainWindow() | Out-Null } | stop-process –force
#Back up the server folder onto AWS
$date = Get-Date -UFormat "%m%d%Y%H%M"
Compress-Archive -Path C:\Users\Administrator\Desktop\Server -DestinationPath "C:\Users\Administrator\Desktop\$date.zip"
Write-S3Object -BucketName $bucket -File "C:\Users\Administrator\Desktop\$date.zip” -Key “$date.zip”
Create role that has access to EC2 and Route 53
In order for our server to access other parts of AWS and for those scripts to work, we need to create an IAM role that our server uses.
- Open the IAM Console
- Click on "Roles" on the left side
- Click "Create Role"
- Click "EC2" under "Use Case"
We now need to add policies to this role. This role will allow us to:
- EC2 Will be able to get its own metadata.
- Route 53 will be able to auto assign DNS.
- This instance will be allowed to upload to our S3 bucket.
Here is how we do it:
- Click "Create Policy"
- Under "JSON", paste the following.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ec2:DescribeTags",
"ec2:DescribeVpcs",
"ec2:DescribeRegions",
"route53:GetHostedZone",
"route53:ChangeResourceRecordSets",
"route53:ListHostedZonesByName"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::seanhweb-minecraft-server/*"
]
}
]
}
- Save the policy. Remember the name you chose for it.
- Attach the policy to the role.
At that point, go ahead and save it and give it a unique name. Remember what you chose as the name.
Create a Route 53 Hosted Zone
In order for our domain to auto update when we startup or restart the server, we need to set it in DNS. On route 53, create a new hosted zone. Take note of the hosted Zone ID, as we will need it later. Create an A record on your domain and set the IP to whatever you want. It will be changed programatically when your EC2 instance boots.
Configure the EC2 instance and start the server
Go to the EC2 Console. Make sure you choose a region that is close to you.
- Click on "Launch Instances".
- Select "Windows Server 2019 Base".
- Select t3.medium (recommened for Windows and Minecraft)
- Under "IAM Role", select the role you created earlier.
- Under "user data", paste the following.
- Replace "your-bucket-name" with your bucket name.
<powershell>
<#
Powershell Minecraft Server Userdata script
#>
$bucket = "your-bucket-name"
# Install Chrome
$Path = $env:TEMP;
$Installer = "chrome_installer.exe";
Invoke-WebRequest "https://dl.google.com/chrome/install/latest/chrome_installer.exe" -OutFile $Path\$Installer;
Start-Process -FilePath $Path\$Installer -Args "/silent /install" -Verb RunAs -Wait;
Remove-Item $Path\$Installer;
# Set Timezone to PST
Set-TimeZone "Pacific Standard Time"
## Download Java
Read-S3Object -BucketName $bucket -Key "jre-8u281-windows-x64.exe" -File "C:\Users\Administrator\Desktop\java.exe"
# Open Minecraft Ports
netsh advfirewall firewall add rule name="Open Port 25565" dir=in action=allow protocol=TCP localport=25565
netsh advfirewall firewall add rule name="Open Port 25565" dir=in action=allow protocol=TCP localport=25565
# Download preconfigured server
Read-S3Object -BucketName $bucket -Key "server.zip" -File "C:\Users\Administrator\Desktop\server.zip"
## Make expected directory
mkdir "C:\Users\Administrator\Desktop\Server"
## Unzip Server
Expand-Archive -LiteralPath 'C:\Users\Administrator\Desktop\server.zip' -DestinationPath 'C:\Users\Administrator\Desktop\Server'
<#
Creates a startup script that runs a DNS update every time the machine reboots
#>
# Copy Script
Read-S3Object -BucketName $bucket -Key "dns.ps1" -File "C:\dns.ps1"
# Unblock Script
Unblock-File -Path "C:\dns.ps1"
C:\dns.ps1
# Schedule it as a powershell job
$trigger = New-JobTrigger -AtStartup -RandomDelay 00:00:30
Register-ScheduledJob -Trigger $trigger -FilePath C:\dns.ps1 -Name DNSUpdates
# Download a shutdown script. This must be set manually in GP, unfortunately
Read-S3Object -BucketName $bucket -Key "closeandbackup.ps1" -File "C:\Windows\System32\GroupPolicy\Machine\Scripts\Shutdown"
Unblock-File -Path "C:\Windows\System32\GroupPolicy\Machine\Scripts\Shutdown\closeandbackup.ps1"
</powershell>
- Under "Add tags", add the following.
KEY = AUTO_DNS_NAME
VALUE = minecraft.bagofchaos.com (or whatever your custom domain is)
KEY = AUTO_DNS_ZONE
VALUE = YOURDNSZONEVALUE
- On your security group, make sure inbound 25565 TCP/UDP is allowed. This will allow users to connect to your server. Note that in the powershell script above, we unblocked 25565 in windows firewall.
- Create a new key pair. This will allow you to troubleshoot the scripts if they didn't work.
If all is well, your instance should boot and the following will happen.
- Timezone changed to PST
- server.zip on your desktop upon login
- java installer on desktop upon login
- Scheduled job created that runs DNS. Additionally, DNS for your zone should be set to the EC2 instances ip.
Configure Windows and Testing
Go ahead and RDP into your instance and make sure everything is working. Keep in mind that it may take some time for this to happen. You can view the log of your userdata at C:\ProgramData\Amazon\EC2-Windows\Launch\Log\UserdataExecution.log.
We need to manually install Java, as trying to programatically install it causes several errors that I have yet to fix. Once that is done, extract your server and create a file in the directory called "startme.bat". This file should contain the script needed to boot your server. Sample below. (Rename server.jar to your file)
java -Xms3G -Xmx3G -jar server.jar
We also need to add a group policy shutdown script. This script will gracefully shutdown the server and upload the data to S3. The script should have been auto uploaded to the relevant folder, called "closeandbackup.ps1".
We then need to add our startup script. Open task scheduler and create a new task at startup that runs your .bat file. Make sure you set the "start in" parameter to be the folder where your server is.
At this point, you are automated and ready to roll! When the server starts and stops, it auto assigns the IP and starts up the server.