From 280066436a2f02bc9f49da5bf288672c1014a9bb Mon Sep 17 00:00:00 2001 From: phermeys Date: Sun, 26 Oct 2025 22:08:56 -0700 Subject: [PATCH] start pipeline server init --- jenkins/Jenkinsfile.server_init | 73 +++++++++++++ playbooks/disk_service.yaml | 2 + playbooks/server_init.yaml | 13 +++ roles/cosmos_init/defaults/main.yaml | 8 +- roles/cosmos_init/tasks/main.yaml | 33 ++++++ roles/health_api/files/app.py | 109 -------------------- roles/storage_api/README.md | 4 +- roles/storage_api/templates/disk_service.py | 2 +- 8 files changed, 132 insertions(+), 112 deletions(-) create mode 100644 jenkins/Jenkinsfile.server_init create mode 100644 playbooks/server_init.yaml delete mode 100644 roles/health_api/files/app.py diff --git a/jenkins/Jenkinsfile.server_init b/jenkins/Jenkinsfile.server_init new file mode 100644 index 0000000..19e1a2a --- /dev/null +++ b/jenkins/Jenkinsfile.server_init @@ -0,0 +1,73 @@ +pipeline { + agent any + + // Define parameters + parameters { + string(name: 'host_ip', description: 'Target System Address') + // reference for later + // choice(name: 'DEPLOY_ENV', choices: ['dev', 'staging', 'prod'], description: 'Environment to deploy to') + // booleanParam(name: 'rename_host', defaultValue: true, description: 'When checked hostname will be renamed') + // booleanParam(name: 'config_matt', defaultValue: true, description: 'config matt profile') + } + + environment { + ANSIBLE_FORCE_COLOR = '1' + ansible_service_windows = credentials(' ansible-service-windows') + } + + options { + ansiColor('xterm') + } + + stages { + + stage('Generate Inventory File') { + steps { + + // Generate the dynamic inventory file + // Usage: $0 -i IP_LIST -u JENKINS_USER -g JENKINS_GROUP -w WINDOWS_USER -p ANSIBLE_PASSWORD [-a SERVER_SUBNET_GROUP] [-s] [-v] [-e]" + sh """ + + jenkins_group=\$(echo ${env.BUILD_USER_GROUPS} | sed 's/,/\\n/g' | grep -v \$SERVER_SUBNET_GROUP | grep Jenkins | head -n 1) + jenkins_subnet_group=\$(echo ${env.BUILD_USER_GROUPS} | sed 's/,/\\n/g' | grep -e authenticated -e \$SERVER_SUBNET_GROUP | sort -rf | head -n 1) + jenkins_user=\$(echo ${env.BUILD_USER}) + cd /var/jenkins_home/ansible-windows + chmod +x /var/jenkins_home/ansible-windows/inventory/inventory.sh + /var/jenkins_home/ansible-windows/inventory/inventory.sh -a \$jenkins_subnet_group -g \$jenkins_group -u \$jenkins_user -w ${env.ansible_service_windows_USR} -p ${env.ansible_service_windows_PSW} -i ${params.host_ip} + + """ + } + } + + stage('Ansible Playbook') { + steps { + sh """ + echo Generate Hash + echo ${params.host_ip} + hash=\$(echo -n ${params.host_ip} | md5sum | cut -c 1-8) + + inventory_file="/var/jenkins_home/ansible-windows/.inv/inventory-\$hash.yml" + playbook_file="/var/jenkins_home/ansible-windows/playbooks/server_init.yaml" + + cd /var/jenkins_home/ansible-windows + + ansible-playbook -i \$inventory_file \$playbook_file \ + --ssh-common-args='-o StrictHostKeyChecking=no' + """ + } + } + } + + post { + always { + // Remove dynamic Inventory file + sh """ + hash=\$(echo -n "${params.host_ip}" | md5sum | cut -c 1-8) + inventory_file="/var/jenkins_home/ansible-windows/.inv/inventory-\$hash.yml" + rm \$inventory_file + + """ + } + } + +} diff --git a/playbooks/disk_service.yaml b/playbooks/disk_service.yaml index 0e43a2a..0243778 100644 --- a/playbooks/disk_service.yaml +++ b/playbooks/disk_service.yaml @@ -8,6 +8,8 @@ - show_user_vars + - cosmos_init + - storage_api ... \ No newline at end of file diff --git a/playbooks/server_init.yaml b/playbooks/server_init.yaml new file mode 100644 index 0000000..87bc621 --- /dev/null +++ b/playbooks/server_init.yaml @@ -0,0 +1,13 @@ +--- +- name: Matt-Cloud Windows VM Server Initialze + hosts: all + become: yes + become_method: runas + + roles: + + - show_user_vars + + - cosmos_init + +... \ No newline at end of file diff --git a/roles/cosmos_init/defaults/main.yaml b/roles/cosmos_init/defaults/main.yaml index 3cea37d..486a4f7 100644 --- a/roles/cosmos_init/defaults/main.yaml +++ b/roles/cosmos_init/defaults/main.yaml @@ -5,7 +5,13 @@ windows_base_packages: - 7zip - vlc - windirstat + - putty - +windows_features: + - NET-Framework-Features + - Telnet-Client + - Windows-Server-Backup + - RSAT-Print-Services + - RSAT-File-Services ... \ No newline at end of file diff --git a/roles/cosmos_init/tasks/main.yaml b/roles/cosmos_init/tasks/main.yaml index d52a209..58905f3 100644 --- a/roles/cosmos_init/tasks/main.yaml +++ b/roles/cosmos_init/tasks/main.yaml @@ -10,6 +10,39 @@ loop_control: loop_var: windows_base_packages_item +- name: Install Server Services + ansible.windows.win_feature: + name: + - "{{ windows_features_item }}" + state: present + loop: "{{ windows_features }}" + loop_control: + loop_var: windows_features_item +- name: Disable ms_tcpip6 of all the Interface + community.windows.win_net_adapter_feature: + interface: '*' + state: disabled + component_id: + - ms_tcpip6 + +- name: disable IE Enhanced Security + block: + + - name: disable for user + win_shell: 'Set-ItemProperty -Path "HKLM:\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}" -Name "IsInstalled" -Value 0' + + - name: disable for admin + win_shell: 'Set-ItemProperty -Path "HKLM:\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}" -Name "IsInstalled" -Value 0' + +- name: disable automatic updates + ansible.windows.win_regedit: + path: HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate\AU + name: NoAutoUpdate + data: 1 + +- name: disable Firewall + win_shell: "netsh advfirewall set allprofiles state off" + ... \ No newline at end of file diff --git a/roles/health_api/files/app.py b/roles/health_api/files/app.py deleted file mode 100644 index 8d225d7..0000000 --- a/roles/health_api/files/app.py +++ /dev/null @@ -1,109 +0,0 @@ -from flask import Flask, jsonify -import subprocess -import os -import socket -import psutil - - -hostname = socket.gethostname() - -app = Flask(__name__) - -# refresh the text file -def refresh_crystal_info(): - try: - # Run CrystalDiskInfo command to generate DiskInfo.txt - return subprocess.run(["DiskInfo64.exe", "/CopyExit"]) - except Exception as e: - return {"error": str(e)} - -def get_crystal_disk_info(): - try: - # Read the generated DiskInfo.txt file - with open("DiskInfo.txt", "r") as file: - output = file.read() - - # Initialize a list to hold each drive's data - drives = [] - - # Split the file content into sections for each drive - drive_sections = output.split('----------------------------------------------------------------------------') - for section in drive_sections: - lines = section.strip().splitlines() - data = { - "Hostname": None, - "Disk Size": None, - "Model": None, - "Serial Number": None, - "Firmware": None, - "Temperature": None, - "Health Status": None, - "Power On Hours": None, - "Power On Count": None, - "Host Writes": None, - "Wear Level Count": None, - "Drive Letter": None - } - for line in lines: - if "Model" in line: - if ":" in line: - data["Model"] = line.split(":", 1)[1].strip() - elif "Serial Number" in line: - if ":" in line: - data["Serial Number"] = line.split(":", 1)[1].strip() - elif "Firmware" in line: - if ":" in line: - data["Firmware"] = line.split(":", 1)[1].strip() - elif "Temperature" in line: - if ":" in line: - data["Temperature"] = line.split(":", 1)[1].strip() - elif "Health Status" in line: - if ":" in line: - data["Health Status"] = line.split(":", 1)[1].strip() - elif "Power On Hours" in line: - if ":" in line: - data["Power On Hours"] = line.split(":", 1)[1].strip() - elif "Power On Count" in line: - if ":" in line: - data["Power On Count"] = line.split(":", 1)[1].strip() - elif "Host Writes" in line: - if ":" in line: - data["Host Writes"] = line.split(":", 1)[1].strip() - elif "Wear Level Count" in line: - if ":" in line: - data["Wear Level Count"] = line.split(":", 1)[1].strip() - elif "Drive Letter" in line: - if ":" in line: - data["Drive Letter"] = line.split(":", 1)[1].strip() - elif "Disk Size" in line: - if ":" in line: - data["Disk Size"] = line.split(":", 1)[1].strip() - - if any(value is not None for value in data.values()): - drives.append(data) - data["Hostname"] = hostname - - if not drives: - raise ValueError("No drive data found") - - return {"drives": drives} - except Exception as e: - refresh_crystal_info() - return {"error": str(e)} - -def bytes_to_human_readable(bytes): - for unit in ['B', 'KB', 'MB', 'GB', 'TB']: - if bytes < 1024.0: - return f"{bytes:.2f} {unit}" - bytes /= 1024.0 - -@app.route('/udpate_cache', methods=['GET']) -def udpate_cache(): - return jsonify(refresh_crystal_info()) - -@app.route('/drive_health', methods=['GET']) -def drive_health(): - return jsonify(get_crystal_disk_info()) - -if __name__ == '__main__': - app.run(host='0.0.0.0', port=5000) diff --git a/roles/storage_api/README.md b/roles/storage_api/README.md index 5901df4..ed0a71f 100644 --- a/roles/storage_api/README.md +++ b/roles/storage_api/README.md @@ -1 +1,3 @@ -This role will install an API at port 5000 to get disk capacity data \ No newline at end of file +This role will install an API at port 5000 to get disk capacity data and disk health + +This is used with the storage site visible to members of the disk-api group at groups.matt-cloud.com diff --git a/roles/storage_api/templates/disk_service.py b/roles/storage_api/templates/disk_service.py index 46dc721..ba7d31c 100644 --- a/roles/storage_api/templates/disk_service.py +++ b/roles/storage_api/templates/disk_service.py @@ -77,7 +77,7 @@ def get_crystal_disk_info(): if any(value is not None for value in data.values()): drives.append(data) - data["Hostname"] = {{ hostname_output.stdout_lines[0] }} + data["Hostname"] = "{{ hostname_output.stdout_lines[0] }}" if not drives: raise ValueError("No drive data found")