Projects STRLCPY GOAD Commits 218cf9d5
🤬
  • ■ ■ ■ ■
    README.md
    skipped 369 lines
    370 370  - [X] Drop the mic
    371 371  - [X] Shadow credentials
    372 372  - [X] Mitm6
     373 +- [X] Add LAPS
    373 374  - [ ] Add Webdav
    374 375  - [ ] Add Applocker
    375  -- [ ] Add LAPS
    376 376  - [ ] Zone transfert
    377 377  - [ ] GPO abuse
    378 378  - [ ] Wsus
    skipped 206 lines
  • ■ ■ ■ ■ ■
    ad/sevenkingdoms.local/data/config.json
    skipped 55 lines
    56 56   "local_admin_password": "NgtI75cKV+Pu",
    57 57   "domain" : "north.sevenkingdoms.local",
    58 58   "path" : "DC=north,DC=sevenkingdoms,DC=local",
     59 + "use_laps": false,
    59 60   "local_groups" : {
    60 61   "Administrators" : [
    61 62   "north\\jeor.mormont"
    skipped 57 lines
    119 120   "local_admin_password": "978i2pF43UJ-",
    120 121   "domain" : "essos.local",
    121 122   "path" : "DC=essos,DC=local",
     123 + "use_laps": true,
    122 124   "local_groups" : {
    123 125   "Administrators" : [
    124 126   "essos\\khal.drogo"
    skipped 30 lines
    155 157   "netbios_name": "ESSOS",
    156 158   "ca_server": "Braavos",
    157 159   "trust" : "sevenkingdoms.local",
    158  - "organisation_units" : {},
     160 + "laps_path": "OU=Laps,DC=essos,DC=local",
     161 + "organisation_units" : {
     162 + },
     163 + "laps_readers": [
     164 + "jorah.mormont",
     165 + "Spys"
     166 + ],
    159 167   "groups" : {
    160 168   "universal" : {},
    161 169   "global" : {
    skipped 85 lines
    247 255   "domain_password" : "NgtI75cKV+Pu",
    248 256   "netbios_name": "NORTH",
    249 257   "trust" : "",
    250  - "organisation_units" : {},
     258 + "laps_path": "OU=Laps,DC=north,DC=sevenkingdoms,DC=local",
     259 + "organisation_units" : {
     260 + },
    251 261   "groups" : {
    252 262   "universal" : {},
    253 263   "global" : {
    skipped 143 lines
    397 407   "domain_password" : "8dCT-DJjgScp",
    398 408   "netbios_name": "SEVENKINGDOMS",
    399 409   "trust" : "essos.local",
     410 + "laps_path": "OU=Laps,DC=sevenkingdoms,DC=local",
    400 411   "organisation_units" : {
    401 412   "Vale" : { "path" : "DC=sevenkingdoms,DC=local"},
    402 413   "IronIslands" : { "path" : "DC=sevenkingdoms,DC=local"},
    skipped 151 lines
  • ■ ■ ■ ■
    ansible/ad-relations.yml
    skipped 7 lines
    8 8  # set AD datas ==================================================================================================
    9 9   
    10 10  - name: "Adjust rights configuration"
    11  - hosts: dc01,dc02,dc03,srv01,srv02,srv03
     11 + hosts: dc01,dc02,dc03,srv02,srv03
    12 12   roles:
    13 13   - { role: "settings/adjust_rights", tags: 'adjust_rights'}
    14 14   vars:
    skipped 15 lines
  • ■ ■ ■ ■ ■ ■
    ansible/ad-servers.yml
    skipped 7 lines
    8 8  # set AD datas ==================================================================================================
    9 9   
    10 10  - name: Prepare servers set admin password, set hostname
    11  - hosts: dc01,dc02,dc03,srv01,srv02,srv03
     11 + hosts: dc01,dc02,dc03,srv02,srv03
    12 12   roles:
    13 13   - { role: 'settings/admin_password', tags: 'admin_password' }
    14 14   - { role: 'settings/hostname', tags: 'hostname' }
    skipped 26 lines
    41 41   source_dc: "{{lab.hosts[lab.domains[parent_domain].dc].hostname}}.{{parent_domain}}"
    42 42   
    43 43  - name: "play servers AD configuration"
    44  - hosts: srv01,srv02,srv03
     44 + hosts: srv02,srv03
    45 45   roles:
    46 46   - { role: 'member_server', tags: 'server'}
    47 47   vars:
    skipped 3 lines
  • ■ ■ ■ ■
    ansible/ansible.cfg
    1 1  [defaults]
    2 2  host_key_checking = false
    3 3  inventory = ./hosts
    4  - 
     4 +display_skipped_hosts = false
  • ■ ■ ■ ■
    ansible/build.yml
    1 1  ---
    2 2  - name: build all
    3  - hosts: dc01,dc02,dc03,srv01,srv02,srv03
     3 + hosts: dc01,dc02,dc03,srv02,srv03
    4 4   roles:
    5 5   - { role: 'common', tags: 'common', http_proxy: "{{enable_http_proxy}}"}
    6 6   - { role: 'settings/keyboard', tags: 'keyboard', layout: "fr-FR" }
    skipped 12 lines
  • ■ ■ ■ ■
    ansible/elk.yml
    skipped 5 lines
    6 6   - { role: 'elk', tags: 'elk' }
    7 7   
    8 8  - name: Install log agent on windows vms
    9  - hosts: dc01,dc02,dc03,srv01,srv02,srv03
     9 + hosts: dc01,dc02,dc03,srv02,srv03
    10 10   roles:
    11 11   - { role: 'logs_windows', tags: 'agent' }
  • ■ ■ ■ ■ ■ ■
    ansible/laps.yml
     1 +---
     2 +# Load datas
     3 +- import_playbook: data.yml
     4 + vars:
     5 + data_path: "../ad/{{domain_name}}/data/"
     6 + tags: 'data'
     7 + 
     8 +- name: configure laps on DCs
     9 + hosts: dc01, dc02, dc03
     10 + roles:
     11 + - { role: 'laps/dc', tags: 'laps-dc' }
     12 + vars:
     13 + domain: "{{lab.hosts[dict_key].domain}}"
     14 + laps_path: "{{lab.domains[domain].laps_path}}"
     15 + hosts_dict: "{{lab.hosts}}"
     16 + 
     17 +- name: configure laps on servers
     18 + hosts: srv02,srv03
     19 + roles:
     20 + - { role: 'laps/server', tags: 'laps-server'}
     21 + vars:
     22 + domain: "{{lab.hosts[dict_key].domain}}"
     23 + laps_path: "{{lab.domains[domain].laps_path}}"
     24 + use_laps: "{{lab.hosts[dict_key].use_laps if lab.hosts[dict_key].use_laps is defined else false}}"
     25 + 
     26 +- name: verify and show laps passwords
     27 + hosts: dc01, dc02, dc03
     28 + roles:
     29 + - { role: 'laps/verify', tags: 'laps-verify' }
     30 + vars:
     31 + domain: "{{lab.hosts[dict_key].domain}}"
     32 + laps_path: "{{lab.domains[domain].laps_path}}"
     33 + hosts_dict: "{{lab.hosts}}"
     34 + 
     35 +- name: set laps users and groups permission
     36 + hosts: dc01, dc02, dc03
     37 + roles:
     38 + - { role: 'laps/permissions', tags: 'laps-permissions', when: }
     39 + vars:
     40 + domain: "{{lab.hosts[dict_key].domain}}"
     41 + laps_path: "{{lab.domains[domain].laps_path}}"
     42 + laps_readers: "{{lab.domains[domain].laps_readers if lab.domains[domain].laps_readers is defined else [] }}"
     43 + 
  • ■ ■ ■ ■ ■ ■
    ansible/main.yml
    skipped 15 lines
    16 16  - import_playbook: ad-trusts.yml
    17 17  # import the ad datas : users/groups...
    18 18  - import_playbook: ad-data.yml
     19 +# install LAPS
     20 +- import_playbook: laps.yml
     21 +## MSSQL + IIS ----------
     22 +# configure servers vulns (done in the midle of ad install to let time before install relations and acl)
     23 +- import_playbook: servers.yml
     24 +## AD - servers localgroup + rdp + inter domain relations & acl
    19 25  # set the rights and the group domains relations
    20 26  - import_playbook: ad-relations.yml
    21 27  # set adcs
    skipped 16 lines
  • ■ ■ ■ ■ ■
    ansible/roles/elk/tasks/main.yml
    skipped 8 lines
    9 9   - apt-transport-https
    10 10   - gnupg2
    11 11   state: present
     12 + update_cache: yes
    12 13   
    13 14  - name: Add Elasticsearch apt key.
    14 15   apt_key:
    skipped 81 lines
  • ■ ■ ■ ■ ■ ■
    ansible/roles/laps/dc/defaults/main.yml
     1 +---
     2 +move_computer: False
     3 +prep_servers: False
     4 +apply_dacl: False
     5 +create_gpo: False
     6 +gpo_linked: False
     7 +install_servers: False
     8 +test_deployment: False
  • ■ ■ ■ ■ ■ ■
    ansible/roles/laps/dc/files/comment.cmtx
     1 +<?xml version='1.0' encoding='utf-8'?>
     2 +<policyComments xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.0" schemaVersion="1.0" xmlns="http://www.microsoft.com/GroupPolicy/CommentDefinitions">
     3 + <policyNamespaces>
     4 + <using prefix="ns0" namespace="FullArmor.Policies.C9E1D975_EA58_48C3_958E_3BC214D89A2E"></using>
     5 + </policyNamespaces>
     6 + <comments>
     7 + <admTemplate></admTemplate>
     8 + </comments>
     9 + <resources minRequiredRevision="1.0">
     10 + <stringTable></stringTable>
     11 + </resources>
     12 +</policyComments>
  • ■ ■ ■ ■ ■ ■
    ansible/roles/laps/dc/library/win_ad_dacl.ps1
     1 +#!powershell
     2 + 
     3 +# Copyright: (c) 2018, Jordan Borean
     4 +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
     5 + 
     6 +#Requires -Module Ansible.ModuleUtils.Legacy
     7 +#Requires -Module Ansible.ModuleUtils.SID
     8 + 
     9 +$ErrorActionPreference = "Stop"
     10 + 
     11 +$params = Parse-Args -arguments $args -supports_check_mode $true
     12 +$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false
     13 + 
     14 +$path = Get-AnsibleParam -obj $params -name "path" -type "str" -failifempty $true
     15 +$aces = Get-AnsibleParam -obj $params -name "aces" -type "list" -failifempty $true
     16 +$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "absent", "present"
     17 + 
     18 +$result = @{
     19 + changed = $false
     20 +}
     21 + 
     22 +Import-Module -Name ActiveDirectory
     23 + 
     24 +$common_ad_parameters = @{}
     25 + 
     26 +Function ConvertTo-SchemaGuid {
     27 + param(
     28 + [Parameter(Mandatory=$true)]$Value,
     29 + [Parameter(Mandatory=$true)][String]$Name,
     30 + [Parameter(Mandatory=$true)][Int32]$Entry,
     31 + [Hashtable]$CommonParameters
     32 + )
     33 + 
     34 + if ($null -eq $Value.$Name) {
     35 + return [System.Guid]::Empty
     36 + }
     37 + $raw_value = $Value.$Name
     38 + 
     39 + try {
     40 + $guid = [System.Guid]::Parse($raw_value)
     41 + return $guid
     42 + } catch [System.FormatException] {} # not a GUID, we try and convert by scanning AD
     43 + 
     44 + $root_schema = (Get-ADRootDSE @CommonParameters).schemaNamingContext
     45 + $id_object = Get-ADObject -Filter { Name -eq $raw_value } -SearchBase $root_schema -Property schemaIDGUID @CommonParameters
     46 + if ($null -eq $id_object) {
     47 + Fail-Json -obj $result -message "Failed to convert ace entry option $Entry $Name to object guid '$($raw_value)'"
     48 + } else {
     49 + $schema_guid = New-Object -TypeName System.Guid -ArgumentList @(,$id_object.schemaIDGUID)
     50 + return $schema_guid
     51 + }
     52 +}
     53 + 
     54 +# Convert the input ace entries to a format we can compare against the real ACEs
     55 +$valid_access_rights = [System.Enum]::GetNames([System.DirectoryServices.ActiveDirectoryRights])
     56 +$valid_inheritance_types = [System.Enum]::GetNames([System.DirectoryServices.ActiveDirectorySecurityInheritance])
     57 + 
     58 +$raw_aces = [System.Collections.ArrayList]@()
     59 +foreach ($ace in $aces) {
     60 + $identity_sid_str = Convert-ToSID -account_name $ace.account
     61 + 
     62 + if ($null -eq $ace.rights) {
     63 + $msg = "Found undefined ace entry index $($raw_aces.Count) rights. Valid options $($valid_access_rights -join ", ")"
     64 + Fail-Json -obj $result -message $msg
     65 + }
     66 + $ace_rights = $ace.rights
     67 + if ($ace_rights -isnot [Array]) {
     68 + $ace_rights = $ace_rights.ToString().Split(",").Trim()
     69 + }
     70 + 
     71 + foreach ($ace_right in $ace_rights) {
     72 + if ($ace_right -notin $valid_access_rights) {
     73 + $msg = "Invalid value for ace entry index $(raw_aces.Count) rights, '$ace_right'. Valid options $($valid_access_rights -join ", ")"
     74 + Fail-Json -obj $result -message $msg
     75 + }
     76 + }
     77 + $ace_rights = [System.Enum]::Parse([System.DirectoryServices.ActiveDirectoryRights], $ace_rights -join ", ", $true)
     78 + 
     79 + $access = switch($ace.access) {
     80 + "allow" { [System.Security.AccessControl.AccessControlType]::Allow }
     81 + "deny" { [System.Security.AccessControl.AccessControlType]::Deny }
     82 + default { Fail-Json -obj $result -message "Invalid value for ace entry index $($raw_aces.Count) access, '$access'. Valid options allow, deny" }
     83 + }
     84 + 
     85 + if ($ace.inheritance_type -notin $valid_inheritance_types) {
     86 + $msg = "Invalid value for ace entry index $($raw_aces.Count) inheritance_type, '$($ace.inheritance_type)'. Valid options $($valid_inheritance_types -join ", ")"
     87 + Fail-Json -obj $result -message $msg
     88 + }
     89 + $inheritance_type = [System.DirectoryServices.ActiveDirectorySecurityInheritance]$ace.inheritance_type
     90 + 
     91 + $object_type = ConvertTo-SchemaGuid -Value $ace `
     92 + -Name "object_type" -Entry $raw_aces.Length `
     93 + -CommonParameters $common_ad_parameters
     94 + 
     95 + $inherited_object_type = ConvertTo-SchemaGuid -Value $ace `
     96 + -Name "inherited_object_type" -Entry $raw_aces.Count `
     97 + -CommonParameters $common_ad_parameters
     98 + 
     99 + $raw_ace = New-Object -TypeName System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList @(
     100 + (New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList $identity_sid_str),
     101 + $ace_rights,
     102 + $access,
     103 + $object_type,
     104 + $inheritance_type
     105 + $inherited_object_type
     106 + )
     107 + $raw_aces.Add($raw_ace) > $null
     108 +}
     109 + 
     110 +# Now get the actual DACL for the AD object specified
     111 +$ad_object = Get-ADObject -Identity $path -Properties nTSecurityDescriptor @common_ad_parameters
     112 + 
     113 +$actual_sd = $ad_object.nTSecurityDescriptor
     114 +$actual_dacl = $actual_sd.GetAccessRules($true, $false, [System.Security.Principal.SecurityIdentifier])
     115 +$comparison_props = @(
     116 + "ActiveDirectoryRights",
     117 + "InheritanceType",
     118 + "ObjectType",
     119 + "InheritedObjectType",
     120 + "AccessControlType",
     121 + "IdentityReference"
     122 +)
     123 + 
     124 +foreach ($raw_ace in $raw_aces) {
     125 + $found_ace = $null
     126 + 
     127 + foreach ($actual_ace in $actual_dacl) {
     128 + $mismatched = $false
     129 + foreach ($comparison_prop in $comparison_props) {
     130 + if ($actual_ace.$comparison_prop -ne $raw_ace.$comparison_prop) {
     131 + $mismatched = $true
     132 + break
     133 + }
     134 + }
     135 + if (-not $mismatched) {
     136 + $found_ace = $actual_ace
     137 + break
     138 + }
     139 + }
     140 + 
     141 + if ($state -eq "absent" -and $null -ne $found_ace) {
     142 + $ad_object.nTSecurityDescriptor.RemoveAccessRuleSpecific($found_ace)
     143 + $result.changed = $true
     144 + } elseif ($state -eq "present" -and $null -eq $found_ace) {
     145 + $ad_object.nTSecurityDescriptor.AddAccessRule($raw_ace)
     146 + $result.changed = $true
     147 + }
     148 +}
     149 + 
     150 +if ($result.changed -eq $true) {
     151 + $replacements = @{
     152 + nTSecurityDescriptor = $ad_object.nTSecurityDescriptor
     153 + }
     154 + Set-ADObject -Identity $ad_object.ObjectGUID -Replace $replacements -WhatIf:$check_mode @common_ad_parameters
     155 +}
     156 + 
     157 +Exit-Json -obj $result
  • ■ ■ ■ ■ ■ ■
    ansible/roles/laps/dc/library/win_ad_object.ps1
     1 +#!powershell
     2 + 
     3 +# Copyright: (c) 2018, Jordan Borean
     4 +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
     5 + 
     6 +#Requires -Module Ansible.ModuleUtils.Legacy
     7 + 
     8 +$ErrorActionPreference = "Stop"
     9 + 
     10 +$params = Parse-Args -arguments $args -supports_check_mode $true
     11 +$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false
     12 + 
     13 +$attributes = Get-AnsibleParam -obj $params -name "attributes"
     14 +$context = Get-AnsibleParam -obj $params -name "context" -type "str" -default "default" -validateset "configuration", "default", "root_domain", "schema"
     15 +# $domain_server = Get-AnsibleParam -obj $params -name "domain_server" -type "str"
     16 +# $domain_username = Get-AnsibleParam -obj $params -name "domain_username" -type "str"
     17 +# $domain_password = Get-AnsibleParam -obj $params -name "domain_password" -type "str" -failifempty ($null -ne $domain_username)
     18 +$may_contain = Get-AnsibleParam -obj $params -name "may_contain" -type "list"
     19 +$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true
     20 +$type = Get-AnsibleParam -obj $params -name "type" -type "str" -default "attribute" -validateset "attribute", "class"
     21 +$update_schema = Get-AnsibleParam -obj $params -name "update_schema" -type "bool" -default $false
     22 + 
     23 +$result = @{
     24 + changed = $false
     25 +}
     26 + 
     27 +Import-Module -Name ActiveDirectory
     28 + 
     29 +$common_params = @{}
     30 +# if ($null -ne $domain_server) {
     31 +# $common_params.Server = $domain_server
     32 +# }
     33 +# if ($null -ne $domain_username) {
     34 +# $sec_pass = ConvertTo-SecureString -String $domain_password -AsPlainText -Force
     35 +# $common_params.Credential = (New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $domain_username, $sec_pass)
     36 +# }
     37 + 
     38 +$root_dse = Get-ADRootDSE -Properties schemaNamingContext, rootDomainNamingContext @common_params
     39 +$context = switch($context) {
     40 + "configuration" { $root_dse.configurationNamingContext }
     41 + "default" { $root_dse.defaultNamingContext }
     42 + "root_domain" { $root_dse.rootDomainNamingContext }
     43 + "schema" { $root_dse.schemaNamingContext }
     44 +}
     45 + 
     46 +try {
     47 + $search_filter = { DistinguishedName -eq $name -or ldapDisplayName -eq $name }
     48 + $existing_obj = Get-ADObject -SearchBase $context -Filter $search_filter -Properties * @common_params
     49 +} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
     50 + $existing_obj = $null
     51 +}
     52 + 
     53 +if ($null -eq $existing_obj) {
     54 + $ldap_type = switch ($type) {
     55 + "attribute" { "attributeSchema" }
     56 + "class" { "classSchema" }
     57 + }
     58 + New-ADObject -Name $name -Path $context -OtherAttributes $attributes `
     59 + -Type $ldap_type -WhatIf:$check_mode @common_params
     60 + $result.changed = $true
     61 +} elseif ($null -ne $attributes) {
     62 + # Compare the input attributes with the existing attributes and change if required
     63 + $replacements = @{}
     64 + $additions = @{}
     65 + $clear = [System.Collections.Generic.List`1[String]]@()
     66 + 
     67 + foreach ($attribute_entry in $attributes.GetEnumerator()) {
     68 + $attribute_key = $attribute_entry.Key
     69 + $attribute_value = $attribute_entry.Value
     70 + 
     71 + $existing_value = $existing_obj.$attribute_key
     72 + if ($null -eq $attribute_value -and $null -ne $existing_value) {
     73 + $clear.Add($attribute_key) > $null
     74 + } elseif ($null -ne $attribute_value -and $null -eq $existing_value) {
     75 + $additions.$attribute_key = $attribute_value
     76 + } elseif ($attribute_value -ne $existing_value) {
     77 + $replacements.$attribute_key = $attribute_value
     78 + }
     79 + }
     80 + 
     81 + if ($replacements.Count -gt 0 -or $additions.Count -gt 0 -or $clear.Count -gt 0) {
     82 + $set_params = $common_params.Clone()
     83 + if ($replacements.Count -gt 0) {
     84 + $set_params.Replace = $replacements
     85 + }
     86 + if ($additions.Count -gt 0) {
     87 + $set_params.Add = $additions
     88 + }
     89 + if ($clear.Count -gt 0) {
     90 + $set_params.Clear = $clear.ToArray()
     91 + }
     92 + 
     93 + Set-ADObject -Identity $existing_obj.ObjectGuid @set_params
     94 + $result.changed = $true
     95 + }
     96 +}
     97 + 
     98 +# Now set the mayContain attributes, we do a last check on existing_obj in case we are in check mode
     99 +if ($null -ne $may_contain -and $null -ne $existing_obj) {
     100 + foreach ($may_contain_entry in $may_contain) {
     101 + if (-not $existing_obj.mayContain.Contains($may_contain_entry)) {
     102 + Set-ADObject -Identity $existing_obj.ObjectGuid -Add @{ mayContain = $may_contain_entry } @common_params > $null
     103 + $result.changed = $true
     104 + }
     105 + }
     106 +}
     107 + 
     108 +# Reload the schema cache if a change occurred
     109 +if ($result.changed -and $update_schema) {
     110 + $ctor_args = [System.Collections.Generic.List`1[String]]@("LDAP://$($root_dse.dnsHostName)/RootDSE")
     111 + if ($null -ne $domain_username) {
     112 + $ctor_args.Add($domain_username) > $null
     113 + $ctor_args.Add($domain_password) > $null
     114 + }
     115 + $dse = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $ctor_args.ToArray()
     116 + try {
     117 + $dse.Put("SchemaUpdateNow", 1)
     118 + if (-not $check_mode) {
     119 + $dse.SetInfo()
     120 + }
     121 + } finally {
     122 + $dse.Dispose()
     123 + }
     124 +}
     125 + 
     126 +Exit-Json -obj $result
     127 + 
     128 + 
  • ■ ■ ■ ■ ■ ■
    ansible/roles/laps/dc/library/win_gpo.ps1
     1 +#!powershell
     2 + 
     3 +# Copyright: (c) 2018, Jordan Borean
     4 +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
     5 + 
     6 +#Requires -Module Ansible.ModuleUtils.Legacy
     7 + 
     8 +$ErrorActionPreference = "Stop"
     9 + 
     10 +$params = Parse-Args -arguments $args -supports_check_mode $true
     11 +$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false
     12 + 
     13 +$description = Get-AnsibleParam -obj $params -name "description" -type "str"
     14 +$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true
     15 +$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "absent", "present"
     16 + 
     17 +$result = @{
     18 + changed = $false
     19 +}
     20 + 
     21 +$gpo = Get-GPO -Name $name -ErrorAction SilentlyContinue
     22 +if ($state -eq "absent" -and $null -ne $gpo) {
     23 + $result.id = $gpo.Id
     24 + $result.path = $gpo.Path
     25 + $gpo | Remove-GPO -WhatIf:$check_mode
     26 + $result.changed = $true
     27 +} elseif ($state -eq "present") {
     28 + if ($null -eq $gpo) {
     29 + $new_params = @{
     30 + Name = $name
     31 + WhatIf = $check_mode
     32 + }
     33 + if ($null -ne $description) {
     34 + $new_params.Comment = $description
     35 + }
     36 + $gpo = New-GPO @new_params
     37 + $result.changed = $true
     38 + }
     39 + 
     40 + # When creating a GPO in check mode these values won't be set
     41 + if ($null -ne $gpo) {
     42 + $result.id = $gpo.Id
     43 + $result.path = $gpo.Path
     44 + } else {
     45 + $result.id = [System.Guid]::Empty.ToString()
     46 + $result.path = "cn=$($result.id),cn=policies,cn=system,dc=check,dc=domain"
     47 + }
     48 +}
     49 + 
     50 +Exit-Json -obj $result
  • ■ ■ ■ ■ ■ ■
    ansible/roles/laps/dc/library/win_gpo_link.ps1
     1 +#!powershell
     2 + 
     3 +# Copyright: (c) 2018, Jordan Borean
     4 +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
     5 + 
     6 +#Requires -Module Ansible.ModuleUtils.Legacy
     7 + 
     8 +$ErrorActionPreference = "Stop"
     9 + 
     10 +$params = Parse-Args -arguments $args -supports_check_mode $true
     11 +$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false
     12 + 
     13 +$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true
     14 +$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -ValidateSet "absent", "present"
     15 +$enforced = Get-AnsibleParam -obj $params -name "enforced" -type "bool"
     16 +$enabled = Get-AnsibleParam -obj $params -name "enabled" -type "bool"
     17 +$target = Get-AnsibleParam -obj $params -name "target" -type "str"
     18 + 
     19 +if (-not $target) {
     20 + $target = (Get-ADRootDSE).defaultNamingContext
     21 +}
     22 + 
     23 +$result = @{
     24 + changed = $false
     25 +}
     26 + 
     27 +$link = (Get-GPInheritance -Target $target).GpoLinks | Where-Object { $_.DisplayName -eq $name }
     28 +if ($state -eq "present") {
     29 + if (-not $link) {
     30 + $link = New-GPLink -Name $name -Target $target -WhatIf:$check_mode
     31 + $result.changed = $true
     32 + }
     33 + 
     34 + if ($null -ne $enabled -and $link.Enabled -ne $enabled) {
     35 + $enabled_value = if ($enabled) { "Yes" } else { "No" }
     36 + $link = $link | Set-GPLink -LinkEnabled $enabled_value -WhatIf:$check_mode
     37 + $result.changed = $true
     38 + }
     39 + if ($null -ne $enforced -and $link.Enforced -ne $enforced) {
     40 + $enforced_value = if ($enforced) { "Yes" } else { "No" }
     41 + $link = $link | Set-GPLink -Enforced $enforced_value -WhatIf:$check_mode
     42 + $result.changed = $true
     43 + }
     44 +} else {
     45 + if ($link) {
     46 + $link | Remove-GPLink -WhatIf:$check_mode
     47 + $result.changed = $true
     48 + }
     49 +}
     50 + 
     51 +Exit-Json -obj $result
     52 + 
     53 + 
  • ■ ■ ■ ■ ■ ■
    ansible/roles/laps/dc/library/win_gpo_reg.ps1
     1 +#!powershell
     2 + 
     3 +# Copyright: (c) 2018, Jordan Borean
     4 +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
     5 + 
     6 +#Requires -Module Ansible.ModuleUtils.Legacy
     7 + 
     8 +$ErrorActionPreference = "Stop"
     9 + 
     10 +$params = Parse-Args -arguments $args -supports_check_mode $true
     11 +$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false
     12 +$diff = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false
     13 + 
     14 +$gpo = Get-AnsibleParam -obj $params -name "gpo" -type "str" -failifempty $true
     15 +$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true
     16 +$path = Get-AnsibleParam -obj $params -name "path" -type "str" -failifempty $true
     17 +$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "absent", "disabled", "present"
     18 +$type = Get-AnsibleParam -obj $params -name "type" -type "str" -default "string" -ValidateSet "string", "expandstring", "binary", "dword", "multistring", "qword"
     19 +$value = Get-AnsibleParam -obj $params -name "value"
     20 + 
     21 +$result = @{
     22 + changed = $false
     23 +}
     24 +if ($diff) {
     25 + $result.diff = @{
     26 + before = ""
     27 + after = ""
     28 + }
     29 +}
     30 + 
     31 +if ($state -in @("absent", "disabled") -and $null -ne $value) {
     32 + Fail-Json -obj $result -message "Cannot set a value with state=$state"
     33 +}
     34 + 
     35 +Function Convert-RegExportHexStringToByteArray {
     36 + <#
     37 + .SYNOPSIS
     38 + Simplified version of Convert-HexStringToByteArray from
     39 + https://cyber-defense.sans.org/blog/2010/02/11/powershell-byte-array-hex-convert
     40 + Expects a hex in the format you get when you run reg.exe export and
     41 + converts to a byte array so powershell can modify binary registry entries
     42 + 
     43 + .PARAMETER String
     44 + The string to convert in the format hex:be,ef,be,ef,be,ef,be,ef,be,ef
     45 + #>#
     46 + param(
     47 + [Parameter(Mandatory=$true)][String]$String
     48 + )
     49 + # Remove 'hex:' from the front of the string if present
     50 + $String = $String.ToLower() -replace '^hex\:',''
     51 + 
     52 + # Remove whitespace and any other non-hex crud.
     53 + $String = $String -replace '[^a-f0-9\\,x\-\:]',''
     54 + 
     55 + # Turn commas into colons
     56 + $String = $String -replace ',',':'
     57 + 
     58 + # Maybe there's nothing left over to convert...
     59 + if ($String.Length -eq 0) {
     60 + return ,@()
     61 + }
     62 + 
     63 + # Split string with or without colon delimiters.
     64 + if ($String.Length -eq 1) {
     65 + return ,@([System.Convert]::ToByte($String,16))
     66 + } elseif (($String.Length % 2 -eq 0) -and ($String.IndexOf(":") -eq -1)) {
     67 + return ,@($String -split '([a-f0-9]{2})' | ForEach-Object { if ($_) {[System.Convert]::ToByte($_,16)}})
     68 + } elseif ($String.IndexOf(":") -ne -1) {
     69 + return ,@($String -split ':+' | ForEach-Object {[System.Convert]::ToByte($_,16)})
     70 + } else {
     71 + return ,@()
     72 + }
     73 +}
     74 + 
     75 +Function Get-DiffValueString {
     76 + <#
     77 + .SYNOPSIS
     78 + Converts an input value to a string for use in a diff comparison.
     79 + 
     80 + .PARAMETER Type
     81 + The registry value type that is used to display the appropriate string
     82 + output.
     83 + 
     84 + .PARAMETER Value
     85 + The value to stringify.
     86 + #>
     87 + param(
     88 + [Parameter(Mandatory=$true)]
     89 + [ValidateSet("string", "expandstring", "binary", "dword", "multistring", "qword")]
     90 + [String]$Type,
     91 + [Parameter(Mandatory=$true)]$Value
     92 + )
     93 + if ($Type -eq "binary") {
     94 + $hex_values = [System.Collections.ArrayList]@()
     95 + foreach ($dec_value in $Value) {
     96 + $hex_values.Add("0x$("{0:x2}" -f $dec_value)") > $null
     97 + }
     98 + $diff_value = "[$($hex_values -join ", ")]"
     99 + } elseif ($Type -eq "dword") {
     100 + $diff_value = "0x$("{0:x8}" -f $Value)"
     101 + } elseif ($Type -eq "qword") {
     102 + $diff_value = "0x$("{0:x16}" -f $Value)"
     103 + } elseif ($Type -eq "multistring") {
     104 + $diff_value = "[$($Value -join ", ")]"
     105 + } else {
     106 + if ($Value.EndsWith([char]0x0000)) {
     107 + $Value = $Value.Substring(0, $Value.Length - 1)
     108 + }
     109 + 
     110 + $diff_value = $Value
     111 + }
     112 + 
     113 + return $diff_value
     114 +}
     115 + 
     116 +# Convert the input value to the type specified
     117 +if ($type -eq "binary") {
     118 + if ($null -eq $value) {
     119 + $value = ""
     120 + }
     121 + 
     122 + if ($value -is [String]) {
     123 + $value = [byte[]](Convert-RegExportHexStringToByteArray -String $value)
     124 + } elseif ($value -is [Int]) {
     125 + if ($value -gt 255) {
     126 + Fail-Json -obj $result -message "Cannot convert binary value '$value' to byte array, please specify this value as a yaml byte array or comma separated hex value string"
     127 + }
     128 + $value = [byte[]]@([byte]$value)
     129 + } elseif ($value -is [Array]) {
     130 + $value = [byte[]]$value
     131 + }
     132 +} elseif ($type -in @("dword", "qword")) {
     133 + # dword's and dword's don't allow null values, set to 0
     134 + if ($null -eq $value) {
     135 + $value = 0
     136 + }
     137 + 
     138 + if ($value -is [String]) {
     139 + # If the value is a string we need to convert it to an unsigned int64
     140 + # it needs to be unsigned as Ansible passes in an unsigned value while
     141 + # powershell uses a signed value type. The value will then be converted
     142 + # below
     143 + $value = [UInt64]$value
     144 + }
     145 + 
     146 + if ($type -eq "dword") {
     147 + if ($value -gt [UInt32]::MaxValue) {
     148 + Fail-Json -obj $result -message "value cannot be larger than 0xffffffff when type is dword"
     149 + } elseif ($value -gt [Int32]::MaxValue) {
     150 + # When dealing with larger int32 (> 2147483647 or 0x7FFFFFFF) PowerShell
     151 + # automatically converts it to a signed int64. We need to convert this to
     152 + # signed int32 by parsing the hex string value.
     153 + $value = "0x$("{0:x}" -f $value)"
     154 + }
     155 + $value = [Int32]$value
     156 + } else {
     157 + if ($value -gt [UInt64]::MaxValue) {
     158 + Fail-Json -obj $result -message "value cannot be larger than 0xffffffffffffffff when type is qword"
     159 + } elseif ($value -gt [Int64]::MaxValue) {
     160 + $value = "0x$("{0:x}" -f $value)"
     161 + }
     162 + $value = [Int64]$value
     163 + }
     164 +} elseif ($type -in @("string", "expandstring")) {
     165 + # A null string or expandstring must be empty quotes
     166 + if ($null -eq $value) {
     167 + $value = ""
     168 + }
     169 +} elseif ($type -eq "multistring") {
     170 + # Convert the value for a multistring to a String[] array
     171 + if ($null -eq $value) {
     172 + $value = [String[]]@()
     173 + } elseif ($value -isnot [Array]) {
     174 + $new_value = New-Object -TypeName String[] -ArgumentList 1
     175 + $new_value[0] = $value.ToString([CultureInfo]::InvariantCulture)
     176 + $value = $new_value
     177 + } else {
     178 + $new_value = New-Object -TypeName String[] -ArgumentList $value.Count
     179 + foreach ($entry in $value) {
     180 + $new_value[$value.IndexOf($entry)] = $entry.ToString([CultureInfo]::InvariantCulture)
     181 + }
     182 + $value = $new_value
     183 + }
     184 +}
     185 + 
     186 +$existing_value = Get-GPRegistryValue -Name $gpo -Key $path -ValueName $name -ErrorAction SilentlyContinue
     187 +if ($null -ne $existing_value -and $diff) {
     188 + $result.diff.before = @{
     189 + disabled = ($existing_value.PolicyState -eq "Delete")
     190 + gpo = $gpo
     191 + name = $existing_value.ValueName
     192 + path = $existing_value.KeyPath
     193 + type = $existing_value.Type.ToString()
     194 + value = (Get-DiffValueString -Type $existing_value.Type.ToString() -Value $existing_value.Value)
     195 + }
     196 +}
     197 + 
     198 +if ($state -eq "absent") {
     199 + if ($null -ne $existing_value) {
     200 + Remove-GPRegistryValue -Name $gpo -Key $path -ValueName $name -WhatIf:$check_mode > $null
     201 + $result.changed = $true
     202 + }
     203 +} else {
     204 + $common_params = @{
     205 + Name = $gpo
     206 + Key = $path
     207 + ValueName = $name
     208 + WhatIf = $check_mode
     209 + }
     210 + 
     211 + if ($null -eq $existing_value) {
     212 + if ($state -eq "disabled") {
     213 + $common_params.Disable = $true
     214 + } else {
     215 + $common_params.Value = $value
     216 + $common_params.Type = $type
     217 + }
     218 + 
     219 + Set-GPRegistryValue @common_params > $null
     220 + $result.changed = $true
     221 + } elseif ($state -eq "disabled") {
     222 + if ($existing_value.PolicyState -ne "Delete") {
     223 + Set-GPRegistryValue -Disable @common_params > $null
     224 + $result.changed = $true
     225 + }
     226 + } elseif ($existing_value.PolicyState -eq "Delete") {
     227 + # If the previous state was disabled then we need to remove that value
     228 + # before we set the new one as it will cause double ups on the key
     229 + Remove-GPRegistryValue @common_params > $null
     230 + Set-GPRegistryValue -Value $value -Type $type @common_params > $null
     231 + $result.changed = $true
     232 + } else {
     233 + $before_type = $existing_value.Type.ToString()
     234 + $before_value = Get-DiffValueString -Type $before_type -Value $existing_value.Value
     235 + $new_value = Get-DiffValueString -Type $type -Value $value
     236 + 
     237 + if (($before_value -ne $new_value) -or ($before_type -ne $type)) {
     238 + Set-GPRegistryValue -Value $value -Type $type @common_params > $null
     239 + $result.changed = $true
     240 + }
     241 + }
     242 + 
     243 + if ($diff) {
     244 + if ($check_mode) {
     245 + # in check mode we won't have access to the new values so just used the inputs
     246 + $result.diff.after = @{
     247 + disabled = ($state -eq "disabled")
     248 + gpo = $gpo
     249 + name = $name
     250 + path = $path
     251 + type = $type
     252 + value = (Get-DiffValueString -Type $type -Value $value)
     253 + }
     254 + } else {
     255 + $new_value = Get-GPRegistryValue -Name $gpo -Key $path -ValueName $name
     256 + $result.diff.after = @{
     257 + disabled = ($new_value.PolicyState -eq "Delete")
     258 + gpo = $gpo
     259 + name = $new_value.ValueName
     260 + path = $new_value.KeyPath
     261 + type = $new_value.Type.ToString()
     262 + value = (Get-DiffValueString -Type $new_value.Type.ToString() -Value $new_value.Value)
     263 + }
     264 + }
     265 + }
     266 +}
     267 + 
     268 +Exit-Json -obj $result
     269 + 
     270 + 
  • ■ ■ ■ ■ ■ ■
    ansible/roles/laps/dc/tasks/install.yml
     1 +---
     2 +- name: "Create Laps OU if not exist"
     3 + win_dsc:
     4 + resource_name: ADOrganizationalUnit
     5 + name: "{{ laps_path.split(',')[0].split('=')[1] }}"
     6 + path: "{{ ','.join(laps_path.split(',')[1:]) }}"
     7 + 
     8 +- name: Install LAPS Package on Servers
     9 + ansible.windows.win_package:
     10 + arguments: "ADDLOCAL=Management.PS,Management.ADMX ALLUSERS=1 /qn"
     11 + path: https://download.microsoft.com/download/C/7/A/C7AAD914-A8A6-4904-88A1-29E657445D03/LAPS.x64.msi
     12 + state: present
     13 + creates_path: "%ProgramFiles%\\LAPS"
     14 + register: pri_laps_install
     15 + until: pri_laps_install is success
     16 + retries: 3 # Try 3 times just in case it failed to download the URL
     17 + delay: 1
     18 + 
     19 +- name: Reboot After Installing LAPS on Servers
     20 + ansible.windows.win_reboot:
     21 + when: pri_laps_install.reboot_required
     22 + 
     23 +- name: Configure Password Properties
     24 + win_ad_object:
     25 + name: ms-Mcs-AdmPwd
     26 + attributes:
     27 + adminDescription: LAPS Password Attribute
     28 + lDAPDisplayName: ms-Mcs-AdmPwd
     29 + adminDisplayName: ms-Mcs-AdmPwd
     30 + attributeId: 1.2.840.113556.1.8000.2554.50051.45980.28112.18903.35903.6685103.1224907.2.1
     31 + attributeSyntax: '2.5.5.5' # String(IAS)
     32 + omSyntax: 19 # String(Printable)
     33 + isSingleValued: True
     34 + systemOnly: False
     35 + isMemberOfPartialAttributeSet: False
     36 + searchFlags: 904 # RO,NV,CF,PR - http://www.frickelsoft.net/blog/?p=151
     37 + showInAdvancedViewOnly: False
     38 + context: schema
     39 + type: attribute
     40 + update_schema: True
     41 + # privileges required to update the schema attributes
     42 + become: yes
     43 + become_method: runas
     44 + become_user: SYSTEM
     45 + 
     46 +- name: Configure Password Expiry Time
     47 + win_ad_object:
     48 + name: ms-Mcs-AdmPwdExpirationTime
     49 + attributes:
     50 + adminDescription: LAPS Password Expiration Time Attribute
     51 + lDAPDisplayName: ms-Mcs-AdmPwdExpirationTime
     52 + adminDisplayName: ms-Mcs-AdmPwdExpirationTime
     53 + attributeId: 1.2.840.113556.1.8000.2554.50051.45980.28112.18903.35903.6685103.1224907.2.2
     54 + attributeSyntax: '2.5.5.16' # LargeInteger
     55 + omSyntax: 65 # LargeInteger
     56 + isSingleValued: True
     57 + systemOnly: False
     58 + isMemberOfPartialAttributeSet: False
     59 + searchFlags: 0
     60 + showInAdvancedViewOnly: False
     61 + context: schema
     62 + type: attribute
     63 + update_schema: True
     64 + become: yes
     65 + become_method: runas
     66 + become_user: SYSTEM
     67 + 
     68 +- name: Add LAPS attributes to the Computer Attribute
     69 + win_ad_object:
     70 + name: Computer
     71 + may_contain:
     72 + - ms-Mcs-AdmPwd
     73 + - ms-Mcs-AdmPwdExpirationTime
     74 + context: schema
     75 + update_schema: True
     76 + become: yes
     77 + become_method: runas
     78 + become_user: SYSTEM
     79 + 
     80 +- name: "Apply DACL to OU Containers"
     81 + win_ad_dacl:
     82 + path: "{{laps_path}}"
     83 + state: present
     84 + aces:
     85 + - rights:
     86 + - ReadProperty
     87 + - WriteProperty
     88 + inheritance_type: Descendents
     89 + inherited_object_type: Computer
     90 + object_type: ms-Mcs-AdmPwdExpirationTime
     91 + access: allow
     92 + account: S-1-5-10 # NT AUTHORITY\SELF
     93 + - rights: WriteProperty
     94 + inheritance_type: Descendents
     95 + inherited_object_type: Computer
     96 + object_type: ms-Mcs-AdmPwd
     97 + access: allow
     98 + account: S-1-5-10
     99 + 
     100 +- name: Create LAPS GPO
     101 + win_gpo:
     102 + name: '{{ opt_laps_gpo_name }}'
     103 + description: Setup by Ansible for LAPS
     104 + state: present
     105 + register: pri_laps_gpo
     106 + 
     107 +- name: Add LAPS extension to GPO
     108 + win_ad_object:
     109 + name: '{{ pri_laps_gpo.path }}'
     110 + attributes:
     111 + # [Registry:Admin Tool][AdmPwd:Admin Tool]
     112 + gPCMachineExtensionNames: "[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F72-3407-48AE-BA88-E8213C6761F1}]\
     113 + [{D76B9641-3288-4F75-942D-087DE603E3EA}{D02B1F72-3407-48AE-BA88-E8213C6761F1}]"
     114 + 
     115 +- name: Configure Password Policy Settings on GPO
     116 + win_gpo_reg:
     117 + gpo: '{{ opt_laps_gpo_name }}'
     118 + name: '{{ item.name }}'
     119 + path: 'HKLM\Software\Policies\Microsoft Services\AdmPwd'
     120 + state: present
     121 + type: dword
     122 + value: '{{ item.value }}'
     123 + with_items:
     124 + - name: PasswordComplexity
     125 + value: 4
     126 + - name: PasswordLength
     127 + value: 14
     128 + - name: PasswordAgeDays
     129 + value: 30
     130 + 
     131 +- name: Configure Expiration Protection on GPO
     132 + win_gpo_reg:
     133 + gpo: '{{ opt_laps_gpo_name }}'
     134 + name: PwdExpirationProtectionEnabled
     135 + path: 'HKLM\Software\Policies\Microsoft Services\AdmPwd'
     136 + state: present
     137 + type: dword
     138 + value: 1
     139 + 
     140 +- name: Remove Configuration for Expiration Protection on GPO
     141 + win_gpo_reg:
     142 + gpo: '{{ opt_laps_gpo_name }}'
     143 + name: PwdExpirationProtectionEnabled
     144 + path: 'HKLM\Software\Policies\Microsoft Services\AdmPwd'
     145 + state: absent
     146 + 
     147 +- name: Configure Custom Admin Username Policy on GPO
     148 + win_gpo_reg:
     149 + gpo: '{{ opt_laps_gpo_name }}'
     150 + name: AdminAccountName
     151 + path: 'HKLM\Software\Policies\Microsoft Services\AdmPwd'
     152 + state: present
     153 + type: string
     154 + 
     155 +- name: Enable the GPO
     156 + win_gpo_reg:
     157 + gpo: '{{ opt_laps_gpo_name }}'
     158 + name: AdmPwdEnabled
     159 + path: 'HKLM\Software\Policies\Microsoft Services\AdmPwd'
     160 + state: present
     161 + type: dword
     162 + value: 1
     163 + 
     164 +- name: Create Comment File for GPO
     165 + ansible.windows.win_copy:
     166 + src: ../files/comment.cmtx
     167 + dest: C:\Windows\SYSVOL\domain\Policies\{{ '{' }}{{ pri_laps_gpo.id }}{{ '}' }}\Machine\comment.cmtx
     168 + 
     169 +- name: Ensure GPO is Linked
     170 + win_gpo_link:
     171 + name: '{{ opt_laps_gpo_name }}'
     172 + target: '{{laps_path}}'
     173 + state: present
     174 + enforced: True
     175 + enabled: True
  • ■ ■ ■ ■ ■ ■
    ansible/roles/laps/dc/tasks/main.yml
     1 +- name: laps dc install
     2 + import_tasks: install.yml
     3 + when: laps_path is defined
     4 + 
     5 +- name: move to laps ou
     6 + import_tasks: move_server_to_ou.yml
     7 + when: laps_path is defined
     8 + 
  • ■ ■ ■ ■ ■ ■
    ansible/roles/laps/dc/tasks/move_server_to_ou.yml
     1 +- name: Move server to Laps OU
     2 + win_shell: |
     3 + try {
     4 + Get-ADOrganizationalUnit -Identity "{{laps_path}}" > $null
     5 + $server=Get-AdComputer -Identity "{{hostname}}"
     6 + Move-ADObject -Identity $server.DistinguishedName -TargetPath "{{laps_path}}"
     7 + $true
     8 + } catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
     9 + $false
     10 + }
     11 + vars:
     12 + hostname: "{{item.value.hostname}}"
     13 + when: item.value.use_laps is defined and item.value.use_laps == true and item.value.domain == domain
     14 + with_dict: "{{hosts_dict}}"
  • ■ ■ ■ ■ ■ ■
    ansible/roles/laps/dc/vars/main.yml
     1 +---
     2 +# converts opt_laps_password_policy_complexity to the value expected by GPO
     3 +pri_laps_password_policy_complexity:
     4 + uppercase: 1
     5 + uppercase,lowercase: 2
     6 + uppercase,lowercase,digits: 3
     7 + uppercase,lowercase,digits,symbols: 4
     8 + 
     9 +# GPO variables
     10 +opt_laps_gpo_name: ansible-laps
     11 +opt_laps_password_policy_complexity: uppercase,lowercase,digits,symbols
     12 +opt_laps_password_policy_length: 14
     13 +opt_laps_password_policy_age: 30
  • ■ ■ ■ ■ ■
    ansible/roles/laps/permissions/tasks/main.yml
     1 +- name: Add user or group permission to read Laps
     2 + win_shell: |
     3 + Set-AdmPwdReadPasswordPermission -OrgUnit "{{laps_path}}" -AllowedPrincipals "{{item}}"
     4 + with_items: "{{laps_readers}}"
     5 + when: laps_readers is defined
  • ■ ■ ■ ■ ■ ■
    ansible/roles/laps/server/tasks/install.yml
     1 +---
     2 +- name: Install to Servers
     3 + ansible.windows.win_package:
     4 + arguments: "ADDLOCAL=CSE"
     5 + path: https://download.microsoft.com/download/C/7/A/C7AAD914-A8A6-4904-88A1-29E657445D03/LAPS.x64.msi
     6 + state: present
     7 + creates_path: "%ProgramFiles%\\LAPS"
     8 + register: pri_laps_install
     9 + until: pri_laps_install is success
     10 + retries: 3 # Try 3 times just in case it failed to download the URL
     11 + delay: 1
     12 + 
     13 +- name: reboot after installing LAPS if required
     14 + ansible.windows.win_reboot:
     15 + when: pri_laps_install.reboot_required
     16 + 
     17 +- name: Refresh GPO on the Clients
     18 + ansible.windows.win_command: gpupdate /force
     19 + 
  • ■ ■ ■ ■ ■ ■
    ansible/roles/laps/server/tasks/main.yml
     1 +- name: laps server install
     2 + import_tasks: install.yml
     3 + when: laps_path is defined and use_laps == true
  • ■ ■ ■ ■ ■ ■
    ansible/roles/laps/verify/tasks/main.yml
     1 +- name: Retrieve LAPS Password on server
     2 + win_shell: |
     3 + $server=Get-AdComputer -Identity "{{hostname}}"
     4 + $obj = Get-ADObject -Identity $server.DistinguishedName -Properties ms-Mcs-AdmPwd
     5 + Write-Output "{{hostname}}" $obj."ms-Mcs-AdmPwd"
     6 + register: powershell_password
     7 + vars:
     8 + hostname: "{{item.value.hostname}}"
     9 + when: item.value.use_laps is defined and item.value.use_laps == true and item.value.domain == domain
     10 + with_dict: "{{hosts_dict}}"
     11 + 
     12 +- name: Show new laps password
     13 + debug: msg="{{item.stdout_lines}}"
     14 + when: item.stdout_lines is defined
     15 + with_items: "{{powershell_password.results}}"
     16 + 
  • ■ ■ ■ ■
    ansible/security.yml
    skipped 5 lines
    6 6   tags: 'data'
    7 7   
    8 8  - name: "Setup enable defender"
    9  - hosts: dc01,dc02,dc03,srv01,srv03
     9 + hosts: dc01,dc02,dc03,srv03
    10 10   roles:
    11 11   - { role: 'settings/windows_defender', tags: 'windows_defender', windows_defender_status: 'on' }
    12 12   vars:
    skipped 8 lines
  • ■ ■ ■ ■ ■ ■
    ansible/vulnerabilities.yml
    skipped 11 lines
    12 12   vars:
    13 13   script_path: "../ad/{{domain_name}}/scripts"
    14 14   
    15  -- name: "Setup vulnerabilities srv01"
    16  - hosts: srv01
    17  - roles:
    18  - vars:
    19  - script_path: "../ad/{{domain_name}}/scripts"
     15 +# - name: "Setup vulnerabilities srv01"
     16 +# hosts: srv01
     17 +# roles:
     18 +# vars:
     19 +# script_path: "../ad/{{domain_name}}/scripts"
    20 20   
    21 21  # north.sevenkingdoms.local
    22 22  - name: "Setup vulnerabilities dc02"
    skipped 36 lines
Please wait...
Page is in error, reload to recover