diff --git a/.gitignore b/.gitignore index 8d85a6e2..93af0987 100644 --- a/.gitignore +++ b/.gitignore @@ -367,3 +367,34 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd + +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log + +# Exclude all .tfvars files, which are likely to contain sentitive 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 + +# 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 tfplan files to ignore the plan output of command: terraform plan -out=tfplan +*tfplan* + +# Ignore CLI configuration files +.terraformrc +terraform.rc diff --git a/docs/IAC-Introduction.md b/docs/IAC-Introduction.md new file mode 100644 index 00000000..55a92c7f --- /dev/null +++ b/docs/IAC-Introduction.md @@ -0,0 +1,45 @@ +# Introduction +## Background +Traditionally, to manage a piece of infrastructure hosted in a cloud provider, we would have to log into an administrative portal/console, and manually provision that infrastructure resource. This is fine if there are not a lot of resources to manage. + +As the project gets more complex, there would be higher volatility in the infrastructure changes. For example, to scale up infrastructure for a peak day, sunsetting legacy infrastructures, etc. Managing infrastructure manually starts to get challenging as there are many changes to make. + +## What is IaC? +Infrastructure as Code (IaC) is the idea of using code to create configuration files for managing a software infrastructure. By specifying infrastructure requirements and dependencies as code, we can then version, automate and release the changes on the infrastructure. + +## What is Terraform? +Terraform is an open source Infrastructure as Code tool to automate the provisioning and management of Cloud infrastructures. The configuration files are written in the **HashiCorp Configuration Language(HCL)**. This language is written in `.tf` files. + +## Terraform Architechture +```mermaid +flowchart TB + subgraph Plugins + Providers + Provisioners + end + id1[Terraform Core] -- RPC --> Plugins + Plugins -- golang --> id2[Client Libraries] + id2 -- "HTTP(s)" --> id3[Upstream APIs] +``` +Terraform is mainly split into several parts: + +**Terraform Core:** This is the binary that communicates with Terraform plugins to manage the infrastructure. It is responsible for reading configuration files, building the dependency graph and communicating with plugins over RPC. + +**Terraform Plugins:** Plugins are executable binaries written in Go. Currently, there are 2 kinds of plugins: Providers and Provisioners. Providers expose implementations to services such as AWS, AzureRM. Provisioners run scripts during resource creation or destruction. + +**Client Libraries:** Client libraries make it easier to communicate with services from a supported language. While it is possible to call the services' API directly, client libraries simplify the code you need to call them + +## Terraform Workflow +The workflow for using Terraform consists of five stages: **Write, Initialisation, Plan, Apply/Destroy** + +**Writing Terraform configuration files** + +Writing the configuration files is the first step of using Terraform. The working directory should have at least include one `.tf` file written using HCL. + +Several common practices when writing the config files are: +1. Store your config files in version control and make small incremental changes to them as you write them. +1. Repeatedly run commands `terraform init` or `terraform plan` to check and fix the syntax errors. + +**Initialization** + +When `terraform init` command is executed, Terraform Core reads the configuration files in the working directory, downloads the plugins from several sources, and generates a lock file for subsequent `terraform init` executions to decide which plugin versions to be used. diff --git a/terraform/terraform-create-resource/cosmosdb.tf b/terraform/terraform-create-resource/cosmosdb.tf new file mode 100644 index 00000000..2d4bbd8a --- /dev/null +++ b/terraform/terraform-create-resource/cosmosdb.tf @@ -0,0 +1,40 @@ +resource "azurerm_cosmosdb_account" "storage" { + name = "cosmos-couplemgmt-${var.prefix}" + access_key_metadata_writes_enabled = true + analytical_storage_enabled = false + enable_automatic_failover = false + enable_multiple_write_locations = false + is_virtual_network_filter_enabled = false + kind = "GlobalDocumentDB" + local_authentication_disabled = false + location = var.azurerm_region + network_acl_bypass_for_azure_services = false + network_acl_bypass_ids = [] + offer_type = "Standard" + public_network_access_enabled = true + resource_group_name = azurerm_resource_group.app.name + # Only one free tier cosmosDB per subscription + # enable_free_tier = true + + capabilities { + name = "EnableServerless" + } + + backup { + interval_in_minutes = 240 + retention_in_hours = 8 + type = "Periodic" + } + + consistency_policy { + consistency_level = "ConsistentPrefix" + max_interval_in_seconds = 5 + max_staleness_prefix = 100 + } + + geo_location { + failover_priority = 0 + location = "southeastasia" + zone_redundant = false + } +} \ No newline at end of file diff --git a/terraform/terraform-create-resource/functionapp.tf b/terraform/terraform-create-resource/functionapp.tf new file mode 100644 index 00000000..6ceda8a9 --- /dev/null +++ b/terraform/terraform-create-resource/functionapp.tf @@ -0,0 +1,61 @@ +resource "azurerm_app_service_plan" "changeevent" { + name = "changeevent-service-plan" + location = var.azurerm_region + resource_group_name = azurerm_resource_group.app.name + kind = "FunctionApp" + + sku { + tier = "Dynamic" + size = "Y1" + } +} + +resource "azurerm_function_app" "changeevent" { + name = "func-changeevent.azurewebsites.net" + location = var.azurerm_region + resource_group_name = azurerm_resource_group.app.name + app_service_plan_id = azurerm_app_service_plan.change_event_asp.id + storage_account_name = azurerm_storage_account.storage.name + storage_account_access_key = azurerm_storage_account.storage.primary_access_key + + app_settings = { + "AccountEndpoint" = azurerm_cosmosdb_account.storage.endpoint + "AccountKey" = azurerm_cosmosdb_account.storage.primary_key + "DatabaseConnectionString" = azurerm_cosmosdb_account.storage.connection_strings[0] + "DatabaseName" = "database" + "ImagesConnectionString" = "DefaultEndpointsProtocol=https;AccountName=${azurerm_storage_account.storage.name};AccountKey=${azurerm_storage_account.storage.primary_access_key};EndpointSuffix=core.windows.net" + "FUNCTIONS_WORKER_RUNTIME" = "dotnet" + "WEBSITE_RUN_FROM_PACKAGE" = "1" + } +} + +resource "azurerm_app_service_plan" "couple_api" { + name = "coupleapi-service-plan" + location = var.azurerm_region + resource_group_name = azurerm_resource_group.app.name + kind = "FunctionApp" + + sku { + tier = "Dynamic" + size = "Y1" + } +} + +resource "azurerm_function_app" "coupleapi" { + name = "func-coupleapi.azurewebsites.net" + location = var.azurerm_region + resource_group_name = azurerm_resource_group.app.name + app_service_plan_id = azurerm_app_service_plan.couple_api_asp.id + storage_account_name = azurerm_storage_account.storage.name + storage_account_access_key = azurerm_storage_account.storage.primary_access_key + + app_settings = { + "AccountEndpoint" = azurerm_cosmosdb_account.storage.endpoint + "AccountKey" = azurerm_cosmosdb_account.storage.primary_key + "DatabaseConnectionString" = azurerm_cosmosdb_account.storage.connection_strings[0] + "DatabaseName" = "database" + "ImagesConnectionString" = "DefaultEndpointsProtocol=https;AccountName=${azurerm_storage_account.storage.name};AccountKey=${azurerm_storage_account.storage.primary_access_key};EndpointSuffix=core.windows.net" + "FUNCTIONS_WORKER_RUNTIME" = "dotnet" + "WEBSITE_RUN_FROM_PACKAGE" = "1" + } +} \ No newline at end of file diff --git a/terraform/terraform-create-resource/main.tf b/terraform/terraform-create-resource/main.tf new file mode 100644 index 00000000..dab30cf2 --- /dev/null +++ b/terraform/terraform-create-resource/main.tf @@ -0,0 +1,25 @@ +terraform { + required_version = ">=0.12" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~>2.91.0" + } + } + + backend "azurerm" { + resource_group_name = "rg-couplemgmt-tfstate" + storage_account_name = "stcouplemgmtstate" + container_name = "container-tfstate" + key = "terraform.tfstate" + } +} + +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "app" { + name = "rg-couplemgmt" + location = var.azurerm_region +} diff --git a/terraform/terraform-create-resource/storage.tf b/terraform/terraform-create-resource/storage.tf new file mode 100644 index 00000000..536da5b9 --- /dev/null +++ b/terraform/terraform-create-resource/storage.tf @@ -0,0 +1,14 @@ +resource "azurerm_storage_account" "images" { + name = "stcouplemgmtimages" + resource_group_name = azurerm_resource_group.app.name + location = var.azurerm_region + account_tier = "Standard" + account_replication_type = "LRS" + allow_blob_public_access = true +} + +resource "azurerm_storage_container" "images" { + name = "container-images" + storage_account_name = azurerm_storage_account.images.name + container_access_type = "private" +} diff --git a/terraform/terraform-create-resource/variables.tf b/terraform/terraform-create-resource/variables.tf new file mode 100644 index 00000000..4510441b --- /dev/null +++ b/terraform/terraform-create-resource/variables.tf @@ -0,0 +1,13 @@ +# Shortnames for regions can be found here: +# https://github.com/claranet/terraform-azurerm-regions/blob/master/REGIONS.md +variable "azurerm_region" { + description = "Standard Azure region in shortname format for resource naming purpose" + type = string + default = "southeastasia" +} + +variable "env" { + description = "Environment for resource names" + type = string + default = "test" +} \ No newline at end of file diff --git a/terraform/terraform-remote-state/main.tf b/terraform/terraform-remote-state/main.tf new file mode 100644 index 00000000..119ecdcf --- /dev/null +++ b/terraform/terraform-remote-state/main.tf @@ -0,0 +1,18 @@ +terraform { + required_version = ">=0.12" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~>2.91.0" + } + } +} + +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "tfstate" { + name = "rg-couplemgmt-tfstate" + location = var.azurerm_region +} diff --git a/terraform/terraform-remote-state/storage.tf b/terraform/terraform-remote-state/storage.tf new file mode 100644 index 00000000..31a4c338 --- /dev/null +++ b/terraform/terraform-remote-state/storage.tf @@ -0,0 +1,16 @@ +resource "azurerm_storage_account" "tfstate" { + name = "stcouplemgmtstate" + resource_group_name = azurerm_resource_group.tfstate.name + location = var.azurerm_region + account_tier = "Standard" + account_replication_type = "LRS" + account_kind = "StorageV2" + access_tier = "Hot" + allow_blob_public_access = true +} + +resource "azurerm_storage_container" "tfstate" { + name = "container-tfstate" + storage_account_name = azurerm_storage_account.tfstate.name + container_access_type = "blob" +} \ No newline at end of file diff --git a/terraform/terraform-remote-state/variables.tf b/terraform/terraform-remote-state/variables.tf new file mode 100644 index 00000000..f88a612f --- /dev/null +++ b/terraform/terraform-remote-state/variables.tf @@ -0,0 +1,7 @@ +# Shortnames for regions can be found here: +# https://github.com/claranet/terraform-azurerm-regions/blob/master/REGIONS.md +variable "azurerm_region" { + description = "Standard Azure region in shortname format for resource naming purpose" + type = string + default = "southeastasia" +} \ No newline at end of file