1 Commits

Author SHA1 Message Date
3252db4505 Update Sharepoint.yaml 2025-08-14 07:39:25 +00:00
5 changed files with 27 additions and 394 deletions

View File

@@ -16,7 +16,7 @@
job_url: "{{ tower_job_url | default('') }}"
status: "{{ (tower_job_failed | default(false)) | ternary('failed','successful') }}"
# Timestamps (avoid ansible_date_time since gather_facts: false)
# Timestamps (works without gather_facts)
run_start: "{{ lookup('pipe','date -u +%Y-%m-%dT%H:%M:%SZ') }}"
run_end: "{{ lookup('pipe','date -u +%Y-%m-%dT%H:%M:%SZ') }}"
@@ -82,11 +82,6 @@
Skipped={{ recap_skipped | default(0) }},
Unreachable={{ recap_unreach | default(0) }}
- name: Build final status from recap
run_once: true
set_fact:
status_final: "{{ 'failed' if (recap_failed | int > 0 or recap_unreach | int > 0) else 'successful' }}"
# --- Optional probes (keep while debugging) ---
- name: Verify siteId resolves
delegate_to: localhost
@@ -138,11 +133,12 @@
msg:
names: "{{ (cols_probe.json.value | default([])) | map(attribute='name') | list }}"
# --- Create list item (no block/rescue; use ignore_errors + diagnostics) ---
# --- Create list item ---
- name: Create SharePoint list item (Graph)
delegate_to: localhost
run_once: true
uri:
block:
- uri:
url: "https://graph.microsoft.com/v1.0/sites/{{ site_id }}/lists/{{ list_id }}/items"
method: POST
headers:
@@ -154,40 +150,15 @@
body:
fields:
Title: "{{ job_name }} ({{ job_id }})"
Status: "{{ status_final }}"
Status: "{{ status }}"
RunStart: "{{ run_start }}"
RunEnd: "{{ run_end }}"
Notes: |-
{{ summary_text }}
Recap: {{ hostvars['localhost'].recap_line }}
register: sp_create
ignore_errors: true
no_log: true
- name: Show sanitized Graph error (if any)
rescue:
- name: Sanitize and print the error
run_once: true
when: sp_create is failed
vars:
_json: "{{ sp_create.json | default({}) }}"
debug:
msg:
status: "{{ sp_create.status | default('n/a') }}"
graph_error: >-
{{ _json.error.message
| default(_json.message
| default(sp_create.msg | default('Unknown error'))) }}
hint: >
400: column internal names; 401: scope/audience; 403: permissions;
404: siteId/listId.
- name: Fail if SharePoint item was not created
run_once: true
when: sp_create is failed
fail:
msg: "Failed to create SharePoint item (see previous message)."
- name: Show created list item id
run_once: true
when: sp_create is succeeded
debug:
var: sp_create.json.id

View File

@@ -1,6 +0,0 @@
---
collections:
- name: ansible.windows
version: ">=2.5.0,<3.0.0"
- name: community.windows
version: ">=2.3.0,<3.0.0"

View File

@@ -1,7 +0,0 @@
- name: Patch Domain Controllers via PSRP
hosts: domain_controllers
gather_facts: no # ✅ Important: disables Python requirement
tasks:
- name: Install updates
command: Install-WindowsUpdate -AcceptAll -AutoReboot

View File

@@ -1,262 +0,0 @@
---
- name: Windows Update Installation from Assessment Report
hosts: windows
gather_facts: no
tasks:
- name: Get current timestamp
set_fact:
current_timestamp: "{{ lookup('pipe', 'date +%Y-%m-%dT%H:%M:%S') }}"
- name: Check if KB updates report file exists
win_stat:
path: 'C:\Temp\windows_updates_with_kb.txt'
register: kb_updates_file
# ---- DO NOT hard-fail; flag host and stop further tasks on this host ----
- name: Mark host failed if report is missing (but continue overall run)
set_fact:
patch_failed_host: true
patch_failed_count: "{{ (patch_failed_count | default(0) | int) + 1 }}"
patch_fail_reason: "KB report missing at C:\\Temp\\windows_updates_with_kb.txt"
when: not kb_updates_file.stat.exists
- name: Stop further tasks on this host (report missing)
meta: end_host
when: not kb_updates_file.stat.exists
# ------------------------------------------------------------------------
- name: Read KB updates report content
win_shell: Get-Content -Path 'C:\Temp\windows_updates_with_kb.txt'
register: updates_content
when: kb_updates_file.stat.exists
- name: Extract KB numbers from report file
set_fact:
kb_numbers: "{{ updates_content.stdout_lines
| select('match', '.*KB: .*')
| map('regex_replace', '.*KB: ([0-9,\\s]+).*', '\\1')
| map('split', ',') | flatten | map('trim')
| select('match', '^[0-9]+$') | list | unique }}"
when:
- kb_updates_file.stat.exists
- updates_content.stdout_lines is defined
- name: Display KB numbers to be installed
debug:
msg:
- "Found {{ kb_numbers | length }} unique KB numbers to install:"
- "{{ kb_numbers | join(', ') }}"
when:
- kb_updates_file.stat.exists
- kb_numbers is defined
- kb_numbers | length > 0
# ---- Patch with failure capture (block/rescue) ----
- name: Install Windows updates by KB numbers (with failure capture)
block:
- name: Install Windows updates by KB numbers
win_updates:
category_names: '*'
state: installed
accept_list: "{{ kb_numbers }}"
log_path: 'C:\Temp\windows_update_installation.log'
register: installation_result
when:
- kb_updates_file.stat.exists
- kb_numbers is defined
- kb_numbers | length > 0
- name: Mark host failed if any KB installs failed (partial failures)
set_fact:
patch_failed_host: "{{ (installation_result.failed_update_count | default(0) | int) > 0 }}"
patch_failed_count: "{{ installation_result.failed_update_count | default(0) | int }}"
when: installation_result is defined
rescue:
- name: Mark host failed because win_updates task error/exception
set_fact:
patch_failed_host: true
patch_failed_count: "{{ (patch_failed_count | default(0) | int) + 1 }}"
always:
- name: Ensure failure flags exist (default to false/0)
set_fact:
patch_failed_host: "{{ patch_failed_host | default(false) }}"
patch_failed_count: "{{ patch_failed_count | default(0) | int }}"
- name: Display installation summary
debug:
msg:
- "=== WINDOWS UPDATE INSTALLATION COMPLETE ==="
- "Host: {{ inventory_hostname }}"
- "Updates Found: {{ installation_result.found_update_count | default(0) }}"
- "Updates Installed: {{ installation_result.installed_update_count | default(0) }}"
- "Updates Failed: {{ installation_result.failed_update_count | default(0) }}"
- "Reboot Required: {{ 'Yes' if installation_result.reboot_required | default(false) else 'No' }}"
- "Patch failed flag: {{ patch_failed_host | default(false) }}"
when:
- kb_updates_file.stat.exists
- kb_numbers is defined
- kb_numbers | length > 0
- name: Reboot if required
win_reboot:
reboot_timeout: 1800
when: installation_result is defined and (installation_result.reboot_required | default(false))
- name: Create installation report
set_fact:
installation_summary: |
Windows Update Installation Report
=================================
Host: {{ inventory_hostname }}
Date: {{ current_timestamp }}
Summary:
--------
Total Updates Found: {{ installation_result.found_update_count | default(0) }}
Successfully Installed: {{ installation_result.installed_update_count | default(0) }}
Failed Installations: {{ installation_result.failed_update_count | default(0) }}
Reboot Required: {{ installation_result.reboot_required | default('No') }}
Requested KB Numbers: {{ kb_numbers | default([]) | join(', ') }}
{% if installation_result is defined and installation_result.updates is defined %}
Installed Updates:
-----------------
{% for update_id, update_info in installation_result.updates.items() %}
- {{ update_info.title }}
KB: {{ update_info.kb | join(', ') if update_info.kb else 'None' }}
{% endfor %}
{% endif %}
when: kb_updates_file.stat.exists
- name: Save installation report to file
win_copy:
content: "{{ installation_summary }}"
dest: 'C:\Temp\windows_update_installation_report.txt'
when: installation_summary is defined
- name: Give a report when no KB numbers were found on updates
debug:
msg: "No valid KB numbers found in the updates report file. Please verify the assessment report."
when:
- kb_updates_file.stat.exists
- (kb_numbers is not defined or kb_numbers | length == 0)
# ------------------------------------------------------------------------------
- name: Aggregate results and post to SharePoint (always runs)
hosts: localhost
connection: local
gather_facts: false
vars:
tenant_id: "{{ lookup('env', 'SP_TENANT_ID') }}"
client_id: "{{ lookup('env', 'SP_CLIENT_ID') }}"
client_secret: "{{ lookup('env', 'SP_CLIENT_SECRET') }}"
site_id: "{{ lookup('env', 'SP_SITE_ID') }}"
list_id: "{{ lookup('env', 'SP_LIST_ID') }}"
job_id: "{{ tower_job_id | default('n/a') }}"
job_name: "{{ tower_job_template_name | default('Patch run') }}"
job_url: "{{ tower_job_url | default('') }}"
run_start: "{{ lookup('pipe','date -u +%Y-%m-%dT%H:%M:%SZ') }}"
run_end: "{{ lookup('pipe','date -u +%Y-%m-%dT%H:%M:%SZ') }}"
summary_text: >-
Job {{ job_id }}.
Template={{ job_name }}.
URL="http://172.18.13.188:10445/".
tasks:
- name: Init failed hosts list
set_fact:
failed_hosts_list: []
- name: Collect hosts that flagged patch failure
set_fact:
failed_hosts_list: "{{ failed_hosts_list + [item] }}"
loop: "{{ groups['windows'] | default([]) }}"
when: hostvars[item].patch_failed_host | default(false)
- name: Compute final status and CSV
set_fact:
any_patch_failed: "{{ (failed_hosts_list | length) > 0 }}"
failed_hosts_csv: "{{ failed_hosts_list | join(', ') if failed_hosts_list | length > 0 else 'None' }}"
status_final: "{{ 'failed' if (failed_hosts_list | length) > 0 else 'successful' }}"
- name: Sanity — status to post
debug:
msg:
status_final: "{{ status_final }}"
failed_hosts_csv: "{{ failed_hosts_csv }}"
failed_hosts_list: "{{ failed_hosts_list }}"
- name: Acquire Graph token (client credentials)
uri:
url: "https://login.microsoftonline.com/{{ tenant_id }}/oauth2/v2.0/token"
method: POST
headers:
Content-Type: "application/x-www-form-urlencoded"
body: >
client_id={{ client_id }}
&client_secret={{ client_secret | urlencode }}
&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&grant_type=client_credentials
register: graph_token
no_log: true
failed_when: graph_token.status not in [200]
- name: Create SharePoint list item (Graph)
uri:
url: "https://graph.microsoft.com/v1.0/sites/{{ site_id }}/lists/{{ list_id }}/items"
method: POST
headers:
Authorization: "Bearer {{ graph_token.json.access_token }}"
Content-Type: "application/json"
body_format: json
return_content: true
status_code: [200, 201]
body:
fields:
Title: "{{ job_name }} ({{ job_id }})"
Status: "{{ status_final }}"
RunStart: "{{ run_start }}"
RunEnd: "{{ run_end }}"
Notes: |-
{{ summary_text }}
Failed hosts: {{ failed_hosts_csv }}
register: sp_create
ignore_errors: true
no_log: true
- name: Show sanitized Graph error (if any)
when: sp_create is failed
vars:
_json: "{{ sp_create.json | default({}) }}"
debug:
msg:
status: "{{ sp_create.status | default('n/a') }}"
graph_error: >-
{{ _json.error.message
| default(_json.message
| default(sp_create.msg | default('Unknown error'))) }}
- name: Fail if SharePoint item was not created
when: sp_create is failed
fail:
msg: "Failed to create SharePoint item (see previous message)."
- name: Show created list item id
when: sp_create is succeeded
debug:
var: sp_create.json.id
# flip the AWX job status based on your aggregate
- name: Mark AWX job as FAILED if any host failed
when: status_final == 'failed'
fail:
msg: "Patch run failed on hosts: {{ failed_hosts_csv }}"

View File

@@ -2,7 +2,6 @@
- name: Check Free Disk Space
hosts: windows
gather_facts: false
tasks:
- name: "Check Free Disk Space in C:"
win_shell: |
@@ -20,65 +19,3 @@
msg: "the node {{ inventory_hostname }} has insufficient disk space: {{ freediskspace.stdout | trim }} GB (minimum required: 20 GB)"
when:
- freediskspace.stdout | trim | float < 20
- name: Find Recovery partitions (PowerShell → JSON)
ansible.windows.win_powershell:
script: |
$ErrorActionPreference = 'Stop'
$guid = '{DE94BBA4-06D1-4D40-A16A-BFD50179D6AC}'
$parts = @( Get-Partition |
Where-Object { $_.GptType -eq $guid -or $_.Type -eq 'Recovery' -or $_.Type -eq 'Unknown' } |
Select-Object DiskNumber, PartitionNumber,
@{n='SizeBytes';e={$_.Size}},
@{n='SizeMB';e={[math]::Round($_.Size/1MB,2)}},
GptType, Type )
$parts = @($parts)
$parts | ConvertTo-Json -Compress -Depth 4
register: winre_raw
changed_when: false
- name: Parse JSON (string → object)
ansible.builtin.set_fact:
winre_parts: "{{ (winre_raw.output | join('') | trim | default('[]')) | from_json }}"
- name: Normalize to list
ansible.builtin.set_fact:
winre_parts: "{{ [winre_parts] }}"
when: winre_parts is mapping
# Thresholds
- name: Set Recovery size threshold
ansible.builtin.set_fact:
recovery_threshold_mib: 500
recovery_threshold_bytes: "{{ 500 * 1024 * 1024 }}"
changed_when: false
# Build a list of sizes as INTS
- name: Extract recovery sizes (bytes → ints)
ansible.builtin.set_fact:
recovery_sizes_bytes: "{{ winre_parts | map(attribute='SizeBytes') | map('int') | list }}"
changed_when: false
# Compute smallest size (bytes)
- name: Compute smallest Recovery partition size (bytes)
ansible.builtin.set_fact:
recovery_min_bytes: "{{ recovery_sizes_bytes | min }}"
when: recovery_sizes_bytes | length > 0
changed_when: false
# Fail this host if smallest Recovery partition < 500 MiB
- name: Enforce minimum Recovery partition size
ansible.builtin.fail:
msg: >-
Recovery partition too small on {{ inventory_hostname }}:
smallest={{ ((recovery_min_bytes | int) / 1048576.0) | round(2) }} MiB,
required >= {{ recovery_threshold_mib | int }} MiB.
Details: {{ winre_parts }}
when:
- recovery_sizes_bytes | length > 0
- (recovery_min_bytes | int) < (recovery_threshold_bytes | int)
- name: Show Recovery partition sizes
ansible.builtin.debug:
msg: "Disk {{ item.DiskNumber }}, Part {{ item.PartitionNumber }}, Size: {{ item.SizeMB }} MB ({{ item.SizeBytes }} bytes)"
loop: "{{ winre_parts }}"