Tofu code and AWX first
Build and Publish Docker Image / Build and Push Docker Image (push) Successful in 2m13s
Details
Build and Publish Docker Image / Build and Push Docker Image (push) Successful in 2m13s
Details
This commit is contained in:
parent
94ef8585b8
commit
df9960fe32
|
|
@ -1,5 +1,5 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/python,linux
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=python,linux
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/linux,python,terraform
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=linux,python,terraform
|
||||
|
||||
### Linux ###
|
||||
*~
|
||||
|
|
@ -188,4 +188,40 @@ poetry.toml
|
|||
# LSP config files
|
||||
pyrightconfig.json
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/python,linux
|
||||
### Terraform ###
|
||||
# Local .terraform directories
|
||||
**/.terraform/*
|
||||
|
||||
# .tfstate files
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
|
||||
# Crash log files
|
||||
crash.log
|
||||
crash.*.log
|
||||
|
||||
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
|
||||
# password, private keys, and other secrets. These should not be part of version
|
||||
# control as they are data points which are potentially sensitive and subject
|
||||
# to change depending on the environment.
|
||||
*.tfvars
|
||||
*.tfvars.json
|
||||
|
||||
# Ignore override files as they are usually used to override resources locally and so
|
||||
# are not checked in
|
||||
override.tf
|
||||
override.tf.json
|
||||
*_override.tf
|
||||
*_override.tf.json
|
||||
|
||||
# Include override files you do wish to add to version control using negated pattern
|
||||
# !example_override.tf
|
||||
|
||||
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
|
||||
# example: *tfplan*
|
||||
|
||||
# Ignore CLI configuration files
|
||||
.terraformrc
|
||||
terraform.rc
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/linux,python,terraform
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
---
|
||||
- name: Deploy Uptime Kuma on GCP and Configure Non-Interactively
|
||||
hosts: localhost
|
||||
connection: local
|
||||
gather_facts: false
|
||||
|
||||
vars:
|
||||
# GCP Configuration
|
||||
gcp_project: "your-gcp-project-id"
|
||||
gcp_region: "us-central1"
|
||||
gcp_zone: "us-central1-a"
|
||||
instance_name: "uptime-kuma-vm"
|
||||
machine_type: "e2-small" # 2GB RAM is plenty for Uptime Kuma
|
||||
static_ip_name: "uptime-kuma-ip"
|
||||
firewall_rule_name: "allow-uptime-kuma-3001"
|
||||
|
||||
# Uptime Kuma Credentials & Target
|
||||
kuma_admin_user: "admin"
|
||||
kuma_admin_pass: "ChangeMeImmediately123!"
|
||||
target_monitor_name: "Target Web App"
|
||||
target_monitor_url: "https://example.com"
|
||||
|
||||
tasks:
|
||||
# -------------------------------------------------------------
|
||||
# 1. Local Prerequisites
|
||||
# -------------------------------------------------------------
|
||||
- name: Ensure uptime-kuma-api Python library is installed locally
|
||||
ansible.builtin.pip:
|
||||
name: uptime-kuma-api
|
||||
state: present
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# 2. Allocate Static Public IP (Idempotent)
|
||||
# -------------------------------------------------------------
|
||||
- name: Check if static IP exists
|
||||
ansible.builtin.command: >
|
||||
gcloud compute addresses describe {{ static_ip_name }}
|
||||
--region={{ gcp_region }}
|
||||
--project={{ gcp_project }}
|
||||
register: ip_check
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Allocate static external IP in GCP
|
||||
ansible.builtin.command: >
|
||||
gcloud compute addresses create {{ static_ip_name }}
|
||||
--region={{ gcp_region }}
|
||||
--project={{ gcp_project }}
|
||||
--description="Static IP for Uptime Kuma"
|
||||
when: ip_check.rc != 0
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# 3. Create Firewall Rule (Port 3001)
|
||||
# -------------------------------------------------------------
|
||||
- name: Check if firewall rule exists
|
||||
ansible.builtin.command: >
|
||||
gcloud compute firewall-rules describe {{ firewall_rule_name }}
|
||||
--project={{ gcp_project }}
|
||||
register: fw_check
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Allow TCP traffic to port 3001
|
||||
ansible.builtin.command: >
|
||||
gcloud compute firewall-rules create {{ firewall_rule_name }}
|
||||
--project={{ gcp_project }}
|
||||
--allow=tcp:3001
|
||||
--target-tags=uptime-kuma
|
||||
--source-ranges=0.0.0.0/0
|
||||
--description="Allow traffic to Uptime Kuma UI & API"
|
||||
when: fw_check.rc != 0
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# 4. Provision VM with Container-Optimized OS
|
||||
# -------------------------------------------------------------
|
||||
- name: Check if instance already exists
|
||||
ansible.builtin.command: >
|
||||
gcloud compute instances describe {{ instance_name }}
|
||||
--zone={{ gcp_zone }}
|
||||
--project={{ gcp_project }}
|
||||
register: instance_check
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Launch Compute Engine VM with Uptime Kuma container
|
||||
ansible.builtin.command: >
|
||||
gcloud compute instances create-with-container {{ instance_name }}
|
||||
--project={{ gcp_project }}
|
||||
--zone={{ gcp_zone }}
|
||||
--machine-type={{ machine_type }}
|
||||
--tags=uptime-kuma
|
||||
--address={{ static_ip_name }}
|
||||
--boot-disk-size=20GB
|
||||
--container-image=louislam/uptime-kuma:1
|
||||
--container-mount-host-path=host-path=/var/uptime-kuma,mount-path=/app/data,mode=rw
|
||||
when: instance_check.rc != 0
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# 5. Fetch Public IP and Wait for Service Readiness
|
||||
# -------------------------------------------------------------
|
||||
- name: Fetch instance public IP address
|
||||
ansible.builtin.command: >
|
||||
gcloud compute instances describe {{ instance_name }}
|
||||
--zone={{ gcp_zone }}
|
||||
--project={{ gcp_project }}
|
||||
--format="value(networkInterfaces[0].accessConfigs[0].natIP)"
|
||||
register: vm_ip
|
||||
changed_when: false
|
||||
|
||||
- name: Display Uptime Kuma URL
|
||||
ansible.builtin.debug:
|
||||
msg: "Uptime Kuma is booting at: http://{{ vm_ip.stdout.strip() }}:3001"
|
||||
|
||||
- name: Wait for Uptime Kuma to respond with HTTP 200
|
||||
ansible.builtin.uri:
|
||||
url: "http://{{ vm_ip.stdout.strip() }}:3001"
|
||||
status_code: 200
|
||||
register: readiness
|
||||
until: readiness.status == 200
|
||||
retries: 30
|
||||
delay: 6
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# 6. Non-Interactive Configuration via API
|
||||
# -------------------------------------------------------------
|
||||
- name: Perform headless setup and add monitor
|
||||
ansible.builtin.command:
|
||||
cmd: >
|
||||
python3 -c "
|
||||
import sys
|
||||
from uptime_kuma_api import UptimeKumaApi, MonitorType
|
||||
|
||||
kuma_url = 'http://{{ vm_ip.stdout.strip() }}:3001'
|
||||
api = UptimeKumaApi(kuma_url)
|
||||
|
||||
# 1. Initial admin account creation if not yet initialized
|
||||
if api.need_setup():
|
||||
api.setup('{{ kuma_admin_user }}', '{{ kuma_admin_pass }}')
|
||||
print('ADMIN_CREATED')
|
||||
|
||||
# 2. Login
|
||||
api.login('{{ kuma_admin_user }}', '{{ kuma_admin_pass }}')
|
||||
|
||||
# 3. Add monitor idempotently
|
||||
current_monitors = [m['name'] for m in api.get_monitors()]
|
||||
if '{{ target_monitor_name }}' not in current_monitors:
|
||||
api.add_monitor(
|
||||
type=MonitorType.HTTP,
|
||||
name='{{ target_monitor_name }}',
|
||||
url='{{ target_monitor_url }}',
|
||||
interval=60
|
||||
)
|
||||
print('MONITOR_ADDED')
|
||||
else:
|
||||
print('MONITOR_ALREADY_EXISTS')
|
||||
|
||||
api.disconnect()
|
||||
"
|
||||
register: setup_output
|
||||
changed_when: "'MONITOR_ADDED' in setup_output.stdout"
|
||||
|
||||
- name: Show setup summary
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Setup Status: {{ setup_output.stdout.strip() }}"
|
||||
- "Access UI at: http://{{ vm_ip.stdout.strip() }}:3001"
|
||||
- "Username: {{ kuma_admin_user }}"
|
||||
|
|
@ -1,42 +1,6 @@
|
|||
# This file is maintained automatically by "tofu init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/hashicorp/archive" {
|
||||
version = "2.8.1"
|
||||
hashes = [
|
||||
"h1:+zxWNwWTN6nQH4UsAHVU2dHFeQ1dbyti1imUwXo2UkM=",
|
||||
"h1:0nQbGAtw2FkzedC/AoP5stVvIEQT7ae04UDUE7K+rkQ=",
|
||||
"h1:4BH/NzcaIQ+HtfGhBo9boDdYTYLimD9Aw81bXyx4PQ8=",
|
||||
"h1:6cIzOvUMhFPYYCVpuraJCp1OBKQfIdt+vweg/t90nqY=",
|
||||
"h1:D0qy1mIABEFzQEwuASo4g+oGRgxodYsHhbU2VzmafkY=",
|
||||
"h1:Dwonz53sAPonmKnk+YNwbjw0uEatDVPTj/dtfCwBQVw=",
|
||||
"h1:FQLyzsDwRhDvov/FU4I/HTnt9jdNFivK1hmBVxhgj5Y=",
|
||||
"h1:FwIDfXJo5yqRq9DjC8u3cXfGVQhHje3bpdxUufTT4wI=",
|
||||
"h1:M561F8TZn57Si9KJ8ycdyGGOIDSx6uzeILfn3ODTZ7I=",
|
||||
"h1:Yg4us0ttDVnZnECv/eo1Scg2D00bZ8ENxD47TVt/HYQ=",
|
||||
"h1:l7EcsA0WkfQ1ylEm7q1xfJei3bUMFriPybZLIuRCkzg=",
|
||||
"h1:oOm3HeeUHp36HGT2EXEoLX+B3mGz+MIF8BazNsAOB7I=",
|
||||
"h1:rhohSLLoDQyaUzetRvvlrbjZ2LAEfVCyUdD/Or10jos=",
|
||||
"h1:wcukYOssMr3IzGWQUMQkX7On5WeBmBTNIpvPsK3ac90=",
|
||||
"h1:xTRO4rh/BOjz11omsMID2q0MCchmCTX+tgI6v5/OMQw=",
|
||||
"zh:167dcb2f3dfef941d300e4899c6b1852592e98cd5e20782289369bf0c81512f0",
|
||||
"zh:1e0bbf0e538631a49746ff23c7008c7de7d0850e4477a1aee7685cc9881a942a",
|
||||
"zh:4310e3fc60442f17165fef1a97eae8be3778ae9611a526eb5b19647ffbab98bc",
|
||||
"zh:5700d38d2dbf82f1c88dd72aae3cadd12b177554a4ef99bea9bb949fb1961412",
|
||||
"zh:69e2209971a4f87e45c01d3e7581f6864abb1502e7c44809b4b0ffdfdc1d3d5d",
|
||||
"zh:6adbd69f1279d29dfe17fc7eab8e72fffa9ab0c562f87a3f73aaf5d63fe352d8",
|
||||
"zh:76e3cdadb03e6c8a7a5d535d7b3c2509a7e5ba9c37fac66ca360d271f5dc5c17",
|
||||
"zh:8a5bb2ce0746b28f12770e473022076c55e32ea942b514150cf2b07fa673d48a",
|
||||
"zh:9612186dd905850d20144101c722da59a2de660c2bf931a786ac63fe3766f5a6",
|
||||
"zh:a77922d3edcc1288d52e5d225e0b98d6ae8b3ef8ef4f27a40a6c11136489b886",
|
||||
"zh:ac70801bfaa2d39e339c39d4989199981b4d4dcbd0cf1c65a9755940ef2a80ee",
|
||||
"zh:af382792a3ca715ef9f740f18ee1b1c4256e757e93fd5204ab5f6c1757ba2555",
|
||||
"zh:c2bd89b4fc81883f410851f3e4499ee9b2d14039b97a04a2edc9fb4577bcb38e",
|
||||
"zh:f9a8ee2c68f67bc6eefbce642594d4fd51247542599bed8ac47ec8139615c2ce",
|
||||
"zh:fa64f6907c6daf715ad0c435b410fc352567118297f143ee2760230b4592bfde",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.opentofu.org/hashicorp/azurerm" {
|
||||
version = "4.81.0"
|
||||
constraints = "~> 4.0"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -28,13 +28,6 @@ resource "azurerm_storage_account" "example" {
|
|||
account_replication_type = var.sa_account_replication_type
|
||||
}
|
||||
|
||||
# Create a storage container
|
||||
resource "azurerm_storage_container" "example" {
|
||||
name = "example-flexcontainer"
|
||||
storage_account_id = azurerm_storage_account.example.id
|
||||
container_access_type = "private"
|
||||
}
|
||||
|
||||
# Create a Log Analytics workspace for Application Insights
|
||||
resource "azurerm_log_analytics_workspace" "example" {
|
||||
name = coalesce(var.ws_name, random_string.name.result)
|
||||
|
|
@ -53,82 +46,36 @@ resource "azurerm_application_insights" "example" {
|
|||
workspace_id = azurerm_log_analytics_workspace.example.id
|
||||
}
|
||||
|
||||
# Create a service plan
|
||||
# Create a service plan. Custom container images require a Dedicated (or Premium)
|
||||
# plan - Consumption/Flex Consumption plans don't support bring-your-own-container.
|
||||
resource "azurerm_service_plan" "example" {
|
||||
name = coalesce(var.asp_name, random_string.name.result)
|
||||
resource_group_name = azurerm_resource_group.example.name
|
||||
location = azurerm_resource_group.example.location
|
||||
sku_name = "FC1"
|
||||
sku_name = var.asp_sku_name
|
||||
os_type = "Linux"
|
||||
}
|
||||
|
||||
data "archive_file" "function_app" {
|
||||
type = "zip"
|
||||
source_dir = "${path.module}/.."
|
||||
output_path = "${path.module}/function_app.zip"
|
||||
excludes = [".git", ".gitea", "iac", "helm-chart", ".terraform*", ".vscode"]
|
||||
}
|
||||
|
||||
resource "azurerm_storage_blob" "appcode" {
|
||||
name = "app-${random_string.name.result}.zip"
|
||||
storage_account_name = azurerm_storage_account.example.name
|
||||
storage_container_name = azurerm_storage_container.example.name
|
||||
type = "Block"
|
||||
source = data.archive_file.function_app.output_path
|
||||
}
|
||||
|
||||
data "azurerm_storage_account_sas" "sas" {
|
||||
connection_string = azurerm_storage_account.example.primary_connection_string
|
||||
https_only = true
|
||||
|
||||
resource_types {
|
||||
service = false
|
||||
container = false
|
||||
object = true
|
||||
}
|
||||
|
||||
services {
|
||||
blob = true
|
||||
queue = false
|
||||
table = false
|
||||
file = false
|
||||
}
|
||||
|
||||
start = "2023-01-01T00:00:00Z"
|
||||
expiry = "2029-01-01T00:00:00Z"
|
||||
|
||||
permissions {
|
||||
read = true
|
||||
write = false
|
||||
delete = false
|
||||
list = false
|
||||
add = false
|
||||
create = false
|
||||
update = false
|
||||
process = false
|
||||
tag = false
|
||||
filter = false
|
||||
}
|
||||
}
|
||||
|
||||
# Create a function app
|
||||
resource "azurerm_function_app_flex_consumption" "example" {
|
||||
# Create a function app that runs the prebuilt container image straight from Docker Hub
|
||||
resource "azurerm_linux_function_app" "example" {
|
||||
name = coalesce(var.fa_name, random_string.name.result)
|
||||
resource_group_name = azurerm_resource_group.example.name
|
||||
location = azurerm_resource_group.example.location
|
||||
service_plan_id = azurerm_service_plan.example.id
|
||||
|
||||
storage_container_type = "blobContainer"
|
||||
storage_container_endpoint = "${azurerm_storage_account.example.primary_blob_endpoint}${azurerm_storage_container.example.name}"
|
||||
storage_authentication_type = "StorageAccountConnectionString"
|
||||
storage_access_key = azurerm_storage_account.example.primary_access_key
|
||||
runtime_name = var.runtime_name
|
||||
runtime_version = var.runtime_version
|
||||
maximum_instance_count = 50
|
||||
instance_memory_in_mb = 2048
|
||||
storage_account_name = azurerm_storage_account.example.name
|
||||
storage_account_access_key = azurerm_storage_account.example.primary_access_key
|
||||
|
||||
site_config {
|
||||
application_insights_connection_string = azurerm_application_insights.example.connection_string
|
||||
|
||||
application_stack {
|
||||
docker {
|
||||
registry_url = "https://index.docker.io"
|
||||
image_name = var.container_image_name
|
||||
image_tag = var.container_image_tag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app_settings = {
|
||||
|
|
@ -136,6 +83,8 @@ resource "azurerm_function_app_flex_consumption" "example" {
|
|||
"OLLAMA_MODEL" = "gemma4:e4b"
|
||||
"JOKE_PROMPT" = "tell me a dad joke about programming"
|
||||
"PORT" = "80"
|
||||
"WEBSITE_RUN_FROM_PACKAGE" = "${azurerm_storage_account.example.primary_blob_endpoint}${azurerm_storage_container.example.name}/${azurerm_storage_blob.appcode.name}${data.azurerm_storage_account_sas.sas.sas}"
|
||||
"WEBSITES_PORT" = "80"
|
||||
"WEBSITES_ENABLE_APP_SERVICE_STORAGE" = "false"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ output "asp_name" {
|
|||
}
|
||||
|
||||
output "fa_name" {
|
||||
value = azurerm_function_app_flex_consumption.example.name
|
||||
value = azurerm_linux_function_app.example.name
|
||||
}
|
||||
|
||||
output "fa_url" {
|
||||
value = "https://${azurerm_function_app_flex_consumption.example.name}.azurewebsites.net"
|
||||
value = "https://${azurerm_linux_function_app.example.name}.azurewebsites.net"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,14 +89,20 @@ variable "fa_name" {
|
|||
default = ""
|
||||
}
|
||||
|
||||
variable "runtime_name" {
|
||||
description = "The name of the language worker runtime."
|
||||
variable "asp_sku_name" {
|
||||
description = "The SKU of the App Service Plan hosting the Function App. Must be a Dedicated or Premium SKU to support custom containers (e.g. B1, P1v2, EP1)."
|
||||
type = string
|
||||
default = "python" # Allowed: dotnet-isolated, java, node, powershell, python
|
||||
default = "B1"
|
||||
}
|
||||
|
||||
variable "runtime_version" {
|
||||
description = "The version of the language worker runtime."
|
||||
variable "container_image_name" {
|
||||
description = "The Docker Hub image name (without tag) to deploy to the Function App."
|
||||
type = string
|
||||
default = "3.11" # Supported versions: see https://aka.ms/flexfxversions
|
||||
default = "idjohnson/dadjokeapp"
|
||||
}
|
||||
|
||||
variable "container_image_tag" {
|
||||
description = "The tag of the Docker Hub image to deploy to the Function App."
|
||||
type = string
|
||||
default = "0.1"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue