diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index b22c73ad..820c8cce 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -6,6 +6,30 @@ on: - created jobs: + terraform: + runs-on: ubuntu-18.04 + + env: + ARM_CLIENT_ID: ${{secrets.SP_CLIENT}} + ARM_CLIENT_SECRET: ${{secrets.SP_PASSWORD}} + ARM_SUBSCRIPTION_ID: ${{secrets.SUBSCRIPTION_ID}} + ARM_TENANT_ID: ${{secrets.SP_TENANT}} + AZURE_STORAGE_ACCOUNT: "tstate31414" + TF_ACTION_WORKING_DIR: "./docker/setup/terraform" + + steps: + - uses: actions/checkout@v2 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v1 + + - name: Terraform Init + run: terraform init + + - name: Terraform Apply + run: terraform apply -auto-approve + + release: runs-on: ubuntu-18.04 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b53deb98..e42e6ed7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,3 +63,28 @@ jobs: ci build verify-build - run: bash <(curl -s https://codecov.io/bash) if: ${{ success() }} + + terraform-plan: + runs-on: ubuntu-18.04 + + env: + ARM_CLIENT_ID: ${{secrets.SP_CLIENT}} + ARM_CLIENT_SECRET: ${{secrets.SP_PASSWORD}} + ARM_SUBSCRIPTION_ID: ${{secrets.SUBSCRIPTION_ID}} + ARM_TENANT_ID: ${{secrets.SP_TENANT}} + + defaults: + run: + working-directory: "./docker/setup/terraform" + + steps: + - uses: actions/checkout@v2 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v1 + + - name: Terraform Init + run: terraform init + + - name: Terraform Plan + run: terraform plan diff --git a/docker/ci/Dockerfile b/docker/ci/Dockerfile index f1bd7694..0d03fd95 100644 --- a/docker/ci/Dockerfile +++ b/docker/ci/Dockerfile @@ -34,6 +34,13 @@ RUN wget -q https://github.com/mvdan/sh/releases/download/v${SHFMT_VERSION}/shfm && chmod +x /usr/local/bin/shfmt \ && shfmt -version +ARG TERRAFORM_VERSION=0.13.5 +RUN wget -q https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip \ + && unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip \ + && cp terraform /usr/local/bin \ + && rm terraform_${TERRAFORM_VERSION}_linux_amd64.zip \ + && terraform --version + COPY docker/ci/requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt @@ -70,3 +77,7 @@ RUN helm lint --strict ./helm/opwen_cloudserver \ && rm helm.yaml RUN shfmt -d -i 2 -ci . + +RUN terraform fmt -check + +RUN terraform validate -no-color \ No newline at end of file diff --git a/docker/setup/terraform/01_provider.tf b/docker/setup/terraform/01_provider.tf new file mode 100644 index 00000000..24630ae6 --- /dev/null +++ b/docker/setup/terraform/01_provider.tf @@ -0,0 +1,17 @@ +# Configure Azure storage to manage Terraform State +# ref: https://docs.microsoft.com/en-us/azure/developer/terraform/store-state-in-azure-storage +terraform { + backend "azurerm" { + resource_group_name = "tstate" + storage_account_name = "tstate31414" + container_name = "tstate" + key = "terraform.tfstate" + } +} + +# Configure the Microsoft Azure Provider +provider "azurerm" { + version = "=2.23.0" + + features {} +} diff --git a/docker/setup/terraform/02_data_rg.tf b/docker/setup/terraform/02_data_rg.tf new file mode 100644 index 00000000..c38bdf26 --- /dev/null +++ b/docker/setup/terraform/02_data_rg.tf @@ -0,0 +1,166 @@ +# ------------------------------ +# Data Resource Group +# ------------------------------ + +# Create a data resource group if it doesn't exist +resource "azurerm_resource_group" "data" { + name = "${var.RESOURCE_GROUP_NAME}data" + location = var.location + + tags = {} +} + +# Generate random text for a unique storage account name +resource "random_id" "randomId" { + keepers = { + # Generate a new ID only when a new resource group is defined + resource_group = azurerm_resource_group.vm.name + } + + byte_length = 3 +} + +# Create server table storage account if it doesn't exist +resource "azurerm_storage_account" "serverTablesName" { + name = "${var.serverTablesName}${random_id.randomId.hex}" + resource_group_name = azurerm_resource_group.data.name + location = azurerm_resource_group.data.location + account_kind = "Storage" + account_tier = "Standard" + account_replication_type = "GRS" +} + +# Create server blob storage account if it doesn't exist +resource "azurerm_storage_account" "serverBlobsName" { + name = "${var.serverBlobsName}${random_id.randomId.hex}" + resource_group_name = azurerm_resource_group.data.name + location = azurerm_resource_group.data.location + account_kind = "Storage" + account_tier = "Standard" + account_replication_type = "GRS" +} + +# Create client blob storage account if it doesn't exist +resource "azurerm_storage_account" "clientBlobsName" { + name = "${var.clientBlobsName}${random_id.randomId.hex}" + resource_group_name = azurerm_resource_group.data.name + location = azurerm_resource_group.data.location + account_kind = "Storage" + account_tier = "Standard" + account_replication_type = "GRS" +} + +resource "azurerm_storage_container" "clientsauth" { + name = "clientsauth" + storage_account_name = azurerm_storage_account.serverTablesName.name + container_access_type = "private" +} + +resource "azurerm_storage_container" "pendingemails" { + name = "pendingemails" + storage_account_name = azurerm_storage_account.serverTablesName.name + container_access_type = "private" +} + +resource "azurerm_storage_container" "users" { + name = "users" + storage_account_name = azurerm_storage_account.serverTablesName.name + container_access_type = "private" +} + +resource "azurerm_storage_container" "emails" { + name = "emails" + storage_account_name = azurerm_storage_account.serverBlobsName.name + container_access_type = "private" +} + +resource "azurerm_storage_container" "mailbox" { + name = "mailbox" + storage_account_name = azurerm_storage_account.serverBlobsName.name + container_access_type = "private" +} + +resource "azurerm_storage_container" "secretsopwencluster" { + name = "secretsopwencluster" + storage_account_name = azurerm_storage_account.serverBlobsName.name + container_access_type = "private" +} + +resource "azurerm_storage_container" "sendgridinboundemails" { + name = "sendgridinboundemails" + storage_account_name = azurerm_storage_account.serverBlobsName.name + container_access_type = "private" +} + +resource "azurerm_storage_container" "compressedpackages" { + name = "compressedpackages" + storage_account_name = azurerm_storage_account.clientBlobsName.name + container_access_type = "private" +} + +# Create a server queue if it doesn't exist +resource "azurerm_servicebus_namespace" "data" { + name = var.serverQueuesName + location = azurerm_resource_group.data.location + resource_group_name = azurerm_resource_group.data.name + sku = "Standard" + zone_redundant = false +} + +resource "azurerm_servicebus_namespace_authorization_rule" "data" { + name = var.serverQueuesSasName + namespace_name = azurerm_servicebus_namespace.data.name + resource_group_name = azurerm_resource_group.data.name + send = true + listen = true + manage = true +} + +resource "azurerm_servicebus_queue" "serverQueueSendgridMime" { + name = var.serverQueueSendgridMime + resource_group_name = azurerm_resource_group.data.name + namespace_name = azurerm_servicebus_namespace.data.name + max_size_in_megabytes = 512 +} + +resource "azurerm_servicebus_queue" "serverQueueEmailSend" { + name = var.serverQueueEmailSend + resource_group_name = azurerm_resource_group.data.name + namespace_name = azurerm_servicebus_namespace.data.name + max_size_in_megabytes = 5120 +} + +resource "azurerm_servicebus_queue" "serverQueueClientPackage" { + name = var.serverQueueClientPackage + resource_group_name = azurerm_resource_group.data.name + namespace_name = azurerm_servicebus_namespace.data.name + max_size_in_megabytes = 5120 +} + +resource "azurerm_servicebus_queue" "mailboxreceived" { + name = "mailboxreceived" + resource_group_name = azurerm_resource_group.data.name + namespace_name = azurerm_servicebus_namespace.data.name + max_size_in_megabytes = 1024 +} + +resource "azurerm_servicebus_queue" "mailboxsent" { + name = "mailboxsent" + resource_group_name = azurerm_resource_group.data.name + namespace_name = azurerm_servicebus_namespace.data.name + max_size_in_megabytes = 1024 +} + +resource "azurerm_servicebus_queue" "register" { + name = "register" + resource_group_name = azurerm_resource_group.data.name + namespace_name = azurerm_servicebus_namespace.data.name + max_size_in_megabytes = 1024 +} + +resource "azurerm_servicebus_queue" "service" { + name = "service" + resource_group_name = azurerm_resource_group.data.name + namespace_name = azurerm_servicebus_namespace.data.name + max_size_in_megabytes = 1024 +} diff --git a/docker/setup/terraform/03_server_rg.tf b/docker/setup/terraform/03_server_rg.tf new file mode 100644 index 00000000..f977c716 --- /dev/null +++ b/docker/setup/terraform/03_server_rg.tf @@ -0,0 +1,11 @@ +# ------------------------------ +# Server Resource Group +# ------------------------------ + +# Create the server resource group if it doesn't exist +resource "azurerm_resource_group" "server" { + name = "${var.RESOURCE_GROUP_NAME}server" + location = var.location + + tags = {} +} diff --git a/docker/setup/terraform/04_vm_rg.tf b/docker/setup/terraform/04_vm_rg.tf new file mode 100644 index 00000000..451e4f21 --- /dev/null +++ b/docker/setup/terraform/04_vm_rg.tf @@ -0,0 +1,176 @@ +# ------------------------------ +# Virtual Machine Resource Group +# ------------------------------ + +# Create a resource group if it doesn't exist +resource "azurerm_resource_group" "vm" { + name = var.vmName + location = var.location + + tags = {} +} + +# Create virtual network +resource "azurerm_virtual_network" "vm" { + name = "vm-vnet" + location = azurerm_resource_group.vm.location + resource_group_name = azurerm_resource_group.vm.name + address_space = ["10.0.0.0/16"] + + tags = {} +} + +# Create subnet +resource "azurerm_subnet" "vmsubnet" { + name = "vmsubnet" + resource_group_name = azurerm_resource_group.vm.name + virtual_network_name = azurerm_virtual_network.vm.name + address_prefix = "10.0.2.0/24" +} + +# Create public IPs +resource "azurerm_public_ip" "vm" { + name = "vm-ip" + location = azurerm_resource_group.vm.location + resource_group_name = azurerm_resource_group.vm.name + + allocation_method = "Dynamic" + sku = "Basic" + ip_version = "IPv4" + domain_name_label = "opwenvmtest" + idle_timeout_in_minutes = 4 + + tags = {} +} + +# Create Network Security Group and rule +resource "azurerm_network_security_group" "vm" { + name = "vm-nsg" + location = azurerm_resource_group.vm.location + resource_group_name = azurerm_resource_group.vm.name + + security_rule { + name = "SSH" + priority = 300 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "22" + source_address_prefix = "*" + destination_address_prefix = "*" + } + security_rule { + name = "HTTPS" + priority = 320 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "443" + source_address_prefix = "*" + destination_address_prefix = "*" + } + security_rule { + name = "HTTP" + priority = 340 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "80" + source_address_prefix = "*" + destination_address_prefix = "*" + } + + tags = {} +} + +# Create network interface +resource "azurerm_network_interface" "vm" { + name = "vm-nic" + location = azurerm_resource_group.vm.location + resource_group_name = azurerm_resource_group.vm.name + + ip_configuration { + name = "vm-nicConfiguration" + private_ip_address_allocation = "Dynamic" + public_ip_address_id = azurerm_public_ip.vm.id + subnet_id = azurerm_subnet.vmsubnet.id + primary = true + } + + tags = {} +} + +# Connect the security group to the network interface +resource "azurerm_network_interface_security_group_association" "vm" { + network_interface_id = azurerm_network_interface.vm.id + network_security_group_id = azurerm_network_security_group.vm.id +} + +# Generate random text for a unique storage account name +resource "random_id" "randomVmId" { + keepers = { + # Generate a new ID only when a new resource group is defined + resource_group = azurerm_resource_group.vm.name + } + + byte_length = 8 +} + +# Create storage account for boot diagnostics +resource "azurerm_storage_account" "vm" { + name = "diag${random_id.randomVmId.hex}" + location = azurerm_resource_group.vm.location + resource_group_name = azurerm_resource_group.vm.name + account_tier = "Standard" + account_replication_type = "LRS" + + tags = {} +} + +# Create (and display) an SSH key +resource "tls_private_key" "vm" { + algorithm = "RSA" + rsa_bits = 4096 +} +output "tls_private_key" { value = tls_private_key.vm.private_key_pem } + +# Create virtual machine +resource "azurerm_linux_virtual_machine" "vm" { + name = "vm" + location = azurerm_resource_group.vm.location + resource_group_name = azurerm_resource_group.vm.name + network_interface_ids = [azurerm_network_interface.vm.id] + size = "Standard_D4s_v3" + provision_vm_agent = true + + os_disk { + name = "vm-disk" + caching = "ReadWrite" + storage_account_type = "Premium_LRS" + } + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "18.04-LTS" + version = "latest" + } + + admin_username = var.RESOURCE_GROUP_NAME + disable_password_authentication = true + allow_extension_operations = true + + admin_ssh_key { + username = var.RESOURCE_GROUP_NAME + public_key = tls_private_key.vm.public_key_openssh + } + + boot_diagnostics { + storage_account_uri = azurerm_storage_account.vm.primary_blob_endpoint + } + + tags = {} +} diff --git a/docker/setup/terraform/05_test_rg.tf b/docker/setup/terraform/05_test_rg.tf new file mode 100644 index 00000000..6d2404f5 --- /dev/null +++ b/docker/setup/terraform/05_test_rg.tf @@ -0,0 +1,11 @@ +# ------------------------------ +# Test Resource Group +# ------------------------------ + +# Create a resource group if it doesn't exist +resource "azurerm_resource_group" "test" { + name = "${var.RESOURCE_GROUP_NAME}test" + location = var.location + + tags = {} +} diff --git a/docker/setup/terraform/06_dashboards_rg.tf b/docker/setup/terraform/06_dashboards_rg.tf new file mode 100644 index 00000000..7622b465 --- /dev/null +++ b/docker/setup/terraform/06_dashboards_rg.tf @@ -0,0 +1,11 @@ +# ------------------------------ +# Dashboards Resource Group +# ------------------------------ + +# Create a resource group if it doesn't exist +resource "azurerm_resource_group" "dashboards" { + name = "dashboards" + location = var.location + + tags = {} +} diff --git a/docker/setup/terraform/variables.tf b/docker/setup/terraform/variables.tf new file mode 100644 index 00000000..fb38a33b --- /dev/null +++ b/docker/setup/terraform/variables.tf @@ -0,0 +1,66 @@ +variable "RESOURCE_GROUP_NAME" { + type = string + default = "opwen" + description = "The prefix which should be used for all resources in this application." +} + +variable "location" { + type = string + default = "eastus" + description = "The Azure Region in which all resources in this example should be created." +} + +variable "vmName" { + type = string + default = "opwenvm" + description = "The name of Virtual Machine resource group." +} + +variable "appinsightsName" { + type = string + default = "opwenlogs" +} + +variable "clientBlobsName" { + type = string + default = "opwenclient" + description = "The name of the client Blobs storage account within the Data resource group." +} + +variable "serverBlobsName" { + type = string + default = "opwenserverblobs" + description = "The name of the server Blobs storage account within the Data resource group." +} + +variable "serverTablesName" { + type = string + default = "opwenservertables" + description = "The name of the server Tables storage account within the Data resource group." +} + +variable "serverQueuesName" { + type = string + default = "opwenserverqueues" + description = "The name of the server Queues Service Bus Namespace within the Data resource group." +} + +variable "serverQueuesSasName" { + type = string + default = "celery" +} + +variable "serverQueueSendgridMime" { + type = string + default = "inbound" +} + +variable "serverQueueClientPackage" { + type = string + default = "written" +} + +variable "serverQueueEmailSend" { + type = string + default = "send" +}