├── _footer.md
├── examples
├── basic
│ ├── _footer.md
│ ├── _header.md
│ ├── main.tf
│ └── README.md
├── curseforge
│ ├── _footer.md
│ ├── _header.md
│ ├── main.tf
│ └── README.md
└── .terraform-docs.yml
├── terraform.tf
├── outputs.tf
├── locals.tf
├── LICENSE
├── main.law.tf
├── .gitignore
├── .terraform-docs.yml
├── _header.md
├── main.storage.tf
├── main.aci.tf
├── variables.tf
└── README.md
/_footer.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/basic/_footer.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/curseforge/_footer.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/terraform.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = "~> 1.9"
3 | required_providers {
4 | azapi = {
5 | source = "Azure/azapi"
6 | version = "~> 2.2"
7 | }
8 | random = {
9 | source = "hashicorp/random"
10 | version = "~> 3.6"
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/outputs.tf:
--------------------------------------------------------------------------------
1 | output "container_public_ip_address_and_port" {
2 | description = "The public IP address and port number of the Minecraft server."
3 | value = "${azapi_resource.container_instance.output.properties.ipAddress.ip}:${var.port}"
4 | }
5 |
6 | output "container_public_fqdn_and_port" {
7 | description = "The public FQDN of the Minecraft server."
8 | value = "${azapi_resource.container_instance.output.properties.ipAddress.fqdn}:${var.port}"
9 | }
10 |
--------------------------------------------------------------------------------
/examples/basic/_header.md:
--------------------------------------------------------------------------------
1 | # Basic example
2 |
3 | This example shows how to deploy the server in its basic form.
4 |
5 | **Make sure to change your OPS player name in the `ops` section.**
6 |
7 | You can download this example to a local directory and run the following commands to deploy the server:
8 |
9 | > You will need to be logged in to your Azure account and have the Azure CLI installed as well as Terraform.
10 |
11 | ```shell
12 | terraform init
13 | terraform apply
14 | ```
15 |
--------------------------------------------------------------------------------
/examples/curseforge/_header.md:
--------------------------------------------------------------------------------
1 | # Curseforge example
2 |
3 | This example shows how to deploy a server with a modpack from Curseforge.
4 | It also increases the view distance to 24 chunks.
5 |
6 | **Make sure to change your OPS player name in the `ops` section.**
7 |
8 | You can download this example to a local directory and run the following commands to deploy the server:
9 |
10 | > You will need to be logged in to your Azure account and have the Azure CLI installed as well as Terraform.
11 |
12 | ```shell
13 | terraform init
14 | terraform apply
15 | ```
16 |
--------------------------------------------------------------------------------
/locals.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | storage_account_key1 = sensitive(azapi_resource_action.storage_account_list_keys.output.keys[0].value)
3 | storage_account_key2 = sensitive(azapi_resource_action.storage_account_list_keys.output.keys[1].value)
4 | }
5 |
6 | locals {
7 | file_share_name = "minecraftdata"
8 | }
9 |
10 | locals {
11 | container_environment_variables = concat(
12 | [
13 | for key, value in var.minecraft_server_environment_variables : {
14 | name = key
15 | value = value
16 | secureValue = null
17 | }
18 | ],
19 | [
20 | for key, value in nonsensitive(var.minecraft_server_environment_variables_sensitive) : {
21 | name = key
22 | value = null
23 | secureValue = sensitive(value)
24 | }
25 | ]
26 | )
27 | }
28 |
29 | locals {
30 | log_analytics_workspace_primary_key = sensitive(azapi_resource_action.log_analytics_workspace_keys.output.primarySharedKey)
31 | log_analytics_workspace_secondary_key = sensitive(azapi_resource_action.log_analytics_workspace_keys.output.secondarySharedKey)
32 | }
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Matt White
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/main.law.tf:
--------------------------------------------------------------------------------
1 | resource "random_id" "log_analytics_workspace_name" {
2 | byte_length = 6
3 | prefix = "law"
4 | }
5 |
6 | resource "azapi_resource" "log_analytics_workspace" {
7 | type = "Microsoft.OperationalInsights/workspaces@2023-09-01"
8 | parent_id = var.resource_group_resource_id
9 | name = random_id.log_analytics_workspace_name.hex
10 | location = var.location
11 | body = {
12 | properties = {
13 | features = {
14 | immediatePurgeDataOn30Days = true
15 | }
16 | publicNetworkAccessForQuery = "Enabled"
17 | publicNetworkAccessForIngestion = "Enabled"
18 | retentionInDays = 30
19 | sku = {
20 | name = "PerGB2018"
21 | }
22 | }
23 | }
24 | response_export_values = ["properties.customerId"]
25 | }
26 |
27 | resource "azapi_resource_action" "log_analytics_workspace_keys" {
28 | resource_id = azapi_resource.log_analytics_workspace.id
29 | type = "Microsoft.OperationalInsights/workspaces@2023-09-01"
30 | action = "sharedKeys"
31 | method = "POST"
32 | response_export_values = ["*"]
33 | }
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Local .terraform directories
2 | **/.terraform/*
3 |
4 | # .tfstate files
5 | *.tfstate
6 | *.tfstate.*
7 |
8 | # Crash log files
9 | crash.log
10 | crash.*.log
11 |
12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as
13 | # password, private keys, and other secrets. These should not be part of version
14 | # control as they are data points which are potentially sensitive and subject
15 | # to change depending on the environment.
16 | *.tfvars
17 | *.tfvars.json
18 |
19 | # Ignore override files as they are usually used to override resources locally and so
20 | # are not checked in
21 | override.tf
22 | override.tf.json
23 | *_override.tf
24 | *_override.tf.json
25 |
26 | # Ignore transient lock info files created by terraform apply
27 | .terraform.tfstate.lock.info
28 |
29 | # Include override files you do wish to add to version control using negated pattern
30 | # !example_override.tf
31 |
32 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
33 | # example: *tfplan*
34 | *tfplan*
35 |
36 | # Ignore CLI configuration files
37 | .terraformrc
38 | terraform.rc
39 |
40 | # As this is a module, ignore the lock file
41 | .terraform.lock.hcl
42 |
--------------------------------------------------------------------------------
/.terraform-docs.yml:
--------------------------------------------------------------------------------
1 | ### To generate the output file to partially incorporate in the README.md,
2 | ### Execute this command in the Terraform module's code folder:
3 | # terraform-docs -c .terraform-docs.yml .
4 |
5 | formatter: "markdown document" # this is required
6 |
7 | version: "~> 0.18"
8 |
9 | header-from: "_header.md"
10 | footer-from: "_footer.md"
11 |
12 | recursive:
13 | enabled: false
14 | path: modules
15 |
16 | sections:
17 | hide: []
18 | show: []
19 |
20 | content: |-
21 | {{ .Header }}
22 |
23 |
24 | {{ .Requirements }}
25 |
26 | {{ .Resources }}
27 |
28 |
29 | {{ .Inputs }}
30 |
31 | {{ .Outputs }}
32 |
33 | {{ .Modules }}
34 |
35 | {{ .Footer }}
36 |
37 | output:
38 | file: README.md
39 | mode: replace
40 | template: |-
41 |
42 | {{ .Content }}
43 |
44 | output-values:
45 | enabled: false
46 | from: ""
47 |
48 | sort:
49 | enabled: true
50 | by: required
51 |
52 | settings:
53 | anchor: true
54 | color: true
55 | default: true
56 | description: false
57 | escape: true
58 | hide-empty: false
59 | html: true
60 | indent: 2
61 | lockfile: false
62 | read-comments: true
63 | required: true
64 | sensitive: true
65 | type: true
66 |
--------------------------------------------------------------------------------
/_header.md:
--------------------------------------------------------------------------------
1 | # terraform-azure-minecraft-server
2 |
3 | This is a Terraform module to deploy a Minecraft server on Azure.
4 |
5 | ## Features
6 |
7 | - Azure Files for persistent storage
8 | - Azure Container Instances for the server itself
9 | - Uses the industry standard [`itzg/minecraft-server`]() container image
10 |
11 | ## Design Principles
12 |
13 | - Simple to use
14 | - Cost effective
15 |
16 | ## Configuration
17 |
18 | Most configuration is done via environment variables. See the [server properties](https://docker-minecraft-server.readthedocs.io/en/latest/configuration/server-properties/) for details.
19 |
20 | ```hcl
21 | module "minecraft_server" {
22 | source = "matt-FFFFFF/minecraft-server/azure"
23 | version = "..." # Change this to your desired version
24 | resource_group_resource_id = "/subscriptions/..."
25 | location = "swedencentral"
26 |
27 | minecraft_server_environment_variables = {
28 | EULA = "true"
29 | MEMORY = "4G",
30 | DIFFICULTY = "normal",
31 | SERVER_NAME = "Minecraft Server",
32 | OPS = "yourPlayerHandle"
33 | VIEW_DISTANCE = "32"
34 | }
35 | }
36 | ```
37 |
--------------------------------------------------------------------------------
/examples/.terraform-docs.yml:
--------------------------------------------------------------------------------
1 | ### To generate the output file to partially incorporate in the README.md,
2 | ### Execute this command in the Terraform module's code folder:
3 | # terraform-docs -c .terraform-docs.yml .
4 |
5 | formatter: "markdown document" # this is required
6 |
7 | version: "~> 0.18"
8 |
9 | header-from: "_header.md"
10 | footer-from: "_footer.md"
11 |
12 | recursive:
13 | enabled: false
14 | path: modules
15 |
16 | sections:
17 | hide: []
18 | show: []
19 |
20 | content: |-
21 | {{ .Header }}
22 |
23 | ```hcl
24 | {{ include "main.tf" }}
25 | ```
26 |
27 |
28 | {{ .Requirements }}
29 |
30 | {{ .Resources }}
31 |
32 |
33 | {{ .Inputs }}
34 |
35 | {{ .Outputs }}
36 |
37 | {{ .Modules }}
38 |
39 | {{ .Footer }}
40 | output:
41 | file: README.md
42 | mode: replace
43 | template: |-
44 |
45 | {{ .Content }}
46 |
47 | output-values:
48 | enabled: false
49 | from: ""
50 |
51 | sort:
52 | enabled: true
53 | by: required
54 |
55 | settings:
56 | anchor: true
57 | color: true
58 | default: true
59 | description: false
60 | escape: true
61 | hide-empty: false
62 | html: true
63 | indent: 2
64 | lockfile: false
65 | read-comments: true
66 | required: true
67 | sensitive: true
68 | type: true
69 |
--------------------------------------------------------------------------------
/examples/basic/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = "~> 1.9"
3 | required_providers {
4 | azapi = {
5 | source = "Azure/azapi"
6 | version = "~> 2.2"
7 | }
8 | random = {
9 | source = "hashicorp/random"
10 | version = "~> 3.6"
11 | }
12 | }
13 | }
14 |
15 | resource "random_id" "rg" {
16 | byte_length = 6
17 | prefix = "rg"
18 | }
19 |
20 | resource "azapi_resource" "rg" {
21 | type = "Microsoft.Resources/resourceGroups@2024-03-01"
22 | name = random_id.rg.hex
23 | location = "swedencentral"
24 |
25 | }
26 |
27 | # When you copy this example, remove the source line with the relative path and
28 | # uncomment the source lines with the registry path and version
29 | module "minecraft_server" {
30 | source = "../../"
31 | # source = https://registry.terraform.io/modules/matt-FFFFFF/minecraft-server
32 | # version = "0.3.0"
33 | location = azapi_resource.rg.location
34 | resource_group_resource_id = azapi_resource.rg.id
35 | container_request_memory_in_gb = 5
36 | minecraft_server_environment_variables = {
37 | DIFFICULTY = "normal"
38 | EULA = "true"
39 | MEMORY = "4G"
40 | MODE = "survival"
41 | OPS = "yourPlayerHandle" # Set this to your Minecraft player handle to allow you to run admin commands in the server
42 | SERVER_NAME = "Minecraft Server"
43 | VERSION = "1.21.4" # This is the version of minecraft server
44 | VIEW_DISTANCE = "15" # This is the view distance of the server in chunks, this is a sensible default btu can be increased if you have a powerful server
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/main.storage.tf:
--------------------------------------------------------------------------------
1 | resource "random_id" "storage_account" {
2 | byte_length = 6
3 | prefix = "stg"
4 | }
5 |
6 | resource "azapi_resource" "storage_account" {
7 | type = "Microsoft.Storage/storageAccounts@2023-05-01"
8 | body = {
9 | kind = "StorageV2"
10 | properties = {
11 | accessTier = "Hot"
12 | allowSharedKeyAccess = true
13 | defaultToOAuthAuthentication = false
14 | isHnsEnabled = false
15 | isNfsV3Enabled = false
16 | isSftpEnabled = false
17 | minimumTlsVersion = "TLS1_2"
18 | networkAcls = {
19 | defaultAction = "Allow"
20 | }
21 | publicNetworkAccess = "Enabled"
22 | supportsHttpsTrafficOnly = true
23 | }
24 | sku = {
25 | name = "Standard_LRS"
26 | }
27 | }
28 | location = var.location
29 | name = coalesce(var.storage_account_name, random_id.storage_account.hex)
30 | parent_id = var.resource_group_resource_id
31 | }
32 |
33 | resource "azapi_resource" "file_share" {
34 | type = "Microsoft.Storage/storageAccounts/fileServices/shares@2023-05-01"
35 | body = {
36 | properties = {
37 | enabledProtocols = "SMB"
38 | shareQuota = 5
39 | }
40 | }
41 | name = local.file_share_name
42 | parent_id = "${azapi_resource.storage_account.id}/fileServices/default"
43 | }
44 |
45 | resource "azapi_resource_action" "storage_account_list_keys" {
46 | resource_id = azapi_resource.storage_account.id
47 | type = "Microsoft.Storage/storageAccounts@2023-05-01"
48 | action = "listKeys"
49 | method = "POST"
50 | response_export_values = ["keys"]
51 | }
52 |
--------------------------------------------------------------------------------
/examples/curseforge/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = "~> 1.9"
3 | required_providers {
4 | azapi = {
5 | source = "Azure/azapi"
6 | version = "~> 2.2"
7 | }
8 | random = {
9 | source = "hashicorp/random"
10 | version = "~> 3.6"
11 | }
12 | }
13 | }
14 |
15 | resource "random_id" "rg" {
16 | byte_length = 6
17 | prefix = "rg"
18 | }
19 |
20 | resource "azapi_resource" "rg" {
21 | type = "Microsoft.Resources/resourceGroups@2024-03-01"
22 | name = random_id.rg.hex
23 | location = "swedencentral"
24 |
25 | }
26 |
27 | # When you copy this example, remove the source line with the relative path and
28 | # uncomment the source lines with the registry path and version
29 | module "minecraft_server" {
30 | source = "../../"
31 | # source = https://registry.terraform.io/modules/matt-FFFFFF/minecraft-server
32 | # version = "0.3.0"
33 | location = azapi_resource.rg.location
34 | resource_group_resource_id = azapi_resource.rg.id
35 | container_request_memory_in_gb = 9
36 | minecraft_server_environment_variables = {
37 | CF_PAGE_URL = "https://www.curseforge.com/minecraft/modpacks/the-vanilla-experience/files/5967958" # This is the URL of the modpack you want to install
38 | DIFFICULTY = "normal",
39 | EULA = "true"
40 | MEMORY = "6G", # If you have a lot of mods, you may need to increase this value, and also the memory allocated to the container
41 | MODE = "survival",
42 | OPS = "yourPlayerHandle" # Set this to your Minecraft player handle to allow you to run admin commands in the server
43 | SERVER_NAME = "Minecraft Server",
44 | TYPE = "AUTO_CURSEFORGE" # This tells the module to install the modpack from CurseForge and to detect the server type automatically
45 | VERSION = "1.21.4" # This is the version of minecraft server
46 | VIEW_DISTANCE = "24" # This is the view distance of the server in chunks, this is quite high and may need to be lowered depending on the server performance
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/main.aci.tf:
--------------------------------------------------------------------------------
1 | resource "random_id" "container_instance" {
2 | byte_length = 6
3 | prefix = "ci"
4 | }
5 |
6 | resource "random_id" "container_dns_prefix" {
7 | byte_length = 4
8 | }
9 |
10 | resource "azapi_resource" "container_instance" {
11 | type = "Microsoft.ContainerInstance/containerGroups@2023-05-01"
12 | body = {
13 | properties = {
14 | containers = [
15 | {
16 | name = "minecraft"
17 | properties = {
18 | environmentVariables = local.container_environment_variables,
19 | image = var.container_image
20 | ports = [
21 | {
22 | port = var.port
23 | protocol = "TCP"
24 | },
25 | ],
26 | volumeMounts = [
27 | {
28 | name = "filesharevolume",
29 | mountPath = "/data"
30 | }
31 | ]
32 | resources = {
33 | requests = {
34 | cpu = var.container_request_cpu
35 | memoryInGB = var.container_request_memory_in_gb
36 | }
37 | }
38 | }
39 | },
40 | ]
41 | ipAddress = {
42 | dnsNameLabel = coalesce(var.container_dns_prefix, random_id.container_dns_prefix.hex)
43 | ports = [
44 | {
45 | port = var.port
46 | protocol = "TCP"
47 | },
48 | ]
49 | type = "Public"
50 | }
51 | diagnostics = {
52 | logAnalytics = {
53 | workspaceId = azapi_resource.log_analytics_workspace.output.properties.customerId
54 | workspaceKey = azapi_resource_action.log_analytics_workspace_keys.output.primarySharedKey
55 | }
56 | }
57 | osType = "Linux"
58 | restartPolicy = "Always"
59 | volumes = [
60 | {
61 | name = "filesharevolume",
62 | azureFile = {
63 | shareName = local.file_share_name,
64 | storageAccountName = azapi_resource.storage_account.name,
65 | storageAccountKey = local.storage_account_key1
66 | }
67 | }
68 | ]
69 | }
70 | }
71 | location = var.location
72 | name = coalesce(var.container_instance_name, random_id.container_instance.hex)
73 | parent_id = var.resource_group_resource_id
74 | response_export_values = [
75 | "properties.ipAddress.ip",
76 | "properties.ipAddress.fqdn",
77 | ]
78 | replace_triggers_refs = [
79 | "properties.containers[].properties.resources.requests.memoryInGB",
80 | "properties.containers[].properties.resources.requests.cpu",
81 | ]
82 | }
83 |
--------------------------------------------------------------------------------
/variables.tf:
--------------------------------------------------------------------------------
1 | variable "location" {
2 | type = string
3 | description = "The location of the resources."
4 | nullable = false
5 | }
6 |
7 | variable "resource_group_resource_id" {
8 | type = string
9 | description = "The full id of the resource group in which to deploy the resources."
10 | nullable = false
11 | validation {
12 | error_message = "The input value must be a valid resource id for a resource group."
13 | condition = can(regex("^(/subscriptions/[^/]+/resourceGroups/[^/]+)$", var.resource_group_resource_id))
14 | }
15 | }
16 |
17 | variable "container_image" {
18 | type = string
19 | default = "docker.io/itzg/minecraft-server"
20 | description = "The image to use for the Minecraft server container. This should be based on the `itzg/minecraft-server` image."
21 | nullable = false
22 | }
23 |
24 | variable "container_instance_name" {
25 | type = string
26 | default = null
27 | description = "The name of the container instance. Leave as `null` to use the an auto-generated name."
28 | }
29 |
30 | variable "container_request_cpu" {
31 | type = number
32 | default = 2
33 | description = "The number of CPU cores to request for the Minecraft server container."
34 | nullable = false
35 | }
36 |
37 | variable "container_request_memory_in_gb" {
38 | type = number
39 | default = 5
40 | description = "The amount of memory in GB to request for the Minecraft server container."
41 | nullable = false
42 | }
43 |
44 | variable "minecraft_server_environment_variables" {
45 | type = map(string)
46 | default = {
47 | EULA = "true"
48 | }
49 | description = < for more information.
55 | DESCRIPTION
56 | nullable = false
57 | }
58 |
59 | variable "minecraft_server_environment_variables_sensitive" {
60 | type = map(string)
61 | default = {}
62 | description = "A map of sensitive environment variables to pass to the Minecraft server container. The key is the name of the environment variable, and the value is the value of the environment variable."
63 | nullable = false
64 | sensitive = true
65 | }
66 |
67 | variable "port" {
68 | type = number
69 | default = 25565
70 | description = "The port on which to expose the Minecraft server."
71 | nullable = false
72 | }
73 |
74 | variable "storage_account_name" {
75 | type = string
76 | default = null
77 | description = "The name of the storage account. Leave as `null` to use the an auto-generated name."
78 | }
79 |
80 | variable "log_analytics_workspace_name" {
81 | type = string
82 | default = null
83 | description = "The name of the log analytics workspace. Leave as `null` to use the an auto-generated name."
84 | }
85 |
86 | variable "container_dns_prefix" {
87 | type = string
88 | default = null
89 | description = "The DNS prefix to use for the container instance. Leave as `null` to use the an auto-generated name."
90 | }
91 |
--------------------------------------------------------------------------------
/examples/basic/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Basic example
3 |
4 | This example shows how to deploy the server in its basic form.
5 |
6 | **Make sure to change your OPS player name in the `ops` section.**
7 |
8 | You can download this example to a local directory and run the following commands to deploy the server:
9 |
10 | > You will need to be logged in to your Azure account and have the Azure CLI installed as well as Terraform.
11 |
12 | ```shell
13 | terraform init
14 | terraform apply
15 | ```
16 |
17 | ```hcl
18 | terraform {
19 | required_version = "~> 1.9"
20 | required_providers {
21 | azapi = {
22 | source = "Azure/azapi"
23 | version = "~> 2.2"
24 | }
25 | random = {
26 | source = "hashicorp/random"
27 | version = "~> 3.6"
28 | }
29 | }
30 | }
31 |
32 | resource "random_id" "rg" {
33 | byte_length = 6
34 | prefix = "rg"
35 | }
36 |
37 | resource "azapi_resource" "rg" {
38 | type = "Microsoft.Resources/resourceGroups@2024-03-01"
39 | name = random_id.rg.hex
40 | location = "swedencentral"
41 |
42 | }
43 |
44 | # When you copy this example, remove the source line with the relative path and
45 | # uncomment the source lines with the registry path and version
46 | module "minecraft_server" {
47 | source = "../../"
48 | # source = https://registry.terraform.io/modules/matt-FFFFFF/minecraft-server
49 | # version = "0.3.0"
50 | location = azapi_resource.rg.location
51 | resource_group_resource_id = azapi_resource.rg.id
52 | container_request_memory_in_gb = 5
53 | minecraft_server_environment_variables = {
54 | DIFFICULTY = "normal"
55 | EULA = "true"
56 | MEMORY = "4G"
57 | MODE = "survival"
58 | OPS = "yourPlayerHandle" # Set this to your Minecraft player handle to allow you to run admin commands in the server
59 | SERVER_NAME = "Minecraft Server"
60 | VERSION = "1.21.4" # This is the version of minecraft server
61 | VIEW_DISTANCE = "15" # This is the view distance of the server in chunks, this is a sensible default btu can be increased if you have a powerful server
62 | }
63 | }
64 | ```
65 |
66 |
67 | ## Requirements
68 |
69 | The following requirements are needed by this module:
70 |
71 | - [terraform](#requirement\_terraform) (~> 1.9)
72 |
73 | - [azapi](#requirement\_azapi) (~> 2.2)
74 |
75 | - [random](#requirement\_random) (~> 3.6)
76 |
77 | ## Resources
78 |
79 | The following resources are used by this module:
80 |
81 | - [azapi_resource.rg](https://registry.terraform.io/providers/Azure/azapi/latest/docs/resources/resource) (resource)
82 | - [random_id.rg](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) (resource)
83 |
84 |
85 | ## Required Inputs
86 |
87 | No required inputs.
88 |
89 | ## Optional Inputs
90 |
91 | No optional inputs.
92 |
93 | ## Outputs
94 |
95 | No outputs.
96 |
97 | ## Modules
98 |
99 | The following Modules are called:
100 |
101 | ### [minecraft\_server](#module\_minecraft\_server)
102 |
103 | Source: ../../
104 |
105 | Version:
106 |
107 |
--------------------------------------------------------------------------------
/examples/curseforge/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Curseforge example
3 |
4 | This example shows how to deploy a server with a modpack from Curseforge.
5 | It also increases the view distance to 24 chunks.
6 |
7 | **Make sure to change your OPS player name in the `ops` section.**
8 |
9 | You can download this example to a local directory and run the following commands to deploy the server:
10 |
11 | > You will need to be logged in to your Azure account and have the Azure CLI installed as well as Terraform.
12 |
13 | ```shell
14 | terraform init
15 | terraform apply
16 | ```
17 |
18 | ```hcl
19 | terraform {
20 | required_version = "~> 1.9"
21 | required_providers {
22 | azapi = {
23 | source = "Azure/azapi"
24 | version = "~> 2.2"
25 | }
26 | random = {
27 | source = "hashicorp/random"
28 | version = "~> 3.6"
29 | }
30 | }
31 | }
32 |
33 | resource "random_id" "rg" {
34 | byte_length = 6
35 | prefix = "rg"
36 | }
37 |
38 | resource "azapi_resource" "rg" {
39 | type = "Microsoft.Resources/resourceGroups@2024-03-01"
40 | name = random_id.rg.hex
41 | location = "swedencentral"
42 |
43 | }
44 |
45 | # When you copy this example, remove the source line with the relative path and
46 | # uncomment the source lines with the registry path and version
47 | module "minecraft_server" {
48 | source = "../../"
49 | # source = https://registry.terraform.io/modules/matt-FFFFFF/minecraft-server
50 | # version = "0.3.0"
51 | location = azapi_resource.rg.location
52 | resource_group_resource_id = azapi_resource.rg.id
53 | container_request_memory_in_gb = 9
54 | minecraft_server_environment_variables = {
55 | CF_PAGE_URL = "https://www.curseforge.com/minecraft/modpacks/the-vanilla-experience/files/5967958" # This is the URL of the modpack you want to install
56 | DIFFICULTY = "normal",
57 | EULA = "true"
58 | MEMORY = "6G", # If you have a lot of mods, you may need to increase this value, and also the memory allocated to the container
59 | MODE = "survival",
60 | OPS = "yourPlayerHandle" # Set this to your Minecraft player handle to allow you to run admin commands in the server
61 | SERVER_NAME = "Minecraft Server",
62 | TYPE = "AUTO_CURSEFORGE" # This tells the module to install the modpack from CurseForge and to detect the server type automatically
63 | VERSION = "1.21.4" # This is the version of minecraft server
64 | VIEW_DISTANCE = "24" # This is the view distance of the server in chunks, this is quite high and may need to be lowered depending on the server performance
65 | }
66 | }
67 | ```
68 |
69 |
70 | ## Requirements
71 |
72 | The following requirements are needed by this module:
73 |
74 | - [terraform](#requirement\_terraform) (~> 1.9)
75 |
76 | - [azapi](#requirement\_azapi) (~> 2.2)
77 |
78 | - [random](#requirement\_random) (~> 3.6)
79 |
80 | ## Resources
81 |
82 | The following resources are used by this module:
83 |
84 | - [azapi_resource.rg](https://registry.terraform.io/providers/Azure/azapi/latest/docs/resources/resource) (resource)
85 | - [random_id.rg](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) (resource)
86 |
87 |
88 | ## Required Inputs
89 |
90 | No required inputs.
91 |
92 | ## Optional Inputs
93 |
94 | No optional inputs.
95 |
96 | ## Outputs
97 |
98 | No outputs.
99 |
100 | ## Modules
101 |
102 | The following Modules are called:
103 |
104 | ### [minecraft\_server](#module\_minecraft\_server)
105 |
106 | Source: ../../
107 |
108 | Version:
109 |
110 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # terraform-azure-minecraft-server
3 |
4 | This is a Terraform module to deploy a Minecraft server on Azure.
5 |
6 | ## Features
7 |
8 | - Azure Files for persistent storage
9 | - Azure Container Instances for the server itself
10 | - Uses the industry standard [`itzg/minecraft-server`]() container image
11 |
12 | ## Design Principles
13 |
14 | - Simple to use
15 | - Cost effective
16 |
17 | ## Configuration
18 |
19 | Most configuration is done via environment variables. See the [server properties](https://docker-minecraft-server.readthedocs.io/en/latest/configuration/server-properties/) for details.
20 |
21 | ```hcl
22 | module "minecraft_server" {
23 | source = "matt-FFFFFF/minecraft-server/azure"
24 | version = "..." # Change this to your desired version
25 | resource_group_resource_id = "/subscriptions/..."
26 | location = "swedencentral"
27 |
28 | minecraft_server_environment_variables = {
29 | EULA = "true"
30 | MEMORY = "4G",
31 | DIFFICULTY = "normal",
32 | SERVER_NAME = "Minecraft Server",
33 | OPS = "yourPlayerHandle"
34 | VIEW_DISTANCE = "32"
35 | }
36 | }
37 | ```
38 |
39 |
40 | ## Requirements
41 |
42 | The following requirements are needed by this module:
43 |
44 | - [terraform](#requirement\_terraform) (~> 1.9)
45 |
46 | - [azapi](#requirement\_azapi) (~> 2.2)
47 |
48 | - [random](#requirement\_random) (~> 3.6)
49 |
50 | ## Resources
51 |
52 | The following resources are used by this module:
53 |
54 | - [azapi_resource.container_instance](https://registry.terraform.io/providers/Azure/azapi/latest/docs/resources/resource) (resource)
55 | - [azapi_resource.file_share](https://registry.terraform.io/providers/Azure/azapi/latest/docs/resources/resource) (resource)
56 | - [azapi_resource.log_analytics_workspace](https://registry.terraform.io/providers/Azure/azapi/latest/docs/resources/resource) (resource)
57 | - [azapi_resource.storage_account](https://registry.terraform.io/providers/Azure/azapi/latest/docs/resources/resource) (resource)
58 | - [azapi_resource_action.log_analytics_workspace_keys](https://registry.terraform.io/providers/Azure/azapi/latest/docs/resources/resource_action) (resource)
59 | - [azapi_resource_action.storage_account_list_keys](https://registry.terraform.io/providers/Azure/azapi/latest/docs/resources/resource_action) (resource)
60 | - [random_id.container_dns_prefix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) (resource)
61 | - [random_id.container_instance](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) (resource)
62 | - [random_id.log_analytics_workspace_name](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) (resource)
63 | - [random_id.storage_account](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) (resource)
64 |
65 |
66 | ## Required Inputs
67 |
68 | The following input variables are required:
69 |
70 | ### [location](#input\_location)
71 |
72 | Description: The location of the resources.
73 |
74 | Type: `string`
75 |
76 | ### [resource\_group\_resource\_id](#input\_resource\_group\_resource\_id)
77 |
78 | Description: The full id of the resource group in which to deploy the resources.
79 |
80 | Type: `string`
81 |
82 | ## Optional Inputs
83 |
84 | The following input variables are optional (have default values):
85 |
86 | ### [container\_dns\_prefix](#input\_container\_dns\_prefix)
87 |
88 | Description: The DNS prefix to use for the container instance. Leave as `null` to use the an auto-generated name.
89 |
90 | Type: `string`
91 |
92 | Default: `null`
93 |
94 | ### [container\_image](#input\_container\_image)
95 |
96 | Description: The image to use for the Minecraft server container. This should be based on the `itzg/minecraft-server` image.
97 |
98 | Type: `string`
99 |
100 | Default: `"docker.io/itzg/minecraft-server"`
101 |
102 | ### [container\_instance\_name](#input\_container\_instance\_name)
103 |
104 | Description: The name of the container instance. Leave as `null` to use the an auto-generated name.
105 |
106 | Type: `string`
107 |
108 | Default: `null`
109 |
110 | ### [container\_request\_cpu](#input\_container\_request\_cpu)
111 |
112 | Description: The number of CPU cores to request for the Minecraft server container.
113 |
114 | Type: `number`
115 |
116 | Default: `2`
117 |
118 | ### [container\_request\_memory\_in\_gb](#input\_container\_request\_memory\_in\_gb)
119 |
120 | Description: The amount of memory in GB to request for the Minecraft server container.
121 |
122 | Type: `number`
123 |
124 | Default: `5`
125 |
126 | ### [log\_analytics\_workspace\_name](#input\_log\_analytics\_workspace\_name)
127 |
128 | Description: The name of the log analytics workspace. Leave as `null` to use the an auto-generated name.
129 |
130 | Type: `string`
131 |
132 | Default: `null`
133 |
134 | ### [minecraft\_server\_environment\_variables](#input\_minecraft\_server\_environment\_variables)
135 |
136 | Description: A map of environment variables to pass to the Minecraft server container.
137 | The key is the name of the environment variable, and the value is the value of the environment variable.
138 |
139 | For the container to work, you must include the `EULA` environment variable with the value `true`.
140 | See for more information.
141 |
142 | Type: `map(string)`
143 |
144 | Default:
145 |
146 | ```json
147 | {
148 | "EULA": "true"
149 | }
150 | ```
151 |
152 | ### [minecraft\_server\_environment\_variables\_sensitive](#input\_minecraft\_server\_environment\_variables\_sensitive)
153 |
154 | Description: A map of sensitive environment variables to pass to the Minecraft server container. The key is the name of the environment variable, and the value is the value of the environment variable.
155 |
156 | Type: `map(string)`
157 |
158 | Default: `{}`
159 |
160 | ### [port](#input\_port)
161 |
162 | Description: The port on which to expose the Minecraft server.
163 |
164 | Type: `number`
165 |
166 | Default: `25565`
167 |
168 | ### [storage\_account\_name](#input\_storage\_account\_name)
169 |
170 | Description: The name of the storage account. Leave as `null` to use the an auto-generated name.
171 |
172 | Type: `string`
173 |
174 | Default: `null`
175 |
176 | ## Outputs
177 |
178 | The following outputs are exported:
179 |
180 | ### [container\_public\_fqdn\_and\_port](#output\_container\_public\_fqdn\_and\_port)
181 |
182 | Description: The public FQDN of the Minecraft server.
183 |
184 | ### [container\_public\_ip\_address\_and\_port](#output\_container\_public\_ip\_address\_and\_port)
185 |
186 | Description: The public IP address and port number of the Minecraft server.
187 |
188 | ## Modules
189 |
190 | No modules.
191 |
192 |
--------------------------------------------------------------------------------