Restore from Gitea ZIP snapshot (12.08.2026) after full instance reinstall
Git history was lost when the previous Gitea instance was wiped and reinstalled due to an unresolved corruption bug — this commit is the last known-good file content, exported before the reinstall. Prior commit history is not recoverable through this path.
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
---
|
||||
- name: Prepare Dynamic Inventory
|
||||
hosts: localhost
|
||||
gather_facts: yes
|
||||
tasks:
|
||||
- name: Set target host variable
|
||||
set_fact:
|
||||
target_host: "{{ target_ip }}"
|
||||
when: target_ip is defined and target_ip != ''
|
||||
|
||||
- name: Validate target host
|
||||
fail:
|
||||
msg: "Please provide target host IP address or domain name"
|
||||
when: target_host is not defined or target_host == ''
|
||||
|
||||
- name: Display target information
|
||||
debug:
|
||||
msg: |
|
||||
============================================
|
||||
Target Host: {{ target_host }}
|
||||
============================================
|
||||
|
||||
- name: Test connectivity to target (WinRM port)
|
||||
ansible.builtin.wait_for:
|
||||
host: "{{ target_host }}"
|
||||
port: 5985
|
||||
timeout: 10
|
||||
ignore_errors: yes
|
||||
register: connectivity_test
|
||||
|
||||
- name: Warn if WinRM is not accessible
|
||||
debug:
|
||||
msg: |
|
||||
WARNING: Cannot connect to WinRM port 5985 on {{ target_host }}
|
||||
Please check if the host is accessible and WinRM is enabled
|
||||
when: connectivity_test is defined and connectivity_test.failed
|
||||
|
||||
- name: Add target VM from Semaphore Survey to inventory
|
||||
add_host:
|
||||
name: "{{ target_host }}"
|
||||
groups: windows_vms
|
||||
ansible_user: "{{ ansible_user }}"
|
||||
ansible_password: "{{ ansible_password }}"
|
||||
ansible_connection: winrm
|
||||
ansible_port: 5985
|
||||
ansible_winrm_transport: ntlm
|
||||
ansible_winrm_server_cert_validation: ignore
|
||||
|
||||
- name: Deploy and run activations
|
||||
hosts: windows_vms
|
||||
gather_facts: no
|
||||
|
||||
vars:
|
||||
activation_map:
|
||||
windows_only:
|
||||
- windows
|
||||
office_only:
|
||||
- office
|
||||
windows_office:
|
||||
- windows
|
||||
- office
|
||||
autodesk_only:
|
||||
- autodesk
|
||||
all:
|
||||
- windows
|
||||
- office
|
||||
- autodesk
|
||||
|
||||
tasks:
|
||||
# Debug - проверим значение activation_type
|
||||
- name: Debug activation_type
|
||||
debug:
|
||||
msg: "activation_type = {{ activation_type }}"
|
||||
|
||||
- name: 1. Create distr directory
|
||||
ansible.windows.win_file:
|
||||
path: C:\distr
|
||||
state: directory
|
||||
when: activation_type is defined and activation_type in activation_map.keys()
|
||||
|
||||
- name: 2. Copy activation files from network share
|
||||
block:
|
||||
- name: 2a. Copy MAS_AIO.cmd
|
||||
ansible.windows.win_copy:
|
||||
src: \\fs\alls\MAS_AIO.cmd
|
||||
dest: C:\distr\MAS_AIO.cmd
|
||||
remote_src: yes
|
||||
become: yes
|
||||
become_method: runas
|
||||
become_flags: logon_type=new_credentials logon_flags=netcredentials_only
|
||||
vars:
|
||||
ansible_become_user: "{{ domain_user }}"
|
||||
ansible_become_pass: "{{ domain_password }}"
|
||||
when: activation_type is defined and activation_type in ['windows_only', 'windows_office', 'all']
|
||||
|
||||
- name: 2b. Copy AdskNLM.exe
|
||||
ansible.windows.win_copy:
|
||||
src: \\fs\alls\AdskNLM.exe
|
||||
dest: C:\distr\AdskNLM.exe
|
||||
remote_src: yes
|
||||
become: yes
|
||||
become_method: runas
|
||||
become_flags: logon_type=new_credentials logon_flags=netcredentials_only
|
||||
vars:
|
||||
ansible_become_user: "{{ domain_user }}"
|
||||
ansible_become_pass: "{{ domain_password }}"
|
||||
when: activation_type is defined and activation_type in ['autodesk_only', 'all']
|
||||
|
||||
# Windows Activation
|
||||
- name: 3. Activate Windows
|
||||
block:
|
||||
- name: 3a. Execute MAS_AIO.cmd for Windows
|
||||
ansible.windows.win_shell: C:\distr\MAS_AIO.cmd /HWID
|
||||
become: yes
|
||||
become_method: runas
|
||||
become_user: SYSTEM
|
||||
async: 600
|
||||
poll: 30
|
||||
register: windows_activation
|
||||
|
||||
- name: 3b. Check Windows activation status
|
||||
ansible.windows.win_shell: |
|
||||
$status = Get-WmiObject -Class SoftwareLicensingProduct | Where-Object {$_.PartialProductKey} | Select-Object -First 1
|
||||
Write-Host "Windows Activation Status: $($status.LicenseStatus)"
|
||||
if ($status.LicenseStatus -eq 1) {
|
||||
Write-Host "SUCCESS: Windows activated"
|
||||
} else {
|
||||
Write-Host "WARNING: Windows not activated"
|
||||
}
|
||||
register: windows_status
|
||||
ignore_errors: yes
|
||||
|
||||
- name: 3c. Display Windows activation result
|
||||
debug:
|
||||
var: windows_status.stdout_lines
|
||||
when: activation_type is defined and activation_type in ['windows_only', 'windows_office', 'all']
|
||||
|
||||
# Office Activation
|
||||
- name: 4. Activate Office
|
||||
block:
|
||||
- name: 4a. Execute MAS_AIO.cmd for Office
|
||||
ansible.windows.win_shell: C:\distr\MAS_AIO.cmd /Ohook
|
||||
become: yes
|
||||
become_method: runas
|
||||
become_user: SYSTEM
|
||||
async: 600
|
||||
poll: 30
|
||||
register: office_activation
|
||||
|
||||
- name: 4b. Check Office activation status
|
||||
ansible.windows.win_shell: |
|
||||
$office = Get-WmiObject -Class SoftwareLicensingProduct | Where-Object {$_.Name -like "*Office*" -and $_.PartialProductKey}
|
||||
if ($office) {
|
||||
Write-Host "Office found: $($office.Name)"
|
||||
Write-Host "Office Activation Status: $($office.LicenseStatus)"
|
||||
if ($office.LicenseStatus -eq 1) {
|
||||
Write-Host "SUCCESS: Office activated"
|
||||
} else {
|
||||
Write-Host "WARNING: Office not activated"
|
||||
}
|
||||
} else {
|
||||
Write-Host "INFO: Office not installed"
|
||||
}
|
||||
register: office_status
|
||||
ignore_errors: yes
|
||||
|
||||
- name: 4c. Display Office activation result
|
||||
debug:
|
||||
var: office_status.stdout_lines
|
||||
when: activation_type is defined and activation_type in ['office_only', 'windows_office', 'all']
|
||||
|
||||
# Autodesk Activation
|
||||
- name: 5. Activate Autodesk
|
||||
block:
|
||||
- name: 5a. Start AdskNLM and wait for activation
|
||||
ansible.windows.win_shell: |
|
||||
Write-Host "Starting Autodesk activation at $(Get-Date)" -ForegroundColor Green
|
||||
$process = Start-Process -FilePath "C:\distr\AdskNLM.exe" -WindowStyle Hidden -PassThru
|
||||
Start-Sleep -Seconds 15
|
||||
$process | Stop-Process -Force
|
||||
Get-Process -Name "AdskNLM" -ErrorAction SilentlyContinue | Stop-Process -Force
|
||||
Write-Host "Autodesk activation completed at $(Get-Date)" -ForegroundColor Green
|
||||
become: yes
|
||||
become_method: runas
|
||||
become_user: SYSTEM
|
||||
register: autodesk_activation
|
||||
ignore_errors: yes
|
||||
|
||||
- name: 5b. Clean up AdskNLM processes
|
||||
ansible.windows.win_shell: |
|
||||
Get-Process -Name "AdskNLM*" -ErrorAction SilentlyContinue | Stop-Process -Force
|
||||
become: yes
|
||||
become_method: runas
|
||||
become_user: SYSTEM
|
||||
ignore_errors: yes
|
||||
|
||||
- name: 5c. Verify Autodesk activation
|
||||
ansible.windows.win_shell: |
|
||||
$autodeskProducts = Get-ChildItem "C:\Program Files\Autodesk" -ErrorAction SilentlyContinue
|
||||
if ($autodeskProducts) {
|
||||
Write-Host "Autodesk products found:"
|
||||
$autodeskProducts | ForEach-Object { Write-Host " - $($_.Name)" }
|
||||
Write-Host "SUCCESS: Autodesk activation attempted"
|
||||
} else {
|
||||
Write-Host "WARNING: No Autodesk products found"
|
||||
}
|
||||
register: autodesk_status
|
||||
ignore_errors: yes
|
||||
|
||||
- name: 5d. Display Autodesk activation result
|
||||
debug:
|
||||
var: autodesk_status.stdout_lines
|
||||
when: activation_type is defined and activation_type in ['autodesk_only', 'all']
|
||||
|
||||
# Summary
|
||||
- name: 6. Activation Summary
|
||||
debug:
|
||||
msg: |
|
||||
============================================
|
||||
ACTIVATION SUMMARY
|
||||
============================================
|
||||
Target Host: {{ inventory_hostname }}
|
||||
Activation Type: {{ activation_type }}
|
||||
============================================
|
||||
when: activation_type is defined and activation_type in activation_map.keys()
|
||||
@@ -0,0 +1,199 @@
|
||||
---
|
||||
- name: Stage 1 - Add Ubuntu VM to in-memory inventory
|
||||
hosts: localhost
|
||||
connection: local
|
||||
gather_facts: false
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Add Ubuntu VM to in-memory inventory
|
||||
ansible.builtin.add_host:
|
||||
name: ubuntu-resize-target
|
||||
groups: ubuntu_resize_group
|
||||
ansible_host: "{{ vm_ip }}"
|
||||
ansible_user: "{{ ansible_user }}"
|
||||
ansible_password: "{{ ansible_password }}"
|
||||
ansible_become: true
|
||||
ansible_become_password: "{{ ansible_become_password }}"
|
||||
ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
|
||||
|
||||
- name: Stage 2 - Wait for SSH port
|
||||
hosts: localhost
|
||||
connection: local
|
||||
gather_facts: false
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Wait for SSH port on Ubuntu VM
|
||||
ansible.builtin.wait_for:
|
||||
host: "{{ vm_ip }}"
|
||||
port: 22
|
||||
timeout: 60
|
||||
sleep: 2
|
||||
|
||||
- name: Stage 3 - Read current guest disk size
|
||||
hosts: ubuntu_resize_group
|
||||
gather_facts: false
|
||||
become: true
|
||||
|
||||
vars:
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
|
||||
tasks:
|
||||
- name: Get current size of /dev/sda in bytes
|
||||
ansible.builtin.command: lsblk -b -dn -o SIZE /dev/sda
|
||||
register: sda_size_bytes
|
||||
changed_when: false
|
||||
|
||||
- name: Calculate current and target disk size in GB
|
||||
ansible.builtin.set_fact:
|
||||
current_disk_gb: "{{ (sda_size_bytes.stdout | int) // 1024 // 1024 // 1024 }}"
|
||||
target_disk_gb: "{{ ((sda_size_bytes.stdout | int) // 1024 // 1024 // 1024) + (increase_gb | int) }}"
|
||||
|
||||
- name: Stage 4 - Power off VM in vCenter
|
||||
hosts: localhost
|
||||
connection: local
|
||||
gather_facts: false
|
||||
become: false
|
||||
|
||||
vars:
|
||||
ansible_python_interpreter: /opt/semaphore/apps/ansible/11.1.0/venv/bin/python3
|
||||
|
||||
tasks:
|
||||
- name: Show resize plan
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "VM: {{ vm_name }}"
|
||||
- "VM IP: {{ vm_ip }}"
|
||||
- "Current disk: {{ hostvars['ubuntu-resize-target']['current_disk_gb'] }} GB"
|
||||
- "Target disk: {{ hostvars['ubuntu-resize-target']['target_disk_gb'] }} GB"
|
||||
|
||||
- name: Power off VM gracefully
|
||||
community.vmware.vmware_guest_powerstate:
|
||||
hostname: "{{ vsphere_server }}"
|
||||
username: "{{ vsphere_user }}"
|
||||
password: "{{ vsphere_password }}"
|
||||
validate_certs: false
|
||||
datacenter: "{{ vsphere_datacenter }}"
|
||||
name: "{{ vm_name }}"
|
||||
state: powered-off
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Wait until SSH port is closed
|
||||
ansible.builtin.wait_for:
|
||||
host: "{{ vm_ip }}"
|
||||
port: 22
|
||||
state: drained
|
||||
timeout: 180
|
||||
|
||||
- name: Expand VMware disk
|
||||
community.vmware.vmware_guest_disk:
|
||||
hostname: "{{ vsphere_server }}"
|
||||
username: "{{ vsphere_user }}"
|
||||
password: "{{ vsphere_password }}"
|
||||
validate_certs: false
|
||||
datacenter: "{{ vsphere_datacenter }}"
|
||||
name: "{{ vm_name }}"
|
||||
disk:
|
||||
- state: present
|
||||
unit_number: 0
|
||||
controller_number: 0
|
||||
size_gb: "{{ hostvars['ubuntu-resize-target']['target_disk_gb'] }}"
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Power on VM
|
||||
community.vmware.vmware_guest_powerstate:
|
||||
hostname: "{{ vsphere_server }}"
|
||||
username: "{{ vsphere_user }}"
|
||||
password: "{{ vsphere_password }}"
|
||||
validate_certs: false
|
||||
datacenter: "{{ vsphere_datacenter }}"
|
||||
name: "{{ vm_name }}"
|
||||
state: powered-on
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Wait for SSH port to return
|
||||
ansible.builtin.wait_for:
|
||||
host: "{{ vm_ip }}"
|
||||
port: 22
|
||||
timeout: 300
|
||||
sleep: 5
|
||||
|
||||
- name: Stage 5 - Wait for SSH session after reboot
|
||||
hosts: ubuntu_resize_group
|
||||
gather_facts: false
|
||||
|
||||
vars:
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
|
||||
tasks:
|
||||
- name: Wait for SSH connection
|
||||
ansible.builtin.wait_for_connection:
|
||||
timeout: 300
|
||||
|
||||
- name: Stage 6 - Expand partition, LVM and filesystem inside Ubuntu
|
||||
hosts: ubuntu_resize_group
|
||||
gather_facts: false
|
||||
become: true
|
||||
|
||||
vars:
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
root_disk: /dev/sda
|
||||
root_partition_number: 3
|
||||
root_partition: /dev/sda3
|
||||
root_lv: /dev/mapper/ubuntu--vg-ubuntu--lv
|
||||
|
||||
tasks:
|
||||
- name: Rescan SCSI bus
|
||||
ansible.builtin.shell: |
|
||||
for host in /sys/class/scsi_host/host*; do
|
||||
echo "- - -" > "$host/scan"
|
||||
done
|
||||
args:
|
||||
executable: /bin/bash
|
||||
changed_when: true
|
||||
|
||||
- name: Wait after rescan
|
||||
ansible.builtin.pause:
|
||||
seconds: 5
|
||||
|
||||
- name: Ensure growpart is installed
|
||||
ansible.builtin.apt:
|
||||
name: cloud-guest-utils
|
||||
state: present
|
||||
update_cache: yes
|
||||
|
||||
- name: Expand partition if needed
|
||||
ansible.builtin.command: "growpart {{ root_disk }} {{ root_partition_number }}"
|
||||
register: growpart_result
|
||||
changed_when: "'CHANGED:' in growpart_result.stdout"
|
||||
failed_when: >
|
||||
growpart_result.rc != 0 and
|
||||
'NOCHANGE:' not in growpart_result.stdout
|
||||
|
||||
- name: Resize LVM physical volume
|
||||
ansible.builtin.command: "pvresize {{ root_partition }}"
|
||||
register: pvresize_result
|
||||
failed_when: false
|
||||
changed_when: true
|
||||
|
||||
- name: Extend root logical volume and filesystem
|
||||
ansible.builtin.command: "lvextend -r -l +100%FREE {{ root_lv }}"
|
||||
register: lvextend_result
|
||||
failed_when: false
|
||||
changed_when: true
|
||||
|
||||
- name: Show final disk state
|
||||
ansible.builtin.shell: |
|
||||
pvs
|
||||
vgs
|
||||
lvs
|
||||
df -h /
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: final_state
|
||||
changed_when: false
|
||||
|
||||
- name: Print final disk state
|
||||
ansible.builtin.debug:
|
||||
var: final_state.stdout_lines
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
- name: Prepare Dynamic Inventory
|
||||
hosts: localhost
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- name: Add target VM from Semaphore Survey to inventory
|
||||
add_host:
|
||||
name: "{{ target_ip }}"
|
||||
groups: windows_vms
|
||||
ansible_user: "o.grechko"
|
||||
ansible_password: "IrZTON3232"
|
||||
ansible_connection: winrm
|
||||
ansible_port: 5985
|
||||
ansible_winrm_transport: ntlm
|
||||
ansible_winrm_server_cert_validation: ignore
|
||||
|
||||
- name: Install and Configure Radmin Server
|
||||
hosts: windows_vms
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- name: 1. Remove existing file if it exists
|
||||
ansible.windows.win_file:
|
||||
path: C:\Windows\SysWOW64\rserver30
|
||||
state: absent
|
||||
register: remove_result
|
||||
ignore_errors: yes
|
||||
|
||||
- name: 2. Create directories for Antivirus exclusions
|
||||
ansible.windows.win_file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
loop:
|
||||
- C:\distr
|
||||
- C:\Windows\SysWOW64\rserver30
|
||||
|
||||
- name: 3. Add Windows Defender exclusions
|
||||
ansible.windows.win_shell: |
|
||||
Add-MpPreference -ExclusionPath "C:\distr"
|
||||
Add-MpPreference -ExclusionPath "C:\Windows\SysWOW64\rserver30"
|
||||
|
||||
- name: 4. Copy Radmin MSI directly from network share
|
||||
ansible.windows.win_copy:
|
||||
src: \\fs\alls\rs352.msi
|
||||
dest: C:\distr\rs352.msi
|
||||
remote_src: yes
|
||||
become: yes
|
||||
become_method: runas
|
||||
become_flags: logon_type=new_credentials logon_flags=netcredentials_only
|
||||
vars:
|
||||
ansible_become_user: "{{ domain_user }}"
|
||||
ansible_become_pass: "{{ domain_password }}"
|
||||
|
||||
- name: 5. Install Radmin silently (this will auto-start the service)
|
||||
ansible.windows.win_package:
|
||||
path: C:\distr\rs352.msi
|
||||
state: present
|
||||
arguments: /qn
|
||||
|
||||
- name: 6. STOP Radmin Server service to unlock files
|
||||
ansible.windows.win_service:
|
||||
name: rserver3
|
||||
state: stopped
|
||||
|
||||
- name: 7. Extract wsock32.zip from network share directly to rserver30
|
||||
community.windows.win_unzip:
|
||||
src: \\fs\alls\wsock32.zip
|
||||
dest: C:\Windows\SysWOW64\rserver30\
|
||||
become: yes
|
||||
become_method: runas
|
||||
become_flags: logon_type=new_credentials logon_flags=netcredentials_only
|
||||
vars:
|
||||
ansible_become_user: "{{ domain_user }}"
|
||||
ansible_become_pass: "{{ domain_password }}"
|
||||
|
||||
- name: 8. Copy radmin_settings.reg from network share
|
||||
ansible.windows.win_copy:
|
||||
src: \\fs\alls\radmin_settings.reg
|
||||
dest: C:\distr\radmin_settings.reg
|
||||
remote_src: yes
|
||||
become: yes
|
||||
become_method: runas
|
||||
become_flags: logon_type=new_credentials logon_flags=netcredentials_only
|
||||
vars:
|
||||
ansible_become_user: "{{ domain_user }}"
|
||||
ansible_become_pass: "{{ domain_password }}"
|
||||
|
||||
- name: 9. Apply Radmin registry settings (AD permissions)
|
||||
ansible.windows.win_command: regedit.exe /s C:\distr\radmin_settings.reg
|
||||
|
||||
- name: 10. START Radmin Server service to apply patch and settings
|
||||
ansible.windows.win_service:
|
||||
name: rserver3
|
||||
state: started
|
||||
@@ -0,0 +1,208 @@
|
||||
---
|
||||
- name: "Установка Mango Talker для другого пользователя через планировщик"
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
|
||||
vars:
|
||||
target_hosts: "{{ target_ips.split(',') | map('trim') | list }}"
|
||||
|
||||
ansible_connection: winrm
|
||||
ansible_winrm_server_cert_validation: ignore
|
||||
ansible_winrm_transport: ntlm
|
||||
ansible_port: 5985
|
||||
|
||||
source_file: "\\\\fs\\alls\\mango-talker-setup.exe"
|
||||
local_dir: "C:\\distr"
|
||||
local_file: "C:\\distr\\mango-talker-setup.exe"
|
||||
install_args: "--silent"
|
||||
|
||||
target_user_name: "{{ target_user | default('aturenkov') }}"
|
||||
target_user_password: "{{ target_password }}"
|
||||
|
||||
app_check_path: "C:\\Users\\{{ target_user_name }}\\AppData\\Roaming\\Mango Telecom\\M.TALKER\\application\\mango-talker.exe"
|
||||
|
||||
tasks:
|
||||
- name: "Создать динамический инвентарь"
|
||||
add_host:
|
||||
name: "{{ item }}"
|
||||
groups: target_group
|
||||
ansible_connection: winrm
|
||||
ansible_winrm_server_cert_validation: ignore
|
||||
ansible_winrm_transport: ntlm
|
||||
ansible_port: 5985
|
||||
ansible_user: "{{ ansible_user }}"
|
||||
ansible_password: "{{ ansible_password }}"
|
||||
loop: "{{ target_hosts }}"
|
||||
when: target_hosts | length > 0
|
||||
|
||||
- name: "Проверить, что есть хосты"
|
||||
fail:
|
||||
msg: "❌ Не указаны IP-адреса для установки"
|
||||
when: target_hosts | length == 0
|
||||
|
||||
- name: "Показать информацию"
|
||||
debug:
|
||||
msg:
|
||||
- "Установка будет выполнена на: {{ target_hosts }}"
|
||||
- "Установка от имени: {{ ansible_user.split('@')[0] }}"
|
||||
- "Установка ДЛЯ пользователя: {{ target_user_name }}"
|
||||
|
||||
- name: "Выполнить установку"
|
||||
hosts: target_group
|
||||
gather_facts: true
|
||||
vars:
|
||||
source_file: "\\\\fs\\alls\\mango-talker-setup.exe"
|
||||
local_dir: "C:\\distr"
|
||||
local_file: "C:\\distr\\mango-talker-setup.exe"
|
||||
install_args: "--silent"
|
||||
target_user_name: "{{ target_user | default('aturenkov') }}"
|
||||
target_user_password: "{{ target_password }}"
|
||||
app_check_path: "C:\\Users\\{{ target_user_name }}\\AppData\\Roaming\\Mango Telecom\\M.TALKER\\application\\mango-talker.exe"
|
||||
|
||||
tasks:
|
||||
- name: "1. Проверить доступность"
|
||||
ansible.windows.win_ping:
|
||||
|
||||
- name: "2. Создать папку C:\\distr"
|
||||
ansible.windows.win_file:
|
||||
path: "{{ local_dir }}"
|
||||
state: directory
|
||||
|
||||
- name: "3. Скопировать файл"
|
||||
ansible.windows.win_shell: |
|
||||
$source = "{{ source_file }}"
|
||||
$dest = "{{ local_file }}"
|
||||
$username = "{{ ansible_user }}"
|
||||
$password = "{{ ansible_password }}"
|
||||
|
||||
$secpass = ConvertTo-SecureString $password -AsPlainText -Force
|
||||
$cred = New-Object System.Management.Automation.PSCredential($username, $secpass)
|
||||
|
||||
$drive = New-PSDrive -Name "TempDrive" -PSProvider FileSystem -Root (Split-Path $source) -Credential $cred
|
||||
Copy-Item -Path $source -Destination $dest -Force
|
||||
Remove-PSDrive -Name "TempDrive"
|
||||
register: copy_result
|
||||
ignore_errors: true
|
||||
|
||||
- name: "4. Проверить файл"
|
||||
ansible.windows.win_stat:
|
||||
path: "{{ local_file }}"
|
||||
register: local_stat
|
||||
|
||||
- name: "5. Остановить, если файл не скопирован"
|
||||
fail:
|
||||
msg: "❌ Не удалось скопировать файл"
|
||||
when: not local_stat.stat.exists
|
||||
|
||||
- name: "6. Запустить установку через планировщик задач"
|
||||
ansible.windows.win_shell: |
|
||||
$domain = "{{ ansible_user.split('@')[1] }}"
|
||||
$user = "{{ target_user_name }}"
|
||||
$password = "{{ target_user_password }}"
|
||||
$taskName = "MangoTalkerInstall_$(Get-Date -Format 'yyyyMMddHHmmss')"
|
||||
|
||||
# Создаём действие
|
||||
$action = New-ScheduledTaskAction -Execute "{{ local_file }}" -Argument "{{ install_args }}" -WorkingDirectory "{{ local_dir }}"
|
||||
|
||||
# Создаём триггер (однократно)
|
||||
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1)
|
||||
|
||||
# Создаём principal с паролем
|
||||
$principal = New-ScheduledTaskPrincipal -UserId "$domain\\$user" -LogonType Password -RunLevel Highest
|
||||
|
||||
# Создаём настройки
|
||||
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable
|
||||
|
||||
# Регистрируем задачу
|
||||
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Password $password -Force
|
||||
|
||||
# Ждём 5 секунд и запускаем задачу
|
||||
Start-Sleep -Seconds 5
|
||||
Start-ScheduledTask -TaskName $taskName
|
||||
|
||||
# Ждём завершения (максимум 5 минут)
|
||||
$timeout = 300
|
||||
$elapsed = 0
|
||||
$exitCode = $null
|
||||
|
||||
do {
|
||||
$task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
|
||||
if ($task) {
|
||||
$taskInfo = $task | Get-ScheduledTaskInfo
|
||||
if ($taskInfo.LastTaskResult -ne $null -and $taskInfo.LastTaskResult -ne 0) {
|
||||
$exitCode = $taskInfo.LastTaskResult
|
||||
break
|
||||
}
|
||||
# Проверяем, завершилась ли задача
|
||||
$taskState = (Get-ScheduledTask -TaskName $taskName).State
|
||||
if ($taskState -eq "Ready") {
|
||||
$exitCode = $taskInfo.LastTaskResult
|
||||
break
|
||||
}
|
||||
}
|
||||
Start-Sleep -Seconds 5
|
||||
$elapsed += 5
|
||||
} while ($elapsed -lt $timeout)
|
||||
|
||||
# Если таймаут
|
||||
if ($exitCode -eq $null) {
|
||||
$exitCode = 0
|
||||
}
|
||||
|
||||
# Удаляем задачу
|
||||
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
|
||||
|
||||
Write-Host "Exit code: $exitCode"
|
||||
exit $exitCode
|
||||
register: install_result
|
||||
failed_when: install_result.rc not in [0, 3010]
|
||||
changed_when: install_result.rc == 0
|
||||
|
||||
- name: "7. Подождать завершения"
|
||||
ansible.windows.win_shell: |
|
||||
Start-Sleep -Seconds 10
|
||||
when: install_result.rc == 0
|
||||
|
||||
- name: "8. Проверить установку для пользователя {{ target_user_name }}"
|
||||
ansible.windows.win_stat:
|
||||
path: "{{ app_check_path }}"
|
||||
register: app_check
|
||||
|
||||
- name: "9. Поиск установки у всех пользователей"
|
||||
ansible.windows.win_shell: |
|
||||
Get-ChildItem -Path "C:\\Users" -Directory -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
$path = Join-Path $_.FullName "AppData\\Roaming\\Mango Telecom\\M.TALKER\\application\\mango-talker.exe"
|
||||
if (Test-Path $path) {
|
||||
Write-Host $_.Name
|
||||
}
|
||||
}
|
||||
register: found_users
|
||||
ignore_errors: true
|
||||
when: not app_check.stat.exists
|
||||
|
||||
- name: "10. Результат"
|
||||
debug:
|
||||
msg:
|
||||
- "=========================================="
|
||||
- "✅ Хост: {{ inventory_hostname }}"
|
||||
- "👤 Установка от: {{ ansible_user.split('@')[0] }}"
|
||||
- "👤 Установка для: {{ target_user_name }}"
|
||||
- "📊 Код возврата: {{ install_result.rc }}"
|
||||
- "📁 Приложение установлено для {{ target_user_name }}: {{ app_check.stat.exists | default(false) }}"
|
||||
- "📁 Найдено у других: {{ found_users.stdout_lines | default('Нет') }}"
|
||||
- "=========================================="
|
||||
|
||||
- name: "11. Удалить установщик"
|
||||
ansible.windows.win_shell: |
|
||||
Start-Sleep -Seconds 5
|
||||
Remove-Item "{{ local_file }}" -Force -ErrorAction SilentlyContinue
|
||||
when: install_result.rc == 0
|
||||
ignore_errors: true
|
||||
|
||||
- name: "12. Перезагрузить, если требуется"
|
||||
ansible.windows.win_reboot:
|
||||
reboot_timeout: 600
|
||||
post_reboot_delay: 30
|
||||
when:
|
||||
- install_result is defined
|
||||
- install_result.rc == 3010
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
- name: Setup Windows Server 2025 as Additional Domain Controller
|
||||
hosts: all
|
||||
gather_facts: yes
|
||||
|
||||
tasks:
|
||||
- name: Expand C drive if disk was increased
|
||||
ansible.windows.win_shell: |
|
||||
$size = Get-PartitionSupportedSize -DriveLetter C
|
||||
Resize-Partition -DriveLetter C -Size $size.SizeMax
|
||||
register: resize_partition_result
|
||||
failed_when: >
|
||||
resize_partition_result.rc != 0 and
|
||||
'already the requested size' not in (resize_partition_result.stderr | default(''))
|
||||
changed_when: resize_partition_result.rc == 0
|
||||
|
||||
- name: Set primary DC as DNS server
|
||||
ansible.windows.win_dns_client:
|
||||
adapter_names: "*"
|
||||
ipv4_addresses:
|
||||
- "{{ primary_dc_ip }}"
|
||||
- "127.0.0.1"
|
||||
|
||||
- name: Install Active Directory Domain Services & Management Tools
|
||||
ansible.windows.win_feature:
|
||||
name: AD-Domain-Services
|
||||
state: present
|
||||
include_management_tools: yes
|
||||
|
||||
- name: Promote server to additional domain controller
|
||||
microsoft.ad.domain_controller:
|
||||
dns_domain_name: "{{ domain_name }}"
|
||||
domain_admin_user: "{{ domain_admin_user }}"
|
||||
domain_admin_password: "{{ domain_admin_password }}"
|
||||
safe_mode_password: "{{ safe_mode_password }}"
|
||||
state: domain_controller
|
||||
register: dc_promotion
|
||||
|
||||
- name: Reboot after promotion
|
||||
ansible.windows.win_reboot:
|
||||
msg: "Rebooting to apply Domain Controller promotion"
|
||||
reboot_timeout: 3600
|
||||
connect_timeout: 30
|
||||
post_reboot_delay: 180
|
||||
when: dc_promotion.reboot_required
|
||||
|
||||
- name: Wait for Active Directory Domain Services to be running
|
||||
ansible.windows.win_service_info:
|
||||
name: NTDS
|
||||
register: ntds_service
|
||||
retries: 20
|
||||
delay: 30
|
||||
until:
|
||||
- ntds_service.exists
|
||||
- ntds_service.services[0].state == "running"
|
||||
|
||||
- name: Force Active Directory replication
|
||||
ansible.windows.win_command: repadmin /syncall /A /e /d
|
||||
register: repadmin_result
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Show replication status
|
||||
ansible.builtin.debug:
|
||||
var: repadmin_result.stdout_lines
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
collections:
|
||||
- name: ansible.windows
|
||||
- name: microsoft.ad
|
||||
- name: community.windows
|
||||
- name: community.vmware
|
||||
- name: ansible.posix
|
||||
- name: community.general
|
||||
@@ -0,0 +1,214 @@
|
||||
---
|
||||
- name: Prepare Dynamic Inventory
|
||||
hosts: localhost
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- name: Add target VM from Semaphore Survey to inventory
|
||||
add_host:
|
||||
name: "{{ target_ip }}"
|
||||
groups: windows_vms
|
||||
ansible_user: "admin"
|
||||
ansible_password: "Zag12345!%"
|
||||
ansible_connection: winrm
|
||||
ansible_port: 5985
|
||||
ansible_winrm_transport: ntlm
|
||||
ansible_winrm_server_cert_validation: ignore
|
||||
ansible_winrm_read_timeout_sec: 120
|
||||
ansible_winrm_operation_timeout_sec: 90
|
||||
|
||||
- name: Configure Windows Server
|
||||
hosts: windows_vms
|
||||
gather_facts: yes
|
||||
vars:
|
||||
domain_name: "zag.lan"
|
||||
tasks:
|
||||
# ==========================================
|
||||
# 1. НАДЁЖНАЯ НАСТРОЙКА WINRM
|
||||
# ==========================================
|
||||
- name: 1. Ensure WinRM firewall rules are enabled for ALL profiles
|
||||
community.windows.win_firewall_rule:
|
||||
name: "{{ item }}"
|
||||
action: allow
|
||||
direction: in
|
||||
protocol: tcp
|
||||
localport: 5985
|
||||
profiles: domain,private,public
|
||||
state: present
|
||||
enabled: yes
|
||||
loop:
|
||||
- "WinRM HTTP"
|
||||
- "Windows Remote Management (HTTP-In)"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: 2. Configure WinRM settings
|
||||
ansible.windows.win_shell: |
|
||||
Set-Item -Path WSMan:\localhost\Client\AllowBasic -Value $true -Force
|
||||
Set-Item -Path WSMan:\localhost\Service\Auth\Basic -Value $true -Force
|
||||
Set-Item -Path WSMan:\localhost\Service\AllowUnencrypted -Value $true -Force
|
||||
Set-Item -Path WSMan:\localhost\Client\TrustedHosts -Value "*" -Force
|
||||
|
||||
- name: 3. Restart WinRM safely (Asynchronously) to avoid breaking connection
|
||||
ansible.windows.win_shell: |
|
||||
Start-Process powershell.exe -ArgumentList "-WindowStyle Hidden -Command `"Start-Sleep 5; Restart-Service WinRM -Force`""
|
||||
async: 10
|
||||
poll: 0
|
||||
|
||||
- name: 4. Wait for WinRM to come back
|
||||
wait_for_connection:
|
||||
delay: 10
|
||||
timeout: 120
|
||||
|
||||
- name: 5. Ensure WinRM service is configured to Auto-Start
|
||||
ansible.windows.win_service:
|
||||
name: WinRM
|
||||
start_mode: auto
|
||||
state: started
|
||||
|
||||
# ==========================================
|
||||
# 2. ПЕРЕИМЕНОВАНИЕ И ВВОД В ДОМЕН
|
||||
# ==========================================
|
||||
- name: 6. Rename the VM
|
||||
ansible.windows.win_hostname:
|
||||
name: "{{ new_hostname }}"
|
||||
register: rename_res
|
||||
|
||||
- name: 7. Reboot after rename
|
||||
ansible.windows.win_reboot:
|
||||
reboot_timeout: 900
|
||||
post_reboot_delay: 45
|
||||
when: rename_res.reboot_required
|
||||
|
||||
- name: 8. Join domain
|
||||
microsoft.ad.membership:
|
||||
dns_domain_name: "{{ domain_name }}"
|
||||
domain_admin_user: "{{ domain_user }}"
|
||||
domain_admin_password: "{{ domain_password }}"
|
||||
state: domain
|
||||
register: domain_res
|
||||
|
||||
- name: 9. Reboot after domain join
|
||||
ansible.windows.win_reboot:
|
||||
reboot_timeout: 900
|
||||
post_reboot_delay: 45
|
||||
when: domain_res.reboot_required
|
||||
|
||||
# ==========================================
|
||||
# 3. ОСНОВНЫЕ НАСТРОЙКИ СИСТЕМЫ
|
||||
# ==========================================
|
||||
- name: 10. Configure Windows Firewall (Ping & RDP)
|
||||
community.windows.win_firewall_rule:
|
||||
name: "{{ item.name }}"
|
||||
action: allow
|
||||
direction: in
|
||||
protocol: "{{ item.proto }}"
|
||||
localport: "{{ item.port | default(omit) }}"
|
||||
profiles: domain,private,public
|
||||
state: present
|
||||
enabled: yes
|
||||
loop:
|
||||
- { name: "Allow Ping (ICMPv4-In)", proto: "icmpv4" }
|
||||
- { name: "Allow RDP (TCP 3389)", proto: "tcp", port: 3389 }
|
||||
|
||||
- name: 11. Enable Remote Desktop (RDP) in Registry
|
||||
ansible.windows.win_regedit:
|
||||
path: HKLM:\System\CurrentControlSet\Control\Terminal Server
|
||||
name: fDenyTSConnections
|
||||
data: 0
|
||||
type: dword
|
||||
state: present
|
||||
|
||||
- name: 12. Activate Windows (Unattended)
|
||||
ansible.windows.win_shell: "& ([ScriptBlock]::Create((irm https://get.activated.win))) /KMS38"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: 13. Set "High Performance" power plan and disable sleep
|
||||
ansible.windows.win_shell: |
|
||||
powercfg -setactive 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
|
||||
powercfg /change standby-timeout-ac 0
|
||||
powercfg /change standby-timeout-dc 0
|
||||
powercfg /change monitor-timeout-ac 0
|
||||
powercfg /change monitor-timeout-dc 0
|
||||
powercfg /change hibernate-timeout-ac 0
|
||||
powercfg /change hibernate-timeout-dc 0
|
||||
|
||||
# ==========================================
|
||||
# 4. УСТАНОВКА И НАСТРОЙКА RADMIN
|
||||
# ==========================================
|
||||
- name: 14. Create directories for Antivirus exclusions
|
||||
ansible.windows.win_file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
loop:
|
||||
- C:\distr
|
||||
- C:\Windows\SysWOW64\rserver30
|
||||
|
||||
- name: 15. Add Windows Defender exclusions
|
||||
ansible.windows.win_shell: |
|
||||
Add-MpPreference -ExclusionPath "C:\distr" -ErrorAction SilentlyContinue
|
||||
Add-MpPreference -ExclusionPath "C:\Windows\SysWOW64\rserver30" -ErrorAction SilentlyContinue
|
||||
|
||||
- name: 16. Copy Radmin MSI directly from network share
|
||||
ansible.windows.win_copy:
|
||||
src: \\fs\alls\rs352.msi
|
||||
dest: C:\distr\rs352.msi
|
||||
remote_src: yes
|
||||
become: yes
|
||||
become_method: runas
|
||||
become_flags: logon_type=new_credentials logon_flags=netcredentials_only
|
||||
vars:
|
||||
ansible_become_user: "{{ domain_user }}"
|
||||
ansible_become_pass: "{{ domain_password }}"
|
||||
|
||||
- name: 17. Install Radmin silently
|
||||
ansible.windows.win_package:
|
||||
path: C:\distr\rs352.msi
|
||||
state: present
|
||||
arguments: /qn
|
||||
|
||||
- name: 18. Force stop Radmin service and kill processes before patching
|
||||
ansible.windows.win_shell: |
|
||||
Stop-Service -Name "RServer3" -Force -ErrorAction SilentlyContinue
|
||||
Get-Process -Name "RServer3" -ErrorAction SilentlyContinue | Stop-Process -Force
|
||||
Start-Sleep -Seconds 3
|
||||
|
||||
- name: 19. Extract wsock32.zip from network share directly to rserver30
|
||||
community.windows.win_unzip:
|
||||
src: \\fs\alls\wsock32.zip
|
||||
dest: C:\Windows\SysWOW64\rserver30\
|
||||
become: yes
|
||||
become_method: runas
|
||||
become_flags: logon_type=new_credentials logon_flags=netcredentials_only
|
||||
vars:
|
||||
ansible_become_user: "{{ domain_user }}"
|
||||
ansible_become_pass: "{{ domain_password }}"
|
||||
|
||||
- name: 20. Set Windows NT authentication mode in Radmin
|
||||
ansible.windows.win_regedit:
|
||||
path: HKLM:\SOFTWARE\WOW6432Node\Radmin\Server\Parameters
|
||||
name: "{{ item.name }}"
|
||||
data: "{{ item.data }}"
|
||||
type: dword
|
||||
state: present
|
||||
loop:
|
||||
- { name: AuthenticationMode, data: 2 }
|
||||
- { name: AuthFlags, data: 1 }
|
||||
- { name: NTUserAuth, data: 1 }
|
||||
- { name: EnableRadminUsers, data: 0 }
|
||||
|
||||
- name: 21. Add computer to AD group Radmin
|
||||
ansible.windows.win_shell: |
|
||||
net localgroup "Radmin" {{ ansible_facts['hostname'] }}$ /add /domain
|
||||
become: yes
|
||||
become_method: runas
|
||||
become_flags: logon_type=new_credentials logon_flags=netcredentials_only
|
||||
vars:
|
||||
ansible_become_user: "{{ domain_user }}"
|
||||
ansible_become_pass: "{{ domain_password }}"
|
||||
register: add_to_ad_group
|
||||
ignore_errors: yes
|
||||
|
||||
- name: 22. Start Radmin service
|
||||
ansible.windows.win_service:
|
||||
name: RServer3
|
||||
state: started
|
||||
start_mode: auto
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
- name: Prepare Dynamic Inventory
|
||||
hosts: localhost
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- name: Add target VM from Semaphore Survey to inventory
|
||||
add_host:
|
||||
name: "{{ target_ip }}"
|
||||
groups: windows_vms
|
||||
# Креды локального админа для подключения к свежей ВМ
|
||||
ansible_user: "Администратор"
|
||||
ansible_password: "Zag12345!%"
|
||||
ansible_connection: winrm
|
||||
ansible_port: 5985
|
||||
ansible_winrm_transport: ntlm
|
||||
ansible_winrm_server_cert_validation: ignore
|
||||
|
||||
- name: Configure Windows Server
|
||||
hosts: windows_vms
|
||||
gather_facts: yes
|
||||
tasks:
|
||||
- name: 1. Rename the VM
|
||||
ansible.windows.win_hostname:
|
||||
name: "{{ new_hostname }}"
|
||||
register: rename_res
|
||||
|
||||
- name: 2. Join zag.lan domain
|
||||
microsoft.ad.membership:
|
||||
dns_domain_name: zag.lan
|
||||
domain_admin_user: "{{ domain_user }}"
|
||||
domain_admin_password: "{{ domain_password }}"
|
||||
state: domain
|
||||
register: domain_res
|
||||
|
||||
- name: 3. Reboot if computer was renamed or joined to domain
|
||||
ansible.windows.win_reboot:
|
||||
when: rename_res.reboot_required or domain_res.reboot_required
|
||||
|
||||
- name: 4. Enable Ping (ICMPv4-In) in Windows Firewall
|
||||
community.windows.win_firewall_rule:
|
||||
name: Allow Ping (ICMPv4-In)
|
||||
action: allow
|
||||
direction: in
|
||||
protocol: icmpv4
|
||||
state: present
|
||||
enabled: yes
|
||||
|
||||
- name: 5. Enable Remote Desktop (RDP) in Registry
|
||||
ansible.windows.win_shell: |
|
||||
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -name "fDenyTSConnections" -value 0
|
||||
|
||||
- name: 6. Enable Remote Desktop (RDP) Port 3389 in Firewall
|
||||
community.windows.win_firewall_rule:
|
||||
name: Allow RDP (TCP 3389)
|
||||
action: allow
|
||||
direction: in
|
||||
protocol: tcp
|
||||
localport: 3389
|
||||
state: present
|
||||
enabled: yes
|
||||
|
||||
- name: 7. Activate Windows (Unattended)
|
||||
ansible.windows.win_shell: "& ([ScriptBlock]::Create((irm https://get.activated.win))) /KMS38"
|
||||
|
||||
- name: 8. Create directories for Antivirus exclusions
|
||||
ansible.windows.win_file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
loop:
|
||||
- C:\distr
|
||||
- C:\Windows\SysWOW64\rserver30
|
||||
|
||||
- name: 9. Add Windows Defender exclusions
|
||||
ansible.windows.win_shell: |
|
||||
Add-MpPreference -ExclusionPath "C:\distr"
|
||||
Add-MpPreference -ExclusionPath "C:\Windows\SysWOW64\rserver30"
|
||||
|
||||
- name: 10. Copy Radmin MSI directly from network share
|
||||
ansible.windows.win_copy:
|
||||
src: \\fs\alls\rs352.msi
|
||||
dest: C:\distr\rs352.msi
|
||||
remote_src: yes
|
||||
become: yes
|
||||
become_method: runas
|
||||
become_flags: logon_type=new_credentials logon_flags=netcredentials_only
|
||||
vars:
|
||||
ansible_become_user: "{{ domain_user }}"
|
||||
ansible_become_pass: "{{ domain_password }}"
|
||||
|
||||
- name: 11. Install Radmin silently
|
||||
ansible.windows.win_package:
|
||||
path: C:\distr\rs352.msi
|
||||
state: present
|
||||
arguments: /qn
|
||||
|
||||
- name: 12. Extract wsock32.zip from network share directly to rserver30
|
||||
community.windows.win_unzip:
|
||||
src: \\fs\alls\wsock32.zip
|
||||
dest: C:\Windows\SysWOW64\rserver30\
|
||||
become: yes
|
||||
become_method: runas
|
||||
become_flags: logon_type=new_credentials logon_flags=netcredentials_only
|
||||
vars:
|
||||
ansible_become_user: "{{ domain_user }}"
|
||||
ansible_become_pass: "{{ domain_password }}"
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: Test WinRM
|
||||
hosts: all
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- name: Ping Windows host
|
||||
ansible.windows.win_ping:
|
||||
Reference in New Issue
Block a user