Trigger a remote GPUpdate without PSRemoting or PSExec

I recently enabled Windows Firewall on an unused server via GPO, but forgot to include the inbound RDP exception. This, of course, kicked me off my RDP session.

Rather than wait ~90 minutes for my revised GPO to take effect, I found that I could trigger a GPUpdate remotely using WMI (WinRM wasn’t enabled, and I didn’t want to use PSExec)

The following command does the trick:

Invoke-WmiMethod -Class Win32_Process -Name Create -ArgumentList "gpupdate.exe" -ComputerName <computername>

View the creation date for AD-integrated DNS records

6 months in to my new job, and I’ve still got a big mess of old static DNS records to clean up from our Active Directory-integrated DNS.

The DNS management console doesn’t show any sort of date information, but I knew that because the data is stored in AD, there should be some sort of created/modified date on each record.

I had a look using ADSIEdit, and sure enough, there were the dates! Here’s a quick one-liner to pull out the records and their created/modified dates:

Get-ChildItem "AD:DC=contoso.com,CN=MicrosoftDNS,CN=System,DC=contoso,DC=com" | Get-ADObject -Properties Created,Modified | Select-Object Name,Created,Modified | Sort-Object -Property Created

Armed with the creation date of each record, I’m in a better position to determine which ones are no longer needed.

PowerShell: Get all unlinked GPOs

I decided to spend some of the quiet time now at the beginning of the new year to clean up Group Policy here at my new job. We had roughly the same number of GPOs as staff!

I’d already removed at least 10-20 GPOs manually, but I wanted a quick way to find all of the un-linked GPOs that were still just sitting around.

I found this great post by Mark Schill from back in 2013 that still does the job.

Mark’s solution, however, pulls out only the display name property. I needed to modify his solution a little, as I wanted to pipe the results to the Remove-GPO cmdlet. Here’s what I did:

Get-GPO -All | Where-Object { $_ | Get-GPOReport -ReportType XML | Select-String -NotMatch "<LinksTo>" }

The above one-liner gets all unlinked GPOs, and returns Microsoft.GroupPolicy.Gpo objects.

It goes without saying that you should be very careful when bulk-deleting anything. I first backed up all of my GPOs and dumped the list into text format to review it. I manually checked the settings on a few of the GPOs in question, and only then, deleted them.

Here’s how I generated the list to review:

$unlinkedGPOs = Get-GPO -All | Where-Object { $_ | Get-GPOReport -ReportType XML | Select-String -NotMatch "<LinksTo>" }
$unlinkedGPOs | Sort-Object -Property DisplayName | Select-Object DisplayName,CreationTime,ModificationTime | Format-Table

Correct Horse Battery Staple for PowerShell, AKA Random Memorable Password Generator

I’m a fan of using correcthorsebatterystaple.net for generating temporary passwords for users, rather than always using a static password. The site itself is a reference to the XKCD webcomic, and yes, I’m aware that there are plenty of opinions on the web about this topic.

password_strength

I’ve had the idea in the back of my mind for a while to see if I could replicate the site’s functionality in PowerShell. I noticed that the source code for the site is on GitHub, so I ducked over there to check out the word list.

I found that it’s possible to replicate most of the functionality of the site with just two lines of PowerShell (although it doesn’t result in very readable code):

  1. I used Invoke-WebRequest to grab the word list from GitHub
  2. I then expanded out the Content property, and split it up given the comma delimiter
  3. I then used a combination of the Range operator, Foreach-Object, [string]::join,Get-Random and the TextInfo class to generate a given number of passwords along these rules:
    1. 4 random words, each with the first letter capitalised
    2. A separator in between
    3. A random number between 1 and 99 at the end

Note that this isn’t failure-proof, and isn’t intended to be used in any complex scenario. There’s no error handling, and not much flexibility built in. It’s just a quick function you could put into your PowerShell profile.

You can, at least, do the following:
A PowerShell window displaying the output of Get-RandomPassword. Also shows the function being called with a Count parameter as well as a separator parameter

Here’s the code:

ADFS 3.0 Error: The Web request failed because the web.config file is malformed

Had a strange one today after an Azure outage. One of my Server 2012 R2 ADFS proxies wouldn’t start the ADFS service.

When looking in the logs, it appeared like a case of simply having to re-establish the proxy trust, but I got a different error when trying to start the service:

The federation server proxy could not be started.
Reason: Error retrieving proxy configuration from the Federation Service.

Additional Data
Exception details:
An error occurred when attempting to load the proxy configuration.

There were other errors in the ADFS Event logs about a malformed config file:

The Web request failed because the web.config file is malformed.

User Action:
Fix the malformed data in the web.config file.

Exception details:
Root element is missing. (C:\Windows\ADFS\Config\microsoft.identityServer.proxyservice.exe.config)
Root element is missing.

When I opened the abovementioned config file, it was empty. I compared this to the config file on the other ADFS proxy, and that one looked like a normal config file.

My solution, and what ended up fixing the issue in the end, was simply to copy the contents of the .config file from the working ADFS proxy to the broken one. I could then re-establish the proxy trust, and everything started running again.

I’m not sure if this would work, but in case you don’t have another ADFS proxy to grab the config file from, here’s a sanitised version of mine:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="microsoft.identityServer.proxyservice" type="Microsoft.IdentityServer.Management.Proxy.Configuration.ProxyConfiguration, Microsoft.IdentityServer.Management.Proxy, Version=6.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
</configSections>
<microsoft.identityServer.proxyservice>
<congestionControl latencyThresholdInMSec="8000" minCongestionWindowSize="64"
enabled="true" />
<connectionPool connectionPoolSize="200" scavengeInterval="5" />
<diagnostics eventLogLevel="15" />
<host tlsClientPort="49443" httpPort="80" httpsPort="443" name="adfs.example.com" />
<proxy address="" />
<trust thumbprint="FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
proxyTrustRenewPeriod="21600" />
</microsoft.identityServer.proxyservice>
<!-- <system.serviceModel>
<diagnostics>
<messageLogging logEntireMessage="true"
logMessagesAtServiceLevel="true"
logMessagesAtTransportLevel="true">
</messageLogging>
</diagnostics>
</system.serviceModel> -->
</configuration>

Once I’d resolved the problem, I did a bit of searching around for this error message, and it appears that other people have had the same problem previously, with no resolution listed in the one thread that I looked at on the TechNet forums.

Script to create new local admin account for use with LAPS

I’ve got a bunch of older SOE machines that still had the local Administrator account enabled. As part of implementing Microsoft LAPS, I wanted to disable that account, and use a newly-created ‘LocalAdmin’ account with LAPS.

The account is created with a randomly-generated GUID as the password. The account’s password is going to come under the management of LAPS anyway. Additionally, it would be a terrible idea to hard-code a password into a script that’s stored in Sysvol.

If an account with that name already exists, the script will quit. Some basic events are also logged to the Event Log to indicate what happened.

My first revision of the script used ADSI to create the account and add it to the Administrators group, but I found that my mileage varied with that method. Some computers had the account created, but it wasn’t a member of Administrators.

It’s now set up to use plain “NET USER” and “NET LOCALGROUP” commands. This is an example of what would be executed:
2016-04-04 16_01_06

This script is designed to be set up as a computer Startup Script:

Configure Desired State Configuration (DSC) on CentOS 7

Here’s a quick guide on how to set up DSC on CentOS 7. This requires OMI and DSC for Linux. It’s a bit of a pain to track down the downloads for these, and the OMI one doesn’t play ball when using wget, so I’ve put them on Dropbox for ease of use:

I always use the Minimal installation of CentOS. I started with CentOS-7-x86_64-Minimal-1511.iso

  1. Install CentOS, configure an IP address
  2. Connect in via SSH, or the console, run the following commands:
    1. yum install wget -y
    2. cd /home
    3. wget http://bit.ly/omi1084
    4. wget http://bit.ly/dsc_linux_111
    5. tar -zxvf omi1084
    6. tar -zxvf dsc_linux_111
    7. rpm -ivh omi-1.0.8.ssl_100.x64.rpm
    8. systemctl start omid.service
    9. rpm -ivh dsc-1.1.1-70.ssl_100.x64.rpm

To test connectivity from a Windows machine, do the following:

$session = New-CimSession -Credential (Get-Credential) -Authentication Basic -ComputerName <name or IP here> -SessionOption (New-CimSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck -UseSsl)
Get-CimInstance -CimSession $session -ClassName OMI_Identify -Namespace root/omi

You’ll be prompted to enter credentials after that first line. Then, after running Get-CimInstance you should see an output as per below:

2016-02-28 12_36_47

The CentOS machine is now ready to receive configs using DSC. I’ll be blogging more related stuff soon, but there’s a great write-up on the PowerShell Documentation pages that goes into more detail:

Get started with Desired State Configuration (DSC) for Linux