diff --git a/.gitignore b/.gitignore index e532183..0aa144f 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ terraform.rc *-credentials.tfbackend .terraform.lock.hcl + +vault.yml \ No newline at end of file diff --git a/configuration/ansible_fs/ansible.cfg b/configuration/ansible_fs/ansible.cfg new file mode 100644 index 0000000..2a4e2e5 --- /dev/null +++ b/configuration/ansible_fs/ansible.cfg @@ -0,0 +1,20 @@ +[defaults] + +inventory = inventories/production/hosts.yml + +roles_path = roles + +host_key_checking = False + +remote_user = Administrator + +timeout = 30 +interpreter_python = auto_silent +forks = 20 +pipelining = True +bin_ansible_callbacks = True +retry_files_enabled = False + +[ssh_connection] +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no +transfer_method = scp diff --git a/configuration/ansible_fs/inventories/production/group_vars/all/all.yml b/configuration/ansible_fs/inventories/production/group_vars/all/all.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/configuration/ansible_fs/inventories/production/group_vars/all/all.yml @@ -0,0 +1 @@ +--- diff --git a/configuration/ansible_fs/inventories/production/group_vars/all/vault.yml.example b/configuration/ansible_fs/inventories/production/group_vars/all/vault.yml.example new file mode 100644 index 0000000..c1254a8 --- /dev/null +++ b/configuration/ansible_fs/inventories/production/group_vars/all/vault.yml.example @@ -0,0 +1,7 @@ +--- +fs_server_password: "" + +domain_name: "" +domain_join_user: "" +domain_join_password: "" +domain_ou_path: "" diff --git a/configuration/ansible_fs/inventories/production/hosts.yml b/configuration/ansible_fs/inventories/production/hosts.yml new file mode 100644 index 0000000..e309663 --- /dev/null +++ b/configuration/ansible_fs/inventories/production/hosts.yml @@ -0,0 +1,10 @@ +all: + children: + fs_servers: + hosts: + SK3SFS1P.ad.cwx.hr: + ansible_host: 10.10.2.101 + ansible_connection: ssh + ansible_shell_type: powershell + ansible_user: Administrator + ansible_password: "{{ fs_server_password }}" diff --git a/configuration/ansible_fs/playbooks/setup-fs.yml b/configuration/ansible_fs/playbooks/setup-fs.yml new file mode 100644 index 0000000..bbce189 --- /dev/null +++ b/configuration/ansible_fs/playbooks/setup-fs.yml @@ -0,0 +1,6 @@ +--- +- name: Configure file servers + hosts: fs_servers + roles: + - common + - smb_share diff --git a/configuration/ansible_fs/roles/common/defaults/main.yml b/configuration/ansible_fs/roles/common/defaults/main.yml new file mode 100644 index 0000000..e9f7794 --- /dev/null +++ b/configuration/ansible_fs/roles/common/defaults/main.yml @@ -0,0 +1,7 @@ +--- +domain_name: "example.local" +domain_join_user: "{{ domain_join_user }}" +domain_join_password: "{{ domain_join_password }}" +domain_ou_path: "" + +data_disk_number: 1 diff --git a/configuration/ansible_fs/roles/common/tasks/main.yml b/configuration/ansible_fs/roles/common/tasks/main.yml new file mode 100644 index 0000000..016cca5 --- /dev/null +++ b/configuration/ansible_fs/roles/common/tasks/main.yml @@ -0,0 +1,71 @@ +--- +- name: Wait for system to be fully booted + ansible.builtin.wait_for_connection: + timeout: 300 + sleep: 10 + +- name: Ensure Windows Update service is running + ansible.windows.win_service: + name: wuauserv + state: started + start_mode: auto + +- name: Ensure BITS service is running + ansible.windows.win_service: + name: BITS + state: started + start_mode: auto + +- name: Install Windows updates (loop until no more pending) + ansible.windows.win_updates: + category_names: + - SecurityUpdates + - CriticalUpdates + - UpdateRollups + - Updates + state: installed + reboot: true + reboot_timeout: 3600 + server_selection: windows_update + register: win_updates_result + until: win_updates_result.installed_update_count == 0 + retries: 5 + delay: 30 + +- name: Report Windows Update result + ansible.builtin.debug: + msg: >- + Windows Update complete. + Last pass installed {{ win_updates_result.installed_update_count }} update(s). + Reboot required: {{ win_updates_result.reboot_required }}. + +- name: Join Active Directory domain + microsoft.ad.membership: + dns_domain_name: "{{ domain_name }}" + hostname: "{{ inventory_hostname_short }}" + domain_admin_user: "{{ domain_join_user }}" + domain_admin_password: "{{ domain_join_password }}" + domain_ou_path: "{{ domain_ou_path | default(omit) }}" + state: domain + reboot: true + reboot_timeout: 1800 + +- name: Initialize data disk as GPT + community.windows.win_initialize_disk: + disk_number: "{{ data_disk_number }}" + style: gpt + online: true + +- name: Create shares partition (F:) + community.windows.win_partition: + disk_number: "{{ data_disk_number }}" + partition_size: -1 + drive_letter: F + state: present + +- name: Format shares partition (F:) + community.windows.win_format: + drive_letter: F + file_system: NTFS + new_label: FileShares + allocation_unit_size: 65536 diff --git a/configuration/ansible_fs/roles/smb_share/defaults/main.yml b/configuration/ansible_fs/roles/smb_share/defaults/main.yml new file mode 100644 index 0000000..62a5807 --- /dev/null +++ b/configuration/ansible_fs/roles/smb_share/defaults/main.yml @@ -0,0 +1,8 @@ +--- +smb_shares: + - name: k8s + path: 'F:\Shares\k8s' + description: "Kubernetes CSI SMB persistent volume share" + full_access: + - "CWX\\k8s-svc-user" + change_access: [] diff --git a/configuration/ansible_fs/roles/smb_share/tasks/main.yml b/configuration/ansible_fs/roles/smb_share/tasks/main.yml new file mode 100644 index 0000000..4e716c6 --- /dev/null +++ b/configuration/ansible_fs/roles/smb_share/tasks/main.yml @@ -0,0 +1,42 @@ +--- +- name: Install File Server role + ansible.windows.win_feature: + name: FS-FileServer + state: present + include_management_tools: true + register: fs_role + +- name: Reboot if File Server role install requires it + ansible.windows.win_reboot: + when: fs_role.reboot_required + +- name: Create share directories + ansible.windows.win_file: + path: "{{ item.path }}" + state: directory + loop: "{{ smb_shares }}" + +- name: Create SMB shares + ansible.windows.win_share: + name: "{{ item.name }}" + path: "{{ item.path }}" + description: "{{ item.description | default('') }}" + full: "{{ item.full_access | default([]) | join(',') or omit }}" + change: "{{ item.change_access | default([]) | join(',') or omit }}" + state: present + loop: "{{ smb_shares }}" + +- name: Disable SMB1 protocol + ansible.windows.win_powershell: + script: | + Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force + +- name: Open SMB firewall port + community.windows.win_firewall_rule: + name: "File and Printer Sharing (SMB-In)" + localport: 445 + action: allow + direction: in + protocol: tcp + state: present + enabled: true diff --git a/configuration/ansible_mssql/inventories/production/group_vars/all/vault.yml b/configuration/ansible_mssql/inventories/production/group_vars/all/vault.yml index a6998e0..f129e8b 100644 --- a/configuration/ansible_mssql/inventories/production/group_vars/all/vault.yml +++ b/configuration/ansible_mssql/inventories/production/group_vars/all/vault.yml @@ -1,18 +1,18 @@ $ANSIBLE_VAULT;1.1;AES256 -34613730306335623333623230346639323439343264366138623465656138346535663236336635 -3933323931303236313761656337383735373332393366620a396664653239623437353263306563 -64643237666633343165633230363065306561376166613735396562386464623532393730643765 -3135326437616236640a363463383363626539383465663731313833306138633061386666313535 -37636564373437313933636234636339613330613935336666343433376630346664336336383538 -62623761323539363536313031623135333831643638336234643264343632393863363539653461 -32393333646131316337653636386563396562613961383335323431623835653434333839613635 -63353433656630366166336239643766653938363033373137623532353237353261383261623437 -31366237353462623731343766653832343936363738373031633231373038376435613662616234 -34373861656361353863326432616436316537643065313564306131616636383939343438333838 -37326532663639666332313239316635313637333262633636376237653036343465646638303263 -31333866633966346534303564623164373637303337646632326135666535663566333833356537 -30323061613231393332346262393261343731363462623536313964313466666238363532613762 -65666163376231313065393861643635626136316434643761386137306530346533653366643830 -63363738366134323665323232313439323865323235313331316164393431373362646630336261 -39313732376531336230663032363437343862646331613831623063336164373465343136643037 -66643130636538383339393435616563323930386262343134636134373434326561 +62323935633064326661353036336664643438613562613439346166316436613431653664316634 +3536313163636338343664326436653133313531303462320a616135323639313431343231343934 +61343965326235356339656237386365363061303633386431623430373734633535306439353937 +3935313331356336660a303162306433346335336232303733623035396661316664636236393164 +35336330356335396262613739666139356161306663613435366339383761343666306632343732 +65366439643532333532656566313764303061323461326439616235623931663665353961373236 +37623366303430656132373261356466313735383435373133613564396463623638393530613235 +31336466366166333164366564663564366662363236353936306332623738386534326438353363 +65373630313535313166346661303235663966303637623433393164356533626233323632653264 +62626235363939623532393863623665303731393066376562666536623066643863366564333135 +34343030643266393235653239623431633763326533656366663537383461383763383034646563 +64653565383061636530643561306132636532393432356166353063303831396537623662343039 +63363735383530636239313231363332643839336135646262613931633734616663633737363434 +65343233376461643338323232313730326439396161373133376232633866623334396330393437 +37643261343265376466616537313864326436656532393830656135393939386564636235633138 +65316364383230383532396364323739306465353237613533613239613965663134623438646434 +63383730313265363535363466363433316339323666636164616164613832613233 diff --git a/provisioning/SK3SFS1P/backend.tfbackend b/provisioning/SK3SFS1P/backend.tfbackend new file mode 100644 index 0000000..1fa7bbc --- /dev/null +++ b/provisioning/SK3SFS1P/backend.tfbackend @@ -0,0 +1,13 @@ +bucket = "cwxbkp1-prod" +key = "proxmox/prod/SK3SFS1P/terraform.tfstate" +region = "eu-central" + +endpoints = { + s3 = "https://nbg1.your-objectstorage.com" +} + +force_path_style = true +skip_credentials_validation = true +skip_metadata_api_check = true +skip_region_validation = true +skip_requesting_account_id = true diff --git a/provisioning/SK3SFS1P/main.tf b/provisioning/SK3SFS1P/main.tf new file mode 100644 index 0000000..2def32a --- /dev/null +++ b/provisioning/SK3SFS1P/main.tf @@ -0,0 +1,58 @@ +terraform { + required_version = ">= 1.6.0" + + required_providers { + proxmox = { + source = "bpg/proxmox" + version = "~> 0.73" + } + } + + backend "s3" {} +} + +provider "proxmox" { + endpoint = var.proxmox_endpoint + insecure = var.proxmox_insecure + api_token = var.proxmox_api_token + + ssh { + username = var.proxmox_ssh_user + password = var.proxmox_ssh_password + agent = var.proxmox_ssh_agent + } +} + +module "vm" { + source = "../modules/vm" + + node_name = var.node_name + vm_id = var.vm_id + name = var.name + description = var.description + tags = var.tags + bios = "ovmf" + os_type = "win11" + + cpu_cores = var.cpu_cores + memory_dedicated = var.memory_dedicated + + clone_vm_id = var.clone_vm_id + clone_datastore_id = var.clone_datastore_id + + init_ipv4_address = var.init_ipv4_address + init_ipv4_gateway = var.init_ipv4_gateway + init_dns_servers = var.init_dns_servers + init_username = var.init_username + admin_password = var.admin_password + cloud_init_datastore = var.cloud_init_datastore + + disk_datastore = var.disk_datastore + disk_size = var.disk_size + + data_disk_size = var.data_disk_size + data_disk_datastore = var.data_disk_datastore + + network_bridge = var.network_bridge + network_vlan_id = var.network_vlan_id +} diff --git a/provisioning/SK3SFS1P/outputs.tf b/provisioning/SK3SFS1P/outputs.tf new file mode 100644 index 0000000..72a9c3f --- /dev/null +++ b/provisioning/SK3SFS1P/outputs.tf @@ -0,0 +1,19 @@ +output "vm_id" { + description = "The Proxmox VM ID" + value = module.vm.vm_id +} + +output "name" { + description = "The VM hostname" + value = module.vm.name +} + +output "ipv4_addresses" { + description = "IPv4 addresses reported by the QEMU agent" + value = module.vm.ipv4_addresses +} + +output "mac_addresses" { + description = "MAC addresses of the VM's network interfaces" + value = module.vm.mac_addresses +} diff --git a/provisioning/SK3SFS1P/terraform.tfvars.example b/provisioning/SK3SFS1P/terraform.tfvars.example new file mode 100644 index 0000000..00729cc --- /dev/null +++ b/provisioning/SK3SFS1P/terraform.tfvars.example @@ -0,0 +1,27 @@ +proxmox_endpoint = "https://proxmox.example.local:8006/api2/json" +proxmox_insecure = true +proxmox_api_token = "packer@pve!packer=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + +proxmox_ssh_user = "root" +proxmox_ssh_password = "changeme" +proxmox_ssh_agent = false + +node_name = "pve" + +vm_id = 201 +name = "SK3SFS1P" +init_ipv4_address = "10.0.0.101/24" +init_ipv4_gateway = "10.0.0.1" +init_dns_servers = ["10.0.0.2", "10.0.0.3"] +admin_password = "ChangeMe-StrongPassword!" + +cpu_cores = 4 +memory_dedicated = 8192 +disk_size = 80 +disk_datastore = "local-lvm" +data_disk_size = 30 +data_disk_datastore = "local-lvm" +network_bridge = "vmbr0" +cloud_init_datastore = "local-lvm" + +clone_vm_id = 1002 diff --git a/provisioning/SK3SFS1P/variables.tf b/provisioning/SK3SFS1P/variables.tf new file mode 100644 index 0000000..53f5f95 --- /dev/null +++ b/provisioning/SK3SFS1P/variables.tf @@ -0,0 +1,152 @@ +variable "proxmox_endpoint" { + description = "URL of the Proxmox API (e.g. https://192.168.1.10:8006/)" + type = string +} + +variable "proxmox_insecure" { + description = "Skip TLS certificate verification (set true for self-signed certs)" + type = bool + default = false +} + +variable "proxmox_api_token" { + description = "Proxmox API token in the form user@realm!tokenid=secret" + type = string + sensitive = true +} + +variable "proxmox_ssh_user" { + description = "SSH username for the Proxmox node" + type = string +} + +variable "proxmox_ssh_password" { + description = "SSH password for connecting to the Proxmox node" + type = string + sensitive = true + default = null +} + +variable "proxmox_ssh_agent" { + description = "Use SSH agent for authentication" + type = bool + default = true +} + +variable "node_name" { + description = "Proxmox cluster node name" + type = string +} + +variable "vm_id" { + description = "Unique Proxmox VM ID" + type = number +} + +variable "name" { + description = "VM hostname" + type = string +} + +variable "description" { + description = "VM description" + type = string + default = "" +} + +variable "tags" { + description = "Tags to apply to the VM" + type = list(string) + default = [] +} + +variable "cpu_cores" { + type = number + default = 2 +} + +variable "memory_dedicated" { + description = "RAM in MB" + type = number + default = 2048 +} + +variable "clone_vm_id" { + description = "VM ID of the Windows template to clone" + type = number + default = 1002 +} + +variable "clone_datastore_id" { + description = "Target datastore for the cloned disks (null = keep on source datastore)" + type = string + default = null +} + +variable "disk_datastore" { + type = string + default = "local-lvm" +} + +variable "disk_size" { + description = "Disk size in GB" + type = number + default = 60 +} + +variable "data_disk_size" { + description = "Size in GB of the data disk. Set to null to skip." + type = number + default = null +} + +variable "data_disk_datastore" { + description = "Proxmox datastore ID for the data disk." + type = string + default = "local-lvm" +} + +variable "network_bridge" { + type = string + default = "vmbr11" +} + +variable "network_vlan_id" { + type = number + default = null +} + +variable "init_ipv4_address" { + description = "IPv4 address in CIDR notation (e.g. 10.10.2.100/24) or dhcp" + type = string +} + +variable "init_ipv4_gateway" { + description = "IPv4 gateway address" + type = string + default = null +} + +variable "init_dns_servers" { + description = "List of DNS servers for initialization" + type = list(string) + default = [] +} + +variable "init_username" { + description = "Username for the initial account" + type = string + default = "Administrator" +} + +variable "admin_password" { + description = "Password for the initial account" + type = string + sensitive = true +} + +variable "cloud_init_datastore" { + description = "Datastore used to store the cloud-init drive" + type = string + default = "local" +}