├── .goreleaser.tmpl ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ └── feature_request.md ├── workflows │ ├── golanci-lint.yml │ └── release.yml └── PULL_REQUEST_TEMPLATE │ └── pull_request_template.md ├── MAINTAINERS.md ├── concourse ├── develop │ └── endtoend.yaml ├── combinations │ ├── homelab-tanzu-v703-vds-avi21.yaml │ ├── adams-homelab.tfvars │ └── README.md └── README.md ├── main.go ├── examples ├── 03_basic_list │ ├── clusters │ │ └── main.tf │ └── main.tf ├── 02_basic_read │ ├── clusters │ │ └── main.tf │ └── main.tf ├── full_esxi_tanzu_cluster │ ├── vcsim.sh │ ├── README.md │ ├── 04_avi_controller │ │ ├── variables.tf │ │ └── main.tf │ ├── 01_vcenter_create │ │ └── main.tf │ ├── 02_vcenter_join │ │ └── main.tf │ ├── 05_avi_configure │ │ ├── README.md │ │ ├── variables.tf │ │ └── main.tf │ ├── 03_vds_networking │ │ └── main.tf │ ├── h20.tfvars │ ├── adams-homelab.tfvars │ ├── 06_tanzu_enable │ │ ├── variables.tf │ │ └── main.tf │ ├── variables.tf │ └── main.tf └── 01_basic_create │ ├── main.tf │ └── clusters │ └── main.tf ├── .gitignore ├── NOTICE ├── .golangci.yml ├── modules ├── namespace_management_fullstack_vsphere │ └── README.md ├── tanzu_shared_services_cluster │ └── README.md ├── tanzu_workload_cluster │ └── README.md └── README.md ├── Makefile ├── go.mod ├── namespace_management ├── data_source_clusters.go ├── data_source_cluster.go ├── provider.go └── resource_cluster.go ├── .goreleaser.yml ├── CONTRIBUTING.md ├── docs └── cluster-get.md ├── CODE_OF_CONDUCT.md ├── LICENSE └── README.md /.goreleaser.tmpl: -------------------------------------------------------------------------------- 1 | # Release {{ .Version }} ({{ .Date }}) -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Maintainers 2 | 3 | The project maintainers are listed below:- 4 | 5 | Adam Fowler, VMware Inc. - Project Lead - adamf at vmware dot com or adam at adamfowler dot org. 6 | Bill Glover, VMware Inc. - Project Maintainer - bglover at vmware dot com. -------------------------------------------------------------------------------- /concourse/develop/endtoend.yaml: -------------------------------------------------------------------------------- 1 | # End to End tests for Terraform Provider Namespace Management 2 | # Performs the following:- 3 | # - Ask Terraform to ensure an environment exists 4 | # - Run all applicable tests after instantiation 5 | # - Ask Terraform to remove the specified environment 6 | # - Logs success and failure info to a new GitHub Issue -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 VMware, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | 5 | package main 6 | 7 | import ( 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" 9 | 10 | "github.com/vmware/terraform-provider-namespace-management/namespace_management" 11 | ) 12 | 13 | func main() { 14 | plugin.Serve(&plugin.ServeOpts{ 15 | ProviderFunc: namespace_management.Provider}) 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/golanci-lint.yml: -------------------------------------------------------------------------------- 1 | name: golangci-lint 2 | on: 3 | pull_request: 4 | jobs: 5 | golangci: 6 | name: lint 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: golangci-lint 11 | uses: golangci/golangci-lint-action@v2 12 | with: 13 | version: v1.46.2 14 | args: --issues-exit-code=1 15 | only-new-issues: true -------------------------------------------------------------------------------- /examples/03_basic_list/clusters/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | // Force local binary use, rather than public binary 4 | namespace-management = { 5 | version = ">= 0.1" 6 | source = "vmware.com/vcenter/namespace-management" 7 | } 8 | } 9 | } 10 | 11 | # Fetches the namespace cluster summary (if found) 12 | data "namespace-management_clusters" "read" { 13 | } 14 | 15 | # Returns all clusters 16 | output "clusters" { 17 | value = data.namespace-management_clusters.read 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | /bin 8 | 9 | # Test binary, built with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | # Dependency directories (remove the comment below to include it) 16 | # vendor/ 17 | 18 | # vscode 19 | .vscode 20 | 21 | # Terraform example 22 | .terraform 23 | .terraform.lock.hcl 24 | terraform.tfstate 25 | terraform.tfstate.backup 26 | .terraform.tfstate.lock.info 27 | .terraform.tfstate.swp 28 | 29 | *~ -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2022 VMware, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /examples/02_basic_read/clusters/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | // Force local binary use, rather than public binary 4 | namespace-management = { 5 | version = ">= 0.1" 6 | source = "vmware.com/vcenter/namespace-management" 7 | } 8 | } 9 | } 10 | 11 | variable "cluster_name" { 12 | type = string 13 | default = "Cluster01" 14 | } 15 | 16 | # Fetches the namespace cluster summary (if found) 17 | data "namespace-management_cluster" "read" { 18 | name = var.cluster_name 19 | } 20 | 21 | # Only returns Cluster01 22 | output "cluster" { 23 | value = data.namespace-management_cluster.read 24 | } 25 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | issues: 2 | max-per-linter: 0 3 | max-same-issues: 0 4 | 5 | run: 6 | deadline: 5m 7 | 8 | linters: 9 | disable-all: true 10 | enable: 11 | - deadcode 12 | - errcheck 13 | # - gofmt 14 | - goimports 15 | - gosimple 16 | - govet 17 | - ineffassign 18 | - nakedret 19 | - misspell 20 | - revive 21 | - staticcheck 22 | - structcheck 23 | - typecheck 24 | - unused 25 | - unconvert 26 | - varcheck 27 | - vet 28 | - vetshadow 29 | 30 | linters-settings: 31 | errcheck: 32 | ignore: github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema:ForceNew|Set,fmt:.*,io:Close -------------------------------------------------------------------------------- /examples/03_basic_list/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | // Force local binary use, rather than public binary 4 | namespace-management = { 5 | version = ">= 0.1" 6 | source = "vmware.com/vcenter/namespace-management" 7 | } 8 | } 9 | } 10 | 11 | provider "namespace-management" { 12 | vsphere_hostname = "vc01.h2o-4-328.h2o.vmware.com" 13 | vsphere_username = "Administrator@vsphere.local" 14 | vsphere_password = "" # SET ME 15 | 16 | # If you have a self-signed cert 17 | vsphere_insecure = true 18 | # TODO support custom cert/ca bundle 19 | } 20 | 21 | variable "cluster_name" { 22 | default = "vc01cl01" 23 | } 24 | variable "datacenter_name" { 25 | default = "vc01" 26 | } 27 | 28 | # THIS SAMPLE JUST READS ALL CONFIGURES SUPERVISOR CLUSTERS 29 | module "clusters" { 30 | source = "./clusters" 31 | } 32 | 33 | output "clusters" { 34 | value = module.clusters.clusters 35 | } 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: 'enhancement' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Please start with a use case description for a USER of this enhancement** 11 | 12 | [Who] As a ????? [e.g. Developer / App user] 13 | 14 | [What] I need to ??? [e.g. see a list of venues] 15 | 16 | [Value] In order to achieve ??? and/or realise ??? benefit 17 | 18 | **Describe the potential solution you'd like** 19 | 20 | A clear and concise description of what you want to happen within the Provider. 21 | 22 | **Describe alternatives you've considered** 23 | 24 | A clear and concise description of any alternative solutions or features you've considered. 25 | 26 | **Additional context** 27 | 28 | Add any other context or screenshots about the feature request here. 29 | 30 | **Relative priority** 31 | 32 | Please give an indication of a relative priority for this enhancement. -------------------------------------------------------------------------------- /examples/02_basic_read/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | // Force local binary use, rather than public binary 4 | namespace-management = { 5 | version = ">= 0.1" 6 | source = "vmware.com/vcenter/namespace-management" 7 | } 8 | } 9 | } 10 | 11 | provider "namespace-management" { 12 | vsphere_hostname = "vc01.h2o-4-328.h2o.vmware.com" 13 | vsphere_username = "Administrator@vsphere.local" 14 | vsphere_password = "" # SET ME 15 | 16 | # If you have a self-signed cert 17 | vsphere_insecure = true 18 | # TODO support custom cert/ca bundle 19 | } 20 | 21 | variable "cluster_name" { 22 | default = "vc01cl01" 23 | } 24 | variable "datacenter_name" { 25 | default = "vc01" 26 | } 27 | 28 | # THIS SAMPLE JUST READS AN EXISTING SUPERVISOR CLUSTER CONFIG 29 | # BY vSPHERE CLUSTER NAME 30 | module "clusters" { 31 | source = "./clusters" 32 | 33 | cluster_name = var.cluster_name 34 | } 35 | 36 | output "cluster" { 37 | value = module.clusters.cluster 38 | } 39 | -------------------------------------------------------------------------------- /modules/namespace_management_fullstack_vsphere/README.md: -------------------------------------------------------------------------------- 1 | # vSphere + Tanzu + vSphere Networking (vDS) + NSX-ALB (Avi) 2 | 3 | Provisions a full stack Tanzu system using vSphere networking and the NSX 4 | Advanced Load Balancer (AKA Avi). 5 | 6 | By default uses the Enterprise Edition mode - this is NOT the version provided 7 | for free with Tanzu Basic and Tanzu Standard. 8 | 9 | This module uses a vSphere Datastore but does not require a VSAN datastore. 10 | 11 | TODO: Provide configuration options to limit to Essentials Edition config only. 12 | 13 | ## Dependencies 14 | 15 | Requires avi_controller_vsphere and avi_configure_cloud_vsphere in this repository. 16 | Uses our own namespace-management Provider, and the VMware Avi and Hashicorp vSphere 17 | providers too. 18 | 19 | ## Limitations 20 | 21 | vSphere with Tanzu can only be configured on vSphere clusters with 2 or more hosts. 22 | 23 | vSphere with Tanzu has a limitation in that the Avi Cloud must be the Default-Cloud 24 | and thus the Avi Service Engine (SE) Group must be the Default-Group too. 25 | (Applies up to and including 7.0u3) 26 | 27 | 28 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/pull_request_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Pull request 3 | about: Submit a pull request 4 | title: 'Pull request' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Realted to # . 11 | 12 | or 13 | 14 | Fixes # . 15 | 16 | Changes proposed in this pull request:- 17 | - ? 18 | - ? 19 | - ? 20 | 21 | Signed-off-by: Firstname Surname 22 | 23 | **Checklist prior to submission**:- 24 | 25 | - [ ] Have you added a line to **EVERY** commit to this PR with Signed-off-by: Forename Surname ? (This is required for us to be able to merge your contributions) 26 | - See the CONTRIBUTING.md file in this repository for information as to why this is important. 27 | - [ ] Have you Linked to a known feature/bug ID via 'Related to' or 'Fixes', above? 28 | - [ ] (Optional best practice) Is your branch named feature-ISSUENUM ? (See GitFlow for why this is relevant) 29 | 30 | 31 | **Notification** 32 | 33 | Please do not edit the below. It will notify the maintainers once you submit your PR. 34 | 35 | @vmware-tanzu-labs/terraform-provider-namespace-management-maintainers 36 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | jobs: 8 | goreleaser: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - 12 | name: Checkout 13 | uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | - 17 | name: Set up Go 18 | uses: actions/setup-go@v2 19 | with: 20 | go-version: 1.18 21 | - 22 | name: golangci-lint 23 | uses: golangci/golangci-lint-action@v2 24 | with: 25 | version: v1.46.2 26 | args: --issues-exit-code=1 27 | - 28 | name: Import GPG key 29 | id: import_gpg 30 | uses: crazy-max/ghaction-import-gpg@v2 31 | env: 32 | GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} 33 | PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 34 | - 35 | name: Run GoReleaser 36 | uses: goreleaser/goreleaser-action@v2 37 | with: 38 | version: latest 39 | args: release --rm-dist --release-header .goreleaser.tmpl 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/vcsim.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Starts vcsim with the settings necessary for this sample 4 | echo "Starting VCSIM in the background" 5 | # See TODO below for why we're not starting blank today 6 | # nohup vcsim -dc 0 -password pass -username user & 7 | nohup vcsim -api-version 7.0u3 -standalone-host 3 -vm 0 -password pass -username user & 8 | sleep 2 9 | GOVCOUT=`head -n 1 nohup.out` 10 | echo " VCSIM output: $GOVCOUT" 11 | # The following exports GOVC_URL 12 | $GOVCOUT 13 | # Allow self signed certs in all calls from the GOVC command line 14 | export GOVC_INSECURE=true 15 | # export 16 | 17 | # TODO Fix the govmomi API layer (MoveTask) so the below can be 18 | # done instead in Terraform itself as unadopted hosts 19 | 20 | # echo "Creating base vSphere layer with 3 unadopted hosts" 21 | # govc datacenter.create lab01.my.cloud 22 | # govc host.add -hostname vesxi01.lab01.my.cloud -username root -password pass -noverify 23 | # govc host.add -hostname vesxi02.lab01.my.cloud -username root -password pass -noverify 24 | # govc host.add -hostname vesxi03.lab01.my.cloud -username root -password pass -noverify 25 | 26 | echo "Ready for your Terraform commands" 27 | read -p "Press Enter to quit vcsim" 28 | kill $GOVC_SIM_PID -------------------------------------------------------------------------------- /examples/01_basic_create/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | // Force local binary use, rather than public binary 4 | namespace-management = { 5 | version = ">= 0.1" 6 | source = "vmware.com/vcenter/namespace-management" 7 | } 8 | vsphere = { 9 | version = ">= 2.1.1" 10 | source = "hashicorp/vsphere" 11 | } 12 | } 13 | } 14 | 15 | provider "namespace-management" { 16 | vsphere_hostname = "https://vc01.h2o-4-328.h2o.vmware.com/sdk" 17 | vsphere_username = "Administrator@vsphere.local" 18 | vsphere_password = "" # SET ME 19 | 20 | # If you have a self-signed cert 21 | vsphere_insecure = true 22 | # TODO support custom cert/ca bundle 23 | } 24 | provider "vsphere" { 25 | vsphere_server = "https://vc01.h2o-4-328.h2o.vmware.com/sdk" 26 | user = "Administrator@vsphere.local" 27 | password = "" # SET ME 28 | 29 | # If you have a self-signed cert 30 | allow_unverified_ssl = true 31 | # TODO support custom cert/ca bundle 32 | } 33 | 34 | variable "cluster_name" { 35 | default = "vc01cl01" 36 | } 37 | variable "datacenter_name" { 38 | default = "vc01" 39 | } 40 | 41 | module "clusters" { 42 | source = "./clusters" 43 | 44 | cluster_name = var.cluster_name 45 | datacenter_name = var.datacenter_name 46 | } 47 | 48 | output "cluster" { 49 | value = module.clusters.cluster 50 | } 51 | -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/README.md: -------------------------------------------------------------------------------- 1 | # Full vSphere with Tanzu rollout example 2 | 3 | ## Folder notes 4 | 5 | * 01_vcenter_create - not used today, kept as an example 6 | * 02_vcenter_join - not used today, kept as an example 7 | * 03_vds_networking - not used today, kept as an example 8 | * 04_avi_controller - Deploys the initial Avi controller (only 1 today) and changes its password for access 9 | * 05_avi_configure - Reconfigures an Avi controller's Default Cloud for vCenter and Tanzu networking 10 | * 06_tanzu_enable - Deploy vSphere with Tanzu using vDS networking and Avi load balancing 11 | 12 | The main.tf file uses modules 04 to 06 today. This has been tested against:- 13 | 14 | * H2o within VMware, our demo environment, over company VPN, a ESXi vSphere 7.0u2 environment 15 | * Adam's Homelab, a 3 node nested ESXi vSphere 7.0u3 environment 16 | 17 | ## Support statement 18 | 19 | The files in this folder are SAMPLES ONLY, they are not designed as fully tested 20 | or reusable Terraform modules. See the /modules folder for that ongoing work. 21 | 22 | These files may be useful in a custom deployment or in troubleshooting 23 | your own issues. 24 | 25 | ## Running the example 26 | 27 | ```sh 28 | terraform init && terraform apply --var-file adams-homelab.tfvars \ 29 | --var vsphere_password="Obvious1!" \ 30 | --var vsphere_insecure=true \ 31 | --var avi_password="Obvious1!" \ 32 | --var avi_backup_passphrase="Obvious1!" \ 33 | ``` -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/04_avi_controller/variables.tf: -------------------------------------------------------------------------------- 1 | 2 | // Avi Provider variables 3 | variable "avi_username" { 4 | type = string 5 | default = "" 6 | } 7 | 8 | # The NEW password to specify after installation 9 | variable "avi_password" { 10 | type = string 11 | default = "" 12 | sensitive = true 13 | } 14 | 15 | variable "vm_datacenter" { 16 | type = string 17 | default = "vc01" 18 | } 19 | 20 | variable "vm_resource_pool" { 21 | type = string 22 | default = "" 23 | } 24 | 25 | variable "content_library" { 26 | type = string 27 | default = "" 28 | } 29 | 30 | variable "vm_datastore" { 31 | type = string 32 | default = "" 33 | } 34 | 35 | variable "vm_name" { 36 | type = string 37 | default = "" 38 | } 39 | 40 | variable "vm_network" { 41 | type = string 42 | default = "" 43 | } 44 | 45 | variable "vm_folder" { 46 | type = string 47 | default = "" 48 | } 49 | 50 | variable "vm_template" { 51 | type = string 52 | default = "" 53 | } 54 | 55 | 56 | 57 | 58 | // New variables for full OVA settings:- 59 | // TODO support multiple avi controllers 60 | variable "avi_management_hostname" { 61 | type = string 62 | default = "avi-controller-1" 63 | } 64 | variable "avi_management_ip_address" { 65 | type = string 66 | default = "10.2.0.50" 67 | } 68 | 69 | # The following is an integer but written as a string (Avi provider requirement) 70 | variable "avi_management_subnet_mask_int" { 71 | type = string 72 | default = "24" 73 | } 74 | variable "avi_management_default_gateway" { 75 | type = string 76 | default = "10.2.0.1" 77 | } -------------------------------------------------------------------------------- /examples/01_basic_create/clusters/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | // Force local binary use, rather than public binary 4 | namespace-management = { 5 | version = "0.1" 6 | source = "vmware.com/vcenter/namespace-management" 7 | } 8 | vsphere = { 9 | version = ">= 2.1.1" 10 | source = "hashicorp/vsphere" 11 | } 12 | } 13 | } 14 | 15 | variable "cluster_name" { 16 | type = string 17 | default = "Cluster01" 18 | } 19 | variable "datacenter_name" { 20 | type = string 21 | default = "lab01.my.cloud" 22 | } 23 | 24 | data "vsphere_datacenter" "dc" { 25 | name = var.datacenter_name 26 | } 27 | 28 | # Converts the vSphere cluster name to its id 29 | data "vsphere_compute_cluster" "fetch" { 30 | name = var.cluster_name 31 | datacenter_id = data.vsphere_datacenter.dc.id 32 | } 33 | # ALTERNATIVE to the above (if it already exists!):- 34 | # data "namespace-management_cluster" "fetch" { 35 | # name = var.cluster_name 36 | # # Note: No need for datacenter_id in this API 37 | # } 38 | 39 | # Enables the Tanzu Supervisor Cluster 40 | resource "namespace-management_cluster" "supervisor" { 41 | id = data.vsphere_compute_cluster.fetch.id 42 | # If using the alternative cluster lookup method:- 43 | # id = data.namespace-management_cluster.fetch.id 44 | 45 | # TODO other settings here (Hardcoded in the provider today) 46 | } 47 | 48 | # Only the newly enabled cluster (including the cluster ID) 49 | output "cluster" { 50 | value = data.namespace-management_cluster.supervisor 51 | } 52 | 53 | # Convenience output for future commands 54 | output "datacenter_id" { 55 | values = data.vsphere_datacenter.dc.id 56 | } -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/01_vcenter_create/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | vsphere = { 4 | version = ">= 2.1.1" 5 | source = "hashicorp/vsphere" 6 | } 7 | } 8 | } 9 | variable "datacenter" { 10 | default = "lab01.my.cloud" 11 | } 12 | 13 | variable "esxi_hosts" { 14 | default = [ 15 | "vesxi01.lab01.my.cloud", 16 | "vesxi02.lab01.my.cloud", 17 | "vesxi03.lab01.my.cloud", 18 | ] 19 | } 20 | 21 | resource "vsphere_datacenter" "dc" { 22 | name = "${var.datacenter}" 23 | } 24 | 25 | resource "vsphere_host" "esxi01" { 26 | hostname = "${var.esxi_hosts[0]}" 27 | username = "root" 28 | password = "pass" 29 | # license = "00000-00000-00000-00000i-00000" 30 | datacenter = resource.vsphere_datacenter.dc.id 31 | # connected = true 32 | # lockdown = "normal" 33 | # maintenance = true 34 | } 35 | 36 | resource "vsphere_host" "esxi02" { 37 | hostname = "${var.esxi_hosts[1]}" 38 | username = "root" 39 | password = "pass" 40 | # license = "00000-00000-00000-00000i-00000" 41 | datacenter = resource.vsphere_datacenter.dc.id 42 | # connected = true 43 | # lockdown = "normal" 44 | # maintenance = true 45 | } 46 | 47 | resource "vsphere_host" "esxi03" { 48 | hostname = "${var.esxi_hosts[2]}" 49 | username = "root" 50 | password = "pass" 51 | # license = "00000-00000-00000-00000i-00000" 52 | datacenter = resource.vsphere_datacenter.dc.id 53 | # connected = true 54 | # lockdown = "normal" 55 | # maintenance = true 56 | } 57 | 58 | output "datacenter" { 59 | value = resource.vsphere_datacenter.dc.id 60 | } 61 | 62 | output "hosts" { 63 | value = [resource.vsphere_host.esxi01.id,resource.vsphere_host.esxi02.id,resource.vsphere_host.esxi03.id] 64 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TEST?=$$(go list ./... | grep -v 'vendor') 2 | HOSTNAME=vmware.com 3 | NAMESPACE=vcenter 4 | NAME=namespace-management 5 | BINARY=terraform-provider-${NAME} 6 | VERSION=0.1 7 | OS_ARCH=darwin_amd64 8 | 9 | default: install 10 | 11 | build: 12 | go build -o ${BINARY} 13 | 14 | release: 15 | GOOS=darwin GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_darwin_amd64 16 | GOOS=darwin GOARCH=arm64 go build -o ./bin/${BINARY}_${VERSION}_darwin_arm64 17 | GOOS=freebsd GOARCH=386 go build -o ./bin/${BINARY}_${VERSION}_freebsd_386 18 | GOOS=freebsd GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_freebsd_amd64 19 | GOOS=freebsd GOARCH=arm go build -o ./bin/${BINARY}_${VERSION}_freebsd_arm 20 | GOOS=linux GOARCH=386 go build -o ./bin/${BINARY}_${VERSION}_linux_386 21 | GOOS=linux GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_linux_amd64 22 | GOOS=linux GOARCH=arm go build -o ./bin/${BINARY}_${VERSION}_linux_arm 23 | GOOS=openbsd GOARCH=386 go build -o ./bin/${BINARY}_${VERSION}_openbsd_386 24 | GOOS=openbsd GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_openbsd_amd64 25 | GOOS=solaris GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_solaris_amd64 26 | GOOS=windows GOARCH=386 go build -o ./bin/${BINARY}_${VERSION}_windows_386 27 | GOOS=windows GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_windows_amd64 28 | 29 | install: build 30 | mkdir -p ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH} 31 | mv ${BINARY} ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH} 32 | 33 | test: 34 | go test -i $(TEST) || exit 1 35 | echo $(TEST) | xargs -t -n4 go test $(TESTARGS) -timeout=30s -parallel=4 36 | 37 | testacc: 38 | TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 120m -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/vmware/terraform-provider-namespace-management 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.8.0 7 | github.com/vmware/govmomi v0.30.2 8 | ) 9 | 10 | require ( 11 | github.com/agext/levenshtein v1.2.2 // indirect 12 | github.com/apparentlymart/go-textseg v1.0.0 // indirect 13 | github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect 14 | github.com/fatih/color v1.7.0 // indirect 15 | github.com/golang/protobuf v1.5.3 // indirect 16 | github.com/hashicorp/errwrap v1.0.0 // indirect 17 | github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect 18 | github.com/hashicorp/go-hclog v0.15.0 // indirect 19 | github.com/hashicorp/go-multierror v1.0.0 // indirect 20 | github.com/hashicorp/go-plugin v1.4.1 // indirect 21 | github.com/hashicorp/go-uuid v1.0.1 // indirect 22 | github.com/hashicorp/go-version v1.3.0 // indirect 23 | github.com/hashicorp/hcl/v2 v2.3.0 // indirect 24 | github.com/hashicorp/terraform-plugin-go v0.4.0 // indirect 25 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect 26 | github.com/mattn/go-colorable v0.1.4 // indirect 27 | github.com/mattn/go-isatty v0.0.10 // indirect 28 | github.com/mitchellh/copystructure v1.2.0 // indirect 29 | github.com/mitchellh/go-testing-interface v1.0.4 // indirect 30 | github.com/mitchellh/go-wordwrap v1.0.0 // indirect 31 | github.com/mitchellh/mapstructure v1.1.2 // indirect 32 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 33 | github.com/oklog/run v1.0.0 // indirect 34 | github.com/rogpeppe/go-internal v1.8.1 // indirect 35 | github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect 36 | github.com/zclconf/go-cty v1.8.4 // indirect 37 | golang.org/x/net v0.17.0 // indirect 38 | golang.org/x/sys v0.13.0 // indirect 39 | golang.org/x/text v0.13.0 // indirect 40 | google.golang.org/appengine v1.6.7 // indirect 41 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect 42 | google.golang.org/grpc v1.56.3 // indirect 43 | google.golang.org/protobuf v1.33.0 // indirect 44 | ) 45 | -------------------------------------------------------------------------------- /namespace_management/data_source_clusters.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 VMware, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | 5 | package namespace_management 6 | 7 | import ( 8 | "context" 9 | "strconv" 10 | "time" 11 | 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 13 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 14 | 15 | "github.com/vmware/govmomi/vapi/namespace" 16 | ) 17 | 18 | func dataSourceClusters() *schema.Resource { 19 | return &schema.Resource{ 20 | ReadContext: dataSourceClustersRead, 21 | Schema: map[string]*schema.Schema{ 22 | "clusters": { 23 | Type: schema.TypeList, 24 | Computed: true, 25 | Elem: &schema.Resource{ 26 | Schema: map[string]*schema.Schema{ 27 | "id": { 28 | Type: schema.TypeString, 29 | Computed: true, 30 | }, 31 | "name": { 32 | Type: schema.TypeString, 33 | Computed: true, 34 | }, 35 | "kubernetes_status": { 36 | Type: schema.TypeString, 37 | Computed: true, 38 | }, 39 | "config_status": { 40 | Type: schema.TypeString, 41 | Computed: true, 42 | }, 43 | }, 44 | }, 45 | }, 46 | }, 47 | } 48 | } 49 | 50 | func dataSourceClustersRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 51 | ns := m.(*namespace.Manager) 52 | clusterList, err := ns.ListClusters(ctx) 53 | if err != nil { 54 | return diag.FromErr(err) 55 | } 56 | 57 | var diags diag.Diagnostics 58 | 59 | clusters := make([]map[string]interface{}, 0) 60 | for _, cluster := range clusterList { 61 | cl := make(map[string]interface{}) 62 | cl["id"] = cluster.ID 63 | cl["name"] = cluster.Name 64 | cl["kubernetes_status"] = cluster.KubernetesStatus 65 | cl["config_status"] = cluster.ConfigStatus 66 | 67 | clusters = append(clusters, cl) 68 | } 69 | 70 | if err := d.Set("clusters", clusters); err != nil { 71 | return diag.FromErr(err) 72 | } 73 | 74 | // always run 75 | d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) 76 | 77 | return diags 78 | } 79 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # Visit https://goreleaser.com for documentation on how to customize this 2 | # behavior. 3 | before: 4 | hooks: 5 | # this is just an example and not a requirement for provider building/publishing 6 | - go mod tidy 7 | builds: 8 | - env: 9 | # goreleaser does not work with CGO, it could also complicate 10 | # usage by users in CI/CD systems like Terraform Cloud where 11 | # they are unable to install libraries. 12 | - CGO_ENABLED=0 13 | mod_timestamp: '{{ .CommitTimestamp }}' 14 | flags: 15 | - -trimpath 16 | ldflags: 17 | - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' 18 | goos: 19 | - freebsd 20 | - windows 21 | - linux 22 | - darwin 23 | goarch: 24 | - amd64 25 | - '386' 26 | - arm 27 | - arm64 28 | ignore: 29 | - goos: darwin 30 | goarch: '386' 31 | binary: '{{ .ProjectName }}_v{{ .Version }}' 32 | archives: 33 | - format: zip 34 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' 35 | checksum: 36 | extra_files: 37 | - glob: 'terraform-registry-manifest.json' 38 | name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json' 39 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' 40 | algorithm: sha256 41 | signs: 42 | - artifacts: checksum 43 | args: 44 | # if you are using this is a GitHub action or some other automated pipeline, you 45 | # need to pass the batch flag to indicate its not interactive. 46 | - "--batch" 47 | - "--local-user" 48 | - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key 49 | - "--output" 50 | - "${signature}" 51 | - "--detach-sign" 52 | - "${artifact}" 53 | release: 54 | extra_files: 55 | - glob: 'terraform-registry-manifest.json' 56 | name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json' 57 | # Visit your project's GitHub Releases page to publish this release. 58 | draft: true 59 | changelog: 60 | filters: 61 | exclude: 62 | - '^docs:' 63 | - '^test:' 64 | - Merge pull request 65 | - Merge branch 66 | - go mod tidy -------------------------------------------------------------------------------- /namespace_management/data_source_cluster.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 VMware, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | 5 | package namespace_management 6 | 7 | import ( 8 | "context" 9 | 10 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 12 | 13 | "github.com/vmware/govmomi/vapi/namespace" 14 | ) 15 | 16 | func dataSourceCluster() *schema.Resource { 17 | return &schema.Resource{ 18 | ReadContext: dataSourceClusterRead, 19 | Schema: map[string]*schema.Schema{ 20 | "id": &schema.Schema{ 21 | Type: schema.TypeString, 22 | Optional: true, 23 | }, 24 | "name": &schema.Schema{ 25 | Type: schema.TypeString, 26 | Required: true, 27 | }, 28 | "kubernetes_status": &schema.Schema{ 29 | Type: schema.TypeString, 30 | Computed: true, 31 | }, 32 | "config_status": &schema.Schema{ 33 | Type: schema.TypeString, 34 | Computed: true, 35 | }, 36 | }, 37 | } 38 | } 39 | 40 | func dataSourceClusterRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 41 | ns := m.(*namespace.Manager) 42 | clusterList, err := ns.ListClusters(ctx) 43 | if err != nil { 44 | return diag.FromErr(err) 45 | } 46 | 47 | var diags diag.Diagnostics 48 | found := false 49 | 50 | name := d.Get("name").(string) 51 | 52 | // Now search for our (d.Id()) matching cluster 53 | for _, cluster := range clusterList { 54 | if cluster.Name == name { 55 | found = true 56 | d.SetId(cluster.ID) 57 | if err := d.Set("id", cluster.ID); err != nil { 58 | return diag.FromErr(err) 59 | } 60 | if err := d.Set("kubernetes_status", cluster.KubernetesStatus); err != nil { 61 | return diag.FromErr(err) 62 | } 63 | if err := d.Set("config_status", cluster.ConfigStatus); err != nil { 64 | return diag.FromErr(err) 65 | } 66 | } 67 | } 68 | // Handle cluster not found error 69 | if !found { 70 | diags = append(diags, diag.Diagnostic{ 71 | Severity: diag.Error, 72 | Summary: "Unable to read Tanzu Supervisor Cluster", 73 | Detail: "Cluster name not found: " + name, 74 | }) 75 | } 76 | 77 | return diags 78 | } 79 | -------------------------------------------------------------------------------- /modules/tanzu_shared_services_cluster/README.md: -------------------------------------------------------------------------------- 1 | # Tanzu Shared Services Cluster Terraform Module 2 | 3 | This module instantiates a Tanzu Workload Cluster and 4 | configures it for common shared services. All of the 5 | shared services provisioned and configured use products 6 | available in Tanzu Standard Runtime (on-prem, non-SaaS), 7 | as part of Tanzu Data products (Postgres et al), or are 8 | common opensource software available in VMware Application 9 | Catalogue. 10 | 11 | ## Status 12 | 13 | Concept phase - This module is a concept and not yet implemented, even as alpha/beta. 14 | 15 | ## Dependencies 16 | 17 | Requires tanzu_workload_cluster within this repository. 18 | 19 | ## Components 20 | 21 | The following are provided:- 22 | 23 | 1. Creation of a vSphere namespace (Default: devops-team) 24 | 1. Creation of a shared services cluster (Default: shared-services) 25 | 1. Installation of Tanzu Cluster Essentials (kapp-controller and cert-manager) 26 | 1. Installation of a HA Harbor cluster 27 | 1. Installing all containers and charts for other needed elements (allowing airgapped offline installation) 28 | 1. Installation of Prometheus and Grafana (including default Tanzu Grafana dashboards) 29 | - Also acts as a sink for prometheus metrics from other workload clusters 30 | 1. Installation of fluentd to act as a sink for log files from other workload clusters 31 | - Also sinks these logs to vRealize LogInsight, if available 32 | - TODO may sink these log files to OpenSearch in-cluster? (via VMware App Catalogue?) 33 | 1. Installation of GitLab Community Edition to provide code and config storage on-prem (VMware App Catalogue) 34 | 1. Installation of a Postgres HA instance for use by Terraform (Options for Tanzu support, or VMware App Catalogue OSS) 35 | 1. Installation of Concourse for Terraform CI/CD control plane 36 | - TODO consider tekton as well/instead of Concourse 37 | - This is to allow follow-on local management of other workload clusters 38 | - This module can be considered a 'bootstrap' for this future Terraform runner 39 | - Uses Postgres as its backend for the terraform runner via a kubernetes secret 40 | - Uses a GitLab project as the source for Concourse config changes 41 | -------------------------------------------------------------------------------- /concourse/combinations/homelab-tanzu-v703-vds-avi21.yaml: -------------------------------------------------------------------------------- 1 | # TODO This is current an example of what we are aiming to do. 2 | # IT IS NOT USED TODAY. 3 | 4 | # Tanzu v1.16.8+vmware.1-tkg.1 5 | # vSphere ESXI 7.0.3 (7.0u3) 6 | # vSphere vDS networking 7 | # Avi Load Balancer v21.x (latest minor version) 8 | 9 | # Environment is specific to the target test environment 10 | environments: 11 | - title: Adam's Existing Homelab 12 | pipeline: adams-homelab-lab01 13 | name: adams-homelab 14 | lab: lab01 15 | # TODO adopt the lab01 existing config - don't deploy own (i.e. re-use VMs) 16 | shutdown: 17 | - vrnimgr01 18 | - vrni-collector01 19 | - verni-collector02 20 | startup: 21 | - vesxi01 22 | - vesxi02 23 | - vesxi03 24 | startup_after_always: 25 | - vrnimgr01 26 | - vrni-collector01 27 | - verni-collector02 28 | # - title: Adam's Homelab fresh deploy 29 | # pipeline: adams-homelab-fresh 30 | # name: adams-homelab 31 | # lab: emphemeral01 32 | # shutdown: 33 | # - vesxi01 34 | # - vesxi02 35 | # - vesxi03 36 | # - vrnimgr01 37 | # - vrni-collector01 38 | # - verni-collector02 39 | # startup_after_success: 40 | # - vesxi01 41 | # - vesxi02 42 | # - vesxi03 43 | # - vrnimgr01 44 | # - vrni-collector01 45 | # - verni-collector02 46 | # - title: h2o fresh deploy 47 | # pipeline: h20-fresh 48 | # name: h20 49 | # size: medium 50 | 51 | # Versions are deployed versions not (just) provided in terraform variables 52 | versions: 53 | runtime: tkgs 54 | tkr: v1.16.8+vmware.1-tkg.1 55 | esxi: 7.0.3 56 | avi: 21.1.2 57 | 58 | # TF formatted variables for the modules (may duplicate versions info) 59 | tfvars: 60 | file: adams-homelab.tfvars 61 | overrides: 62 | # Note, whilst Terraform is declarative, sometimes you need to explitly prevent deployment (E.g. a new Avi instance) 63 | deploy_esxi: false 64 | deploy_vsphere: false 65 | deploy_vds: false 66 | deploy_nsxt: false 67 | deploy_avi: true 68 | 69 | load_balancer_provider: AVI 70 | avi_version: "21.1.2" 71 | avi_vm_template: "controller-21.1.2-9124" 72 | supervisor_tkr: "v1.16.8---vmware.1-tkg.1" 73 | # workload_tkr: TODO when we support workload tests 74 | avi_password: ((adams_homelab_avi_password)) 75 | avi_backup_passphrase: ((adams_homelab_avi_password)) 76 | vsphere_password: ((adams_homelab_vsphere_password)) 77 | # Tear down via terraform delete after a successful run? 78 | delete_after: true 79 | 80 | # Some tests may not run on some environments 81 | # Note: Tests should detect from tfvars / versions if they are applicable generally - E.g. Avi LB config tests - and should not be included here 82 | skip: 83 | - wibble -------------------------------------------------------------------------------- /modules/tanzu_workload_cluster/README.md: -------------------------------------------------------------------------------- 1 | # Tanzu Workload Cluster Terraform Module 2 | 3 | This module creates a Tanzu Workload Cluster on vSphere with Tanzu and configures 4 | minimum elements on the cluster. This includes default storage classes and other 5 | minimum configuration. 6 | 7 | This module can also optionally be instructed to sink all Prometheus metrics 8 | and log files (using fluentbit) to a shared services cluster. 9 | 10 | This module can also optionally configure Contour or Istio (TSM Istio Mode or OSS Istio) 11 | to act as Ingress controllers, and exposes them via a load balancer. 12 | 13 | TODO consider adding enforcement of namespace quotas linked to some concept of t-shirt 14 | sizes. 15 | 16 | ## Status 17 | 18 | Concept phase - This module is a concept and not yet implemented, even as alpha/beta. 19 | 20 | ## Dependencies 21 | 22 | Assumes you have enabled workload management, but does not call this module directly. 23 | 24 | Uses the Kubernetes Terraform provider. 25 | 26 | ## Components 27 | 28 | This module always creates:- 29 | 30 | - A new cluster using the v1alpha2 API (vSphere 7.0u2+ only) of Tanzu for vSphere 31 | - Sets a default storage class 32 | - Sets the CNI networking (Antrea or Antrea-NSX) 33 | - Allows sizing, resilience, and network configuration overrides 34 | - Restricts each Namespace to a separate Antrea isolated network area 35 | - Assigns quota, permissions, and limits to any namespaces requested during creation 36 | - Sets the default Harbor instance to use, and injects those secrets into namespaces created 37 | - Also adds an istio-injection=enabled/disabled flag to namespaces, if required 38 | - Adds any in-kubernetes cluster role assignments to namespaces 39 | - Installation of Tanzu Cluster Essentials (kapp-controller and cert-manager) 40 | 41 | This module may also create with configure_metrics=true:- 42 | - Sink metrics to a central Prometheus system; OR 43 | - Set up Prometheus and Grafana in this cluster 44 | 45 | This module may also create with configure_logs=true:- 46 | - Sink logs using fluentbit to a shared fluentd engine 47 | 48 | This module may also create with configure_contour=true:- 49 | - Create a tanzu-system-ingress namespace configured with Contour 50 | - Expose Contour via a load balancer 51 | 52 | This module may also create with configure_istio=true:- 53 | - Create an istiod namespace for istio system components 54 | - Create an istio-ingress namespace as a default Ingress gateway 55 | - Create an istio-egress namespace as a default Egress gateway 56 | - Configure Istio injection into namespaces with an explicit istio-injection=enabled flag 57 | - Instantiates a Kiali management UI and exposes this via its own separate load balancer -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to terraform-provider-namespace-management 2 | 3 | The terraform-provider-namespace-management project team welcomes contributions from the community. Before you start working with terraform-provider-namespace-management, please 4 | Read our [Contributor License Agreement](https://cla.vmware.com/cla/1/preview). All contributions to this repository must be 5 | signed as described on that page. Your signature certifies that you wrote the patch or have the right to pass it on 6 | as an open-source patch. 7 | 8 | ## Contribution Flow 9 | 10 | This is a rough outline of what a contributor's workflow looks like: 11 | 12 | - Create a topic branch from where you want to base your work using the naming convention feature-GITHUBISSUENUM as per GitFlow 13 | - Make commits of logical units 14 | - Make sure your commit messages are in the proper format (see below) 15 | - Push your changes to a topic branch in your fork of the repository 16 | - Submit a pull request 17 | 18 | Example: 19 | 20 | ``` shell 21 | git remote add upstream https://github.com/vmware-tanzu-labs/terraform-provider-namespace-management.git 22 | git checkout -b my-new-feature develop 23 | git commit -a 24 | git push origin my-new-feature 25 | ``` 26 | 27 | ### Staying In Sync With Upstream 28 | 29 | When your branch gets out of sync with the vmware-tanzu-labs/develop branch, use the following to update: 30 | 31 | ``` shell 32 | git checkout my-new-feature 33 | git fetch -a 34 | git pull --rebase upstream develop 35 | git push --force-with-lease origin my-new-feature 36 | ``` 37 | 38 | ### Updating pull requests 39 | 40 | If your PR fails to pass CI or needs changes based on code review, you'll most likely want to squash these changes into 41 | existing commits. 42 | 43 | If your pull request contains a single commit or your changes are related to the most recent commit, you can simply 44 | amend the commit. 45 | 46 | ``` shell 47 | git add . 48 | git commit --amend 49 | git push --force-with-lease origin my-new-feature 50 | ``` 51 | 52 | If you need to squash changes into an earlier commit, you can use: 53 | 54 | ``` shell 55 | git add . 56 | git commit --fixup 57 | git rebase -i --autosquash main 58 | git push --force-with-lease origin my-new-feature 59 | ``` 60 | 61 | Be sure to add a comment to the PR indicating your new changes are ready to review, as GitHub does not generate a 62 | notification when you git push. 63 | 64 | ### Code Style 65 | 66 | ### Formatting Commit Messages 67 | 68 | We follow the conventions on [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/). 69 | 70 | Be sure to include any related GitHub issue references in the commit message. See 71 | [GFM syntax](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown) for referencing issues 72 | and commits. 73 | 74 | ## Reporting Bugs and Creating Issues 75 | 76 | When opening a new issue, try to roughly follow the commit message format conventions above. 77 | -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/02_vcenter_join/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | vsphere = { 4 | version = ">= 2.1.1" 5 | source = "hashicorp/vsphere" 6 | } 7 | } 8 | } 9 | 10 | variable "cluster_name" { 11 | default = "DC0_C0" 12 | } 13 | 14 | variable "datacenter_name" { 15 | default = "DC0" 16 | } 17 | 18 | variable "esxi_hosts" { 19 | default = [ 20 | "DC0_C0_H0", 21 | "DC0_C0_H1", 22 | "DC0_C0_H2", 23 | # "vesxi01.lab01.my.cloud", 24 | # "vesxi02.lab01.my.cloud", 25 | # "vesxi03.lab01.my.cloud", 26 | ] 27 | } 28 | 29 | data "vsphere_datacenter" "dc" { 30 | name = "${var.datacenter_name}" 31 | } 32 | 33 | # data "vsphere_host" "hosts" { 34 | # count = "${length(var.esxi_hosts)}" 35 | # name = "${var.esxi_hosts[count.index]}" 36 | # datacenter_id = data.vsphere_datacenter.dc.id 37 | # } 38 | 39 | # data "vsphere_host" "esxi01_id" { 40 | # name = "${var.esxi_hosts[0]}" 41 | # datacenter_id = data.vsphere_datacenter.dc.id 42 | # } 43 | 44 | # data "vsphere_host" "esxi02_id" { 45 | # name = "${var.esxi_hosts[1]}" 46 | # datacenter_id = data.vsphere_datacenter.dc.id 47 | # } 48 | 49 | # data "vsphere_host" "esxi03_id" { 50 | # name = "${var.esxi_hosts[2]}" 51 | # datacenter_id = data.vsphere_datacenter.dc.id 52 | # } 53 | 54 | # data "vsphere_host" "dummy_id" { 55 | # name = "DC0_H0" 56 | # datacenter_id = data.vsphere_datacenter.dc.id 57 | # } 58 | 59 | resource "vsphere_compute_cluster" "compute_cluster" { 60 | name = var.cluster_name 61 | datacenter_id = data.vsphere_datacenter.dc.id 62 | # host_system_ids = [data.vsphere_host.esxi01_id.id,data.vsphere_host.esxi02_id.id,data.vsphere_host.esxi03_id.id] 63 | # host_system_ids = [data.vsphere_host.dummy_id.id] 64 | host_managed = true 65 | 66 | # TODO TRY AGAINST A H20 INSTANCE NEXT 67 | # THEN (IF BRAVE) RUN AGAINST MY LIVE SERVER VIA WIREGUARD 68 | 69 | drs_enabled = true 70 | drs_automation_level = "fullyAutomated" 71 | 72 | ha_enabled = false 73 | } 74 | 75 | // ADD host and place into maintenance most 76 | resource "vsphere_host" "esxi01" { 77 | hostname = var.esxi_hosts[0] 78 | username = "root" 79 | password = "pass" 80 | cluster = vsphere_compute_cluster.compute_cluster.id 81 | force = true 82 | } 83 | resource "vsphere_host" "esxi02" { 84 | hostname = "${var.esxi_hosts[1]}" 85 | username = "root" 86 | password = "pass" 87 | cluster = vsphere_compute_cluster.compute_cluster.id 88 | force = true 89 | } 90 | resource "vsphere_host" "esxi03" { 91 | hostname = "${var.esxi_hosts[2]}" 92 | username = "root" 93 | password = "pass" 94 | cluster = vsphere_compute_cluster.compute_cluster.id 95 | force = true 96 | } 97 | 98 | // TODO remove hosts from maintenance mode 99 | 100 | output "cluster" { 101 | value = resource.vsphere_compute_cluster.compute_cluster.id 102 | } -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/05_avi_configure/README.md: -------------------------------------------------------------------------------- 1 | # avi system configuration notes 2 | 3 | 4 | ## Incorrect default cloud deletion 5 | 6 | After initial config, always tries to delete cloud on apply 7 | 8 | ```sh 9 | module.avi-config.avi_cloud.default_cloud: Destroying... [id=https://10.220.50.10/api/cloud/cloud-583cf9bd-cf88-4558-9965-1c782fe6df6d] 10 | module.avi-config.avi_cloud.default_cloud: Still destroying... [id=https://10.220.50.10/api/cloud/cloud-583cf9bd-cf88-4558-9965-1c782fe6df6d, 10s elapsed] 11 | module.avi-config.avi_cloud.default_cloud: Destruction complete after 14s 12 | module.avi-config.avi_backupconfiguration.avi_backup_config: Creating... 13 | ╷ 14 | │ Error: Encountered an error on PUT request to URL https://10.220.50.10/api/backupconfiguration/backupconfiguration-4186c3ee-f669-47a7-ad0e-ddf5779d5e35?skip_default=true: HTTP code: 400; error from Avi: map[error:Atleast one backup destination needs to be provided] 15 | │ 16 | │ with module.avi-config.avi_backupconfiguration.avi_backup_config, 17 | │ on 05_avi_configure/main.tf line 30, in resource "avi_backupconfiguration" "avi_backup_config": 18 | │ 30: resource "avi_backupconfiguration" "avi_backup_config" { 19 | ``` 20 | 21 | ```sh 22 | 23 | module.avi-config.avi_cloud.default_cloud: Destroying... [id=https://10.220.50.10/api/cloud/cloud-583cf9bd-cf88-4558-9965-1c782fe6df6d] 24 | module.avi-config.avi_cloud.default_cloud: Still destroying... [id=https://10.220.50.10/api/cloud/cloud-583cf9bd-cf88-4558-9965-1c782fe6df6d, 10s elapsed] 25 | module.avi-config.avi_cloud.default_cloud: Destruction complete after 11s 26 | module.avi-config.avi_backupconfiguration.avi_backup_config: Modifying... [id=https://10.220.50.10/api/backupconfiguration/backupconfiguration-4186c3ee-f669-47a7-ad0e-ddf5779d5e35] 27 | module.avi-config.avi_backupconfiguration.avi_backup_config: Modifications complete after 1s [id=https://10.220.50.10/api/backupconfiguration/backupconfiguration-4186c3ee-f669-47a7-ad0e-ddf5779d5e35] 28 | module.avi-config.avi_systemconfiguration.avi_system_config: Modifying... [id=https://10.220.50.10/api/systemconfiguration] 29 | module.avi-config.avi_systemconfiguration.avi_system_config: Modifications complete after 0s [id=https://10.220.50.10/api/systemconfiguration] 30 | module.avi-config.avi_cloud.default_cloud: Creating... 31 | ╷ 32 | │ Error: Encountered an error on PUT request to URL https://10.220.50.10/api/cloud/cloud-583cf9bd-cf88-4558-9965-1c782fe6df6d?skip_default=true: HTTP code: 400; error from Avi: map[error:Change of Vcenter URL/Datacenter for a vCenter cloud is not supported. Please delete the cloud and create a new one] 33 | │ 34 | │ with module.avi-config.avi_cloud.default_cloud, 35 | │ on 05_avi_configure/main.tf line 81, in resource "avi_cloud" "default_cloud": 36 | │ 81: resource "avi_cloud" "default_cloud" { 37 | ``` 38 | 39 | Note: This is caused by delete not changing the Default Cloud. After 40 | applying config it is IMPOSSIBLE to modify the Default-Cloud, thus 41 | we should avoid ever using the Default-Cloud with Terraform. 42 | 43 | -------------------------------------------------------------------------------- /modules/README.md: -------------------------------------------------------------------------------- 1 | # Terraform modules from the VMware vSphere with Tanzu Namespace Management project 2 | 3 | We provide a set of Terraform modules for use to configure the entire 4 | system of Tanzu Kubernetes Grid up to and including workload clusters. 5 | 6 | ## System boundaries 7 | 8 | There are several system components with boundaries for our project. We provide 9 | not only Terraform Providers but also well maintained Terraform Modules 10 | that you can refer to directly rather than copying/pasting code. We also provide 11 | examples that use these modules which you should not link to, but instead copy 12 | and customise for your own system environments. 13 | 14 | This means that there are some things this project team does support, but also 15 | quite a few things we stay clear of and do not support. These support boundaries 16 | are explained below. 17 | 18 | Note: By 'support' we mean open source project support through best efforts rather 19 | than formal VMware product support. 20 | 21 | ### IaaS boundary 22 | 23 | This involves using Terraform to provision services on top of vSphere that 24 | are necessary in order to use Tanzu for vSphere. 25 | 26 | This project provides Terraform modules for this, but only supports the 27 | namespace-management provider ourselves, using other terraform providers 28 | created and maintained by other teams for other components. Examples 29 | of these other components are vSphere (resource pools, vm creation, networking), 30 | NSX-T (software defined networking), Avi (Software defined advanced load 31 | balancing). We do support the terraform modules published by THIS project that 32 | uses these externally maintained providers. 33 | 34 | The top most layer of this boundary is a running Tanzu Supervisor Cluster 35 | on top of vSphere. 36 | 37 | Our terraform modules for this start with avi_, nsx_ and namespace_management_ in 38 | the modules folder. 39 | 40 | ### Kubernetes Workload Cluster boundary 41 | 42 | This involves instantiating, configuring and securing a workload cluster on 43 | top of a Tanzu for vSphere system. 44 | 45 | We only provide a set of Terraform Modules for this. These all start with 46 | tanzu_ in the modules folder. 47 | 48 | This does include setting up common shared services on shared service clusters. 49 | 50 | At this point you can have a shared services cluster providing services that 51 | are configured manually by your devops teams, and stand up workload clusters 52 | that are linked to these provisioned shared services. 53 | 54 | ### App Workload and orchestration boundary 55 | 56 | This involves setting up cross-boundary CI/CD for software and platform updates 57 | and configuring application to run in cluster. 58 | 59 | Whilst we provide modules (tanzu_ named) to configure clusters we stop at the 60 | shared service layer and do not automate these steps ourselves. This is because 61 | this type of configuration is organisation specific, and typically managed in 62 | a Platform as a Product approach by DevOps teams themselves. 63 | 64 | This is where installing something like Tanzu Application Platform (TAP) and using 65 | its Supply Chains is involved. 66 | 67 | We neither provide Terraform modules or Providers for this layer. -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/03_vds_networking/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | vsphere = { 4 | version = ">= 2.1.1" 5 | source = "hashicorp/vsphere" 6 | } 7 | } 8 | } 9 | 10 | variable "esxi_hosts" { 11 | default = [ 12 | "vesxi01.lab01.my.cloud", 13 | "vesxi02.lab01.my.cloud", 14 | "vesxi03.lab01.my.cloud", 15 | ] 16 | } 17 | 18 | variable "management_network_interfaces" { 19 | default = [ 20 | "vmnic0" 21 | ] 22 | } 23 | 24 | variable "workload_network_interfaces" { 25 | default = [ 26 | "vmnic2", 27 | ] 28 | } 29 | 30 | data "vsphere_datacenter" "datacenter" { 31 | name = "lab01.my.cloud" 32 | } 33 | 34 | data "vsphere_host" "host" { 35 | count = length(var.esxi_hosts) 36 | name = var.esxi_hosts[count.index] 37 | datacenter_id = data.vsphere_datacenter.datacenter.id 38 | } 39 | 40 | data "vsphere_distributed_virtual_switch" "management" { 41 | name = "Management vSwitch" 42 | datacenter_id = data.vsphere_datacenter.datacenter.id 43 | # max_mtu = 1500 44 | 45 | # uplinks = ["uplink1"] 46 | # active_uplinks = ["uplink1"] 47 | # standby_uplinks = [] 48 | 49 | # host { 50 | # host_system_id = data.vsphere_host.host.0.id 51 | # devices = ["${var.management_network_interfaces}"] 52 | # } 53 | 54 | # host { 55 | # host_system_id = data.vsphere_host.host.1.id 56 | # devices = ["${var.management_network_interfaces}"] 57 | # } 58 | 59 | # host { 60 | # host_system_id = data.vsphere_host.host.2.id 61 | # devices = ["${var.management_network_interfaces}"] 62 | # } 63 | } 64 | 65 | resource "vsphere_distributed_port_group" "management_portgroup" { 66 | name = "Management Network" 67 | distributed_virtual_switch_uuid = data.vsphere_distributed_virtual_switch.management.id 68 | 69 | vlan_id = 11 70 | } 71 | 72 | # data "vsphere_distributed_virtual_switch" "workload" { 73 | # name = "Workload vSwitch" 74 | # datacenter_id = data.vsphere_datacenter.datacenter.id 75 | # # max_mtu = 9000 76 | 77 | # uplinks = ["uplink1","uplink2"] 78 | # active_uplinks = ["uplink1"] 79 | # standby_uplinks = ["uplink2"] 80 | 81 | # host { 82 | # host_system_id = data.vsphere_host.host.0.id 83 | # devices = ["${var.workload_network_interfaces}"] 84 | # } 85 | 86 | # host { 87 | # host_system_id = data.vsphere_host.host.1.id 88 | # devices = ["${var.workload_network_interfaces}"] 89 | # } 90 | 91 | # host { 92 | # host_system_id = data.vsphere_host.host.2.id 93 | # devices = ["${var.workload_network_interfaces}"] 94 | # } 95 | # } 96 | 97 | # resource "vsphere_distributed_port_group" "workload_portgroup" { 98 | # name = "Distributed VM Network" 99 | # distributed_virtual_switch_uuid = vsphere_distributed_virtual_switch.workload.id 100 | 101 | # vlan_id = 1000 102 | # } 103 | 104 | # Returns all clusters 105 | output "management_vswitch" { 106 | value = data.vsphere_distributed_virtual_switch.management.id 107 | } 108 | 109 | # # Only returns Cluster01 110 | # output "workload_vswitch" { 111 | # value = data.vsphere_distributed_virtual_switch.workload.id 112 | # } 113 | -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/h20.tfvars: -------------------------------------------------------------------------------- 1 | 2 | # General settings 3 | 4 | # A general deployment name for tagging or unique reference purposes 5 | deployment_name = "h2o" 6 | 7 | # vSphere settings 8 | 9 | vsphere_hostname = "vc01.h2o-4-328.h2o.vmware.com" 10 | vsphere_username = "Administrator@vsphere.local" 11 | vsphere_password = "" 12 | vsphere_insecure = true 13 | 14 | vsphere_datacenter = "vc01" 15 | vsphere_cluster = "vc01cl01" 16 | vsphere_vm_datastore = "vsanDatastore" 17 | vsphere_vm_folder = "" 18 | 19 | esxi_vm_name = "esxi01.h2o-4-328.h2o.vmware.com" 20 | 21 | # General network settings 22 | 23 | management_network_name = "esxi-mgmt" 24 | management_dns_server = "10.79.2.5" 25 | management_ntp_server1 = "time1.oc.vmware.com" 26 | management_domain = "h2o-4-328.h2o.vmware.com" 27 | management_start_ipv4 = "10.220.50.13" 28 | management_end_ipv4 = "10.220.50.19" 29 | management_network_ipv4 = "10.220.50.0" 30 | management_subnet_mask_int = 27 31 | management_subnet_mask_long = "255.255.255.224" 32 | management_default_gateway = "10.220.50.30" 33 | 34 | 35 | data_network_name = "user-workload" 36 | // Data DNS server? 37 | data_network_start_ipv4 = "10.220.50.33" 38 | data_network_end_ipv4 = "10.220.50.39" 39 | data_network_address_count = 7 40 | // Note leaving .40-.61 for Tanzu Workload Network IPs... 41 | // This is because h2o only gives us one routable workload network!!! 42 | data_network_ipv4 = "10.220.50.32" 43 | data_network_subnet_mask_int = 27 44 | data_network_default_gateway = "10.220.50.62" 45 | 46 | workload_network_name = "user-workload" 47 | workload_dns_server = "10.79.2.5" 48 | workload_ntp_server1 = "time1.oc.vmware.com" 49 | workload_start_ipv4 = "10.220.50.40" 50 | workload_address_count = 20 51 | # workload_network_end_ipv4 = "10.220.50.61" 52 | workload_subnet_mask_int = 27 53 | workload_subnet_mask_long = "255.255.255.224" 54 | workload_network_ipv4 = "10.220.50.32" 55 | workload_default_gateway = "10.220.50.62" 56 | 57 | 58 | # AVI settings 59 | 60 | # Avi Server version number to be deployed (E.g. from controller OVA filename) 61 | avi_version = "21.1.2" 62 | # Leave this as admin for now!!! Not tested with other values. 63 | # avi_tenant = "admin" 64 | 65 | # Manually created for Avi before install:- 66 | avi_content_library = "DefaultContentLibrary" 67 | avi_vm_template = "controller-21.1.2-9124" // no .ova 68 | 69 | # The rest we create for the user 70 | # avi_vm_resource_pool = "AVI_CTRL" 71 | avi_vm_name = "avi-controller" // patterned for -1, -2 etc. 72 | 73 | // Specified here because we've not created Avi yet 74 | avi_username = "admin" 75 | # avi_password = "" 76 | 77 | // template customisation 78 | avi_management_hostname = "avi-controller-01" 79 | // TODO support multiple controller instances 80 | avi_management_address_ipv4 = "10.220.50.10" 81 | # avi_management_port = 443 82 | 83 | # avi_cloud_name = "VSphere-Cloud" 84 | 85 | // Make this different from the admin password... 86 | # avi_backup_passphrase = "" 87 | 88 | 89 | 90 | # Tanzu settings here 91 | 92 | tanzu_image_storage_policy_name = "K8sDefaultStoragePolicy" 93 | tanzu_supervisor_storage_policy_name = "K8sDefaultStoragePolicy" 94 | tanzu_ephemeral_storage_policy_name = "K8sDefaultStoragePolicy" 95 | tanzu_default_kubernetes_service_content_library_name = "tanzu-content-library" 96 | 97 | tanzu_supervisor_dns_names = "tanzu.h2o-4-328.h2o.vmware.com" 98 | tanzu_supervisor_start_ipv4 = "10.220.50.20" 99 | # tanzu_supervisor_address_count = 5 100 | -------------------------------------------------------------------------------- /namespace_management/provider.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 VMware, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | 5 | package namespace_management 6 | 7 | import ( 8 | "context" 9 | "net/url" 10 | 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 13 | 14 | "github.com/vmware/govmomi/session/cache" 15 | "github.com/vmware/govmomi/vapi/namespace" 16 | "github.com/vmware/govmomi/vapi/rest" 17 | "github.com/vmware/govmomi/vim25/debug" 18 | ) 19 | 20 | // Provider - 21 | func Provider() *schema.Provider { 22 | return &schema.Provider{ 23 | Schema: map[string]*schema.Schema{ 24 | "vsphere_hostname": &schema.Schema{ 25 | Type: schema.TypeString, 26 | Required: true, 27 | DefaultFunc: schema.EnvDefaultFunc("VSPHERE_HOSTNAME", nil), 28 | }, 29 | "vsphere_username": &schema.Schema{ 30 | Type: schema.TypeString, 31 | Required: true, 32 | DefaultFunc: schema.EnvDefaultFunc("VSPHERE_USERNAME", nil), 33 | }, 34 | "vsphere_password": &schema.Schema{ 35 | Type: schema.TypeString, 36 | Required: true, 37 | Sensitive: true, 38 | DefaultFunc: schema.EnvDefaultFunc("VSPHERE_PASSWORD", nil), 39 | }, 40 | "vsphere_insecure": &schema.Schema{ 41 | Type: schema.TypeBool, 42 | Optional: true, 43 | Default: false, 44 | }, 45 | }, 46 | ResourcesMap: map[string]*schema.Resource{ 47 | "namespace-management_cluster": resourceCluster(), 48 | }, 49 | DataSourcesMap: map[string]*schema.Resource{ 50 | "namespace-management_cluster": dataSourceCluster(), 51 | "namespace-management_clusters": dataSourceClusters(), 52 | }, 53 | ConfigureContextFunc: providerConfigure, 54 | } 55 | } 56 | 57 | func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { 58 | hostname := d.Get("vsphere_hostname").(string) 59 | username := d.Get("vsphere_username").(string) 60 | password := d.Get("vsphere_password").(string) 61 | insecure := d.Get("vsphere_insecure").(bool) 62 | 63 | var diags diag.Diagnostics 64 | 65 | debug.SetProvider(&debug.LogProvider{}) 66 | 67 | if (hostname != "") && (username != "") && (password != "") { 68 | u, err := url.Parse("https://" + hostname) 69 | if err != nil { 70 | diags = append(diags, diag.Diagnostic{ 71 | Severity: diag.Error, 72 | Summary: "Unable to create vSphere GOVMOMI session cache", 73 | Detail: "Unable to parse REST URL", 74 | }) 75 | return nil, diags 76 | } 77 | u.User = url.UserPassword(username, password) 78 | restClient := new(rest.Client) 79 | s := &cache.Session{ 80 | URL: u, 81 | Insecure: insecure, 82 | } 83 | err = s.Login(ctx, restClient, nil) 84 | // TODO determine if we need to log the cached info anywhere in particular 85 | // (E.g. in the $PWD/.terraform folder, and not user .govmoni folder) 86 | 87 | if err != nil { 88 | diags = append(diags, diag.Diagnostic{ 89 | Severity: diag.Error, 90 | Summary: "Unable to login via vSphere GOVMOMI client", 91 | Detail: "Unable to auth user for authenticated vSphere client", 92 | }) 93 | return nil, diags 94 | } 95 | // this creates the client wrapper for NAMESPACE 96 | m := namespace.NewManager(restClient) 97 | // Note: We only set up a REST API as we only need that for vapi 98 | // and namespace functionality - no SOAP required 99 | 100 | return m, diags 101 | } 102 | 103 | return nil, diags 104 | } 105 | -------------------------------------------------------------------------------- /concourse/combinations/adams-homelab.tfvars: -------------------------------------------------------------------------------- 1 | 2 | # General settings 3 | 4 | # A general deployment name for tagging or unique reference purposes 5 | deployment_name = "lab01" 6 | 7 | # vSphere settings 8 | 9 | vsphere_hostname = "vcenter01.lab01.my.cloud" 10 | vsphere_username = "Administrator@lab01.my.cloud" 11 | # vsphere_password = "" 12 | vsphere_insecure = true 13 | 14 | vsphere_datacenter = "lab01.my.cloud" 15 | vsphere_cluster = "Cluster01" 16 | vsphere_vm_datastore = "lab01" 17 | vsphere_vm_folder = "" 18 | 19 | esxi_vm_name = "vesxi01.lab01.my.cloud" 20 | 21 | # General network settings 22 | 23 | management_network_name = "VM Network" 24 | management_dns_server = "10.2.0.1" 25 | management_ntp_server1 = "10.2.0.1" 26 | management_domain = "lab01.my.cloud" 27 | // MUST NOT OVERLAP WITH TANZU SUPERVISOR CLUSTER IP RANGE 28 | management_start_ipv4 = "10.2.0.55" 29 | management_end_ipv4 = "10.2.0.64" 30 | management_network_ipv4 = "10.2.0.0" 31 | management_subnet_mask_int = 24 32 | management_subnet_mask_long = "255.255.255.0" 33 | management_default_gateway = "10.2.0.1" 34 | 35 | 36 | data_network_name = "Avi Data Network" 37 | // Data DNS server? 38 | data_network_start_ipv4 = "172.16.6.16" 39 | data_network_end_ipv4 = "172.16.6.254" 40 | data_network_address_count = 239 41 | // Note leaving .40-.61 for Tanzu Workload Network IPs... 42 | // This is because h2o only gives us one routable workload network!!! 43 | data_network_ipv4 = "172.16.6.0" 44 | data_network_subnet_mask_int = 24 45 | data_network_default_gateway = "172.16.6.1" 46 | 47 | workload_network_name = "K8S Workload01 Network" 48 | # Must be a DNS-1123 compliant name 49 | # workload_network_name = "k8s-workload01-network" 50 | workload_dns_server = "172.16.7.1" 51 | workload_ntp_server1 = "172.16.7.1" 52 | workload_start_ipv4 = "172.16.7.16" 53 | workload_address_count = 239 54 | # workload_network_end_ipv4 = "10.220.50.61" 55 | workload_subnet_mask_int = 24 56 | workload_subnet_mask_long = "255.255.255.0" 57 | workload_network_ipv4 = "172.16.7.0" 58 | workload_default_gateway = "172.16.7.1" 59 | 60 | 61 | # AVI settings 62 | 63 | # Avi Server version number to be deployed (E.g. from controller OVA filename) 64 | avi_version = "21.1.2" 65 | # Leave this as admin for now!!! Not tested with other values. 66 | # avi_tenant = "admin" 67 | 68 | # Manually created for Avi before install:- 69 | avi_content_library = "VMDefaultContentLibrary" 70 | avi_vm_template = "controller-21.1.2-9124" // no .ova 71 | 72 | # The rest we create for the user 73 | # avi_vm_resource_pool = "AVI_CTRL" 74 | avi_vm_name = "avi-controller" // patterned for -1, -2 etc. 75 | 76 | // Specified here because we've not created Avi yet 77 | avi_username = "admin" 78 | # avi_password = "" 79 | 80 | avi_cloud_name = "Default-Cloud" 81 | avi_se_group_name = "Default-Group" 82 | 83 | // template customisation 84 | avi_management_hostname = "avi-controller-1" 85 | // TODO support multiple controller instances 86 | avi_management_address_ipv4 = "10.2.0.48" 87 | # avi_management_port = 443 88 | 89 | # avi_cloud_name = "VSphere-Cloud" 90 | 91 | // Make this different from the admin password... 92 | # avi_backup_passphrase = "" 93 | 94 | 95 | 96 | # Tanzu settings here 97 | 98 | tanzu_image_storage_policy_name = "K8S Storage Policy" 99 | tanzu_supervisor_storage_policy_name = "K8S Storage Policy" 100 | tanzu_ephemeral_storage_policy_name = "K8S Storage Policy" 101 | tanzu_default_kubernetes_service_content_library_name = "TanzuDefaultContentLibrary" 102 | 103 | tanzu_supervisor_dns_names = "tanzu.lab01.my.cloud" 104 | // MUST NOT OVERLAP WITH MANAGEMENT IP RANGE (USED BY AVI SE VMs) 105 | tanzu_supervisor_start_ipv4 = "10.2.0.50" 106 | # tanzu_supervisor_address_count = 5 107 | -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/adams-homelab.tfvars: -------------------------------------------------------------------------------- 1 | 2 | # General settings 3 | 4 | # A general deployment name for tagging or unique reference purposes 5 | deployment_name = "lab01" 6 | 7 | # vSphere settings 8 | 9 | vsphere_hostname = "vcenter01.lab01.my.cloud" 10 | vsphere_username = "Administrator@lab01.my.cloud" 11 | # vsphere_password = "" 12 | # vsphere_insecure = true 13 | 14 | vsphere_datacenter = "lab01.my.cloud" 15 | vsphere_cluster = "Cluster01" 16 | vsphere_vm_datastore = "lab01" 17 | vsphere_vm_folder = "" 18 | 19 | esxi_vm_name = "vesxi01.lab01.my.cloud" 20 | 21 | # General network settings 22 | 23 | management_network_name = "VM Network" 24 | management_dns_server = "10.2.0.1" 25 | management_ntp_server1 = "10.2.0.1" 26 | management_domain = "lab01.my.cloud" 27 | // MUST NOT OVERLAP WITH TANZU SUPERVISOR CLUSTER IP RANGE 28 | management_start_ipv4 = "10.2.0.55" 29 | management_end_ipv4 = "10.2.0.64" 30 | management_network_ipv4 = "10.2.0.0" 31 | management_subnet_mask_int = 24 32 | management_subnet_mask_long = "255.255.255.0" 33 | management_default_gateway = "10.2.0.1" 34 | 35 | 36 | data_network_name = "Avi Data Network" 37 | // Data DNS server? 38 | data_network_start_ipv4 = "172.16.6.16" 39 | data_network_end_ipv4 = "172.16.6.254" 40 | data_network_address_count = 239 41 | // Note leaving .40-.61 for Tanzu Workload Network IPs... 42 | // This is because h2o only gives us one routable workload network!!! 43 | data_network_ipv4 = "172.16.6.0" 44 | data_network_subnet_mask_int = 24 45 | data_network_default_gateway = "172.16.6.1" 46 | 47 | workload_network_name = "K8S Workload01 Network" 48 | # Must be a DNS-1123 compliant name 49 | # workload_network_name = "k8s-workload01-network" 50 | workload_dns_server = "172.16.7.1" 51 | workload_ntp_server1 = "172.16.7.1" 52 | workload_start_ipv4 = "172.16.7.16" 53 | workload_address_count = 239 54 | # workload_network_end_ipv4 = "10.220.50.61" 55 | workload_subnet_mask_int = 24 56 | workload_subnet_mask_long = "255.255.255.0" 57 | workload_network_ipv4 = "172.16.7.0" 58 | workload_default_gateway = "172.16.7.1" 59 | 60 | 61 | # AVI settings 62 | 63 | # Avi Server version number to be deployed (E.g. from controller OVA filename) 64 | avi_version = "21.1.2" 65 | # Leave this as admin for now!!! Not tested with other values. 66 | # avi_tenant = "admin" 67 | 68 | # Manually created for Avi before install:- 69 | avi_content_library = "VMDefaultContentLibrary" 70 | avi_vm_template = "controller-21.1.2-9124" // no .ova 71 | 72 | # The rest we create for the user 73 | # avi_vm_resource_pool = "AVI_CTRL" 74 | avi_vm_name = "avi-controller" // patterned for -1, -2 etc. 75 | 76 | // Specified here because we've not created Avi yet 77 | avi_username = "admin" 78 | # avi_password = "" 79 | 80 | avi_cloud_name = "Default-Cloud" 81 | avi_se_group_name = "Default-Group" 82 | 83 | // template customisation 84 | avi_management_hostname = "avi-controller-1" 85 | // TODO support multiple controller instances 86 | avi_management_address_ipv4 = "10.2.0.48" 87 | # avi_management_port = 443 88 | 89 | # avi_cloud_name = "VSphere-Cloud" 90 | 91 | // Make this different from the admin password... 92 | # avi_backup_passphrase = "" 93 | 94 | 95 | 96 | # Tanzu settings here 97 | 98 | tanzu_image_storage_policy_name = "K8S Storage Policy" 99 | tanzu_supervisor_storage_policy_name = "K8S Storage Policy" 100 | tanzu_ephemeral_storage_policy_name = "K8S Storage Policy" 101 | tanzu_default_kubernetes_service_content_library_name = "TanzuDefaultContentLibrary" 102 | 103 | tanzu_supervisor_dns_names = "tanzu.lab01.my.cloud" 104 | // MUST NOT OVERLAP WITH MANAGEMENT IP RANGE (USED BY AVI SE VMs) 105 | tanzu_supervisor_start_ipv4 = "10.2.0.50" 106 | # tanzu_supervisor_address_count = 5 107 | -------------------------------------------------------------------------------- /docs/cluster-get.md: -------------------------------------------------------------------------------- 1 | # GET namespace-management/cluster/{cluster_id} 2 | 3 | Sample response:- 4 | 5 | ```json 6 | { 7 | "cluster_proxy_config": { 8 | "proxy_settings_source": "VC_INHERITED" 9 | }, 10 | "workload_ntp_servers": [ 11 | "172.16.7.1" 12 | ], 13 | "image_storage": { 14 | "storage_policy": "379687da-922d-4b07-8810-88fe117628b3" 15 | }, 16 | "api_servers": [ 17 | "10.2.0.50", 18 | "172.16.7.16", 19 | "10.2.0.53", 20 | "172.16.7.17", 21 | "10.2.0.51", 22 | "172.16.7.18", 23 | "10.2.0.52" 24 | ], 25 | "api_server_management_endpoint": "10.2.0.50", 26 | "master_NTP_servers": [ 27 | "10.2.0.1" 28 | ], 29 | "default_image_repository": "", 30 | "ephemeral_storage_policy": "379687da-922d-4b07-8810-88fe117628b3", 31 | "service_cidr": { 32 | "address": "10.96.0.0", 33 | "prefix": 23 34 | }, 35 | "size_hint": "SMALL", 36 | "default_image_registry": { 37 | "hostname": "", 38 | "port": 443 39 | }, 40 | "worker_DNS": [ 41 | "172.16.7.1" 42 | ], 43 | "master_DNS": [ 44 | "10.2.0.1" 45 | ], 46 | "network_provider": "VSPHERE_NETWORK", 47 | "master_storage_policy": "379687da-922d-4b07-8810-88fe117628b3", 48 | "master_DNS_search_domains": [ 49 | "lab01.my.cloud" 50 | ], 51 | "stat_summary": { 52 | "cpu_used": 0, 53 | "storage_capacity": 0, 54 | "memory_used": 0, 55 | "cpu_capacity": 0, 56 | "memory_capacity": 0, 57 | "storage_used": 0 58 | }, 59 | "api_server_cluster_endpoint": "172.16.6.18", 60 | "master_management_network": { 61 | "mode": "STATICRANGE", 62 | "address_range": { 63 | "subnet_mask": "255.255.255.0", 64 | "starting_address": "10.2.0.50", 65 | "gateway": "10.2.0.1", 66 | "address_count": 5 67 | }, 68 | "network": "network-12" 69 | }, 70 | "load_balancers": [ 71 | { 72 | "avi_info": { 73 | "server": { 74 | "port": 443, 75 | "host": "10.2.0.48" 76 | }, 77 | "certificate_authority_chain": "-----BEGIN CERTIFICATE-----\nREDACTED\n-----END CERTIFICATE-----", 78 | "username": "admin" 79 | }, 80 | "address_ranges": [], 81 | "provider": "AVI", 82 | "id": "avi-lb" 83 | } 84 | ], 85 | "Master_DNS_names": [ 86 | "k8s-supervisor-api-lb.lab01.my.cloud" 87 | ], 88 | "config_status": "RUNNING", 89 | "tls_management_endpoint_certificate": "-----BEGIN CERTIFICATE-----\nREDACTED\n-----END CERTIFICATE-----\n", 90 | "login_banner": "", 91 | "kubernetes_status": "READY", 92 | "kubernetes_status_messages": [], 93 | "tls_endpoint_certificate": "-----BEGIN CERTIFICATE-----\nREDACTED==\n-----END CERTIFICATE-----\n", 94 | "messages": [], 95 | "default_kubernetes_service_content_library": "fd58ca72-a439-4085-833e-05da5fd743b4", 96 | "workload_networks": { 97 | "supervisor_primary_workload_network": { 98 | "vsphere_network": { 99 | "portgroup": "dvportgroup-5025", 100 | "address_ranges": [ 101 | { 102 | "address": "172.16.7.16", 103 | "count": 239 104 | } 105 | ], 106 | "subnet_mask": "255.255.255.0", 107 | "gateway": "172.16.7.1" 108 | }, 109 | "network_provider": "VSPHERE_NETWORK", 110 | "namespaces": [ 111 | "devteam-a", 112 | "devteam-b", 113 | "devops-team" 114 | ], 115 | "network": "k8s-workload01-network" 116 | } 117 | } 118 | } 119 | ``` -------------------------------------------------------------------------------- /concourse/README.md: -------------------------------------------------------------------------------- 1 | # Concourse automation 2 | 3 | Due to the nature of complex multi-host testing it is not possible 4 | to use standard GitHub CI automation to test everything within 5 | this project's repositories. 6 | 7 | It is insufficient to test just the provider code or to 8 | use the vcsim software to validate this terraform module. 9 | This is because vcsim's code is created to precisely match 10 | the vCenter API Go wrapper in the same repository - so it is 11 | much more likely any bug in the Go API wrapper has been duplicated 12 | in the vcsim software that was created for it. 13 | 14 | There is also the problem of regressions. As new software comes 15 | out we need to run tests against this new infrastructure. 16 | GitHub CI only tests the code at the point when code is created 17 | or a Pull Request submitted. So we need another way to monitor 18 | and detect upstream software changes - as they happen - and test 19 | various supported combinations of VMware software against these 20 | Terraform modules. We also need to do this against multiple 21 | platforms many of which are on different networks. 22 | 23 | This is where Concourse, the generic thing do-er, comes in. 24 | Concourse is a Free and Open Source Software (FOSS) project 25 | that allows for a declarative event-driven approach to testing 26 | software as new builds are released. Concourse proactively 27 | monitors a set of dependencies and if they change will kick 28 | off a build. 29 | 30 | This folder holds the tests and supported platform combinations 31 | and configurations for both the development branch (/concourse/develop) 32 | and main branch (/concourse/main) code repositories. 33 | 34 | When changes occur, the Concourse runtime on multiple servers will 35 | initiate, perform a FRESH infra rollout and automate any additional 36 | practical checks and tests. If any issues are found they will be 37 | summarised and added to a GitHub Issue automatically. 38 | 39 | ## Contents 40 | 41 | * main folder - tests that run against the current release and prior releases 42 | * develop folder - tests that run against the current develop branch 43 | * FOLDER/pipeline-*.yaml - A single pipeline in Concourse 44 | * FOLDER/settings-*.yaml - Settings that automated CI may update 45 | * combinations folder - One file per supported product combination 46 | 47 | ### Common question: Shouldn't the develop tests live in the develop branch? 48 | 49 | No. The develop branch is for 'changes undergoing testing' Thus the develop 50 | branch of the main folder is 'changes we're trying out to tests we want 51 | to apply to current and previous releases' and the develop branch of the 52 | develop folder is 'changes we're trying out to the tests of the things 53 | we're adding/building in the develop branch'. So whilst the naming 54 | is similar, the reasoning for using BOTH folder names and branches is sound. 55 | 56 | ## How this works 57 | 58 | We maintain a set of settings in the settings-combinations.yaml file. This 59 | uses the [experimental instance groups feature](https://concourse-ci.org/instanced-pipelines.html) 60 | of Concourse to instantiate a pipeline per target test environment. 61 | This means a single pipeline definition is instantiated for each version combination 62 | and settings, enabling us to test upcoming versions we've not specifically 63 | coded for upon release. 64 | 65 | ## TODO items 66 | 67 | In order of decreasing value to this project and its users:- 68 | 69 | Immediate / Proof of Concept:- 70 | 71 | * As a new develop merge occurs, run the real tests against a new rollout on Adam's Homelab on 7.0.3 with vDS and Avi 72 | 73 | Before v1.0 release:- 74 | 75 | * Support for NSX-T deployments 76 | * Support for NSX-T with Avi deployments 77 | * Most secure by default (See ../security/README.md for details) settings and tests (poor man's pen testing) 78 | * Automation for current supported (Beyond Sep 2022) version combinations of vSphere with Tanzu (See combinations/ for details) 79 | 80 | Futures:- 81 | 82 | * Help other teams deliver the same for TKGm (Multicloud) and TKGI (integrated) on different cloud platforms 83 | -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/06_tanzu_enable/variables.tf: -------------------------------------------------------------------------------- 1 | 2 | variable "datacenter_name" { 3 | type = string 4 | # default = "lab01.my.cloud" 5 | } 6 | # THE VSPHERE CLUSTER NAME (converted to ID in main.tf) 7 | variable "cluster_name" { 8 | type = string 9 | } 10 | # variable "cluster_id" { 11 | # type = string 12 | # } 13 | variable "image_storage_policy_name" { 14 | type = string 15 | } 16 | # variable "image_storage_policy_id" { 17 | # type = string 18 | # } 19 | variable "master_storage_policy_name" { 20 | type = string 21 | } 22 | # variable "master_storage_policy_id" { 23 | # type = string 24 | # } 25 | variable "ephemeral_storage_policy_name" { 26 | type = string 27 | } 28 | # variable "ephemeral_storage_policy_id" { 29 | # type = string 30 | # } 31 | variable "default_kubernetes_service_content_library_name" { 32 | type = string 33 | } 34 | # variable "default_kubernetes_service_content_library_id" { 35 | # type = string 36 | # } 37 | # These are vSphere on Tanzu defaults, not mine 38 | # WARNING YOU WILL LIKELY NEED TO OVERRIDE THE POD NETWORK 39 | # AND REPLACE WITH A 172.66.0.0/16 OR SIMILAR 40 | # (Some NSX-T and vSphere installs use 192.168, as do home networks) 41 | variable "service_cidr_network" { 42 | type = string 43 | default = "10.96.0.0" 44 | } 45 | variable "service_cidr_mask" { 46 | type = number 47 | default = 12 48 | } 49 | variable "pod_cidr_network" { 50 | type = string 51 | default = "192.168.0.0" 52 | } 53 | variable "pod_cidr_mask" { 54 | type = number 55 | default = 16 56 | } 57 | variable "master_dns_servers" { 58 | type = string 59 | } 60 | variable "master_dns_search_domain" { 61 | type = string 62 | } 63 | variable "master_ntp_servers" { 64 | type = string 65 | } 66 | variable "master_dns_names" { 67 | type = string 68 | } 69 | variable "master_network_provider" { 70 | type = string 71 | } 72 | # TODO verify how to look this up (network-15 NOT dvportgroup-12) 73 | variable "master_network_name" { 74 | type = string 75 | } 76 | # variable "master_network_id" { 77 | # type = string 78 | # } 79 | variable "master_network_ip_assignment_mode" { 80 | type = string 81 | default = "" 82 | } 83 | variable "master_network_static_gateway_ipv4" { 84 | type = string 85 | } 86 | variable "master_network_static_starting_address_ipv4" { 87 | type = string 88 | } 89 | variable "master_network_static_address_count" { 90 | type = number 91 | } 92 | variable "master_network_static_subnet_mask" { 93 | type = string 94 | } 95 | 96 | variable "data_network_static_starting_address_ipv4" { 97 | type = string 98 | } 99 | variable "data_network_static_address_count" { 100 | type = number 101 | } 102 | 103 | variable "worker_dns_servers" { 104 | type = string 105 | } 106 | variable "workload_ntp_servers" { 107 | type = string 108 | } 109 | variable "primary_workload_network_name" { 110 | type = string 111 | } 112 | variable "primary_workload_network_provider" { 113 | type = string 114 | } 115 | variable "primary_workload_network_static_gateway_ipv4" { 116 | type = string 117 | } 118 | variable "primary_workload_network_static_starting_address_ipv4" { 119 | type = string 120 | } 121 | variable "primary_workload_network_static_address_count" { 122 | type = number 123 | default = 5 124 | } 125 | variable "primary_workload_network_static_subnet_mask" { 126 | type = string 127 | } 128 | variable "primary_workload_network_vsphere_portgroup_name" { 129 | type = string 130 | } 131 | # variable "primary_workload_network_vsphere_portgroup_id" { 132 | # type = string 133 | # } 134 | 135 | variable "load_balancer_provider" { 136 | type = string 137 | } 138 | variable "load_balancer_avi_host" { 139 | type = string 140 | } 141 | variable "load_balancer_avi_port" { 142 | type = number 143 | } 144 | variable "load_balancer_avi_username" { 145 | type = string 146 | default = "admin" 147 | } 148 | variable "load_balancer_avi_password" { 149 | type = string 150 | sensitive = true 151 | } 152 | variable "load_balancer_avi_ca_chain" { 153 | type = string 154 | } 155 | -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/05_avi_configure/variables.tf: -------------------------------------------------------------------------------- 1 | 2 | // Avi Provider variables 3 | variable "avi_username" { 4 | type = string 5 | default = "" 6 | } 7 | 8 | variable "avi_password" { 9 | type = string 10 | default = "" 11 | sensitive = true 12 | } 13 | 14 | 15 | 16 | 17 | variable "vm_datacenter" { 18 | type = string 19 | default = "vc01" 20 | } 21 | 22 | variable "vm_cluster" { 23 | type = string 24 | default = "vc01cl01" 25 | } 26 | 27 | variable "esxi_vm_name" { 28 | type = string 29 | default = "vesxi01" 30 | } 31 | 32 | variable "vcenter_username" { 33 | type = string 34 | default = "administrator@vsphere.local" 35 | } 36 | 37 | variable "vcenter_password" { 38 | type = string 39 | default = "" 40 | sensitive = true 41 | } 42 | 43 | variable "vcenter_url" { 44 | type = string 45 | # NOTE: Hostname ONLY, no protocol or path 46 | default = "" 47 | } 48 | 49 | 50 | 51 | // New variables for full OVA settings:- 52 | // TODO support multiple avi controllers 53 | variable "avi_management_hostname" { 54 | type = string 55 | default = "avi-controller-1" 56 | } 57 | variable "avi_management_domain" { 58 | type = string 59 | default = "example.com" 60 | } 61 | variable "avi_management_dns_server" { 62 | type = string 63 | default = "10.2.0.1" 64 | } 65 | variable "avi_management_dns_suffix" { 66 | type = string 67 | default = "example.com" 68 | } 69 | 70 | variable "avi_management_network_name" { 71 | type = string 72 | default = "" 73 | } 74 | variable "avi_management_ip_address" { 75 | type = string 76 | default = "10.2.0.50" 77 | } 78 | variable "avi_management_ip_network" { 79 | type = string 80 | default = "10.2.0.0" 81 | } 82 | # Start IP range for avi_se instances 83 | variable "avi_management_ip_address_start" { 84 | type = string 85 | default = "10.2.0.53" 86 | } 87 | variable "avi_management_ip_address_end" { 88 | type = string 89 | default = "10.2.0.59" 90 | } 91 | # variable "avi_management_subnet_mask_dot" { 92 | # type = string 93 | # default = "255.255.255.224" 94 | # } 95 | 96 | # The following is an integer but written as a string (Avi provider requirement) 97 | variable "avi_management_subnet_mask_int" { 98 | type = string 99 | default = "24" 100 | } 101 | variable "avi_management_default_gateway" { 102 | type = string 103 | default = "10.2.0.1" 104 | } 105 | # TODO support multiple 106 | variable "avi_ntp_server" { 107 | type = string 108 | default = "10.2.0.1" 109 | } 110 | 111 | # More variables for configuring deployment of Avi:- 112 | 113 | # This is used in VM tags for "vm_group_name" 114 | # and has "avi-" prepended 115 | # and has "-controller" or "-se" appended 116 | variable "avi_deployment_name" { 117 | type = string 118 | default = "deployment-1" 119 | } 120 | 121 | variable "avi_cloud_name" { 122 | type = string 123 | default = "Default-Cloud" 124 | } 125 | variable "avi_se_group_name" { 126 | type = string 127 | default = "Default-Group" 128 | } 129 | 130 | // UNUSED 131 | # variable "avi_management_ssh_key" { 132 | # type = string 133 | # default = "" 134 | # } 135 | 136 | 137 | # Licensing related variables 138 | # Note: if true, requires an Avi Enterprise (not essentials) license 139 | variable "avi_prefer_static_routes" { 140 | type = bool 141 | default = true 142 | } 143 | 144 | # Avi Data network configuration 145 | variable "avi_data_network_name" { 146 | type = string 147 | default = "AviDataNetwork" 148 | } 149 | variable "avi_data_network_network_ipv4" { 150 | type = string 151 | default = "10.220.50.32" 152 | } 153 | variable "avi_data_network_start_ipv4" { 154 | type = string 155 | default = "10.220.50.33" 156 | } 157 | variable "avi_data_network_end_ipv4" { 158 | type = string 159 | default = "10.220.50.61" 160 | } 161 | variable "avi_data_network_subnet_mask_int" { 162 | type = string 163 | default = "27" 164 | } 165 | variable "avi_data_network_default_gateway" { 166 | type = string 167 | default = "10.220.50.62" 168 | } 169 | 170 | # System config elements for confirmation 171 | variable "avi_backup_passphrase" { 172 | type = string 173 | default = "" 174 | } -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/06_tanzu_enable/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | // Force local binary use, rather than public binary 4 | namespace-management = { 5 | version = ">= 0.1" 6 | source = "vmware.com/vcenter/namespace-management" 7 | # Use this for your user terraform scripts (above is for our devs only) 8 | # source = "vmware/namespace-management" 9 | } 10 | vsphere = { 11 | version = ">= 2.1.1" 12 | source = "hashicorp/vsphere" 13 | } 14 | } 15 | } 16 | 17 | data "vsphere_datacenter" "dc" { 18 | name = var.datacenter_name 19 | } 20 | 21 | # Converts the vSphere cluster name to its id 22 | data "vsphere_compute_cluster" "fetch" { 23 | name = var.cluster_name 24 | datacenter_id = data.vsphere_datacenter.dc.id 25 | } 26 | # ALTERNATIVE to the above, but cannot use as SC may not exist yet:- 27 | # data "namespace-management_cluster" "fetch" { 28 | # name = var.cluster_name 29 | # # Note: No need for datacenter_id in this API 30 | # } 31 | 32 | data "vsphere_network" "mgmt_net" { 33 | name = var.master_network_name 34 | datacenter_id = data.vsphere_datacenter.dc.id 35 | } 36 | data "vsphere_storage_policy" "image_policy" { 37 | name = var.image_storage_policy_name 38 | } 39 | data "vsphere_storage_policy" "master_policy" { 40 | name = var.master_storage_policy_name 41 | } 42 | data "vsphere_storage_policy" "ephemeral_policy" { 43 | name = var.ephemeral_storage_policy_name 44 | } 45 | 46 | data "vsphere_content_library" "default_k8s_library" { 47 | name = var.default_kubernetes_service_content_library_name 48 | } 49 | 50 | data "vsphere_network" "workload_pg" { 51 | datacenter_id = data.vsphere_datacenter.dc.id 52 | # type = "DISTRIBUTED_PORTGROUP" 53 | name = var.primary_workload_network_vsphere_portgroup_name 54 | } 55 | 56 | # Enables the Tanzu Supervisor Cluster 57 | resource "namespace-management_cluster" "supervisor" { 58 | // No ID specified! Let the READ operation lookup via id in terraform 59 | 60 | cluster_id = data.vsphere_compute_cluster.fetch.id 61 | image_storage_policy_id = data.vsphere_storage_policy.image_policy.id 62 | master_storage_policy_id = data.vsphere_storage_policy.master_policy.id 63 | ephemeral_storage_policy_id = data.vsphere_storage_policy.ephemeral_policy.id 64 | default_kubernetes_service_content_library_id = data.vsphere_content_library.default_k8s_library.id 65 | service_cidr_network = var.service_cidr_network 66 | service_cidr_mask = var.service_cidr_mask 67 | master_dns_servers = var.master_dns_servers 68 | master_dns_search_domain = var.master_dns_search_domain 69 | master_ntp_servers = var.master_ntp_servers 70 | master_dns_names = var.master_dns_names 71 | master_network_provider = var.master_network_provider 72 | master_network_id = data.vsphere_network.mgmt_net.id 73 | // Since 7.0u3:- 74 | # master_network_ip_assignment_mode = var.master_network_ip_assignment_mode 75 | master_network_static_gateway_ipv4 = var.master_network_static_gateway_ipv4 76 | master_network_static_starting_address_ipv4 = var.master_network_static_starting_address_ipv4 77 | master_network_static_address_count = var.master_network_static_address_count 78 | master_network_static_subnet_mask = var.master_network_static_subnet_mask 79 | 80 | data_network_static_starting_address_ipv4 = var.data_network_static_starting_address_ipv4 81 | data_network_static_address_count = var.data_network_static_address_count 82 | 83 | worker_dns_servers = var.worker_dns_servers 84 | workload_ntp_servers = var.workload_ntp_servers 85 | primary_workload_network_name = data.vsphere_network.workload_pg.id 86 | primary_workload_network_provider = var.primary_workload_network_provider 87 | primary_workload_network_static_gateway_ipv4 = var.primary_workload_network_static_gateway_ipv4 88 | primary_workload_network_static_starting_address_ipv4 = var.primary_workload_network_static_starting_address_ipv4 89 | primary_workload_network_static_address_count = var.primary_workload_network_static_address_count 90 | primary_workload_network_static_subnet_mask = var.primary_workload_network_static_subnet_mask 91 | primary_workload_network_vsphere_portgroup_id = data.vsphere_network.workload_pg.id 92 | load_balancer_provider = var.load_balancer_provider 93 | load_balancer_avi_host = var.load_balancer_avi_host 94 | load_balancer_avi_port = var.load_balancer_avi_port 95 | load_balancer_avi_username = var.load_balancer_avi_username 96 | load_balancer_avi_password = var.load_balancer_avi_password 97 | load_balancer_avi_ca_chain = var.load_balancer_avi_ca_chain 98 | } 99 | 100 | # Only the newly enabled cluster (including the cluster ID) 101 | output "cluster" { 102 | value = namespace-management_cluster.supervisor 103 | } 104 | 105 | # Convenience output for future commands 106 | output "datacenter_id" { 107 | value = data.vsphere_datacenter.dc.id 108 | } 109 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in terraform-provider-namespace-management project and our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at oss-coc@vmware.com. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series 85 | of actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or 92 | permanent ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within 112 | the community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.0, available at 118 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 119 | 120 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 121 | enforcement ladder](https://github.com/mozilla/diversity). 122 | 123 | [homepage]: https://www.contributor-covenant.org 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | https://www.contributor-covenant.org/faq. Translations are available at 127 | https://www.contributor-covenant.org/translations. 128 | -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/variables.tf: -------------------------------------------------------------------------------- 1 | 2 | 3 | # This is used in VM tags for "vm_group_name" 4 | # and has "avi-" prepended 5 | # and has "-controller" or "-se" appended 6 | variable "deployment_name" { 7 | type = string 8 | default = "deployment-1" 9 | } 10 | 11 | 12 | # vSphere settings 13 | 14 | variable "vsphere_datacenter" { 15 | type = string 16 | } 17 | 18 | variable "vsphere_cluster" { 19 | type = string 20 | } 21 | 22 | variable "esxi_vm_name" { 23 | type = string 24 | } 25 | 26 | variable "vsphere_username" { 27 | type = string 28 | default = "administrator@vsphere.local" 29 | } 30 | 31 | variable "vsphere_password" { 32 | type = string 33 | sensitive = true 34 | } 35 | variable "vsphere_insecure" { 36 | type = bool 37 | default = false 38 | } 39 | 40 | variable "vsphere_hostname" { 41 | type = string 42 | } 43 | 44 | variable "vsphere_api_timeout" { 45 | type = number 46 | default = 60 47 | } 48 | 49 | 50 | variable "vsphere_vm_datastore" { 51 | type = string 52 | } 53 | variable "vsphere_vm_folder" { 54 | type = string 55 | } 56 | 57 | # Networking settings 58 | 59 | variable "management_network_name" { 60 | type = string 61 | } 62 | variable "management_dns_server" { 63 | type = string 64 | } 65 | variable "management_ntp_server1" { 66 | type = string 67 | } 68 | variable "management_domain" { 69 | type = string 70 | } 71 | variable "management_start_ipv4" { 72 | type = string 73 | } 74 | variable "management_end_ipv4" { 75 | type = string 76 | } 77 | variable "management_network_ipv4" { 78 | type = string 79 | } 80 | variable "management_subnet_mask_int" { 81 | type = number 82 | } 83 | variable "management_subnet_mask_long" { 84 | type = string 85 | } 86 | variable "management_default_gateway" { 87 | type = string 88 | } 89 | 90 | variable "data_network_name" { 91 | type = string 92 | } 93 | # variable "data_dns_server" { 94 | # type = string 95 | # } 96 | # variable "data_ntp_server1" { 97 | # type = string 98 | # } 99 | # variable "data_domain" { 100 | # type = string 101 | # } 102 | variable "data_network_start_ipv4" { 103 | type = string 104 | } 105 | variable "data_network_end_ipv4" { 106 | type = string 107 | } 108 | variable "data_network_address_count" { 109 | type = number 110 | } 111 | variable "data_network_ipv4" { 112 | type = string 113 | } 114 | variable "data_network_subnet_mask_int" { 115 | type = number 116 | } 117 | # variable "data_subnet_mask_long" { 118 | # type = string 119 | # } 120 | variable "data_network_default_gateway" { 121 | type = string 122 | } 123 | 124 | variable "workload_network_name" { 125 | type = string 126 | } 127 | variable "workload_dns_server" { 128 | type = string 129 | } 130 | variable "workload_ntp_server1" { 131 | type = string 132 | } 133 | variable "workload_start_ipv4" { 134 | type = string 135 | } 136 | variable "workload_address_count" { 137 | type = number 138 | } 139 | # variable "workload_end_ipv4" { 140 | # type = string 141 | # } 142 | variable "workload_network_ipv4" { 143 | type = string 144 | } 145 | variable "workload_subnet_mask_int" { 146 | type = number 147 | } 148 | variable "workload_subnet_mask_long" { 149 | type = string 150 | } 151 | variable "workload_default_gateway" { 152 | type = string 153 | } 154 | 155 | 156 | 157 | 158 | // Avi settings 159 | 160 | variable "avi_username" { 161 | type = string 162 | default = "admin" 163 | } 164 | 165 | variable "avi_password" { 166 | type = string 167 | sensitive = true 168 | } 169 | 170 | // New variables for full OVA settings:- 171 | // TODO support multiple avi controllers 172 | # Name for the controller VM (MAY also be its hostname (or FQDN)) 173 | variable "avi_management_hostname" { 174 | type = string 175 | } 176 | variable "avi_management_address_ipv4" { 177 | type = string 178 | } 179 | variable "avi_management_port" { 180 | type = number 181 | default = 443 182 | } 183 | # More variables for configuring deployment of Avi:- 184 | 185 | variable "avi_version" { 186 | type = string 187 | } 188 | variable "avi_tenant" { 189 | type = string 190 | # DO NOT CHANGE THIS YET 191 | default = "admin" 192 | } 193 | 194 | variable "avi_content_library" { 195 | type = string 196 | } 197 | variable "avi_vm_template" { 198 | type = string 199 | } 200 | variable "avi_vm_resource_pool" { 201 | type = string 202 | default = "AVI_CTRL" 203 | } 204 | variable "avi_vm_name" { 205 | type = string 206 | } 207 | 208 | variable "avi_cloud_name" { 209 | type = string 210 | # DO NOT USE Default-Cloud UNDER ANY CIRCUMSTANCES 211 | # Terraform delete does NOT reset Default-Cloud 212 | # default = "Default-Cloud" 213 | # default = "vSphere-Cloud" 214 | } 215 | variable "avi_se_group_name" { 216 | type = string 217 | } 218 | 219 | 220 | # Licensing related variables 221 | # Note: if true, requires an Avi Enterprise (not essentials) license 222 | variable "avi_prefer_static_routes" { 223 | type = bool 224 | default = true 225 | } 226 | 227 | # System config elements for confirmation 228 | variable "avi_backup_passphrase" { 229 | type = string 230 | sensitive = true 231 | } 232 | 233 | 234 | 235 | # TANZU specific settings 236 | 237 | variable "tanzu_image_storage_policy_name" { 238 | type = string 239 | } 240 | variable "tanzu_supervisor_storage_policy_name" { 241 | type = string 242 | } 243 | variable "tanzu_ephemeral_storage_policy_name" { 244 | type = string 245 | } 246 | variable "tanzu_default_kubernetes_service_content_library_name" { 247 | type = string 248 | } 249 | 250 | variable "tanzu_supervisor_start_ipv4" { 251 | type = string 252 | } 253 | variable "tanzu_supervisor_address_count" { 254 | type = number 255 | default = 5 256 | } 257 | variable "tanzu_supervisor_dns_names" { 258 | type = string 259 | } 260 | 261 | variable "tanzu_service_cidr_network" { 262 | type = string 263 | default = "10.96.0.0" 264 | } 265 | variable "tanzu_service_cidr_mask" { 266 | type = number 267 | default = 12 268 | } 269 | 270 | variable "tanzu_network_ip_assignment_mode" { 271 | type = string 272 | default = "" 273 | } 274 | -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/04_avi_controller/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | avi = { 4 | # version = ">= 21.1" 5 | source = "vmware/avi" 6 | } 7 | vsphere = { 8 | version = ">= 2.1.1" 9 | source = "hashicorp/vsphere" 10 | } 11 | } 12 | } 13 | 14 | data "vsphere_datacenter" "dc" { 15 | name = var.vm_datacenter 16 | } 17 | 18 | data "vsphere_datastore" "datastore" { 19 | name = var.vm_datastore 20 | datacenter_id = data.vsphere_datacenter.dc.id 21 | } 22 | 23 | data "vsphere_resource_pool" "pool" { 24 | name = var.vm_resource_pool 25 | datacenter_id = data.vsphere_datacenter.dc.id 26 | } 27 | 28 | data "vsphere_network" "network" { 29 | name = var.vm_network 30 | datacenter_id = data.vsphere_datacenter.dc.id 31 | } 32 | 33 | data "vsphere_content_library" "library" { 34 | name = var.content_library 35 | } 36 | 37 | data "vsphere_content_library_item" "item" { 38 | name = var.vm_template 39 | library_id = data.vsphere_content_library.library.id 40 | type = "" // Is actually a Read item, not a set one 41 | } 42 | 43 | # TODO multiple servers See https://github.com/vmware/terraform-provider-avi/blob/69020e02183968fd05244eb9b11cd9fb89747706/modules/nia/pool/main.tf#L72 44 | 45 | resource "vsphere_virtual_machine" "vm" { 46 | // For settings with vSphere networking:- 47 | // See: https://docs.vmware.com/en/VMware-vSphere/7.0/vmware-vsphere-with-tanzu/GUID-CBA041AB-DC1D-4EEC-8047-184F2CF2FE0F.html 48 | 49 | // Is name the same as hostname? (doubtful - VM name in vCenter) 50 | name = "${var.vm_name}-${count.index+1}" 51 | resource_pool_id = data.vsphere_resource_pool.pool.id 52 | datastore_id = data.vsphere_datastore.datastore.id 53 | count = 1 54 | num_cpus = 4 55 | memory = 24576 56 | folder = var.vm_folder 57 | // Management network (only interface in OVA) 58 | // TODO determine if OVA setting is automatically linked to this 59 | network_interface { 60 | network_id = data.vsphere_network.network.id 61 | } 62 | lifecycle { 63 | ignore_changes = [guest_id] 64 | } 65 | disk { 66 | label = "disk1" 67 | size = 130 68 | // Thin unless you have a very good reason not to 69 | thin_provisioned = true 70 | } 71 | clone { 72 | template_uuid = data.vsphere_content_library_item.item.id 73 | } 74 | vapp { 75 | properties = { 76 | // Missing from Avi examples, but in the OVA:- 77 | // TODO how to do NTP, DNS, search domain??? 78 | # dns_server_list = [var.avi_management_dns_server] 79 | # dns_suffix_list = [var.avi_management_dns_suffix] 80 | # linux_options { 81 | # host_name = var.avi_management_hostname 82 | # domain = var.avi_management_domain 83 | # } 84 | 85 | // Hostname for the controller vm 86 | // OVA Docs: Hostname of Avi controller (For modification by NSX Manager only. This field should not be filled in or modified by the user directly) 87 | hostname = var.avi_management_hostname 88 | // OVA Docs: IP address for the Management Interface. Leave blank if using DHCP. Example: 192.168.10.4 89 | // Tanzu Docs: Enter the IP address for the [this] Controller VM, such as 10.999.17.51. 90 | // REQUIRED field if DHCP is not being used (which normally it isn't for a controller) 91 | mgmt-ip = var.avi_management_ip_address 92 | // OVA Docs: Subnet mask for the Management Interface. Leave blank if using DHCP. Example : 24 or 255.255.255.0 93 | // Tanzu Docs: Enter the subnet mask, such as 255.255.255.0. 94 | // REQUIRED if management ip specified (Static IP) 95 | mgmt-mask = var.avi_management_subnet_mask_int 96 | // OVA Docs: Optional default gateway for the Management Network. Leave blank if using DHCP. 97 | // Tanzu Docs: Enter the default gateway for the Management Network, such as 10.199.17.235. 98 | // REQUIRED if management ip specified (Static IP) 99 | default-gw = var.avi_management_default_gateway 100 | 101 | // OVA Docs: Sysadmin login authentication key 102 | // Tanzu Docs: Paste the contents of a private key (optional). 103 | // This is the private SSH key that you require to SSH into the VM. You can create it using OpenSSH or PuTTY. 104 | // Optional: Don't specify for now. sysadmin-public-key = var.avi_management_ssh_key 105 | // NSX-T Node ID 106 | // OVA Docs: NSX-T Node ID to uniquely identify node in a NSX-T cluster (For modification by NSX Manager only. This field should not be filled in or modified by the user directly) 107 | // nsx-t-node-id = "" 108 | // NSX-T IP Address 109 | // OVA Docs: IP address of the NSX-T which will manage this controller (For modification by NSX Manager only. This field should not be filled in or modified by the user directly) 110 | // nsx-t-ip = "" 111 | // Authentication token of NSX-T 112 | // OVA Docs: Authentication token of the NSX-T which will manage this controller (For modification by NSX Manager only. This field should not be filled in or modified by the user directly) 113 | // nsx-t-auth-token = "" 114 | // NSX-T thumbprint 115 | // OVA Docs: Thumbprint of the MP node of NSX-T which will manage this controller (For modification by NSX Manager only. This field should not be filled in or modified by the user directly) 116 | // nsx-t-thumbprint = "" 117 | } // properties 118 | } // vapp 119 | wait_for_guest_ip_timeout = 30 120 | } 121 | 122 | provider "avi" { 123 | avi_username = var.avi_username 124 | avi_password = "58NFaGDJm(PJH0G" 125 | # avi_controller = vsphere_virtual_machine.vm[0].default_ip_address 126 | avi_tenant = "admin" 127 | 128 | # For after creation by vsphere 129 | avi_controller = var.avi_management_ip_address 130 | 131 | # Required for Terraform provider not to puke 132 | # Without this it complains about 'common_criteria' being there (even though false by default) as the terraform provider defaults to v18.8 133 | avi_version = "21.1.2" 134 | } 135 | 136 | # Can take up to 11 minutes approximately before Avi responds 137 | data "avi_systemconfiguration" "ensure_server_responding" { 138 | depends_on = [vsphere_virtual_machine.vm] 139 | } 140 | 141 | 142 | # WARNING ENSURE THIS IS THE LAST SO AS NOT TO PUKE ON RETRY 143 | 144 | resource "avi_useraccount" "avi_user" { 145 | username = var.avi_username 146 | name = var.avi_username 147 | # id = var.avi_username 148 | # Stupidly, the provider relies on old and new password differing, and not being empty 149 | old_password = "58NFaGDJm(PJH0G" 150 | # Even more stupidly, since v17.2.2 the default admin password is a hardcoded value available from the customer portal. This is NOT DOCUMENTED in ANY of the terraform examples from Avi 151 | password = var.avi_password 152 | 153 | depends_on = [data.avi_systemconfiguration.ensure_server_responding] 154 | } 155 | 156 | # resource "avi_user" "avi_user" { 157 | # # id = var.avi_username 158 | # name = var.avi_username 159 | # uuid = var.avi_username 160 | # # username = var.avi_username 161 | # password = var.avi_password 162 | 163 | # depends_on = [data.avi_systemconfiguration.ensure_server_responding] 164 | # } 165 | 166 | 167 | # TODO vm-vm host anti-affinity groups for Avi-Controller VMs 168 | 169 | 170 | # Maybe output the full calculated tag values too for VMs 171 | # output "avi_cluster_output" { 172 | # value = avi_cluster.vmware_cluster 173 | # } 174 | 175 | output "initial_configuration" { 176 | value = data.avi_systemconfiguration.ensure_server_responding 177 | } 178 | 179 | output "admin_user" { 180 | value = avi_useraccount.avi_user 181 | } -------------------------------------------------------------------------------- /concourse/combinations/README.md: -------------------------------------------------------------------------------- 1 | # Supported combinations 2 | 3 | We only test currently supported versions, and upcoming versions. 4 | We presume upcoming versions are backward compatible with the latest 5 | previous minor version unless told otherwise. 6 | 7 | Note: Currently (July 2022) this project supports its modules and providers for 8 | Terraform on a best efforts, open source basis. This is a Tanzu Labs project rather 9 | than a fully VMware supported product offering at this time. If you'd like this to 10 | change please provide regular feedback to your VMware Tanzu Sales Engineer. 11 | 12 | Informal advice: Always use the most recent build of TKR available for the 13 | Supervisor cluster at the time of install, and use the most recent Ubuntu build 14 | for the workload cluster at the time of workload cluster creation. 15 | 16 | Note: 17 | - It is possible to run a ESXi/vSphere system at v7.0.3 but have a vDS configured 18 | at v7.0.2. (The opposite isn't of course possible.) 19 | - 'TESTING' means we have activated testing for the develop branch at least. 20 | - 'FUTURE' means we aim to test this but haven't developed automation for this yet. 21 | - [1] Means we're testing on a newly created build dynamically 22 | - [2] Means we're testing on a 7.0.2 internal VMware environment (H2o) 23 | - [3] Means we're testing on a 7.0.3 existing system (Adams Homelab) 24 | 25 | |Combo Name|vSphere/ESXi|LB|Networking|Supervisor TKR|Workload TKR|Support Ends|Notes| 26 | |---|---|---|---|---|---|---|---| 27 | |**7.0.3e**||||||| 28 | |SOON [3]|7.0.3e|Avi-21.1.2|vDS-7.0.2|v1.22.9---vmware.1-tkg.1.cc71bc8|v1.22.9---vmware.1-tkg.1.cc71bc8|?|CSI 2.4.1, Antrea 1.2.3. New Release. No known TKG issues| 29 | |**7.0.3,7.0.3e**||||||| 30 | |TESTING [2][3]|7.0.3|Avi-21.1.2|vDS-7.0.2|v1.22.9---vmware.1-tkg.1.cc71bc8|v1.22.9---vmware.1-tkg.1.cc71bc8|?|New Release. No known TKG Issues| 31 | |TESTING [2][3]|7.0.3|Avi-21.1.2|vDS-7.0.2|v1.22.9---vmware.1-tkg.1.cc71bc8|ubuntu-2004-v1.21.6---vmware.1-tkg.1|?|Known TKG Issues| 32 | |FUTURE [2][3]|7.0.3|Avi-21.1.2|vDS-7.0.2|v1.22.9---vmware.1-tkg.1.cc71bc8|ubuntu-2004-v1.20.8-vmware.1-tkg.2|?|For AI/ML. Known TKG Issues| 33 | |FUTURE [2][3]|7.0.3|Avi-21.1.2|vDS-7.0.2|v1.22.9---vmware.1-tkg.1.cc71bc8|v1.21.6+vmware.1-tkg.1.b3d708a|?|Known TKG Issues| 34 | ||7.0.3|Avi-21.1.2|vDS-7.0.2|v1.22.9---vmware.1-tkg.1.cc71bc8|v1.21.2---vmware.1-tkg.1.ee25d55|?|Known TKG Issues| 35 | |FUTURE|7.0.3|Avi-21.1.2|vDS-7.0.2|v1.21.6+vmware.1-tkg.1.b3d708a|v1.22.9---vmware.1-tkg.1.cc71bc8|?|Known TKG Issues| 36 | |FUTURE|7.0.3|Avi-21.1.2|vDS-7.0.2|v1.21.6+vmware.1-tkg.1.b3d708a|ubuntu-2004-v1.21.6---vmware.1-tkg.1|?|Known TKG Issues| 37 | |FUTURE|7.0.3|Avi-21.1.2|vDS-7.0.2|v1.21.6+vmware.1-tkg.1.b3d708a|ubuntu-2004-v1.20.8-vmware.1-tkg.2|?|For AI/ML. Known TKG Issues| 38 | |TESTING [3]|7.0.3|Avi-21.1.2|vDS-7.0.2|v1.21.6+vmware.1-tkg.1.b3d708a|v1.21.6+vmware.1-tkg.1.b3d708a|?|Known TKG Issues| 39 | ||7.0.3|Avi-21.1.2|vDS-7.0.2|v1.21.6+vmware.1-tkg.1.b3d708a|v1.21.2---vmware.1-tkg.1.ee25d55|?|Known TKG Issues| 40 | ||7.0.3|Avi-21.1.2|vDS-7.0.2|v1.21.2---vmware.1-tkg.1.ee25d55|v1.22.9---vmware.1-tkg.1.cc71bc8|?|Known TKG Issues| 41 | ||7.0.3|Avi-21.1.2|vDS-7.0.2|v1.21.2---vmware.1-tkg.1.ee25d55|ubuntu-2004-v1.21.6---vmware.1-tkg.1|?|Known TKG Issues| 42 | ||7.0.3|Avi-21.1.2|vDS-7.0.2|v1.21.2---vmware.1-tkg.1.ee25d55|ubuntu-2004-v1.20.8-vmware.1-tkg.2|?|For AI/ML. Known TKG Issues| 43 | ||7.0.3|Avi-21.1.2|vDS-7.0.2|v1.21.2---vmware.1-tkg.1.ee25d55|v1.21.6+vmware.1-tkg.1.b3d708a|?|Known TKG Issues| 44 | ||7.0.3|Avi-21.1.2|vDS-7.0.2|v1.21.2---vmware.1-tkg.1.ee25d55|v1.21.2---vmware.1-tkg.1.ee25d55|?|Known TKG Issues| 45 | |**7.0.2,7.0.3,7.0.3e**||||||| 46 | |FUTURE|7.0.2|Avi-21.1.2|vDS-7.0.2|v1.22.9---vmware.1-tkg.1.cc71bc8|v1.20.12+vmware.1-tkg.1.b9a42f3|?|Known TKG Issues| 47 | ||7.0.2|Avi-21.1.2|vDS-7.0.2|v1.22.9---vmware.1-tkg.1.cc71bc8|v1.20.9+vmware.1-tkg.1.a4cee5b.900|?|Known TKG Issues| 48 | |FUTURE|7.0.2|Avi-21.1.2|vDS-7.0.2|v1.22.9---vmware.1-tkg.1.cc71bc8|v1.19.16+vmware.1-tkg.1.df910e2|?|Known TKG Issues| 49 | ||7.0.2|Avi-21.1.2|vDS-7.0.2|v1.22.9---vmware.1-tkg.1.cc71bc8|v1.19.14+vmware.1-tkg.1.8753786.9254|?|Known TKG Issues| 50 | ||7.0.2|Avi-21.1.2|vDS-7.0.2|v1.21.6+vmware.1-tkg.1.b3d708a|v1.20.12+vmware.1-tkg.1.b9a42f3|?|Known TKG Issues| 51 | ||7.0.2|Avi-21.1.2|vDS-7.0.2|v1.21.6+vmware.1-tkg.1.b3d708a|v1.20.9+vmware.1-tkg.1.a4cee5b.900|?|Known TKG Issues| 52 | ||7.0.2|Avi-21.1.2|vDS-7.0.2|v1.21.6+vmware.1-tkg.1.b3d708a|v1.19.16+vmware.1-tkg.1.df910e2|?|Known TKG Issues| 53 | ||7.0.2|Avi-21.1.2|vDS-7.0.2|v1.21.6+vmware.1-tkg.1.b3d708a|v1.19.14+vmware.1-tkg.1.8753786.9254|?|Known TKG Issues| 54 | |FUTURE|7.0.2|Avi-21.1.2|vDS-7.0.2|v1.20.12+vmware.1-tkg.1.b9a42f3|v1.20.12+vmware.1-tkg.1.b9a42f3|?|Known TKG Issues| 55 | ||7.0.2|Avi-21.1.2|vDS-7.0.2|v1.20.12+vmware.1-tkg.1.b9a42f3|v1.20.9+vmware.1-tkg.1.a4cee5b.900|?|Known TKG Issues| 56 | |FUTURE|7.0.2|Avi-21.1.2|vDS-7.0.2|v1.20.12+vmware.1-tkg.1.b9a42f3|v1.19.16+vmware.1-tkg.1.df910e2|?|Known TKG Issues| 57 | ||7.0.2|Avi-21.1.2|vDS-7.0.2|v1.20.12+vmware.1-tkg.1.b9a42f3|v1.19.14+vmware.1-tkg.1.8753786.9254|?|Known TKG Issues| 58 | |FUTURE|7.0.2|Avi-21.1.2|vDS-7.0.2|v1.19.16+vmware.1-tkg.1.df910e2|v1.20.12+vmware.1-tkg.1.b9a42f3|?|Known TKG Issues| 59 | ||7.0.2|Avi-21.1.2|vDS-7.0.2|v1.19.16+vmware.1-tkg.1.df910e2|v1.20.9+vmware.1-tkg.1.a4cee5b.900|?|Known TKG Issues| 60 | |FUTURE|7.0.2|Avi-21.1.2|vDS-7.0.2|v1.19.16+vmware.1-tkg.1.df910e2|v1.19.16+vmware.1-tkg.1.df910e2|?|Known TKG Issues| 61 | ||7.0.2|Avi-21.1.2|vDS-7.0.2|v1.19.16+vmware.1-tkg.1.df910e2|v1.19.14+vmware.1-tkg.1.8753786.9254|?|Known TKG Issues| 62 | ||7.0.2|Avi-21.1.2|vDS-7.0.2|v1.18.19+vmware.1-tkg.1.17af790|v1.20.12+vmware.1-tkg.1.b9a42f3|?|Known TKG Issues| 63 | ||7.0.2|Avi-21.1.2|vDS-7.0.2|v1.18.19+vmware.1-tkg.1.17af790|v1.20.9+vmware.1-tkg.1.a4cee5b.900|?|Known TKG Issues| 64 | ||7.0.2|Avi-21.1.2|vDS-7.0.2|v1.18.19+vmware.1-tkg.1.17af790|v1.19.16+vmware.1-tkg.1.df910e2|?|Known TKG Issues| 65 | ||7.0.2|Avi-21.1.2|vDS-7.0.2|v1.18.19+vmware.1-tkg.1.17af790|v1.19.14+vmware.1-tkg.1.8753786.9254|?|Known TKG Issues| 66 | 67 | ## Why aren't they all tested? 68 | 69 | We're looking to test all combinations pre-release, but in the meantime there's some rationale 70 | in what we are testing:- 71 | 72 | * We test the latest TKR version when the vSphere for Tanzu version was released - as many people will have moved to this immediately (Fresh installs) 73 | * We test the latest combination as TKRs are released - which over time will pick up most likely combinations (through regression testing and release testing) (Similar to customer upgrade patterns) 74 | * We test the last 3 (n-2) supported minor versions of kubernetes in line with their support policy on both photon and ubuntu (ubuntu was only supported in TKGS since 7.0.3) 75 | - Note: The latest K8S public release may be 1.23.x but we won't drop 1.19.x testing until VMware releases a TKR for 1.23.x (So we always have n-2 on what is available to customers) 76 | 77 | We also don't test versions that have been withdrawn due to a CVE. You can see these mentioned in 'Additional Tanzu Kubernetes Releases' at the bottom 78 | of this page: https://docs.vmware.com/en/VMware-Tanzu-Kubernetes-releases/services/rn/vmware-tanzu-kubernetes-releases-release-notes/index.html#compatibility-for-vmware-tanzu-kubernetes-releases 79 | 80 | 81 | ## Background information 82 | 83 | For TKR Release notes see: https://docs.vmware.com/en/VMware-Tanzu-Kubernetes-releases/services/rn/vmware-tanzu-kubernetes-releases-release-notes/index.html 84 | 85 | For SUPPORTED combinations see: https://docs.vmware.com/en/VMware-Tanzu-Kubernetes-releases/services/rn/vmware-tanzu-kubernetes-releases-release-notes/index.html#compatibility-for-vmware-tanzu-kubernetes-releases 86 | 87 | 1.22.x 88 | photon-3-k8s-v1.22.9---vmware.1-tkg.1.cc71bc8 89 | 90 | 1.21.x 91 | ubuntu-2004-v1.21.6---vmware.1-tkg.1 92 | photon-3-k8s-v1.21.6+vmware.1-tkg.1.b3d708a 93 | photon-3-k8s-v1.21.2---vmware.1-tkg.1.ee25d55 94 | 95 | 1.20.x 96 | photon-3-k8s-v1.20.12+vmware.1-tkg.1.b9a42f3 97 | photon-3-k8s-v1.20.9---vmware.1-tkg.1.a4cee5b 98 | ubuntu-2004-v1.20.8---vmware.1-tkg.2 99 | 100 | 101 | 1.19.x 102 | photon-3-k8s-v1.19.16+vmware.1-tkg.1.df910e2 103 | photon-3-k8s-v1.19.14---vmware.1-tkg.1.8753786 104 | 105 | -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | // Force local binary use, rather than public binary 4 | namespace-management = { 5 | version = ">= 0.1" 6 | source = "vmware.com/vcenter/namespace-management" 7 | } 8 | vsphere = { 9 | version = ">= 2.1.1" 10 | source = "hashicorp/vsphere" 11 | } 12 | # Fix for random 401s when initialising Avi 13 | avi = { 14 | version = "21.1.2-p1.0" 15 | source = "vmware/avi" 16 | } 17 | } 18 | } 19 | 20 | provider "namespace-management" { 21 | vsphere_hostname = var.vsphere_hostname 22 | vsphere_username = var.vsphere_username 23 | vsphere_password = var.vsphere_password 24 | vsphere_insecure = var.vsphere_insecure 25 | } 26 | 27 | provider "vsphere" { 28 | vsphere_server = var.vsphere_hostname 29 | user = var.vsphere_username 30 | password = var.vsphere_password 31 | api_timeout = var.vsphere_api_timeout 32 | # debug_client = true 33 | 34 | # If you have a self-signed cert 35 | allow_unverified_ssl = var.vsphere_insecure 36 | } 37 | 38 | # module "dc" { 39 | # source = "./01_vcenter_create" 40 | 41 | # datacenter = var.datacenter_name 42 | # esxi_hosts = var.esxi_hosts 43 | # } 44 | 45 | # module "cluster" { 46 | # source = "./02_vcenter_join" 47 | 48 | # # Set these values if you are not using the defaults in vcsim 49 | # # datacenter_name = "lab01.my.cloud" 50 | # # cluster_name = "Cluster01" 51 | # # esxi_hosts = [ 52 | # # "vesxi01.lab01.my.cloud", 53 | # # "vesxi02.lab01.my.cloud", 54 | # # "vesxi03.lab01.my.cloud", 55 | # # ] 56 | # datacenter_name = var.datacenter_name 57 | # cluster_name = var.cluster_name 58 | # esxi_hosts = var.esxi_hosts 59 | 60 | # # depends_on = [module.dc] 61 | # } 62 | 63 | # module "vds" { 64 | # source = "./03_vds_networking" 65 | 66 | # // TODO parameters here 67 | # } 68 | 69 | module "avi-controller" { 70 | source = "./04_avi_controller" 71 | 72 | # vSphere existing infrastructure 73 | vm_datacenter = var.vsphere_datacenter 74 | vm_datastore = var.vsphere_vm_datastore 75 | vm_folder = var.vsphere_vm_folder 76 | 77 | # Manually created for Avi before install:- 78 | content_library = var.avi_content_library 79 | vm_template = var.avi_vm_template 80 | 81 | # The rest we create for the user 82 | vm_resource_pool = var.avi_vm_resource_pool 83 | vm_network = var.management_network_name 84 | vm_name = var.avi_vm_name // patterned for -1, -2 etc. 85 | 86 | // Specified here because we've not created Avi yet 87 | avi_username = var.avi_username 88 | avi_password = var.avi_password 89 | 90 | // template customisation 91 | avi_management_hostname = var.avi_management_hostname 92 | // TODO support multiple controller instances 93 | avi_management_ip_address = var.avi_management_address_ipv4 94 | // 27 == "255.255.255.224" 95 | avi_management_subnet_mask_int = var.management_subnet_mask_int 96 | avi_management_default_gateway = var.management_default_gateway 97 | 98 | // output avi_cluster_output 99 | } 100 | 101 | 102 | provider "avi" { 103 | avi_username = var.avi_username 104 | avi_password = var.avi_password 105 | avi_tenant = var.avi_tenant 106 | avi_controller = var.avi_management_address_ipv4 107 | 108 | # Required for Terraform provider not to puke 109 | # Without this it complains about 'common_criteria' being there (even though false by default) as the terraform provider defaults to v18.8 110 | avi_version = var.avi_version 111 | } 112 | 113 | module "avi-config" { 114 | source = "./05_avi_configure" 115 | depends_on = [module.avi-controller] 116 | 117 | # vSphere existing infrastructure 118 | vm_datacenter = var.vsphere_datacenter 119 | vm_cluster = var.vsphere_cluster 120 | esxi_vm_name = var.esxi_vm_name 121 | 122 | # The rest we create for the user 123 | avi_management_network_name = var.management_network_name 124 | 125 | // Specified here because we've not created Avi yet 126 | avi_username = var.avi_username 127 | avi_password = var.avi_password 128 | 129 | // Modify the cloud from Default 130 | avi_cloud_name = var.avi_cloud_name 131 | 132 | // template customisation 133 | avi_management_dns_server = var.management_dns_server 134 | # TODO why are there two of these? :- 135 | avi_management_dns_suffix = var.management_domain 136 | avi_management_domain = var.management_domain 137 | avi_management_hostname = var.avi_management_hostname 138 | // TODO support multiple controller instances 139 | avi_management_ip_address = var.avi_management_address_ipv4 140 | avi_management_ip_address_start = var.management_start_ipv4 141 | avi_management_ip_address_end = var.management_end_ipv4 142 | // 27 == "255.255.255.224" 143 | avi_management_ip_network = var.management_network_ipv4 144 | avi_management_subnet_mask_int = var.management_subnet_mask_int 145 | avi_management_default_gateway = var.management_default_gateway 146 | 147 | avi_ntp_server = var.management_ntp_server1 148 | 149 | avi_deployment_name = var.deployment_name 150 | 151 | avi_data_network_name = var.data_network_name 152 | avi_data_network_start_ipv4 = var.data_network_start_ipv4 153 | avi_data_network_end_ipv4 = var.data_network_end_ipv4 154 | // Note leaving .40-.61 for Tanzu Workload Network IPs... 155 | // This is because h2o only gives us one workload network!!! 156 | // (pfsense vm to the rescue???) 157 | avi_data_network_network_ipv4 = var.data_network_ipv4 158 | avi_data_network_subnet_mask_int = var.data_network_subnet_mask_int 159 | avi_data_network_default_gateway = var.data_network_default_gateway 160 | 161 | vcenter_username = var.vsphere_username 162 | vcenter_password = var.vsphere_password 163 | # WARNING THE BELOW IS HOSTNAME/IP ONLY, NO HTTPS OR REQUEST PATH 164 | vcenter_url = var.vsphere_hostname 165 | 166 | // Make this different from the admin password... 167 | avi_backup_passphrase = var.avi_backup_passphrase 168 | } 169 | 170 | module "tanzu" { 171 | source = "./06_tanzu_enable" 172 | 173 | // The following 6 items must already exist, along with the 2 vDS networks 174 | datacenter_name = var.vsphere_datacenter 175 | cluster_name = var.vsphere_cluster 176 | image_storage_policy_name = var.tanzu_image_storage_policy_name 177 | master_storage_policy_name = var.tanzu_supervisor_storage_policy_name 178 | ephemeral_storage_policy_name = var.tanzu_ephemeral_storage_policy_name 179 | default_kubernetes_service_content_library_name = var.tanzu_default_kubernetes_service_content_library_name 180 | 181 | master_dns_servers = var.management_dns_server 182 | master_dns_search_domain = var.management_domain 183 | master_ntp_servers = var.management_ntp_server1 184 | master_dns_names = var.tanzu_supervisor_dns_names 185 | master_network_provider = "VSPHERE_NETWORK" 186 | master_network_name = var.management_network_name 187 | 188 | master_network_static_gateway_ipv4 = var.management_default_gateway 189 | master_network_static_starting_address_ipv4 = var.tanzu_supervisor_start_ipv4 190 | master_network_static_address_count = var.tanzu_supervisor_address_count 191 | master_network_static_subnet_mask = var.management_subnet_mask_long 192 | 193 | data_network_static_starting_address_ipv4 = var.data_network_start_ipv4 194 | data_network_static_address_count = var.data_network_address_count 195 | 196 | worker_dns_servers = var.workload_dns_server 197 | workload_ntp_servers = var.workload_ntp_server1 198 | primary_workload_network_name = var.workload_network_name 199 | primary_workload_network_provider = "VSPHERE_NETWORK" 200 | primary_workload_network_static_gateway_ipv4 = var.workload_default_gateway 201 | primary_workload_network_static_starting_address_ipv4 = var.workload_start_ipv4 202 | primary_workload_network_static_address_count = var.workload_address_count 203 | primary_workload_network_static_subnet_mask = var.workload_subnet_mask_long 204 | primary_workload_network_vsphere_portgroup_name = var.workload_network_name 205 | load_balancer_provider = "AVI" 206 | load_balancer_avi_host = var.avi_management_address_ipv4 207 | load_balancer_avi_port = var.avi_management_port 208 | load_balancer_avi_username = var.avi_username 209 | load_balancer_avi_password = var.avi_password 210 | 211 | # load_balancer_avi_ca_chain = "${module.avi-config.cert.certificate}${module.avi-config.rootcert.certificate}" 212 | # Not in quotes to avoid potential poor formatting issue 213 | load_balancer_avi_ca_chain = module.avi-config.cert.certificate 214 | 215 | depends_on = [ 216 | module.avi-config 217 | ] 218 | } 219 | 220 | # OUTPUT FROM INITIAL AVI CONTROLLER INSTALLATION 221 | output "avi_initial_config" { 222 | value = module.avi-controller.initial_configuration 223 | # sensitive = true 224 | } 225 | output "avi_admin_user" { 226 | value = module.avi-controller.admin_user 227 | sensitive = true 228 | } 229 | 230 | // OUTPUT FROM AVI CONFIGURATION 231 | output "avi_final_config" { 232 | value = module.avi-config.system_configuration 233 | sensitive = true 234 | } 235 | output "avi_cloud" { 236 | value = module.avi-config.cloud 237 | sensitive = true 238 | } 239 | output "avi_data_network" { 240 | value = module.avi-config.data_network 241 | } 242 | output "avi_management_network" { 243 | value = module.avi-config.management_network 244 | } 245 | # output "cachain" { 246 | # value = module.avi-config.cachain 247 | # } 248 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by the 14 | copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all other 17 | entities that control, are controlled by, or are under common control 18 | with that entity. For the purposes of this definition, "control" means 19 | (i) the power, direct or indirect, to cause the direction or management 20 | of such entity, whether by contract or otherwise, or (ii) ownership 21 | of fifty percent (50%) or more of the outstanding shares, or (iii) 22 | beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity exercising 25 | permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation source, 29 | and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical transformation 32 | or translation of a Source form, including but not limited to compiled 33 | object code, generated documentation, and conversions to other media 34 | types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a copyright 38 | notice that is included in or attached to the work (an example is provided 39 | in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object form, 42 | that is based on (or derived from) the Work and for which the editorial 43 | revisions, annotations, elaborations, or other modifications represent, 44 | as a whole, an original work of authorship. For the purposes of this 45 | License, Derivative Works shall not include works that remain separable 46 | from, or merely link (or bind by name) to the interfaces of, the Work 47 | and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including the 50 | original version of the Work and any modifications or additions to 51 | that Work or Derivative Works thereof, that is intentionally submitted 52 | to Licensor for inclusion in the Work by the copyright owner or by an 53 | individual or Legal Entity authorized to submit on behalf of the copyright 54 | owner. For the purposes of this definition, "submitted" means any form of 55 | electronic, verbal, or written communication sent to the Licensor or its 56 | representatives, including but not limited to communication on electronic 57 | mailing lists, source code control systems, and issue tracking systems 58 | that are managed by, or on behalf of, the Licensor for the purpose of 59 | discussing and improving the Work, but excluding communication that is 60 | conspicuously marked or otherwise designated in writing by the copyright 61 | owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. 68 | Subject to the terms and conditions of this License, each Contributor 69 | hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, 70 | royalty-free, irrevocable copyright license to reproduce, prepare 71 | Derivative Works of, publicly display, publicly perform, sublicense, and 72 | distribute the Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. 75 | Subject to the terms and conditions of this License, each Contributor 76 | hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, 77 | royalty- free, irrevocable (except as stated in this section) patent 78 | license to make, have made, use, offer to sell, sell, import, and 79 | otherwise transfer the Work, where such license applies only to those 80 | patent claims licensable by such Contributor that are necessarily 81 | infringed by their Contribution(s) alone or by combination of 82 | their Contribution(s) with the Work to which such Contribution(s) 83 | was submitted. If You institute patent litigation against any entity 84 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 85 | Work or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses granted 87 | to You under this License for that Work shall terminate as of the date 88 | such litigation is filed. 89 | 90 | 4. Redistribution. 91 | You may reproduce and distribute copies of the Work or Derivative Works 92 | thereof in any medium, with or without modifications, and in Source or 93 | Object form, provided that You meet the following conditions: 94 | 95 | a. You must give any other recipients of the Work or Derivative Works 96 | a copy of this License; and 97 | 98 | b. You must cause any modified files to carry prominent notices stating 99 | that You changed the files; and 100 | 101 | c. You must retain, in the Source form of any Derivative Works that 102 | You distribute, all copyright, patent, trademark, and attribution 103 | notices from the Source form of the Work, excluding those notices 104 | that do not pertain to any part of the Derivative Works; and 105 | 106 | d. If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one of 111 | the following places: within a NOTICE text file distributed as part 112 | of the Derivative Works; within the Source form or documentation, 113 | if provided along with the Derivative Works; or, within a display 114 | generated by the Derivative Works, if and wherever such third-party 115 | notices normally appear. The contents of the NOTICE file are for 116 | informational purposes only and do not modify the License. You 117 | may add Your own attribution notices within Derivative Works that 118 | You distribute, alongside or as an addendum to the NOTICE text 119 | from the Work, provided that such additional attribution notices 120 | cannot be construed as modifying the License. You may add Your own 121 | copyright statement to Your modifications and may provide additional 122 | or different license terms and conditions for use, reproduction, or 123 | distribution of Your modifications, or for any such Derivative Works 124 | as a whole, provided Your use, reproduction, and distribution of the 125 | Work otherwise complies with the conditions stated in this License. 126 | 127 | 5. Submission of Contributions. 128 | Unless You explicitly state otherwise, any Contribution intentionally 129 | submitted for inclusion in the Work by You to the Licensor shall be 130 | under the terms and conditions of this License, without any additional 131 | terms or conditions. Notwithstanding the above, nothing herein shall 132 | supersede or modify the terms of any separate license agreement you may 133 | have executed with Licensor regarding such Contributions. 134 | 135 | 6. Trademarks. 136 | This License does not grant permission to use the trade names, trademarks, 137 | service marks, or product names of the Licensor, except as required for 138 | reasonable and customary use in describing the origin of the Work and 139 | reproducing the content of the NOTICE file. 140 | 141 | 7. Disclaimer of Warranty. 142 | Unless required by applicable law or agreed to in writing, Licensor 143 | provides the Work (and each Contributor provides its Contributions) on 144 | an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 145 | express or implied, including, without limitation, any warranties or 146 | conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR 147 | A PARTICULAR PURPOSE. You are solely responsible for determining the 148 | appropriateness of using or redistributing the Work and assume any risks 149 | associated with Your exercise of permissions under this License. 150 | 151 | 8. Limitation of Liability. 152 | In no event and under no legal theory, whether in tort (including 153 | negligence), contract, or otherwise, unless required by applicable law 154 | (such as deliberate and grossly negligent acts) or agreed to in writing, 155 | shall any Contributor be liable to You for damages, including any direct, 156 | indirect, special, incidental, or consequential damages of any character 157 | arising as a result of this License or out of the use or inability to 158 | use the Work (including but not limited to damages for loss of goodwill, 159 | work stoppage, computer failure or malfunction, or any and all other 160 | commercial damages or losses), even if such Contributor has been advised 161 | of the possibility of such damages. 162 | 163 | 9. Accepting Warranty or Additional Liability. 164 | While redistributing the Work or Derivative Works thereof, You may 165 | choose to offer, and charge a fee for, acceptance of support, warranty, 166 | indemnity, or other liability obligations and/or rights consistent with 167 | this License. However, in accepting such obligations, You may act only 168 | on Your own behalf and on Your sole responsibility, not on behalf of 169 | any other Contributor, and only if You agree to indemnify, defend, and 170 | hold each Contributor harmless for any liability incurred by, or claims 171 | asserted against, such Contributor by reason of your accepting any such 172 | warranty or additional liability. 173 | 174 | END OF TERMS AND CONDITIONS 175 | 176 | APPENDIX: How to apply the Apache License to your work. 177 | 178 | To apply the Apache License to your work, attach the following 179 | boilerplate notice, with the fields enclosed by brackets "[]" 180 | replaced with your own identifying information. (Don't include 181 | the brackets!) The text should be enclosed in the appropriate 182 | comment syntax for the file format. We also recommend that a 183 | file or class name and description of purpose be included on the 184 | same "printed page" as the copyright notice for easier 185 | identification within third-party archives. 186 | 187 | Copyright [yyyy] [name of copyright owner] 188 | 189 | Licensed under the Apache License, Version 2.0 (the "License"); 190 | you may not use this file except in compliance with the License. 191 | You may obtain a copy of the License at 192 | 193 | http://www.apache.org/licenses/LICENSE-2.0 194 | 195 | Unless required by applicable law or agreed to in writing, software 196 | distributed under the License is distributed on an "AS IS" BASIS, 197 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 198 | See the License for the specific language governing permissions and 199 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Namespace Management Terraform provider 2 | 3 | This Terraform Provider enables control of vSphere Workload Management. 4 | This includes enabling or disabling workload management 5 | (effectively creating, and destroying, Tanzu Supervisor Clusters), 6 | and enabling or disabling supervisor cluster services. 7 | 8 | The project is called namespace-management rather than tanzu-supervisor 9 | to be consistent with the vSphere API name which it wraps. 10 | 11 | Note that Tanzu Workload Clusters should be requested by sending YAML 12 | configuration via kubectl to the appropriate Supervisor Cluster's 13 | vSphere namespace. There is no internal vSphere REST API for this by design. 14 | (This is internalised in CAPV - Cluster API for vSphere). 15 | 16 | Likewise, creating the underlying infrastructure in vSphere can be 17 | accomplished by using the 18 | [HashiCorp vSphere provider](https://registry.terraform.io/providers/hashicorp/vsphere) 19 | and the [Avi Terraform Provider](https://registry.terraform.io/providers/vmware/avi). 20 | An end to end example of these being used alongside our Workload Management 21 | Terraform provider is provided in the 22 | examples/full_esxi_tanzu_cluster sample. 23 | 24 | ## Planned feature sprints 25 | 26 | - Alpha 1 (Completed Friday 15 Jul 2022) 27 | - Module: Single Avi controller instance, Avi Essentials configuration only, v21.1.2 28 | - Module: vDS networking 7.0u2 and 7.0u3 support for vCenter 29 | - Works with latest photon build for TKGS (see concourse/combinations/README.md) 30 | - Tested against h2o.vmware.com and my own homelab nested esxi environment 31 | - No automated CI/CD testing 32 | - Module: Manual uploading of OVAs and manual Content Library creation 33 | - Add all necessary repo files (Update CLA from DCO, CONTRIBUTING changes for this too) 34 | - Support manual build only (Provider not yet added to Terraform registry) 35 | - Add develop branch 36 | - REQUEST REPO BE MADE PUBLIC 37 | - Alpha 2 38 | - Provider: Govmomi bug fixes and enhancements contributed back to project as PRs 39 | - Provider: Basic CI testing of Terraform Provider in isolation (In Go, via GitHub CI) for develop branch 40 | - Provider: Functional validation tests post cluster creation (Node up, node reachable) 41 | - Provider: Produce test report summary files for develop branch 42 | - Provider: Delete, create and status work as Terraform requires 43 | - Module: Include initial vDS creation 44 | - Module: Include file upload from staging to datastore 45 | - Module: Include content library creation and uploading of TKR releases 46 | - Offline 'local' support (Airgapped) 47 | - Online subscribed support (non Airgapped) 48 | - Project introduction video 49 | - Support manual build only (Provider not yet added to Terraform registry) 50 | - Beta 1 51 | - Beta builds submitted automatically to Terraform registry on tag and release (main branch) 52 | - Full sample documentation 53 | - Provider: Custom CA cert support for Supervisor Cluster 54 | - Module: Multi-node Avi controller support 55 | - Module: Avi Enterprise support (including license key upload) 56 | - Module: More detailed build success reports 57 | - Announcement email internal to VMware Tanzu SE community to try and test out 58 | - Beta 2 59 | - Provider: NSX-T networking configuration support 60 | - Provider: Enable the built in Harbor on Supervisor Cluster 61 | - Module: Add more version and environment combinations 62 | - Include basic Workload Cluster creation for photon and ubuntu TKR at n-2 (Only 2 supported currently) 63 | - Latest NSX-T support with own load balancer 64 | - This is 48 combinations in total 65 | - Module: Support for shared and standalone prometheus, grafana 66 | - Release Testing: A Tagged branch results in an end to end test, and test results being added to a (Beta) release 67 | - NSX-T support intro video 68 | - Beta 3 69 | - Provider: Create/delete namespace (With network, storage policy, resource limits, t-shirt sizes allowed) 70 | - Provider: Add/delete workload network 71 | - Provider: Assign/remove vSphere SSO group access to namespace 72 | - Provider: Apply license (and check license as per: https://developer.vmware.com/apis/vsphere-automation/latest/vcenter/namespace_management/hosts_config/) 73 | - Module: (Kubernetes Provider) Include support for TkgServiceConfiguration injection (and customisation) to Supervisor Cluster 74 | - Module: (Kubernetes Provider) Create workload cluster config and submit it 75 | - Determine releasing version number convention (Recommend v1 until terraform config incompatability) 76 | - v1.0 useable product at this point for end to end creation by customers 77 | - Provider: Add supported vSphere version check to enablement call 78 | - Pre-release documentation 79 | - Provider: Document supported vSphere versions 80 | - Pre-release videos 81 | - Work with OSPO and Tanzu teams for announcement 82 | - v1.1 Day 2 operations focused 83 | - Module: Add new TKR release to Content Library 84 | - Provider: Add fail-fast checks (compatible networks, hosts, versions (TKR, vSphere, Avi, NSX-T, vDS Switch)) 85 | - v1.2 Security lockdown configuration simplification 86 | - Module: Include support for Custom ingress and egress CIDR ranges in TkgServiceConfiguration 87 | - Module: CA certs for all components 88 | - Module: Tests to verify ca cert changes 89 | - Module: Include restriction of Certs used for EC P-256 90 | - v1.3 Workload cluster common patterns 91 | - Module: Bootstrap Harbor VM support (Requires a Harbor OVA) 92 | - Module: Helm Harbor services cluster support and sample (Requires a bootstrap harbor) 93 | - Module: Node/pod communication check tests (VMs, Pods) 94 | - v1.4 End to End regression testing against our other products 95 | - Regression CD testing, via tag rather than commit 96 | - Provider: Overarching tests for develop branch 97 | - Provider: Concourse loads environment combinations and runs multiple env pipelines in order using Terraform 98 | - Support n-2 photon versions 99 | - Automate testing on h20 (7.0u2) and homelab (7.0u3) using remote workers 100 | - This is a total of 6 combinations 101 | - Module: Add more version combinations 102 | - n-2 Avi version support 103 | - This is a total of 12 combinations 104 | - v1.5 Full testing suite for CD (No changes to Provider) 105 | - Add new environment and versions 106 | - NSX-T n-2 version support 107 | - NSX-T support with Avi load balancer 108 | - Latest ESXi/vSphere version tested (currently 7.0u3d) 109 | - n-2 tests for Avi Load Balancer, Avi Terraform Plugin (Matched to Avi), NSX-T and NSX-T Terraform Plugin (Matched to NSX-T) 110 | - This is 576 combinations 111 | - Full suite of tests (using tags on the main and develop branches) with all latest minor releases of k8s TKRs 112 | - Module: Tanzu Standard on top of vSphere for Tanzu, with restricted psp/opa 113 | - Istio with ingress, egress, istio-cni, minimum extra permissions (just the CNI pod) 114 | - Kiali support for istio configuration validation/manual checking 115 | - Full release documentation 116 | - Video 117 | - TODO Discuss with Automation team on additional features for their tool 118 | - v? Other namespace-management API endpoints not discussed above 119 | - Based on customer feedback only 120 | - No other endpoints known used today 121 | - For a full list: https://developer.vmware.com/apis/vsphere-automation/latest/vcenter/namespace_management/ 122 | - E.g. changing password rotation settings as per https://developer.vmware.com/apis/vsphere-automation/latest/vcenter/api/vcenter/namespace-management/clusters/clusteractionrotate_password/post/ 123 | 124 | ## Namespace Management API support status 125 | 126 | - data_source_clusters 127 | - clustersRead() 128 | - lists clusters with id, name, k8s status, config status 129 | - Uses GET /api/vcenter/namespace-management/clusters 130 | - Working, see examples/03_basic_list/clusters/main.tf 131 | - Returns { clusters: [ {id: "domain-c1005", name:"Cluster01", kubernetes_status:"READY", config_status:"RUNNING"}, ... ] } 132 | - data_source_cluster 133 | - clusterRead() 134 | - Given a cluster NAME (NOT id) like 'Cluster01' returns the cluster's Tanzu Supervisor Cluster summary 135 | - Uses GET /api/vcenter/namespace-management/clusters 136 | - Summary includes (only) id, name, kubernetes_status, config_status 137 | - Working, see see examples/02_basic_read/clusters/main.tf 138 | - Returns {id: "domain-c1005", name:"Cluster01", kubernetes_status:"READY", config_status:"RUNNING"} 139 | - resource_cluster 140 | - clusterCreate() 141 | - Given a vSphere cluster ID (NOT name) like 'domain-c1005', enables workload management 142 | - Uses POST /api/vcenter/namespace-management/clusters/{cluster}?action=enable 143 | - Implemented, tested, see examples/01_basic_create/clusters/main.tf and examples/full_esxi_tanzu_cluster/main.tf 144 | - Only tested on vSphere networking (not NSX-T) today 145 | - clusterRead() 146 | - Given a cluster NAME (NOT id) like 'Cluster01' returns the cluster's Tanzu Supervisor Cluster summary 147 | - Uses List method as data_clusters clusterRead today 148 | - Working, see see examples/02_basic_read/clusters/main.tf 149 | - Limited to cluster summary today rather than full information due to missing govmomi feature: https://github.com/vmware/govmomi/issues/2860 150 | - Implies that we cannot implement clusterUpdate() too until this issue is resolved upstream 151 | - clusterUpdate() 152 | - Given a vSphere cluster ID (NOT name) like 'domain-c1005', replaces the current cluster enable spec with a new full spec 153 | - Not yet implemented 154 | - clusterDelete() 155 | - Given a vSphere cluster ID (NOT name) like 'domain-c1005', disables workload management 156 | - Doesn't actually delete the vSphere cluster, just the Tanzu Supervisor Cluster 157 | - Not yet implemented 158 | 159 | ## Try it out 160 | 161 | ### Prerequisites 162 | 163 | * You must have Terraform installed on your system 164 | * You must have a Go runtime installed with corresponding build tools 165 | * You must have a vSphere 7.0 update 2 (7.0.2) or above system configured with a vCenter and at least ESXi two hosts (ideally 3 or more) 166 | 167 | ## Building the provider 168 | 169 | Note that in the current version a patched release of Govmomi is required. You can fetch, build and install this from this URL: https://github.com/adamfowleruk/govmomi/tree/issue-2860 . We will remove this before the first major release once the fixes are applied in Govmomi. 170 | 171 | Run the following command to build the provider 172 | 173 | ```shell 174 | go build -o terraform-provider-namespace-management 175 | ``` 176 | 177 | ## Test sample configuration 178 | 179 | First, build and install the provider. 180 | 181 | ```shell 182 | make install 183 | ``` 184 | 185 | Edit the sample file to customise it to your vSphere environment. You can find this in examples/02-basic-create/main.tf 186 | 187 | Then, run the following command to initialize the workspace and apply the sample configuration. 188 | 189 | ```shell 190 | cd examples/02-basic-create 191 | terraform init && terraform apply 192 | ``` 193 | 194 | ## Documentation 195 | 196 | ## Contributing 197 | 198 | The terraform-provider-namespace-management project team welcomes contributions from the community. Before you start working with terraform-provider-namespace-management, please 199 | Read our [Contributor License Agreement](https://cla.vmware.com/cla/1/preview). All contributions to this repository must be 200 | signed as described on that page. Your signature certifies that you wrote the patch or have the right to pass it on 201 | as an open-source patch. For more detailed information, refer to [CONTRIBUTING.md](CONTRIBUTING.md). 202 | 203 | ## License 204 | 205 | This project is licensed under the terms of the Apache-2.0 license and is Copyright VMware, Inc. 2022. See the LICENSE file for full details. -------------------------------------------------------------------------------- /namespace_management/resource_cluster.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 VMware, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | 5 | package namespace_management 6 | 7 | import ( 8 | "context" 9 | 10 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 12 | 13 | "github.com/vmware/govmomi/vapi/namespace" 14 | ) 15 | 16 | func resourceCluster() *schema.Resource { 17 | return &schema.Resource{ 18 | CreateContext: resourceClusterCreate, 19 | ReadContext: resourceClusterRead, 20 | UpdateContext: resourceClusterUpdate, 21 | DeleteContext: resourceClusterDelete, 22 | Schema: map[string]*schema.Schema{ 23 | // Note this is the equivalent of ClusterSummary 24 | // See https://github.com/vmware/govmomi/blob/4a6c4b155da486e3f9058aac9dfc10a8f364c6ce/vapi/namespace/namespace.go#L116 25 | // The TERRAFORM ID for this element. Same as the cluster_id in vSphere. 26 | "id": { 27 | Type: schema.TypeString, 28 | Computed: true, 29 | Optional: true, 30 | }, 31 | // The vSphere cluster_id to enable workload management for (use ID for read) 32 | "cluster_id": { 33 | Type: schema.TypeString, 34 | Required: true, 35 | }, 36 | // Textual name for the vSphere cluster (returned from the API) 37 | "cluster_name": { 38 | Type: schema.TypeString, 39 | Computed: true, 40 | }, 41 | "kubernetes_status": { 42 | Type: schema.TypeString, 43 | Computed: true, 44 | }, 45 | "config_status": { 46 | Type: schema.TypeString, 47 | Computed: true, 48 | }, 49 | // TODO add all ENABLING and detail fields here for supervisor cluster (NOT just the summary as in the data call equivalent) 50 | "image_storage_policy_id": { 51 | Type: schema.TypeString, 52 | Optional: true, 53 | }, 54 | "master_storage_policy_id": { 55 | Type: schema.TypeString, 56 | Optional: true, 57 | }, 58 | "ephemeral_storage_policy_id": { 59 | Type: schema.TypeString, 60 | Optional: true, 61 | }, 62 | "default_kubernetes_service_content_library_id": { 63 | Type: schema.TypeString, 64 | Optional: true, 65 | }, 66 | // Internal K8S service network 67 | "service_cidr_network": { 68 | Type: schema.TypeString, 69 | Optional: true, 70 | Default: "10.96.0.0", 71 | }, 72 | "service_cidr_mask": { 73 | Type: schema.TypeInt, 74 | Optional: true, 75 | Default: 23, 76 | }, 77 | 78 | // K8S supervisor Networking 79 | "master_dns_servers": { 80 | Type: schema.TypeString, 81 | Optional: true, 82 | }, 83 | "master_dns_search_domain": { 84 | Type: schema.TypeString, 85 | Optional: true, 86 | }, 87 | "master_ntp_servers": { 88 | Type: schema.TypeString, 89 | Optional: true, 90 | }, 91 | // E.g. tanzu.SEARCH_DOMAIN (full, for certs) 92 | "master_dns_names": { 93 | Type: schema.TypeString, 94 | Optional: true, 95 | }, 96 | "master_network_provider": { 97 | Type: schema.TypeString, 98 | Optional: true, 99 | Default: "VSPHERE_NETWORK", 100 | }, 101 | // E.g. network-15 102 | "master_network_id": { 103 | Type: schema.TypeString, 104 | Optional: true, 105 | }, 106 | "master_network_ip_assignment_mode": { 107 | Type: schema.TypeString, 108 | Optional: true, 109 | Default: "STATICRANGE", // OR DHCP 110 | }, 111 | "master_network_static_gateway_ipv4": { 112 | Type: schema.TypeString, 113 | Optional: true, 114 | }, 115 | "master_network_static_starting_address_ipv4": { 116 | Type: schema.TypeString, 117 | Optional: true, 118 | }, 119 | "master_network_static_address_count": { 120 | Type: schema.TypeInt, 121 | Optional: true, 122 | }, 123 | // E.g. 255.255.255.0 124 | "master_network_static_subnet_mask": { 125 | Type: schema.TypeString, 126 | Optional: true, 127 | }, 128 | // E.g. dvportgroup-15 - NOT USED - uses master_network_id (not pg) instead 129 | // "master_network_vsphere_portgroup_id": { 130 | // Type: schema.TypeString, 131 | // Optional: true, 132 | // }, 133 | // TODO NSX-T settings for master network 134 | 135 | "data_network_static_starting_address_ipv4": { 136 | Type: schema.TypeString, 137 | Optional: true, 138 | }, 139 | "data_network_static_address_count": { 140 | Type: schema.TypeInt, 141 | Optional: true, 142 | }, 143 | 144 | // K8S Workload Networking 145 | // worker_dns Name is historical from Rest API 146 | "worker_dns_servers": { 147 | Type: schema.TypeString, 148 | Optional: true, 149 | }, 150 | "workload_ntp_servers": { 151 | Type: schema.TypeString, 152 | Optional: true, 153 | }, 154 | // E.g. k8s-workload-network (MUST be a DNS compliant name) 155 | "primary_workload_network_name": { 156 | Type: schema.TypeString, 157 | Optional: true, 158 | }, 159 | "primary_workload_network_provider": { 160 | Type: schema.TypeString, 161 | Optional: true, 162 | Default: "VSPHERE_NETWORK", 163 | }, 164 | "primary_workload_network_static_gateway_ipv4": { 165 | Type: schema.TypeString, 166 | Optional: true, 167 | }, 168 | "primary_workload_network_static_starting_address_ipv4": { 169 | Type: schema.TypeString, 170 | Optional: true, 171 | }, 172 | "primary_workload_network_static_address_count": { 173 | Type: schema.TypeInt, 174 | Optional: true, 175 | }, 176 | // E.g. 255.255.255.0 177 | "primary_workload_network_static_subnet_mask": { 178 | Type: schema.TypeString, 179 | Optional: true, 180 | }, 181 | // E.g. dvportgroup-15 182 | "primary_workload_network_vsphere_portgroup_id": { 183 | Type: schema.TypeString, 184 | Optional: true, 185 | }, 186 | // TODO NSX-T settings for workload network 187 | 188 | // TODO other LB types for Tanzu (E.g. NSX-T) 189 | "load_balancer_provider": { 190 | Type: schema.TypeString, 191 | Optional: true, 192 | Default: "AVI", 193 | }, 194 | // Network resolvable name for the load balancer (not used for network routing!!!) 195 | "load_balancer_id": { 196 | Type: schema.TypeString, 197 | Optional: true, 198 | Default: "avi-lb", 199 | }, 200 | "load_balancer_avi_host": { 201 | Type: schema.TypeString, 202 | Optional: true, 203 | }, 204 | "load_balancer_avi_port": { 205 | Type: schema.TypeInt, 206 | Optional: true, 207 | Default: 443, 208 | }, 209 | "load_balancer_avi_username": { 210 | Type: schema.TypeString, 211 | Optional: true, 212 | }, 213 | "load_balancer_avi_password": { 214 | Type: schema.TypeString, 215 | Optional: true, 216 | Sensitive: true, 217 | }, 218 | "load_balancer_avi_ca_chain": { 219 | Type: schema.TypeString, 220 | Optional: true, 221 | }, 222 | }, 223 | } 224 | } 225 | 226 | func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 227 | // Warning or errors can be collected in a slice type 228 | var diags diag.Diagnostics 229 | ns := m.(*namespace.Manager) 230 | 231 | clusterId := d.Get("cluster_id").(string) 232 | // clusterName := d.Get("name").(string) 233 | imagePolicyId := d.Get("image_storage_policy_id").(string) 234 | masterPolicyId := d.Get("master_storage_policy_id").(string) 235 | ephemeralPolicyId := d.Get("ephemeral_storage_policy_id").(string) 236 | defaultLibraryId := d.Get("default_kubernetes_service_content_library_id").(string) 237 | serviceCidrNetwork := d.Get("service_cidr_network").(string) 238 | serviceCidrMask := d.Get("service_cidr_mask").(int) 239 | 240 | masterDnsServers := d.Get("master_dns_servers").(string) 241 | masterDnsSearchDomains := d.Get("master_dns_search_domain").(string) 242 | masterDnsNames := d.Get("master_dns_names").(string) 243 | masterNtpServers := d.Get("master_ntp_servers").(string) 244 | masterNetworkId := d.Get("master_network_id").(string) 245 | masterNetworkProvider := d.Get("master_network_provider").(string) 246 | 247 | masterNetworkStartingAddressIpv4 := d.Get("master_network_static_starting_address_ipv4").(string) 248 | masterNetworkAddressCount := d.Get("master_network_static_address_count").(int) 249 | if "" == masterNetworkStartingAddressIpv4 || 0 == masterNetworkAddressCount { 250 | diags = append(diags, diag.Diagnostic{ 251 | Severity: diag.Error, 252 | Summary: "Supervisor starting address or address count was empty", 253 | }) 254 | return diags 255 | } 256 | masterNetworkSubnetMask := d.Get("master_network_static_subnet_mask").(string) 257 | masterNetworkGateway := d.Get("master_network_static_gateway_ipv4").(string) 258 | // masterNetworkPortgroup := d.Get("master_network_vsphere_portgroup_id").(string) 259 | 260 | workloadDnsServers := d.Get("worker_dns_servers").(string) 261 | workloadNtpServers := d.Get("workload_ntp_servers").(string) 262 | workloadNetworkName := d.Get("primary_workload_network_name").(string) 263 | workloadNetworkProvider := d.Get("primary_workload_network_provider").(string) 264 | workloadNetworkStartingAddressIpv4 := d.Get("primary_workload_network_static_starting_address_ipv4").(string) 265 | workloadNetworkAddressCount := d.Get("primary_workload_network_static_address_count").(int) 266 | if "" == workloadNetworkStartingAddressIpv4 || 0 == workloadNetworkAddressCount { 267 | diags = append(diags, diag.Diagnostic{ 268 | Severity: diag.Error, 269 | Summary: "Workload starting address or address count was empty", 270 | }) 271 | return diags 272 | } 273 | workloadNetworkSubnetMask := d.Get("primary_workload_network_static_subnet_mask").(string) 274 | workloadNetworkGateway := d.Get("primary_workload_network_static_gateway_ipv4").(string) 275 | workloadNetworkPortgroup := d.Get("primary_workload_network_vsphere_portgroup_id").(string) 276 | 277 | dataNetworkStartingAddressIpv4 := d.Get("data_network_static_starting_address_ipv4").(string) 278 | dataNetworkAddressCount := d.Get("data_network_static_address_count").(int) 279 | 280 | lbType := d.Get("load_balancer_provider").(string) 281 | lbId := d.Get("load_balancer_id").(string) 282 | lbAviControllerHost := d.Get("load_balancer_avi_host").(string) 283 | lbAviControllerPort := d.Get("load_balancer_avi_port").(int) 284 | lbAviCaChain := d.Get("load_balancer_avi_ca_chain").(string) 285 | lbAviUsername := d.Get("load_balancer_avi_username").(string) 286 | lbAviPassword := d.Get("load_balancer_avi_password").(string) 287 | 288 | // Now object conversion before operation 289 | masterNetworkProviderObj := namespace.ClusterNetworkProviderFromString(masterNetworkProvider) 290 | workloadNetworkProviderObj := namespace.ClusterNetworkProviderFromString(workloadNetworkProvider) 291 | lbTypeObj := namespace.LoadBalancerFromString(lbType) 292 | 293 | // Note: Passing in an empty slive WITHOUT omitempty gives an empty json array [] 294 | var ranges []namespace.IpRange 295 | // Empty array in JSON if Avi (Avi assigns IPs in its config) 296 | // if namespace.AviLoadBalancerProvider != lbTypeObj { 297 | 298 | // This is actually management network range, and never specified via the GUI, so leaving blank here 299 | ranges = []namespace.IpRange{{ 300 | Address: dataNetworkStartingAddressIpv4, 301 | Count: dataNetworkAddressCount, 302 | }} 303 | // } 304 | 305 | // Hard code a viable cluster for now 306 | spec := &namespace.EnableClusterSpec{ 307 | ImageStorage: namespace.ImageStorageSpec{ 308 | StoragePolicy: imagePolicyId, // GUID on submission 309 | }, 310 | MasterNTPServers: []string{masterNtpServers}, 311 | DefaultImageRepository: "", // blank 312 | EphemeralStoragePolicy: ephemeralPolicyId, // GUID on submission 313 | ServiceCidr: &namespace.Cidr{ 314 | Address: serviceCidrNetwork, 315 | Prefix: serviceCidrMask, 316 | }, 317 | SizeHint: &namespace.SmallSizingHint, 318 | // TODO SPECIFY DEFAULT IMAGE REGISTRY (OPTIONAL) 319 | // DefaultImageRegistry = ns.DefaultImageRegistry{} 320 | WorkerDNS: []string{workloadDnsServers}, 321 | MasterDNS: []string{masterDnsServers}, 322 | NetworkProvider: &masterNetworkProviderObj, 323 | MasterStoragePolicy: masterPolicyId, // GUID on submission 324 | MasterDNSSearchDomains: []string{masterDnsSearchDomains}, 325 | MasterManagementNetwork: &namespace.MasterManagementNetwork{ 326 | Mode: &namespace.StaticRangeIpAssignmentMode, 327 | AddressRange: &namespace.AddressRange{ 328 | SubnetMask: masterNetworkSubnetMask, 329 | StartingAddress: masterNetworkStartingAddressIpv4, 330 | Gateway: masterNetworkGateway, 331 | AddressCount: masterNetworkAddressCount, 332 | }, 333 | Network: masterNetworkId, 334 | }, 335 | MasterDNSNames: []string{masterDnsNames}, 336 | // TODO TLS_MGMT_ENDPOINT_CERT? 337 | // TODO TLS ENDPOINT CERT? 338 | DefaultKubernetesServiceContentLibrary: defaultLibraryId, // GUID on 339 | NcpClusterNetworkSpec: nil, 340 | // TODO WORKLOAD NETWORKS 341 | // Option A: NSX-T:- 342 | // NcpClusterNetworkSpec: &namespace.NcpClusterNetworkSpec{ 343 | // NsxEdgeCluster: "", 344 | // PodCidrs: []namespace.Cidr{ 345 | // { 346 | // Address: "", 347 | // Prefix: 24, 348 | // }, 349 | // }, 350 | // EgressCidrs: []namespace.Cidr{ 351 | // { 352 | // Address: "", 353 | // Prefix: 24, 354 | // }, 355 | // }, 356 | // ClusterDistributedSwitch: "", 357 | // IngressCidrs: []namespace.Cidr{ 358 | // { 359 | // Address: "", 360 | // Prefix: 24, 361 | // }, 362 | // }, 363 | // }, 364 | // Option B: VSPHERE NETWORKING 365 | WorkloadNetworksSpec: &namespace.WorkloadNetworksEnableSpec{ 366 | SupervisorPrimaryWorkloadNetwork: &namespace.NetworksCreateSpec{ 367 | NetworkProvider: &workloadNetworkProviderObj, 368 | VSphereNetwork: &namespace.VsphereDVPGNetworkCreateSpec{ 369 | PortGroup: workloadNetworkPortgroup, 370 | AddressRanges: []namespace.IpRange{ 371 | { 372 | Address: workloadNetworkStartingAddressIpv4, 373 | Count: workloadNetworkAddressCount, 374 | }, 375 | }, 376 | SubnetMask: workloadNetworkSubnetMask, 377 | Gateway: workloadNetworkGateway, 378 | // TODO IP Assignment mode on 7.0u3+ 379 | }, 380 | Network: workloadNetworkName, 381 | }, 382 | }, 383 | // Missing items, added since 7.0u1 and above:- 384 | WorkloadNTPServers: []string{workloadNtpServers}, 385 | LoadBalancerConfigSpec: &namespace.LoadBalancerConfigSpec{ 386 | Provider: &lbTypeObj, 387 | AddressRanges: ranges, 388 | Id: lbId, 389 | AviConfigCreateSpec: &namespace.AviConfigCreateSpec{ 390 | Server: &namespace.LoadBalancersServer{ 391 | Host: lbAviControllerHost, 392 | Port: lbAviControllerPort, 393 | }, 394 | CertificateAuthorityChain: lbAviCaChain, 395 | Username: lbAviUsername, 396 | Password: lbAviPassword, 397 | }, 398 | }, 399 | } 400 | // TODO for options (vsphere vs nsxt, avi vs other), have logic here 401 | 402 | err := ns.EnableCluster(ctx, clusterId, spec) 403 | if err != nil { 404 | return diag.FromErr(err) 405 | } 406 | 407 | // The cluster ID upon enablement is the underlying vSphere Cluster 408 | d.SetId(clusterId) 409 | return diags 410 | } 411 | 412 | func resourceClusterRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 413 | // Warning or errors can be collected in a slice type 414 | var diags diag.Diagnostics 415 | // ns := m.(*namespace.Manager) 416 | // clusterList, err := ns.ListClusters(ctx) 417 | // if err != nil { 418 | // return diag.FromErr(err) 419 | // } 420 | 421 | found := false 422 | name := "unknown" 423 | // name := d.Get("name").(string) 424 | 425 | // // Now search for our (d.Id()) matching cluster 426 | // for _, cluster := range clusterList { 427 | // if cluster.Name == name { 428 | // found = true 429 | // d.SetId(cluster.ID) 430 | // if err := d.Set("id", cluster.ID); err != nil { 431 | // return diag.FromErr(err) 432 | // } 433 | // if err := d.Set("kubernetes_status", cluster.KubernetesStatus); err != nil { 434 | // return diag.FromErr(err) 435 | // } 436 | // if err := d.Set("config_status", cluster.ConfigStatus); err != nil { 437 | // return diag.FromErr(err) 438 | // } 439 | // // TODO all detail fields here too 440 | // } 441 | // } 442 | // Handle cluster not found error 443 | if !found { 444 | diags = append(diags, diag.Diagnostic{ 445 | Severity: diag.Error, 446 | Summary: "Unable to read Tanzu Supervisor Cluster", 447 | Detail: "Cluster name not found: " + name, 448 | }) 449 | } 450 | 451 | return diags 452 | } 453 | 454 | func flattenCluster(cluster namespace.ClusterSummary) map[string]interface{} { 455 | c := make(map[string]interface{}) 456 | c["id"] = cluster.ID 457 | c["name"] = cluster.Name 458 | c["kubernetes_status"] = cluster.KubernetesStatus 459 | c["config_status"] = cluster.ConfigStatus 460 | 461 | return c 462 | } 463 | 464 | func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 465 | return resourceClusterRead(ctx, d, m) 466 | } 467 | 468 | func resourceClusterDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 469 | // Warning or errors can be collected in a slice type 470 | var diags diag.Diagnostics 471 | 472 | return diags 473 | } 474 | -------------------------------------------------------------------------------- /examples/full_esxi_tanzu_cluster/05_avi_configure/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | avi = { 4 | # version = ">= 21.1" 5 | source = "vmware/avi" 6 | } 7 | vsphere = { 8 | version = ">= 2.1.1" 9 | source = "hashicorp/vsphere" 10 | } 11 | } 12 | } 13 | 14 | data "vsphere_datacenter" "dc" { 15 | name = var.vm_datacenter 16 | } 17 | 18 | resource "avi_cluster" "vmware_cluster" { 19 | name = "cluster-0-1" 20 | nodes { 21 | ip { 22 | type = "V4" 23 | addr = var.avi_management_ip_address 24 | } 25 | name = var.avi_management_hostname 26 | } 27 | 28 | } 29 | 30 | resource "avi_backupconfiguration" "avi_backup_config" { 31 | name = "Backup-Configuration" 32 | backup_passphrase = var.avi_backup_passphrase 33 | save_local = true 34 | 35 | depends_on = [ 36 | avi_cluster.vmware_cluster 37 | ] 38 | } 39 | 40 | # TODO determine cipher restrictions IAW UK NCSC requirements 41 | resource "avi_systemconfiguration" "avi_system_config" { 42 | welcome_workflow_complete = true 43 | # TODO check the implications of the below two out, as they would make good defaults 44 | # common_criteria_mode = true 45 | # fips_mode = true 46 | 47 | # This is required else it defaults to ENTERPRISE_WITH_CLOUD_SERVICES, which fails with HTTP 500 48 | default_license_tier = "ENTERPRISE" 49 | 50 | dns_configuration { 51 | search_domain = var.avi_management_dns_suffix 52 | server_list { 53 | addr = var.avi_management_dns_server 54 | type = "V4" 55 | } 56 | } 57 | ntp_configuration { 58 | // Note the alternate config (ntp_servers) allows you to specify index numbers for each of these too 59 | // NOTE YOU GET HTTP 500 on Avi 21.1.4 using ntp_server_list 60 | # ntp_server_list { 61 | # addr = var.avi_ntp_server 62 | # type = "DNS" 63 | # } 64 | ntp_servers { 65 | key_number = 1 66 | server { 67 | addr = var.avi_ntp_server 68 | type = "DNS" 69 | } 70 | } 71 | } 72 | 73 | depends_on = [ 74 | avi_backupconfiguration.avi_backup_config 75 | ] 76 | } 77 | 78 | # data "avi_cloud" "default_cloud_ref" { 79 | # name = "Default-Cloud" 80 | # } 81 | 82 | # data "avi_vrfcontext" "lookup_default_route" { 83 | # name = "global" 84 | 85 | # depends_on = [ 86 | # avi_systemconfiguration.avi_system_config 87 | # ] 88 | # } 89 | 90 | 91 | // NOTE ALWAYS DO THIS ON THE DEFAULT CLOUD 92 | resource "avi_vrfcontext" "avi_mgmt_context" { 93 | cloud_ref = "/api/cloud?name=Default-Cloud" 94 | name = "management" 95 | system_default = true 96 | static_routes { 97 | route_id = 1 98 | prefix { 99 | ip_addr { 100 | addr = "0.0.0.0" 101 | type = "V4" 102 | } 103 | mask = 0 104 | } 105 | next_hop { 106 | addr = var.avi_management_default_gateway 107 | type = "V4" 108 | } 109 | } 110 | depends_on = [ 111 | avi_systemconfiguration.avi_system_config 112 | ] 113 | } 114 | 115 | // CREATE DNS PROFILE NOW 116 | resource "avi_ipamdnsproviderprofile" "dns_profile" { 117 | name = "ManagementDNSProfile" 118 | # cloud_ref = avi_cloud.create.id # WHY??? 119 | # TODO verify if this is supported in Essentials 120 | type = "IPAMDNS_TYPE_INTERNAL_DNS" 121 | internal_profile { 122 | dns_service_domain { 123 | domain_name = var.avi_management_domain 124 | pass_through = true 125 | } 126 | ttl = 30 127 | } 128 | allocate_ip_in_vrf = false 129 | 130 | depends_on = [ 131 | avi_systemconfiguration.avi_system_config, 132 | avi_vrfcontext.avi_mgmt_context 133 | ] 134 | } 135 | 136 | # 2. Create a Cloud that can be modified and, crucially, deleted by Terraform 137 | 138 | // Create the new cloud, but don't link until DNS set 139 | resource "avi_cloud" "create" { 140 | // NOTE: DNS must be configured before the vcenter hostname can be resolved, 141 | // so we create the cloud with no implementation reference then create 142 | // the network, DNS profile, and IPAM profile so we can later update 143 | // (WITHOUT ID - terraform weirdness) later on in vsphere_cloud. 144 | name = var.avi_cloud_name 145 | vtype = "CLOUD_NONE" 146 | # vtype = "CLOUD_VCENTER" 147 | custom_tags { 148 | tag_key = "vm_group_name" 149 | tag_val = "avi-${var.avi_deployment_name}-controller" 150 | } 151 | dhcp_enabled = false 152 | dns_resolvers { 153 | use_mgmt = true 154 | resolver_name = "mgmt-dns-resolver" 155 | nameserver_ips { 156 | addr = var.avi_management_dns_server 157 | type = "V4" 158 | } 159 | } 160 | # vcenter_configuration { 161 | # # Note: Default gateway and static ip address pool is Via Network above and IPAM/DNS profiles 162 | # datacenter = var.vm_datacenter 163 | # # static IP configuration for controllers 164 | # # management_ip_subnet { 165 | # # ip_addr { 166 | # # addr = var.avi_management_ip_network 167 | # # type = "V4" 168 | # # } 169 | # # mask = var.avi_management_subnet_mask_int 170 | # # } 171 | # # management_network = var.avi_management_network_name 172 | # # management_network = avi_network.avi_management_network.id 173 | # # management_network = "/api/vimgrnwruntime/?name=${avi_network.avi_management_network.name}" 174 | # management_network = "/api/vimgrnwruntime/?name=${var.avi_management_network_name}" 175 | # // WARNING UNLIKE ANSIBLE PROVIDER, THE AVI TERRAFORM PROVIDER 176 | # // ALWAYS SEND A BLANK (AND MALFORMED) MANAGEMENT_NETWORK VALUE 177 | # // UNLESS WE SPECIFY IT HERE 178 | # #management_network = "https://10.220.50.10/api/network/${data.vsphere_network.mgmt_port_group.id}-${avi_cloud.create.uuid}#${var.avi_management_network_name}" 179 | # // Note: MGMT NETWORK REF LOOKUP - can only be done AFTER we've connected to vSphere, so using direct name hack 180 | # password = var.vcenter_password 181 | # username = var.vcenter_username 182 | # privilege = "WRITE_ACCESS" 183 | # vcenter_url = var.vcenter_url 184 | # } 185 | 186 | depends_on = [ 187 | avi_ipamdnsproviderprofile.dns_profile 188 | ] 189 | } 190 | 191 | # Note: If DNS Server is not on the same subnet, you need to define a default route first 192 | # resource "avi_vrfcontext" "default_route" { 193 | # // HAS TO BE NAMED GLOBAL IF 0.0.0.0 194 | # # name = "${var.avi_cloud_name}_default_route_new" 195 | # name = "global" 196 | # # CLOUD CANNOT BE UPDATED FOR DEFAULT ROUTE 197 | # # cloud_ref = avi_cloud.create.id 198 | # # uuid = data.avi_vrfcontext.lookup_default_route.id 199 | # static_routes { 200 | # route_id = 0 201 | # prefix { 202 | # ip_addr { 203 | # addr = "0.0.0.0" 204 | # type = "V4" 205 | # } 206 | # mask = 0 207 | # } 208 | # next_hop { 209 | # addr = var.avi_management_default_gateway 210 | # type = "V4" 211 | # } 212 | # } 213 | # # These default to true for reasons passing understanding 214 | # system_default = true 215 | # lldp_enable = false 216 | 217 | # depends_on = [ 218 | # avi_systemconfiguration.avi_system_config 219 | # ] 220 | # } 221 | 222 | // Cloud Default-Cloud resource??? 223 | 224 | 225 | # Now lookup / create full URL (Avi id) for management network 226 | # ID is dvportgroup-15-cloud-CLOUDGUID 227 | data "vsphere_network" "mgmt_port_group" { 228 | name = var.avi_management_network_name 229 | datacenter_id = data.vsphere_datacenter.dc.id 230 | } 231 | 232 | 233 | // 3. Create the IPAM profile for the vCenter Cloud network 234 | resource "avi_ipamdnsproviderprofile" "ipam_profile" { 235 | name = "ManagementIPAMProfile" 236 | # The following is supported in Essentials and above 237 | type = "IPAMDNS_TYPE_INTERNAL" 238 | internal_profile { 239 | usable_networks { 240 | nw_ref = "https://${var.avi_management_ip_address}/api/network/${data.vsphere_network.mgmt_port_group.id}-${avi_cloud.create.uuid}#${var.avi_management_network_name}" 241 | # nw_ref = avi_network.avi_management_network.id 242 | } 243 | ttl = 30 244 | } 245 | depends_on = [ 246 | # avi_network.avi_management_network, 247 | avi_ipamdnsproviderprofile.dns_profile 248 | ] 249 | } 250 | 251 | // 4. Now we can finally update the cloud with the vcenter connection info. 252 | // This also configures the management network, subnets, IPAM and DNS. 253 | resource "avi_cloud" "vsphere_cloud" { 254 | // NOTE: DNS must be configured before here for the vcenter hostname to be resolved 255 | name = var.avi_cloud_name 256 | # uuid = avi_cloud.create.uuid 257 | vtype = "CLOUD_VCENTER" 258 | custom_tags { 259 | tag_key = "vm_group_name" 260 | tag_val = "avi-${var.avi_deployment_name}-controller" 261 | } 262 | dhcp_enabled = false 263 | ip6_autocfg_enabled = false 264 | dns_resolvers { 265 | use_mgmt = true 266 | resolver_name = "mgmt-dns-resolver" 267 | nameserver_ips { 268 | addr = var.avi_management_dns_server 269 | type = "V4" 270 | } 271 | } 272 | # Note: Below is useful if resolution different on mgmt and data networks 273 | # dns_resolution_on_se = true 274 | # Tanzu Docs say to leave the following as its default:- 275 | # enable_vip_static_routes = true 276 | # enable_vip_on_all_interfaces = true 277 | 278 | # TODO see if we can configure the below before the rest of the config 279 | # Note in the Tanzu docs we configure everything else first BUT it's not 280 | # clear that this is an ordering requirement, so lets test it eventually 281 | # ipam_provider_ref = TBD 282 | 283 | 284 | # mtu = 1500 or 9000 285 | 286 | # Note the below is recommended as true (less network interfaces on SEs) 287 | # BUT requires an Avi Enterprise (not essentials) license 288 | prefer_static_routes = var.avi_prefer_static_routes 289 | enable_vip_static_routes = false 290 | # se_group_template_ref = TBD 291 | # Note, only one tenant by default ("admin") 292 | # tenent_ref = TBD 293 | vcenter_configuration { 294 | # Note: Default gateway and static ip address pool is Via Network above and IPAM/DNS profiles 295 | datacenter = var.vm_datacenter 296 | # static IP configuration for controllers 297 | management_ip_subnet { 298 | ip_addr { 299 | addr = var.avi_management_ip_network 300 | type = "V4" 301 | } 302 | mask = var.avi_management_subnet_mask_int 303 | # gateway = var.avi_management_default_gateway 304 | } 305 | # management_network = var.avi_management_network_name 306 | # management_network = avi_network.avi_management_network.id 307 | # management_network = "https://10.220.50.10/api/vimgrnwruntime?name=${var.avi_management_network_name}" 308 | # management_network = "" 309 | // WARNING UNLIKE ANSIBLE PROVIDER, THE AVI TERRAFORM PROVIDER 310 | // ALWAYS SEND A BLANK (AND MALFORMED) MANAGEMENT_NETWORK VALUE 311 | // UNLESS WE SPECIFY IT HERE 312 | management_network = "https://${var.avi_management_ip_address}/api/network/${data.vsphere_network.mgmt_port_group.id}-${avi_cloud.create.uuid}#${var.avi_management_network_name}" 313 | // Note: MGMT NETWORK REF LOOKUP - can only be done AFTER we've connected to vSphere, so using direct name hack 314 | password = var.vcenter_password 315 | username = var.vcenter_username 316 | privilege = "WRITE_ACCESS" 317 | vcenter_url = var.vcenter_url 318 | } 319 | # ipam_provider_ref = avi_ipamdnsproviderprofile.ipam_profile.id 320 | # dns_provider_ref = avi_ipamdnsproviderprofile.dns_profile.id 321 | 322 | depends_on = [ 323 | # avi_network.avi_management_network, 324 | avi_ipamdnsproviderprofile.ipam_profile 325 | ] 326 | } 327 | 328 | # NOTE: Unlike in the UI, terraform approach doesn't create default network sets 329 | resource "avi_network" "avi_management_network" { 330 | # Management network is the vm_network for the controller - same name 331 | name = var.avi_management_network_name 332 | cloud_ref = avi_cloud.create.id 333 | # cloud_ref = avi_cloud.vsphere_cloud.id 334 | # vimgrnw_ref = "https://10.220.50.10/api/network/${data.vsphere_network.mgmt_port_group.id}-${avi_cloud.create.uuid}#${var.avi_management_network_name}" 335 | vcenter_dvs = true 336 | 337 | dhcp_enabled = "false" 338 | ip6_autocfg_enabled = false 339 | configured_subnets { 340 | prefix { 341 | ip_addr { 342 | addr = var.avi_management_ip_network 343 | type = "V4" 344 | } 345 | mask = var.avi_management_subnet_mask_int 346 | } 347 | static_ip_ranges { 348 | range { 349 | begin { 350 | addr = var.avi_management_ip_address_start 351 | type = "V4" 352 | } 353 | end { 354 | addr = var.avi_management_ip_address_end 355 | type = "V4" 356 | } 357 | } 358 | type = "STATIC_IPS_FOR_VIP_AND_SE" 359 | } 360 | # gateway = var.avi_management_default_gateway 361 | } 362 | 363 | depends_on = [ 364 | avi_cloud.vsphere_cloud 365 | ] 366 | } 367 | 368 | # THIS IS IDENTICAL BUT FORCES THE VIMGRNWRUNTIME LINK TO BE ADDED 369 | # resource "avi_network" "avi_management_network_reconfigure" { 370 | # # Management network is the vm_network for the controller - same name 371 | # name = var.avi_management_network_name 372 | # cloud_ref = avi_cloud.vsphere_cloud.id 373 | # id = avi_network.avi_management_network.id 374 | # # cloud_ref = avi_cloud.vsphere_cloud.id 375 | # dhcp_enabled = "false" 376 | # ip6_autocfg_enabled = false 377 | # configured_subnets { 378 | # prefix { 379 | # ip_addr { 380 | # addr = var.avi_management_ip_network 381 | # type = "V4" 382 | # } 383 | # mask = var.avi_management_subnet_mask_int 384 | # } 385 | # static_ip_ranges { 386 | # range { 387 | # begin { 388 | # addr = var.avi_management_ip_address_start 389 | # type = "V4" 390 | # } 391 | # end { 392 | # addr = var.avi_management_ip_address_end 393 | # type = "V4" 394 | # } 395 | # } 396 | # type = "STATIC_IPS_FOR_VIP_AND_SE" 397 | # } 398 | # } 399 | 400 | # depends_on = [ 401 | # avi_cloud.vsphere_cloud 402 | # ] 403 | # } 404 | 405 | resource "avi_vrfcontext" "avi_data_context" { 406 | # Sets default route for the "Data Network" (aka "VIP Network" in old docs) 407 | cloud_ref = avi_cloud.vsphere_cloud.id 408 | name = "global" 409 | system_default = true 410 | static_routes { 411 | route_id = 0 412 | prefix { 413 | ip_addr { 414 | addr = "0.0.0.0" 415 | type = "V4" 416 | } 417 | mask = 0 418 | } 419 | next_hop { 420 | addr = var.avi_data_network_default_gateway 421 | type = "V4" 422 | } 423 | } 424 | 425 | depends_on = [ 426 | avi_cloud.vsphere_cloud 427 | ] 428 | } 429 | 430 | 431 | 432 | // NOTE ALWAYS DO THIS ON THE DEFAULT CLOUD 433 | resource "avi_vrfcontext" "avi_mgmt_context_new_cloud" { 434 | cloud_ref = avi_cloud.vsphere_cloud.id 435 | name = "management" 436 | system_default = true 437 | static_routes { 438 | route_id = 1 439 | prefix { 440 | ip_addr { 441 | addr = "0.0.0.0" 442 | type = "V4" 443 | } 444 | mask = 0 445 | } 446 | next_hop { 447 | addr = var.avi_management_default_gateway 448 | type = "V4" 449 | } 450 | } 451 | depends_on = [ 452 | avi_vrfcontext.avi_data_context, 453 | avi_cloud.vsphere_cloud 454 | ] 455 | } 456 | 457 | // IPAM FOR DATA NETWORK 458 | resource "avi_ipamdnsproviderprofile" "ipam_data_profile" { 459 | name = "DataIPAMProfile" 460 | # The following is supported in Essentials and above 461 | type = "IPAMDNS_TYPE_INTERNAL" 462 | internal_profile { 463 | usable_networks { 464 | # nw_ref = "https://10.220.50.10/api/network/${data.vsphere_network.mgmt_port_group.id}-${avi_cloud.create.uuid}#${var.avi_management_network_name}" 465 | nw_ref = "https://${var.avi_management_ip_address}/api/network?name=${var.avi_data_network_name}" 466 | } 467 | ttl = 30 468 | } 469 | 470 | depends_on = [ 471 | avi_vrfcontext.avi_data_context, 472 | avi_vrfcontext.avi_mgmt_context_new_cloud 473 | ] 474 | } 475 | 476 | resource "avi_network" "avi_data_network" { 477 | # Data network is network the Avi SEs run on, and VIPs created on 478 | name = var.avi_data_network_name 479 | # cloud_ref = avi_cloud.vsphere_cloud.id 480 | cloud_ref = avi_cloud.create.id 481 | # cloud_ref = var.avi_cloud_name 482 | ip6_autocfg_enabled = false 483 | dhcp_enabled = "false" 484 | // Note: No default gateway. Configured in SE group config??? 485 | configured_subnets { 486 | prefix { 487 | ip_addr { 488 | addr = var.avi_data_network_network_ipv4 489 | type = "V4" 490 | } 491 | mask = var.avi_data_network_subnet_mask_int 492 | } 493 | static_ip_ranges { 494 | range { 495 | begin { 496 | addr = var.avi_data_network_start_ipv4 497 | type = "V4" 498 | } 499 | end { 500 | addr = var.avi_data_network_end_ipv4 501 | type = "V4" 502 | } 503 | } 504 | type = "STATIC_IPS_FOR_VIP_AND_SE" 505 | } 506 | } 507 | 508 | depends_on = [ 509 | avi_ipamdnsproviderprofile.ipam_data_profile 510 | ] 511 | } 512 | 513 | resource "avi_serviceenginegroup" "default_group" { 514 | # name = "Default-Group" 515 | name = var.avi_se_group_name 516 | cloud_ref = avi_cloud.vsphere_cloud.id 517 | 518 | ha_mode = "HA_MODE_SHARED" # means N+M (Enterprise license required) 519 | se_name_prefix = "Avi" # results in Avi-se-abcde 520 | # TODO can we specify a resource group as well as a folder? (like for controllers) (Not currently, it would appear, we'll have to move them after creation) 521 | vcenter_folder = "AviSeFolder" 522 | vcenter_clusters { 523 | include = true 524 | cluster_refs = [ 525 | "https://${var.avi_management_ip_address}/api/vimgrclusterruntime?name=${var.vm_cluster}" 526 | ] 527 | } 528 | 529 | # HOSTS NOT REQUIRED, ONLY CLUSTER 530 | # vcenter_hosts { 531 | # include = true 532 | # host_refs = [ 533 | # # "https://10.220.50.10/api/vimgrhostruntime/host-14-cloud-c74473a8-f12a-4ece-854f-a2f16c0667b1#esxi01.h2o-4-328.h2o.vmware.com" 534 | # "https://10.220.50.10/api/vimgrclusterruntime/${data.vsphere_host.esxi01.id}#${var.esxi_vm_name}" 535 | # # TODO support multiple ESXi hosts per cluster 536 | # ] 537 | # } 538 | 539 | # data_network_id = avi_network.avi_data_network.id 540 | # mgmt_network_ref = avi_network.avi_management_network.id 541 | # TODO placement_across_ses changed to distributed by default 542 | min_se = 1 543 | max_se = 7 544 | buffer_se = 1 545 | # hypervisor = "" 546 | max_vs_per_se = 100 547 | min_scaleout_per_vs = 1 548 | max_scaleout_per_vs = 4 549 | # TODO check if the below results in VM attributes (useful for vm-vm anti-affinity rules later) -> Can't, need to use VM names(IDs) only 550 | # labels = "" 551 | # custom_tag = [] 552 | 553 | depends_on = [ 554 | avi_network.avi_management_network, 555 | avi_network.avi_data_network 556 | ] 557 | } 558 | 559 | // 5. Now we can finally update the cloud with the default SE template. 560 | // This has to be done AFTER the data and management networks are 561 | // finally configured. 562 | resource "avi_cloud" "vsphere_cloud_with_se" { 563 | // NOTE: DNS must be configured before here for the vcenter hostname to be resolved 564 | name = var.avi_cloud_name 565 | # uuid = avi_cloud.create.uuid 566 | vtype = "CLOUD_VCENTER" 567 | custom_tags { 568 | tag_key = "vm_group_name" 569 | tag_val = "avi-${var.avi_deployment_name}-controller" 570 | } 571 | dhcp_enabled = false 572 | ip6_autocfg_enabled = false 573 | dns_resolvers { 574 | use_mgmt = true 575 | resolver_name = "mgmt-dns-resolver" 576 | nameserver_ips { 577 | addr = var.avi_management_dns_server 578 | type = "V4" 579 | } 580 | } 581 | # Note: Below is useful if resolution different on mgmt and data networks 582 | # dns_resolution_on_se = true 583 | # Tanzu Docs say to leave the following as its default:- 584 | # enable_vip_static_routes = true 585 | # enable_vip_on_all_interfaces = true 586 | 587 | # TODO see if we can configure the below before the rest of the config 588 | # Note in the Tanzu docs we configure everything else first BUT it's not 589 | # clear that this is an ordering requirement, so lets test it eventually 590 | # ipam_provider_ref = TBD 591 | 592 | 593 | # mtu = 1500 or 9000 594 | 595 | # Note the below is recommended as true (less network interfaces on SEs) 596 | # BUT requires an Avi Enterprise (not essentials) license 597 | prefer_static_routes = var.avi_prefer_static_routes 598 | enable_vip_static_routes = false 599 | # se_group_template_ref = TBD 600 | # Note, only one tenant by default ("admin") 601 | # tenent_ref = TBD 602 | vcenter_configuration { 603 | # Note: Default gateway and static ip address pool is Via Network above and IPAM/DNS profiles 604 | datacenter = var.vm_datacenter 605 | # static IP configuration for controllers 606 | management_ip_subnet { 607 | ip_addr { 608 | addr = var.avi_management_ip_network 609 | type = "V4" 610 | } 611 | mask = var.avi_management_subnet_mask_int 612 | } 613 | # management_network = var.avi_management_network_name 614 | # management_network = avi_network.avi_management_network.id 615 | # management_network = "https://10.220.50.10/api/vimgrnwruntime?name=${var.avi_management_network_name}" 616 | # management_network = "" 617 | // WARNING UNLIKE ANSIBLE PROVIDER, THE AVI TERRAFORM PROVIDER 618 | // ALWAYS SEND A BLANK (AND MALFORMED) MANAGEMENT_NETWORK VALUE 619 | // UNLESS WE SPECIFY IT HERE 620 | management_network = "https://${var.avi_management_ip_address}/api/network/${data.vsphere_network.mgmt_port_group.id}-${avi_cloud.create.uuid}#${var.avi_management_network_name}" 621 | // Note: MGMT NETWORK REF LOOKUP - can only be done AFTER we've connected to vSphere, so using direct name hack 622 | password = var.vcenter_password 623 | username = var.vcenter_username 624 | privilege = "WRITE_ACCESS" 625 | vcenter_url = var.vcenter_url 626 | 627 | } 628 | // NOTE WE'VE NOW SWITCHED TO THE DATA IPAM 629 | ipam_provider_ref = avi_ipamdnsproviderprofile.ipam_data_profile.id 630 | # ipam_provider_ref = avi_ipamdnsproviderprofile.ipam_profile.id 631 | # dns_provider_ref = avi_ipamdnsproviderprofile.dns_profile.id 632 | 633 | // NOTE WE'VE ALSO NOW SPECIFIED THE SE GROUP TEMPLATE TO USE 634 | # se_group_template_ref = avi_serviceenginegroup.default_group.id 635 | 636 | depends_on = [ 637 | # avi_network.avi_management_network, 638 | avi_serviceenginegroup.default_group 639 | ] 640 | } 641 | 642 | # TODO vm-vm host anti-affinity groups for Avi-SE VMs 643 | 644 | # TODO just output the VIP name, the rest were from known config 645 | 646 | # Read raw cert if it already exists 647 | data "avi_sslkeyandcertificate" "loadcertifexists" { 648 | name = "NewControllerCert" 649 | depends_on = [ 650 | avi_cloud.vsphere_cloud_with_se 651 | ] 652 | } 653 | 654 | # Generate a new certificate and download 655 | resource "avi_sslkeyandcertificate" "cert" { 656 | certificate { 657 | expiry_status = "SSL_CERTIFICATE_GOOD" 658 | days_until_expire = 365 659 | self_signed = true 660 | subject { 661 | common_name = var.avi_management_ip_address 662 | } 663 | subject_alt_names = [ 664 | var.avi_management_ip_address, 665 | "avi.${var.avi_management_domain}" 666 | ] 667 | // Hack for Avi provider bug - certificate value required even for generate not upload 668 | certificate = (null != data.avi_sslkeyandcertificate.loadcertifexists.certificate ? "${one(data.avi_sslkeyandcertificate.loadcertifexists.certificate[*]).certificate}" : "") 669 | } 670 | status = "SSL_CERTIFICATE_FINISHED" 671 | format = "SSL_PEM" 672 | key_params { 673 | algorithm = "SSL_KEY_ALGORITHM_EC" 674 | ec_params { 675 | curve = "SSL_KEY_EC_CURVE_SECP256R1" 676 | } 677 | } 678 | certificate_base64 = true 679 | key_base64 = true 680 | enable_ocsp_stapling = false 681 | ocsp_config { 682 | ocsp_req_interval = 86400 683 | url_action = "OCSP_RESPONDER_URL_FAILOVER" 684 | failed_ocsp_jobs_retry_interval = 3600 685 | max_tries = 10 686 | } 687 | type = "SSL_CERTIFICATE_TYPE_SYSTEM" 688 | name = "NewControllerCert" 689 | 690 | depends_on = [ 691 | # avi_serviceenginegroup.default_group 692 | data.avi_sslkeyandcertificate.loadcertifexists 693 | ] 694 | } 695 | 696 | # Now load the certificate to fetch the full details for ca chain 697 | data "avi_sslkeyandcertificate" "loadcert" { 698 | name = avi_sslkeyandcertificate.cert.name 699 | } 700 | # Assuming a single level of indirection... 701 | data "avi_sslkeyandcertificate" "loadrootcert" { 702 | // TODO make this dynamic... somehow... 703 | # name = data.avi_sslkeyandcertificate.loadcert.ca_certs[0].name 704 | name = "System-Default-Root-CA" 705 | 706 | depends_on = [ 707 | data.avi_sslkeyandcertificate.loadcert 708 | ] 709 | } 710 | 711 | # Now attach the cert to the portal 712 | 713 | resource "avi_systemconfiguration" "avi_system_final_config" { 714 | welcome_workflow_complete = true 715 | # TODO check the implications of the below two out, as they would make good defaults 716 | # common_criteria_mode = true 717 | # fips_mode = true 718 | 719 | # This is required else it defaults to ENTERPRISE_WITH_CLOUD_SERVICES, which fails with HTTP 500 720 | default_license_tier = "ENTERPRISE" 721 | 722 | dns_configuration { 723 | search_domain = var.avi_management_dns_suffix 724 | server_list { 725 | addr = var.avi_management_dns_server 726 | type = "V4" 727 | } 728 | } 729 | ntp_configuration { 730 | // Note the alternate config (ntp_servers) allows you to specify index numbers for each of these too 731 | // NOTE YOU GET HTTP 500 on Avi 21.1.4 using ntp_server_list 732 | # ntp_server_list { 733 | # addr = var.avi_ntp_server 734 | # type = "DNS" 735 | # } 736 | ntp_servers { 737 | key_number = 1 738 | server { 739 | addr = var.avi_ntp_server 740 | type = "DNS" 741 | } 742 | } 743 | } 744 | portal_configuration { 745 | sslkeyandcertificate_refs = [ 746 | data.avi_sslkeyandcertificate.loadcert.id 747 | ] 748 | } 749 | 750 | depends_on = [ 751 | data.avi_sslkeyandcertificate.loadrootcert, 752 | data.avi_sslkeyandcertificate.loadcert 753 | ] 754 | } 755 | 756 | output "system_configuration" { 757 | value = avi_systemconfiguration.avi_system_final_config 758 | } 759 | 760 | output "cloud" { 761 | value = avi_cloud.vsphere_cloud_with_se 762 | sensitive = true 763 | } 764 | 765 | output "data_network" { 766 | value = avi_network.avi_data_network 767 | } 768 | 769 | output "management_network" { 770 | value = avi_network.avi_management_network 771 | } 772 | 773 | output "cert" { 774 | value = one(data.avi_sslkeyandcertificate.loadcert.certificate[*]) 775 | } 776 | 777 | output "rootcert" { 778 | value = one(data.avi_sslkeyandcertificate.loadrootcert.certificate[*]) 779 | } 780 | 781 | # output "cachain" { 782 | # value = "${data.avi_sslkeyandcertificate.loadcert.certificate.1.certificate}\n${data.avi_sslkeyandcertificate.loadrootcert.certificate.1.certificate}" 783 | # } --------------------------------------------------------------------------------