Ansible: syncing Linux to AD time

Requirements: the PDC address is looked up from Active Directory DNS, so the machine you’re running the playbook against must be able to resolve names from the domain namespace. Assumes your timezone is set correctly.

ansible/setup-domaintime-linux/setup-domaintime-linux.yaml :

- hosts: all
  user: localuser
  become: true

      - chrony
      - bind-utils

    - name: domain_fqdn
      prompt: "Enter domain FQDN to search for PDC (example: mydomain.local)"
      private: no


  - name: Checking if running RedHat/CentOS
      msg: The system is not running RedHat/CentOS, aborting
    when: ansible_facts['os_family'] != 'RedHat'

  - name: Ensuring ntpdate is absent
      name: ntpdate
      state: absent

  - name: Ensuring chrony and bind-utils are present
    yum: name={{ pkgs }} state=present update_cache=yes

  - name: Masking ntpd service
      name: ntpd
      enabled: no
      masked: yes
      state: stopped

  - name: Looking up PDC in AD DNS
    shell: nslookup -q=SRV _ldap._tcp.pdc._msdcs.{{ domain_fqdn }} | grep _ldap | awk '{print $7}' | head --bytes -2
    register: PDClookup
  - set_fact:
      PDCfqdn : "{{ PDClookup.stdout }}"

  - name: Configuring chrony.conf
      src: chrony.conf.j2
      dest: /etc/chrony.conf
      owner: root
      group: root
      mode: 0644

  - name: Enabling chronyd service
      name: chronyd
      enabled: yes
      state: restarted

ansible/setup-domaintime-linux/templates/chrony.conf.j2 :

server {{ PDCfqdn }} iburst

driftfile /var/lib/chrony/drift
makestep 0.1 3

logdir /var/log/chrony
log measurements statistics tracking

leapsectz right/UTC

ansible/setup-domaintime-linux/ansible.cfg :

inventory           = hosts
host_key_checking   = False
become_method       = sudo

ansible/setup-domaintime-linux/hosts :

virtualhostname ansible_host= ansible_ssh_user=localuser

Auditing File Share access

Say you’ve been tasked with cleaning up an old and poorly-maintained file server and need to figure out which network shares see lots of use and which ones little to none. While Windows provides totally adequate facilities for creating logs, actually parsing said logs without paying for an expensive 3rd-party solution can be frustrating.

Here’s a quick Powershell snippet that should give you the information you most likely want:

$FilterHashTable = @{
    LogName   = 'Security'
    ID        = 5140
    StartTime = (Get-Date).AddHours(-24)

Get-WinEvent -FilterHashtable $FilterHashTable |
    Select-Object -Property @{Name='Time';Expression={$_.TimeCreated}}, `
                            @{Name='Share';Expression={$_.Properties[7].Value}}, `
                            @{Name='SourceIP';Expression={$_.Properties[5].Value}}, `
                            @{Name='User';Expression={$_.Properties[1].Value}} |
Format-Table -AutoSize

If nothing shows up in the results:
* Do you have the permissions required to read the Security log of the server being audited?
* Does the Security log contain 5140 events? (You still need to actually enable auditing for them to show up)

Figuring out the last time a user logged into an AD domain

When tasked with figuring out the last time a user logged on into an Active Directory domain, some people might be awfully tempted to just look at lastLogonTimestamp attribute of a user’s AD account and then call it a day. This approach has a multitude of gotchas:

  • lastLogonTimestamp does get synced across DCs, but only if the entry is more than 14 days old
  • It can get updated even when the user has NOT logged on (Kerberos S4U)
  • It can fail to get updated even when the user HAS logged in (seen with some VPN solutions)
  • Probably something else I haven’t yet run into

So the appropriate solution that is less prone to error is to look at the lastLogon value for the account and you have to look it up on all DCs active in the domain. Since individually querying each and every one can and does get tedious, here’s a Powershell one-liner:

[datetime]::FromFileTime((Get-ADDomainController -Filter * | foreach {Get-ADUser USERNAME -Properties LastLogon -Server $_.Name | select LastLogon} | Measure-Object -Property LastLogon -Maximum).Maximum)

The basics of dynamic AD security group management

Consider a situation where you’re tasked with maintaining dynamic Active Directory group membership based on the user account Office-attribute:

$users = Get-ADUser -Filter {Office -like "New York"}
foreach($user in $users)
Add-ADGroupMember -Identity GroupName -Members $user.samaccountname -ErrorAction SilentlyContinue

$members = Get-ADGroupMember -Identity GroupName
foreach($member in $members)
if ((Get-ADUser -identity $member -properties Office|Select-Object Office).office -notlike "New York")
Remove-ADGroupMember -Identity GroupName -Member $member.samaccountname -Confirm:$false

Now set this to run once per day via Task Scheduler and you’re set. Make sure to check the service account used to run the scheduled task has permissions to modify the group and user objects involved.

Installing Server 2019 Core on Proxmox

Notes on initial VM configuration

During the initial creation of the VM, make sure your hard drive Bus/Device is set to SCSI and that your SCSI Controller Type is set to either “VirtIO SCSI” or “VirtIO SCSI single“. While IDE and SATA bus options being slow might be somewhat obvious to some, you will find a lot of guides recommending VirtIO Block and that’s a trap I fell into when first getting started.

Alas, while it’s raw performance is great, is does not support the discard=on option (make sure the discard checkbox is ticked), an option that makes your life A LOT easier by automatically reclaiming unused space from the Proxmox host as you delete data inside the guest OS from a thin-provisioned virtual disk.

It’s a bit counter-intuitive, but the SCSI single option uses 1 controller per virtual disk while regular SCSI uses 1 controller for all virtual disks. While the performance difference might be negligible for most users, the main difference between the two is that SCSI single allows the use of iothread and multiqueue features, which may be of use for some workloads.

Additionally, make sure that Qemu Agent is set to Enabled in VM options and that your have the latest VirtIO Windows driver .ISO image downloaded to the Proxmox host and mounted as a CD-ROM drive inside the Windows guest VM. While it may sound very tempting to go with the stable VirtIO driver .ISO image based on the name alone, I highly recommend going with latest image instead as the stable release severely lags behind and you are almost guaranteed to run into issues trying to use it with the very latest Windows OS versions.

Initial graphical installation

As you start the installation process, you will inevitably encounter a point where the installer cannot find your virtual hard drive. This is due to Windows not including the drivers for VirtIO SCSI storage, so this is where you press the Load Driver button and browse to the appropriate folder on your VirtIO driver image (for example D:\vioscsi\2k19\ ) and load your storage driver. The Windows installer should now recognize your virtual disk and let you partition it.

If you chose to use VirtIO as your network card during the initial VM configuration instead of the default Intel E1000 option, you should be able to load the appropriate NetKVM driver at this point in a similar fashion.

Setting up drivers and the guest agent post-install:

So you’ve completed the install, rebooted, logged in and are staring at the command-line, now what? Launch powershell.exe and get a list of available drives with:


Create a local driver directory and copy drivers from the .ISO to local storage:

mkdir c:\drivers 
Copy-Item D:\vioscsi\2k19\ -Destination C:\drivers\vioscsi\2k19 -Recurse
Copy-Item D:\NetKVM\2k19\ -Destination C:\drivers\NetKVM\2k19 -Recurse
Copy-Item D:\Balloon\2k19\ -Destination C:\drivers\Balloon\2k19 -Recurse
Copy-Item D:\vioserial\2k19\ -Destination C:\drivers\vioserial\2k19 -Recurse
Copy-Item D:\guest-agent\ -Destination C:\drivers\guest-agent -Recurse

And then install them:

 pnputil -i -a C:\Drivers\NetKVM\2k19\amd64*.inf
 pnputil -i -a C:\Drivers\Balloon\2k19\amd64*.inf
 pnputil -i -a C:\Drivers\vioserial\2k19\amd64*.inf

Set up the guest agent:

 Set-Location C:\drivers\guest-agent

Even if you don’t intend to actually use the balloon feature, you should still install the service as it’s required for the guest to properly report RAM use to the Proxmox host:

Copy-Item C:\drivers\Balloon\2k19\amd64 -Destination 'C:\Program Files\Balloon' -Recurse
Set-Location 'C:\Program Files\Balloon'
blnsvr.exe -i

At this point you should be more or less done with the basics and you will probably want to configure your network settings, set up PSRemoting and perhaps do other things that are beyond the scope of this little guide.

A few gotchas:

“Help! My entire Proxmox host is crashing and rebooting during heavy writes!”

Odds are you are using ZFS on the host and during abnormally high write io (such as running a benchmarking utility inside a guest) are running out of RAM on the host, causing a panic. This is normally not a cause for concern as synthetic write benchmarks rarely approximate real use conditions. If you are still concerned or are running into issues in an actual real world scenario, consider adding more RAM, tuning your cache or adding some swap.

“Reboot / Shutdown refuses to work on Windows guests in the Proxmox GUI, WTF?”

You forgot to enable Qemu Agent in the VM options and/or didn’t install guest-agent.