├── .github └── workflows │ └── zip-release.yml ├── .gitignore ├── LICENSE ├── README.md ├── Unifi-OracleCloud.zip ├── cloud-init └── unifi-init.yaml ├── images ├── attached-vnics.jpg ├── availability-domain.jpg ├── create-stack.jpg ├── destroy-stack.jpg ├── edit-ip.jpg └── stacks.jpg ├── main.tf ├── network-security-group.tf ├── network.tf ├── output.tf ├── storage.tf └── variables.tf /.github/workflows/zip-release.yml: -------------------------------------------------------------------------------- 1 | name: MasterDeployCI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v1 11 | - name: Zip Folder 12 | run: zip -r unifi-oraclecloud.zip . -x ".git/*" ".github/*" 13 | - name: Release to Github 14 | run: echo "Release" 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | terraform.tfstate 2 | terraform.tfstate.backup 3 | oci_api_key_public.pem 4 | oci_api_key.pem 5 | private-key.ppk 6 | public-key 7 | public-key.pub 8 | .terraform.lock.hcl 9 | connect.tf 10 | 11 | .terraform/ 12 | terraform.exe 13 | .terraform.tfstate.lock.info 14 | zip-files.ps1 15 | notes.md 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 MallocArray 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unifi Controller on Oracle Cloud Infrastructure for Free 2 | 3 | Oracle Cloud Free Tier offer includes Always Free resources, such as 2 VMs with 1 CPU and 1 GB RAM each or Ampere ARM based VMs equivalent to 4 CPU and 24 GB RAM per month, which is sufficient to run a Unifi Controller for a small installation with no charge and no expiration date 4 | 5 | [https://www.oracle.com/cloud/free/](https://www.oracle.com/cloud/free/) 6 | 7 | Oracle uses "stacks" that automates the provisioning of an environment using Terraform. Using only a single zip file, a Unifi Controller can be provisioned quickly with very little interaction. 8 | 9 | ## Breaking Changes 10 | 11 | * The project name now only allows alphanumeric characters and no longer supports - or _. This is due to enabling DNS on the VCN to allow for static IP assignment 12 | 13 | ## Configuration 14 | 15 | 1) Download the .zip file. 16 | 2) Register an account on Oracle Cloud. 17 | [https://myservices.us.oraclecloud.com/mycloud/signup](https://myservices.us.oraclecloud.com/mycloud/signup) 18 | * Recommended: Go to Menu>Compute>Instances>Create Instance. Click on "Edit" on the Placement panel and take note of the Availability domain number that is tagged with "Always Free Eligible". The process can then be cancelled. This number will be used later. (At this time, Ampere A1 can be created in any Availability Domain) ![Availability Domains](./images/availability-domain.jpg) 19 | 3) Navigate to Menu>Storage>Object Storage & Archive>Buckets>Create Bucket. Give the bucket a name, such as "unifibackup". This will be used to store backup files outside of the VM Instance. Optional: Take note of the "Namespace: " value after creating the bucket as it will be used if mounting the bucket for autobackups. 20 | 4) Navigate to the Menu>Developer Services>Resource Manager>Stacks 21 | 5) Click "Create Stack" 22 | ![alt text](./images/stacks.jpg) 23 | 6) Leave the default option of "My Configuration". Change the "Terraform configuration source" to ".Zip file", then drag or Browse the zip file to the Stack Configuration section. Provide a name for the stack if desired or keep the auto-generated name. Leave the default Terraform version as 1.2.x then click Next ![alt text](./images/create-stack.jpg) 24 | 7) Review the variables and modify if needed. Click Next, then Create 25 | * Enter/Verify the Availability Domain number in the list of variables. 26 | * Enter the name of the storage bucket created earlier 27 | * Defaults to an Ampere ARM based instance, or change to VM.Standard.E2.1.Micro for an Intel based instance with lower CPU/Memory allowance for Free Tier 28 | 8) On the final Create page, check the box to "Run Apply" 29 | * Alternatively, in the list of Stacks, click on the name of the newly created Stack. Click on **Apply** followed by Apply. 30 | 9) In a few minutes, the Stacks job will complete and show the public IP address and URL to access the controller. It may take 15 minutes or more to complete the installation of the Unifi software. 31 | * If the process encounters an error stating "shape VM.Standard.E2.1.Micro not found", verify the Availability Domain that is Always Free Eligible for your region, or try a different number 1-3. 32 | * If the process encounters an error stating "Out of host capacity", your Region does not currently have available resources for Always Free instances. In the Oracle Forums regarding this error, they recommend trying again later as capacity is always being added. You can also upgrade to "Pay As You Go" (PAYG) which makes the Ampere instances much more available, and as long as you stay under the Free Tier limits, continues to be free. 33 | [Managing Account Upgrades and Payment Method](https://docs.oracle.com/en-us/iaas/Content/Billing/Tasks/changingpaymentmethod.htm) 34 | 10) Open the URL to the controller web interface and configure or restore a backup file. If using a DNS name, update the entry to reflect the new IP address. 35 | 11) Setup the controller or restore from a backup file 36 | 37 | **Note**: When navigating around the Oracle interface, make sure to change the Compartment option on the left side to "unificontroller" or your Compartment name to view the newly created objects. To view the Stacks, change the Compartment back to root 38 | 39 | ## Information 40 | 41 | The zip file contains one or more .TF files with Terraform instructions. These configure the following: 42 | 43 | * Container for all of the newly created objects 44 | * Virtual Cloud Network 45 | * Subnet 46 | * Route Table 47 | * Internet Gateway 48 | * Network Security Groups with required ports for Unifi Controller 49 | * Computer Instance sized for Always Free running Ubuntu 18.04 with public IP address 50 | * iptables firewall rules added and saved for future reboots 51 | * Packages updated on first boot and Unifi Controller installed using [GlennR's Installation Script](https://community.ui.com/questions/UniFi-Installation-Scripts-or-UniFi-Easy-Update-Script-or-Ubuntu-16-04-18-04-18-10-19-04-and-19-10-/ccbc7530-dd61-40a7-82ec-22b17f027776) 52 | 53 | ## SSH Access to Instance 54 | 55 | To enable SSH to the Instance, follow the Oracle guide on [Managing Key Pair on Linux Instances](https://docs.cloud.oracle.com/iaas/Content/Compute/Tasks/managingkeypairs.htm?Highlight=ssh) 56 | 57 | Paste the public key into the "ssh_public_key" variable when Applying the stack or modify and existing Instance under Resources>Console Connections 58 | 59 | Additional information can be found on the Oracle Support Page under [Instance Console Connections](https://docs.cloud.oracle.com/iaas/Content/Compute/References/serialconsole.htm) 60 | 61 | ## Autobackup to storage Bucket 62 | 63 | One method for retaining backup files is to mount the Oracle storage Bucket as the Unifi autobackup folder, so files are automatically copied to the Bucket, and upon recreation of the instance, the autobackup data is readily available. Additional steps are needed ahead of time to support this: 64 | 65 | ### Creating a Customer Secret Key 66 | 67 | To mount a bucket, a Customer Secret Key is needed. To create one, in the Oracle OCI interface, click on the Profile icon in the upper right and select User Settings. In the lower left under "Resources" click "Customer Secret Keys", then click "Generate Secret Key". Give the key a name of your choosing and click "Generate Secret Key". Copy the Generated Key and store it somewhere, as it will be added to the Stack Variables as "Customer Secret Key". Close the popup window and also copy the "Access Key" value and store it somewhere as it will be added to the Stack Variables as "Customer Access Key". Navigate to the Stack and edit the Variables to add these values and then Apply the Stack to recreate the instance. If successful, the Unifi autobackup folder will now be the Bucket and files added to the folder are automatically stored in the Bucket outside of the Instance. 68 | 69 | ## Static IP Reservation 70 | 71 | A static IP address can be reserved to keep the same address even if the original instance is deleted or recreated. This is not done automatically by the Terraform file, but can configured after creation 72 | 73 | 1) Navigate to Menu>Compute>Instances and select the Unifi controller instance name. (Ensure Compartment on the left side is changed to "unificontroller") 74 | 2) Scroll down to "Attached VNICs" under Resources on the left side and click on the Primary VNIC name ![alt text](./images/attached-vnics.jpg) 75 | 3) Scroll down to "IPv4 Addresses" under Resources on the left side and click the "..." icon to the far right and select Edit ![alt text](./images/edit-ip.jpg) 76 | 4) Change **Public IP Type** to "No Public IP" and click Update. Then click Edit again and select **Reserved Public IP** and "Create a New Reserved Public IP" or select a previously created entry. Click Update. 77 | 78 | ## Deleting or re-creating an instance 79 | 80 | Instances created using Stacks can easily be destroyed to remove all associated items and optionally recreate them 81 | 82 | 1) Navigate to Menu>Resource Manager>Stacks and select the previously used Stack name 83 | 2) Select **Destroy**. Confirm by clicking Destroy again. ![alt text](./images/destroy-stack.jpg) 84 | 85 | Once completed, return to Stacks to use the Apply option to create a new instance with the original configuration. It is not necessary to **Delete Stack** unless the stack configuration is changing. 86 | 87 | **Note** If a Reserved IP address as assigned to the Instance, it will need to be removed from the VM prior to Destroying the stack. Since it was not created as part of the Stack, it will not be removed when Destroying the stack. 88 | 89 | ## Future To-Do List 90 | 91 | * Incorporate some benefits of [GCP Unifi Controller Startup Script](https://metis.fi/en/2018/02/gcp-unifi-code/) created by PetriR to work with Ubuntu as an option. Need to determine how much is applicable. 92 | 93 | * Fail2Ban 94 | * MongoDB Fix 95 | * Lighttpd 96 | -------------------------------------------------------------------------------- /Unifi-OracleCloud.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MallocArray/Unifi-OracleCloud/b0cab6f8f4fbac12fd6b5e4e0239e56fbe1ec98d/Unifi-OracleCloud.zip -------------------------------------------------------------------------------- /cloud-init/unifi-init.yaml: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | package_update: true 3 | package_upgrade: true 4 | package_reboot_if_required: true 5 | packages: 6 | - jq 7 | - ca-certificates 8 | - wget 9 | - s3fs 10 | 11 | runcmd: 12 | # Set timezone if defined 13 | - | 14 | if (cloud-init query ds.metadata.timezone) 15 | then 16 | timedatectl set-timezone $(cloud-init query ds.metadata.timezone) 17 | fi 18 | # Dynamic DNS Update using URL from variables stored in metadata 19 | - curl $(cloud-init query ds.metadata.ddns_url) 20 | # Install latest Unifi controller 21 | - /usr/lib/unifi/unifi-install.sh 22 | # Mount bucket as autobackup folder for storage or setup script to copy backups to bucket if configured 23 | - | 24 | if [ $(cloud-init query ds.metadata.bucket_name) ] 25 | then 26 | # If options for secret key are configured, create credential file and mount the autobackup folder using storage bucket 27 | if [ $(cloud-init query ds.metadata.customer_secret_key) ] 28 | then 29 | echo $(cloud-init query ds.metadata.customer_access_key):$(cloud-init query ds.metadata.customer_secret_key) > /home/ubuntu/.passwd-s3fs 30 | chmod 600 /home/ubuntu/.passwd-s3fs 31 | bash /usr/lib/unifi/mount-bucket.sh 32 | # Add to fstab so it is mounted on each reboot 33 | echo "s3fs#$(cloud-init query ds.metadata.bucket_name) /var/lib/unifi/backup/autobackup fuse _netdev,allow_other,nomultipart,use_path_request_style,passwd_file=/home/ubuntu/.passwd-s3fs,url=https://$(cloud-init query ds.metadata.bucket_namespace).compat.objectstorage.$(cloud-init query ds.metadata.region).oraclecloud.com/ 0 0" | tee -a /etc/fstab 34 | fi 35 | else 36 | # Add cron entry for daily copy of most recent backup to bucket 37 | crontab -l | { cat; echo "0 1 * * * root /usr/lib/unifi/unifi-backup.sh"; } | crontab - 38 | fi 39 | 40 | write_files: 41 | - content: | 42 | iptables -I INPUT 2 -p udp --dport 1900 -j ACCEPT 43 | iptables -I INPUT 2 -p udp --dport 10001 -j ACCEPT 44 | iptables -I INPUT 2 -p udp --destination-port "5656:5699" -j ACCEPT 45 | iptables -I INPUT 2 -p tcp --dport 27117 -j ACCEPT 46 | iptables -I INPUT 2 -p tcp --dport 6789 -j ACCEPT 47 | iptables -I INPUT 2 -p tcp --dport 8843 -j ACCEPT 48 | iptables -I INPUT 2 -p tcp --dport 8880 -j ACCEPT 49 | iptables -I INPUT 2 -p tcp --dport 8443 -j ACCEPT 50 | iptables -I INPUT 2 -p tcp --dport 443 -j ACCEPT 51 | iptables -I INPUT 2 -p tcp --dport 8080 -j ACCEPT 52 | iptables -I INPUT 2 -p udp --dport 3478 -j ACCEPT 53 | iptables -I INPUT 2 -p udp --dport 5514 -j ACCEPT 54 | iptables -I INPUT 2 -p udp --dport 123 -j ACCEPT 55 | iptables -I INPUT 2 -p tcp --dport 80 -j ACCEPT 56 | iptables-save > /etc/iptables/rules.v4 57 | 58 | # https://help.ui.com/hc/en-us/articles/218506997-UniFi-Ports-Used 59 | # If using ufw, the install script does not configure Lets Encrypt properly as it only sees the internal IP 60 | # ufw allow ssh # Remote access 61 | # ufw allow 53/tcp # Port used for DNS. This is required for Guest Portal redirection, downloading updates, and remote access 62 | # ufw allow 53/udp # Port used for DNS. This is required for Guest Portal redirection, downloading updates, and remote access 63 | # ufw allow 80/tcp # Port for verifying SSL certificate with Lets Encrypt 64 | # ufw allow 3478/udp # Port used for STUN 65 | # ufw allow 5514/udp # Port used for remote syslog capture 66 | # ufw allow 8080/tcp # Port used for device and application communication. 67 | # ufw allow 443/tcp # Port used for application GUI/API as seen in a web browser 68 | # ufw allow 8443/tcp # Port used for application GUI/API as seen in a web browser. 69 | # ufw allow 8880/tcp # Port used for HTTP portal redirection 70 | # ufw allow 8843/tcp # Port used for HTTPS portal redirection 71 | # ufw allow 6789/tcp # Port used for UniFi mobile speed test 72 | # ufw allow 27117/tcp # Port used for local-bound database communication 73 | # ufw allow 5656:5699/udp # Ports used by AP-EDU broadcasting 74 | # ufw allow 10001/udp # Port used for device discovery 75 | # ufw allow 1900/udp # Port used for "Make application discoverable on L2 network" in the UniFi Network settings 76 | # ufw allow 123/udp # Port used for NTP (date/time). Required for establishing secure communication with remote access servers 77 | # # Enable UFW so script can open firewall ports automatically 78 | # ufw --force enable 79 | 80 | # Create default parameter set for the Unifi script to run automatically 81 | PARAMETERS="--skip --add-repository" 82 | 83 | # If DNS name was provided, use it when setting up Unifi 84 | if [ $(cloud-init query ds.metadata.dns_name) ] 85 | then 86 | PARAMETERS="$PARAMETERS --fqdn $(cloud-init query ds.metadata.dns_name) --retry 5" 87 | fi 88 | 89 | if [ $(cloud-init query ds.metadata.email) ] 90 | then 91 | PARAMETERS="$PARAMETERS --email $(cloud-init query ds.metadata.email)" 92 | fi 93 | 94 | #Running GlennR's install script which also installs prerequisites 95 | # https://community.ui.com/questions/UniFi-Installation-Scripts-or-UniFi-Easy-Update-Script-or-UniFi-Lets-Encrypt-or-UniFi-Easy-Encrypt-/ccbc7530-dd61-40a7-82ec-22b17f027776 96 | rm unifi-latest.sh &> /dev/null 97 | wget 'https://get.glennr.nl/unifi/install/install_latest/unifi-latest.sh' 98 | bash unifi-latest.sh $PARAMETERS 99 | path: /usr/lib/unifi/unifi-install.sh 100 | permissions: '0744' 101 | 102 | - content: | 103 | #!/bin/bash 104 | curl -T $(/usr/bin/find /var/lib/unifi/backup -iname '*.unf') $(cloud-init query ds.metadata.bucket_url) 105 | path: /usr/lib/unifi/unifi-backup.sh 106 | permissions: '0744' 107 | 108 | 109 | - content: | 110 | # Renews certificates for Unifi applications 111 | # Create default parameter set for the Unifi script to run automatically 112 | PARAMETERS=" --skip" 113 | 114 | # If DNS name was provided, use it when setting up Unifi 115 | if [ $(cloud-init query ds.metadata.dns_name) ] 116 | then 117 | PARAMETERS="$PARAMETERS --fqdn $(cloud-init query ds.metadata.dns_name) --retry 5" 118 | fi 119 | 120 | if [ $(cloud-init query ds.metadata.email) ] 121 | then 122 | PARAMETERS="$PARAMETERS --email $(cloud-init query ds.metadata.email)" 123 | fi 124 | 125 | rm unifi-easy-encrypt.sh &> /dev/null 126 | wget https://get.glennr.nl/unifi/extra/unifi-easy-encrypt.sh 127 | echo "Preparing to run 'bash unifi-easy-encrypt.sh $PARAMETERS'" 128 | bash unifi-easy-encrypt.sh $PARAMETERS 129 | path: /usr/lib/unifi/unifi-encrypt.sh 130 | permissions: '0744' 131 | 132 | # Update Lets Encrypt certificates at 1:00 am on the first day of the month, every other month 133 | - content: | 134 | 0 1 1 1,2,4,6,8,10 * root /usr/lib/unifi/unifi-encrypt.sh 135 | path: /etc/crontab 136 | append: true 137 | 138 | # Update Controller at 2:00 am every Monday 139 | - content: | 140 | 0 2 * * 1 root apt update && apt upgrade -y 141 | path: /etc/crontab 142 | append: true 143 | 144 | # Mounts a bucket as part of the file system for Unifi backups 145 | - content: | 146 | # https://blogs.oracle.com/cloud-infrastructure/post/mounting-an-object-storage-bucket-as-file-system-on-oracle-linux 147 | s3fs $(cloud-init query ds.metadata.bucket_name) /var/lib/unifi/backup/autobackup -o passwd_file=/home/ubuntu/.passwd-s3fs -o url=https://$(cloud-init query ds.metadata.bucket_namespace).compat.objectstorage.$(cloud-init query ds.metadata.region).oraclecloud.com/ -o nomultipart -o use_path_request_style -o nonempty 148 | path: /usr/lib/unifi/mount-bucket.sh 149 | 150 | # Perform a reboot once cloud-init has completed. 151 | power_state: 152 | mode: reboot 153 | 154 | # Reference Links 155 | # https://cloudinit.readthedocs.io/en/latest/topics/modules.html 156 | # https://docs.microsoft.com/en-us/azure/virtual-machines/linux/using-cloud-init 157 | # https://community.ui.com/questions/UniFi-Installation-Scripts-or-UniFi-Easy-Update-Script-or-Ubuntu-16-04-18-04-18-10-19-04-and-19-10-/ccbc7530-dd61-40a7-82ec-22b17f027776 158 | # https://help.ubnt.com/hc/en-us/articles/218506997-UniFi-Ports-Used 159 | -------------------------------------------------------------------------------- /images/attached-vnics.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MallocArray/Unifi-OracleCloud/b0cab6f8f4fbac12fd6b5e4e0239e56fbe1ec98d/images/attached-vnics.jpg -------------------------------------------------------------------------------- /images/availability-domain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MallocArray/Unifi-OracleCloud/b0cab6f8f4fbac12fd6b5e4e0239e56fbe1ec98d/images/availability-domain.jpg -------------------------------------------------------------------------------- /images/create-stack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MallocArray/Unifi-OracleCloud/b0cab6f8f4fbac12fd6b5e4e0239e56fbe1ec98d/images/create-stack.jpg -------------------------------------------------------------------------------- /images/destroy-stack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MallocArray/Unifi-OracleCloud/b0cab6f8f4fbac12fd6b5e4e0239e56fbe1ec98d/images/destroy-stack.jpg -------------------------------------------------------------------------------- /images/edit-ip.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MallocArray/Unifi-OracleCloud/b0cab6f8f4fbac12fd6b5e4e0239e56fbe1ec98d/images/edit-ip.jpg -------------------------------------------------------------------------------- /images/stacks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MallocArray/Unifi-OracleCloud/b0cab6f8f4fbac12fd6b5e4e0239e56fbe1ec98d/images/stacks.jpg -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | data "template_file" "unifi-init" { 2 | template = "${file("./cloud-init/unifi-init.yaml")}" 3 | } 4 | 5 | resource "oci_identity_compartment" "unificontroller_compartment" { 6 | compartment_id = "${var.compartment_ocid}" 7 | description = "Unifi Controller Compartment" 8 | name = "${var.project_name}" 9 | } 10 | 11 | resource "oci_core_instance" "unificontroller_instance" { 12 | availability_domain = "${data.oci_identity_availability_domain.unificontroller-ad.name}" 13 | compartment_id = "${oci_identity_compartment.unificontroller_compartment.id}" 14 | shape = "${var.instance_shape}" 15 | display_name = "${var.project_name}-${random_id.unificontroller_id.dec}" 16 | shape_config { 17 | #Optional 18 | # baseline_ocpu_utilization = var.instance_shape_config_baseline_ocpu_utilization 19 | memory_in_gbs = var.instance_shape_config_memory_in_gbs 20 | ocpus = var.instance_shape_config_ocpus 21 | } 22 | 23 | create_vnic_details { 24 | subnet_id = "${oci_core_subnet.unificontrollerSubnet.id}" 25 | nsg_ids = ["${oci_core_network_security_group.unificontroller_network_security_group.id}"] 26 | } 27 | 28 | source_details { 29 | source_type = "image" 30 | source_id = "${lookup(data.oci_core_images.supported_shape_images.images[0], "id")}" 31 | } 32 | 33 | metadata = { 34 | ssh_authorized_keys = "${var.ssh_public_key}" 35 | user_data = "${base64encode(data.template_file.unifi-init.rendered)}" 36 | ddns_url = "${var.ddns_url}" 37 | email = "${var.email}" 38 | timezone = "${var.timezone}" 39 | dns_name = "${var.dns_name}" 40 | bucket_url = "https://objectstorage.${var.region}.oraclecloud.com${oci_objectstorage_preauthrequest.unifi_backup_preauthenticated_request.access_uri}" 41 | bucket_name = "${var.bucket_name}" 42 | bucket_namespace = "${var.bucket_namespace}" 43 | customer_secret_key = "${var.customer_secret_key}" 44 | customer_access_key = "${var.customer_access_key}" 45 | region = "${var.region}" 46 | } 47 | 48 | } 49 | 50 | 51 | data "oci_identity_availability_domain" "unificontroller-ad" { 52 | #Required 53 | compartment_id = "${var.compartment_ocid}" 54 | #Optional 55 | ad_number = "${var.availability_domain}" 56 | } 57 | 58 | 59 | # Gets a list of images within a tenancy with the specified criteria 60 | data "oci_core_images" "supported_shape_images" { 61 | compartment_id = "${var.compartment_ocid}" 62 | 63 | # Uncomment below to filter images that support a specific instance shape 64 | shape = "${var.instance_shape}" 65 | operating_system = "${var.operating_system}" 66 | operating_system_version = "${var.operating_system_version}" 67 | sort_by = "TIMECREATED" 68 | # Default sort order for TIMECREATED is descending (DESC) 69 | #sort_order = "ASC" 70 | 71 | state = "AVAILABLE" 72 | 73 | # Uncomment below to sort images by display name, display name sort order is case-sensitive 74 | #sort_by = "DISPLAYNAME" 75 | # Default sort order for DISPLAYNAME is ascending (ASC) 76 | #sort_order = "DESC" 77 | } 78 | 79 | # Hints to getting the list of available images to always get the most recent 80 | # https://github.com/terraform-providers/terraform-provider-oci/blob/master/examples/compute/image/image.tf 81 | 82 | 83 | # https://www.exitas.be/blog/assigning-reserved-public-ips-to-guests-with-oracle-cloud-and-terraform/ 84 | # Great guide to creating and assigning a Reserved public IP but destroys it when destroying everything else 85 | 86 | #data "oci_core_private_ips" "unificontroller_private_ips" { 87 | # ip_address = oci_core_instance.unificontroller-instance.private_ip 88 | # subnet_id = oci_core_subnet.unificontrollerSubnet.id 89 | #} 90 | 91 | #resource "oci_core_public_ip" "unificontroller_public_ip" { 92 | # compartment_id = "${oci_identity_compartment.unificontroller_compartment.id}" 93 | # display_name = "Unifi Controller Public IP" 94 | # lifetime = "RESERVED" 95 | # private_ip_id = data.oci_core_private_ips.unificontroller_private_ips.private_ips[0]["id"] 96 | #} 97 | -------------------------------------------------------------------------------- /network-security-group.tf: -------------------------------------------------------------------------------- 1 | # https://help.ubnt.com/hc/en-us/articles/218506997-UniFi-Ports-Used 2 | 3 | resource "oci_core_network_security_group" "unificontroller_network_security_group" { 4 | compartment_id = "${oci_identity_compartment.unificontroller_compartment.id}" 5 | vcn_id = "${oci_core_vcn.unificontrollerVCN.id}" 6 | display_name = "Unifi Controller Required Ports" 7 | } 8 | 9 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_8080" { 10 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 11 | direction = "INGRESS" 12 | protocol = "6" 13 | description = "Port used for device and application communication" 14 | source = "0.0.0.0/0" 15 | source_type = "CIDR_BLOCK" 16 | tcp_options { 17 | destination_port_range { 18 | max = "8080" 19 | min = "8080" 20 | } 21 | } 22 | } 23 | 24 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_8443" { 25 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 26 | direction = "INGRESS" 27 | protocol = "6" 28 | description = "Port used for controller GUI/API as seen in a web browser" 29 | source = "0.0.0.0/0" 30 | source_type = "CIDR_BLOCK" 31 | tcp_options { 32 | destination_port_range { 33 | max = "8443" 34 | min = "8443" 35 | } 36 | } 37 | } 38 | 39 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_8880" { 40 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 41 | direction = "INGRESS" 42 | protocol = "6" 43 | description = "Port used for HTTP portal redirection" 44 | source = "0.0.0.0/0" 45 | source_type = "CIDR_BLOCK" 46 | tcp_options { 47 | destination_port_range { 48 | max = "8880" 49 | min = "8880" 50 | } 51 | } 52 | } 53 | 54 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_8843" { 55 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 56 | direction = "INGRESS" 57 | protocol = "6" 58 | description = "Port used for HTTPS portal redirection" 59 | source = "0.0.0.0/0" 60 | source_type = "CIDR_BLOCK" 61 | tcp_options { 62 | destination_port_range { 63 | max = "8843" 64 | min = "8843" 65 | } 66 | } 67 | } 68 | 69 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_6789" { 70 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 71 | direction = "INGRESS" 72 | protocol = "6" 73 | description = "Port used for UniFi mobile speed test" 74 | source = "0.0.0.0/0" 75 | source_type = "CIDR_BLOCK" 76 | tcp_options { 77 | destination_port_range { 78 | max = "6789" 79 | min = "6789" 80 | } 81 | } 82 | } 83 | 84 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_27117" { 85 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 86 | direction = "INGRESS" 87 | protocol = "6" 88 | description = "Port used for local-bound database communication" 89 | source = "0.0.0.0/0" 90 | source_type = "CIDR_BLOCK" 91 | tcp_options { 92 | destination_port_range { 93 | max = "27117" 94 | min = "27117" 95 | } 96 | } 97 | } 98 | 99 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_80" { 100 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 101 | direction = "INGRESS" 102 | protocol = "6" 103 | description = "Port used HTTP portal redirection https://help.ubnt.com/hc/en-us/articles/218506997-UniFi-Ports-Used" 104 | source = "0.0.0.0/0" 105 | source_type = "CIDR_BLOCK" 106 | tcp_options { 107 | destination_port_range { 108 | max = "80" 109 | min = "80" 110 | } 111 | } 112 | } 113 | 114 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_3478" { 115 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 116 | direction = "INGRESS" 117 | protocol = "17" 118 | description = "Port used for STUN" 119 | source = "0.0.0.0/0" 120 | source_type = "CIDR_BLOCK" 121 | udp_options { 122 | destination_port_range { 123 | max = "3478" 124 | min = "3478" 125 | } 126 | } 127 | } 128 | 129 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_5514" { 130 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 131 | direction = "INGRESS" 132 | protocol = "17" 133 | description = "Port used for remote syslog capture" 134 | source = "0.0.0.0/0" 135 | source_type = "CIDR_BLOCK" 136 | udp_options { 137 | destination_port_range { 138 | max = "5514" 139 | min = "5514" 140 | } 141 | } 142 | } 143 | 144 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_5656" { 145 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 146 | direction = "INGRESS" 147 | protocol = "17" 148 | description = "Ports used by AP-EDU broadcasting" 149 | source = "0.0.0.0/0" 150 | source_type = "CIDR_BLOCK" 151 | udp_options { 152 | destination_port_range { 153 | max = "5699" 154 | min = "5656" 155 | } 156 | } 157 | } 158 | 159 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_10001" { 160 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 161 | direction = "INGRESS" 162 | protocol = "17" 163 | description = "Port used for device discovery" 164 | source = "0.0.0.0/0" 165 | source_type = "CIDR_BLOCK" 166 | udp_options { 167 | destination_port_range { 168 | max = "10001" 169 | min = "10001" 170 | } 171 | } 172 | } 173 | 174 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_1900" { 175 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 176 | direction = "INGRESS" 177 | protocol = "17" 178 | description = "Port used for 'Make controller discoverable on L2 network' in controller settings" 179 | source = "0.0.0.0/0" 180 | source_type = "CIDR_BLOCK" 181 | udp_options { 182 | destination_port_range { 183 | max = "1900" 184 | min = "1900" 185 | } 186 | } 187 | } 188 | 189 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_123" { 190 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 191 | direction = "INGRESS" 192 | protocol = "17" 193 | description = "Port used for NTP (date/time). Required for establishing secure communication with remote access servers." 194 | source = "0.0.0.0/0" 195 | source_type = "CIDR_BLOCK" 196 | udp_options { 197 | destination_port_range { 198 | max = "123" 199 | min = "123" 200 | } 201 | } 202 | } 203 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_3478_egress" { 204 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 205 | direction = "EGRESS" 206 | protocol = "17" 207 | description = "Port used STUN" 208 | destination = "0.0.0.0/0" 209 | udp_options { 210 | source_port_range { 211 | max = "3478" 212 | min = "3478" 213 | } 214 | } 215 | } 216 | 217 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_443_udp_egress" { 218 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 219 | direction = "EGRESS" 220 | protocol = "17" 221 | description = "Port used for Remote Access service" 222 | destination = "0.0.0.0/0" 223 | udp_options { 224 | source_port_range { 225 | max = "443" 226 | min = "443" 227 | } 228 | } 229 | } 230 | 231 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_443_tcp_egress" { 232 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 233 | direction = "EGRESS" 234 | protocol = "6" 235 | description = "Port used for Remote Access service" 236 | destination = "0.0.0.0/0" 237 | tcp_options { 238 | source_port_range { 239 | max = "443" 240 | min = "443" 241 | } 242 | } 243 | } 244 | 245 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_8883_tcp_egress" { 246 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 247 | direction = "EGRESS" 248 | protocol = "6" 249 | description = "Port used for Remote Access service" 250 | destination = "0.0.0.0/0" 251 | tcp_options { 252 | source_port_range { 253 | max = "8883" 254 | min = "8883" 255 | } 256 | } 257 | } 258 | 259 | resource "oci_core_network_security_group_security_rule" "unificontroller_network_security_group_security_rule_123_egress" { 260 | network_security_group_id = "${oci_core_network_security_group.unificontroller_network_security_group.id}" 261 | direction = "EGRESS" 262 | protocol = "17" 263 | description = "Port used for NTP (date/time). Required for establishing secure communication with remote access servers." 264 | destination = "0.0.0.0/0" 265 | udp_options { 266 | source_port_range { 267 | max = "123" 268 | min = "123" 269 | } 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /network.tf: -------------------------------------------------------------------------------- 1 | resource "oci_core_vcn" "unificontrollerVCN" { 2 | cidr_block = "10.0.0.0/16" 3 | compartment_id = "${oci_identity_compartment.unificontroller_compartment.id}" 4 | display_name = "${var.project_name}-${random_id.unificontroller_id.dec}" 5 | dns_label = "${var.project_name}" 6 | is_ipv6enabled = "false" 7 | } 8 | 9 | resource "oci_core_internet_gateway" "unificontrollerIG" { 10 | compartment_id = "${oci_identity_compartment.unificontroller_compartment.id}" 11 | display_name = "${var.project_name}-IG-${random_id.unificontroller_id.dec}" 12 | vcn_id = "${oci_core_vcn.unificontrollerVCN.id}" 13 | } 14 | 15 | resource "oci_core_route_table" "unificontrollerRT" { 16 | compartment_id = "${oci_identity_compartment.unificontroller_compartment.id}" 17 | vcn_id = "${oci_core_vcn.unificontrollerVCN.id}" 18 | display_name = "${var.project_name}-RT-${random_id.unificontroller_id.dec}" 19 | route_rules { 20 | destination = "0.0.0.0/0" 21 | destination_type = "CIDR_BLOCK" 22 | network_entity_id = "${oci_core_internet_gateway.unificontrollerIG.id}" 23 | } 24 | } 25 | 26 | resource "oci_core_subnet" "unificontrollerSubnet" { 27 | cidr_block = "10.0.100.0/24" 28 | compartment_id = "${oci_identity_compartment.unificontroller_compartment.id}" 29 | vcn_id = "${oci_core_vcn.unificontrollerVCN.id}" 30 | display_name = "${var.project_name}-${random_id.unificontroller_id.dec}" 31 | route_table_id = "${oci_core_route_table.unificontrollerRT.id}" 32 | } 33 | -------------------------------------------------------------------------------- /output.tf: -------------------------------------------------------------------------------- 1 | output "instance_public_ip" { 2 | value = "${oci_core_instance.unificontroller_instance.public_ip}" 3 | } 4 | 5 | output "controller_public_url" { 6 | value = "${format("https://%s:8443", oci_core_instance.unificontroller_instance.public_ip)}" 7 | } 8 | 9 | output "comments" { 10 | value = "The controller should become available in aproximately 15-20 minutes, once updates and installation have completed" 11 | } 12 | -------------------------------------------------------------------------------- /storage.tf: -------------------------------------------------------------------------------- 1 | data "oci_objectstorage_namespace" "objectstorage_namespace" { 2 | #Optional 3 | compartment_id = "${var.compartment_ocid}" 4 | } 5 | 6 | resource "oci_objectstorage_preauthrequest" "unifi_backup_preauthenticated_request" { 7 | #Required 8 | access_type = "AnyObjectReadWrite" 9 | bucket = "${var.bucket_name}" 10 | name = "Unifi_Backup" 11 | namespace = "${data.oci_objectstorage_namespace.objectstorage_namespace.namespace}" 12 | time_expires = "2050-12-31T00:00:00Z" 13 | 14 | } 15 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "availability_domain" { 2 | default = 1 3 | description = "If errors about 'shape not found', try 2 or 3. See README for more information" 4 | } 5 | 6 | variable "ssh_public_key" { 7 | default = "" 8 | } 9 | 10 | variable "bucket_name" { 11 | default = "unifibackup" 12 | description = "Name of the Oracle Storage Bucket created previously" 13 | } 14 | 15 | variable "bucket_namespace" { 16 | default = "" 17 | description = "Namespace of the Oracle Storage Bucket created previously. Found under General under Bucket Information" 18 | } 19 | 20 | variable "dns_name" { 21 | default = "" 22 | description = "DNS name for the public IP assigned." 23 | } 24 | 25 | variable "timezone" { 26 | default = "" 27 | description="Example America/Chicago" 28 | } 29 | 30 | variable "ddns_url" { 31 | default="" 32 | description = "URL to update Dynamic DNS entry such as http://freedns.afraid.org/dynamic/update.php?xxxdynamicTokenxxx or https://www.duckdns.org/update?domains=xxxx&token=xxxxx" 33 | } 34 | 35 | variable "project_name" { 36 | default = "unificontroller" 37 | description = "Alphanumeric only (No special characters like - or _). Used for the Compartment and DNS name for the VCN" 38 | } 39 | 40 | variable "instance_shape" { 41 | default = "VM.Standard.A1.Flex" 42 | description = "Shape Reference: https://docs.cloud.oracle.com/iaas/Content/Compute/References/computeshapes.htm" 43 | } 44 | 45 | # variable "instance_shape_config_baseline_ocpu_utilization" { 46 | # default = 4 47 | # description = "Unknown utilization variable" 48 | # } 49 | 50 | variable "instance_shape_config_memory_in_gbs" { 51 | default = 6 52 | description = "RAM GB" 53 | } 54 | 55 | variable "instance_shape_config_ocpus" { 56 | default = 1 57 | description = "oCPUs" 58 | } 59 | 60 | variable "operating_system" { 61 | default = "Canonical Ubuntu" 62 | description = "Full name of OS without version number such as 'Canonical Ubuntu'" 63 | } 64 | 65 | variable "operating_system_version" { 66 | default = "24.04" 67 | description = "Version name of the specified OS, such as '24.04'" 68 | } 69 | 70 | resource "random_id" "unificontroller_id" { 71 | byte_length = 2 72 | } 73 | 74 | variable "region" {} 75 | variable "compartment_ocid" {} 76 | 77 | variable "email" { 78 | default = "" 79 | description = "E-mail address you want to use for Let's Encrypt renewal notifications." 80 | } 81 | 82 | variable "customer_secret_key" { 83 | default = "" 84 | description = "Secret Key from Profile, User Settings, Customer Secret Keys and enter the generated Secret Key" 85 | } 86 | 87 | variable "customer_access_key" { 88 | default = "" 89 | description = "Access Key from Profile, User Settings, Customer Secret Keys and enter the Access Key" 90 | } 91 | --------------------------------------------------------------------------------