Powershell – Open File Dialog

Powershell – Open File Dialog

I was building a script for my manager the other day and inside the script, he wanted us to triple check the naming of a file path. He went as far as to have us copy the file path from the browser. Paste it, Then click rename on the file itself and copy that and paste it. It was extremely time-consuming and micromanagement. I finally got tired of it as it was about 5 file paths. Using the 80/20 rule, I made a function for this within the script that opened the file dialog box and allowed the user to select the file and have it programmatically push the names into the Variable.

The Script

function Get-SHDOpenFileDialog {
    [cmdletbinding()]
    param (
        [string]$InitialDirectory = "$Env:USERPROFILE",
        [string]$Title = "Please Select A file",
        [string]$Filter = "All files (*.*)| *.*",
        [switch]$MultiSelect
    )
    Add-Type -AssemblyName System.Windows.Forms
    $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog 
    $FileBrowser.InitialDirectory = "$InitialDirectory"
    $FileBrowser.Filter = "$Filter"
    $FileBrowser.Title = "$Title"
    if ($MultiSelect) {
        $FileBrowser.Multiselect = $true
    } else {
        $FileBrowser.Multiselect = $false
    }

    $FileBrowser.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost = $true })) | Out-Null
    $FileBrowser.Filenames
    $FileBrowser.dispose()
}

The Breakdown

Let us break down this script and explain it a little better. Our parameters are as the following:

  • InitialDirectory [string]- This is the directory we will open the Open Dialog in. By default we use the user’s profile.
  • Title [string]- This will be the title on the Open Dialog box. We have this because when you execute the function mutiple times in a single script, this can give some direction.
  • Filter [string]- This is the filter that you will sort by. We have it defaulting to all files with all extensions. If you want to guide the user even more, you can change the filter to something like “XML Files (*.xml)| *.xml” for xml files.
  • Multiselect [switch]- This determines if you want to limit the system to a single file select, which is default or allow mulitple select. This way if you want to select 5 files at once, you can. This is more for the advanced user. If you want to guide the user, then don’t flip this switch.

Now we have our parameters, it’s time to build our assembly. We do this by adding the add-type command. We are loading the Forms assembly.

Add-Type -AssemblyName System.Windows.Forms

Now we have loaded the assembly, we need to create the dialog box. To do this we will create a new-object. System > Windows > Forms > OpenFileDialog. From there we build out the properties.

$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog 

Now the object is created, we need to add the properties. The first three properties we are filling are from our parameters. The InitialDirectory, Filter, and Title.

$FileBrowser.InitialDirectory = $InitialDirectory
$FileBrowser.Filter = $Filter
$FileBrowser.Title = "$Title"

Then we ask if the MultiSelect flag is checked. If it is we set the property to true, if not, we set it to false.

if ($MultiSelect) {
    $FileBrowser.Multiselect = $true
} else {
    $FileBrowser.Multiselect = $false
}

Next, we need to show the dialog box. One thing I ran into was the dialog box would open behind my VS code. In VB.net we set forms to TopMost. I tried a few variations of this with the open file dialog box, but this dialog box doesn’t have the TopMost property. So, what we do is open a form inside the form to force the form to the front. The System > Windows > Forms > Form has the TopMost property. So, inside the showdialog method, we plop the new-object into it.

$FileBrowser.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost = $true }))

However, there is catch! This command outputs the button you click. So if you click cancel, you get a cancel as output, if you click a file and click ok, you get an OK and the file name as the next part of the array it produces. The way to resolve this is pipping the command into an out-null.

$FileBrowser.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost = $true })) | Out-Null

Now we have selected the file/files, we need to display them. The data is stored in the filenames property. So we use the $FileBrowser.Filenames to display that information.

$FileBrowser.Filenames

Finally, we need to dispose of the open file dialog. This way we don’t have hundreds of invisible forms clogging up our system.

$FileBrowser.dispose()

I hope that makes since. Here are some Examples

Examples

PS C:\> Get-SHDOpenFileDialog -Title "Select the required files" -MultiSelect -InitialDirectory c:\temp  

The Output:

C:\temp\tempshow.csv
C:\temp\Moo.txt

Example with Filter

PS C:\> Get-SHDOpenFileDialog -Title "Select the CSV file" -MultiSelect -InitialDirectory c:\temp -Filter "CSV Files (*.csv)| *.csv"

The Output:

C:\temp\tempshow.csv
C:\temp\ACC_Active.csv

Fact Prank with Powershell

Fact Prank with Powershell

I love a good prank. Sometimes pranks need to challenge us. I coded a good prank and it requires a scheduled task and a little bit of PowerShell. The idea behind the prank is the computer will alert you with a beep, then read a random fact to you. Then it closes off with a set of beeps. It looks at this over and over and over again. We will call this script facts.ps1.

Facts.ps1

$random = Get-Random -minimum 20 -maximum 60
Start-Sleep -s $random
[system.console]::beep(600, 2000)
Add-Type -AssemblyName System.Speech
$voice = New-Object System.Speech.Synthesis.SpeechSynthesizer
$Facts = (Invoke-WebRequest -Uri https://therandomadmin.com/nextcloud/index.php/s/9rmSoM2ppY5ggia/download).tostring() -split "[`n]"
$random = Get-Random $Facts
$voice.Speak($random)

Let’s break it down so you can appreciate the greatness of this command. The first thing we do is get a random number between 20 and 60 seconds. We do this so the anti-virus doesn’t attack it from starting by the scheduled task time. Then we start sleeping for that amount of time. Once we are done sleeping, we sound the alarm. The [System.Console]::beep(Pitch,Length) does this part.

Next, we import the speech assembly to read off the Fact. That is done by the Add-Type command. Then we set up the voice by using the new-object command and create the System > Speech > Synthesis > Speech Synthesizer. I remember back in high school this was amazing new technology. I wish it would grow.

Now we get the fact sheet from the web. You can download this file and host it wherever you want. I stole it from various sites myself. We grab it with the invoke-webrequest command. Then we convert it to a string. We split said string up by each new string. Now we have the information, we grab a random item by using the get-random command. Then, we dump that into the $voice we made a while ago. It speaks it out to the user. Finally, 3 final beeps to let the user know it’s done and over.

Now we need to take the next steps:

  • Get the script on the target computer
  • Setup a scheduled task to have it run.

To get it on the other persons computer you can put it into a next cloud and download it from there. Use the invoke-webrequest and download the file to the c:\temp\facts.ps1. Then we create two variables, taskname, and facts. The task name is going to be called “Facts” and the Facts needs to be wherever you downloaded the file.

Invoke-WebRequest -Uri "https://rndadhdman.com/index.php/s/mTA6YaDZ7YXAo9E/download" -OutFile c:\temp\Facts.ps1
$taskName = "Facts"
$Facts = "C:\temp\Facts.ps1"

Then we are going to set up action for our scheduled task with the New-ScheduledTaskAction commandlet. The execute will be powershell.exe. The arguments will be execution policy bypass, Noprofile, nologo, noninteractive, windows style will be hidden, and the file is of course the $facts.

$Action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-executionpolicy bypass -noprofile -nologo -noninteractive -WindowStyle Hidden -file $Facts -Force"

Next we create the trigger with the New-ScheduledTaskTrigger commandlet. This is where the fun can be exciting. We can do this one, daily, minutely, oh yeah. We only want to do this once, so we will select once in this case, but we want it to repeat every minute or so. We do that with the new-timespan commandlet.

$taskTrigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(10) -RepetitionInterval (New-TimeSpan -Minutes 1)

Finally, we put it together with the register-scheduledtask commandlet. The task name will be the $taskname, the action will be $action, and the trigger will be $tasktrigger.

Register-ScheduledTask -TaskName $taskName -Action $Action -Trigger $taskTrigger -Force

For the rest of the day, their computer will read off odd strange facts.

I hope you enjoyed it.

Batch – Chrome and Self-built URLs

Batch – Chrome and Self-built URLs

A previous job of mine had a unique issue. Unstandardized google chromes and self-generated icons. Both of these issues caused a lot of heartaches. Today I will show you how I solved the issue of unstandardized google chromes with batch. At the time the company’s default browser was IE. The site that needed chrome was an in-house product. I was pushing towards standardization of Google chrome 64-bit enterprise. However, management did fully agree with my thinking. So a compromise was created. I created a shortcut link on the desktop that pointed to a batch file. This batch file determined which version of Chrome was installed and loaded that chrome to the special internal page. Here is the script for this icon:

The Script

if exist "C:\Program Files\Google\Chrome\Application\chrome.exe" (
	start "CMD" /D "C:\Windows\System32\" /max "C:\Program Files\Google\Chrome\Application\chrome.exe" "https://www.bolding.us"
) else (
	if exist "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" (
		start "CMD" /D "C:\Windows\System32\" /max "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" "https://www.bolding.us"
	) else (
		msg "%username%" Chrome Not Installed
	)
)

It’s a simple batch script. We first ask if the 64-bit path exists. If it does, then we open chrome to a hyperlink. If it doesn’t, we ask if the x86 version exists. If it does, we open chrome to the hyperlink. If it doesn’t, we tell the user it doesn’t exist. Pretty simple? Yeah, it’s very simple. So, the desktop shortcut would point to the batch file. The batch file would execute and load up the chrome page.

Taking it another step

This process helped a lot when it came to the next issue. The next issue was each location had a custom-hosted site on that location’s Server. There were tablets that had to float between each location. This means, that the icon on the tablet must open the custom website link at center 1 and at center 101. Thankfully each center had it’s own subnet. Each server that hosted this site hosted it at 10.x.x.5. This means all I had to do is find out the Subnet and make a .5 address in the link. Believe it or not, that’s not as hard as you would think. We are taking the script from above and pretty much piping the new URL into it.

The Script

@echo off
rem All i need to do now is grab only the IP address with batch. 
rem ipconfig | find "IPv4"
for /f "usebackq tokens=2 delims=:" %%f in (`ipconfig ^| findstr /c:"IPv4 Address"`) do set "ip=%%f"

for /f "tokens=1-4 delims=. " %%a in ("%ip%") do (
set octetA=%%a
set octetB=%%b
set octetC=%%c
set octetD=%%d
)

if exist "C:\Program Files\Google\Chrome\Application\chrome.exe" (
	start "CMD" /D "C:\Windows\System32\" /max "C:\Program Files\Google\Chrome\Application\chrome.exe" "http://%octetA%.%octetB%.%octetC%.5/Login.aspx"
) else (
	if exist "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" (
		start "CMD" /D "C:\Windows\System32\" /max "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" "http://%octetA%.%octetB%.%octetC%.5/Login.aspx"
	) else (
		msg "%username%" Chrome Not Installed
	)
)
exit

Inside the script you see that we are breaking down the IPv4 address. So, if you are running IPv6, this isn’t going to work. We place each Octect into it’s on veraible. Then we build the site at the link level. Bamn, done with the script.

The next part is to create the icon. Which i’ll leave that to you. I hope this little brain twister is helpful for what it is worth.

Group Policy How-To – Folders

Group Policy How-To – Folders

Whenever we get a new client, one of the first things we do is create a new folder on the root of C called temp. If that client has a group policy we create a policy just for that. This guide is a simple one, it’s how to create a folder with group policy. This post will build on other posts. Let’s get started.

Creating the Policy

  1. Start Group Policy.
  2. Expand to your Group Policy Objects
  3. Right Click and Select new Policy.
  4. Name the Policy. I suggest, “Folder – Temp”
  5. Right Click the “Folder – Temp” Policy and click edit
  6. Naviage to Computer Configuration > Preferences > Windows Settings > Folders.
  7. Right Click in the right plane
  8. Click New > Folder
  9. In the Path location, enter the path you want. In this case it will be c:\temp.
  10. Click OK.

Now we have the policy made, we need to decide who is getting the policy. We want every computer to get this policy. Closeout the edit group policy and click on the policy. Then click on the delegation tab. Here we see our delegations. Right now every user and computer will execute this policy. We want to change that.

Delegating the Policy

  1. Click on the policy in question “Folder – Temp”
  2. Click the delegation tab.
  3. Click Authenticated users
  4. Click advanced
  5. Click autenticated users in this window.
  6. Uncheck apply group policy.
  7. Click the add button
  8. Search for Domain Computers
  9. Give Domain computers Read and Apply by checking the check box on read and apply group policy.
  10. Click Apply
  11. Click Ok.

Linking the policy

Now the policy will only apply to Domain Computers inside whatever OU you link it to. Now we need to link it to an OU. Since we are doing this to all computers on the domain, we can link it at the top level. We do this by right-clicking the domain and clicking the Link existing gpo object. We search for the GPO in question and click it. Then we click ok. Within the next few days, the c:\temp folder will be created.

That is how you create a folder on all computers in your domain. If you have any questions, feel free to reach out.

Powershell HTML Reports

Powershell HTML Reports

From time to time, you will be asked to create a web report with data that can only be easily obtained by PowerShell. There are hundreds of modules out there that can do this, which is very nice. This blog isn’t about those modules, but instead some tricks I have learned over the years. When it comes to custom HTML reports, these are very customized from data sources like Azure, Active Directory, even SLQlite services. If all possible I like to use the Universal dashboard. But the UD doesn’t translate to emails or IIS pages very well. Let’s get started with the here string.

Here-String

$HereString =@"
Information
"@

A here-string is used to create a block of text. In the example above you see the variable HereString is equal to @””@. This is very important. The @ symbol indicates the start and stops of a here-string. You can place variables inside the here-string as well. This means you can build variables outside of the here-string and plump them into the string. Here is an example of our base for the website.

$HTML = @"
<html>
<head>
<title>Employee List</title>
<style>
</style>
</head>
<body>
</body>
</html>
"@

From this template, we can add CSS coding, powershell tables, lists and much more. The next important item we need to talk about is the convertto-html commandlet.

Convertto-html

This command is the heart of creating tables out of PowerShell information. For example, you want to pull all enabled users from the employees OU and display their displaynames, samaccountnames, department, and title in order. That’s a simple get-aduser command.

$users = Get-ADUser -Filter {enabled -eq $true} `
-SearchBase "OU=hospice Users,DC=Hospice,DC=Local" `
-Properties displayname,samaccountname,title,department `
| Select-Object Displayname,Samaccountname,Title,Department `
| sort-object Displayname

Now we need to convert this array of data into a useable table. This is where convertto-html comes into place. We can tell the command to create a full website just for the tabled information or we can tell it to produce a single table using the as flag and the fragment flag.

$UsersToHTML = $users | ConvertTo-Html -as Table -Fragment

Now we have a user table that we can drop into our Here-string from above. The Here-string is the last thing you will create during this custom process.

$HTML = @"
<html>
<head>
<title>Employee List</title>
<style>
</style>
</head>
<body>
$UsersToHTML
</body>
</html>
"@

This is pretty much the basics of how you would create a site of all users and their user names. You export that site using the >> command. So it would look like this:

$HTML >> <Pathtohtml>.html

Going Deeper – Employee Directory With Images

Let’s go a little deeper by making an employee Directory with pictures. In this scenario, we will assume that the active directory has thumbnail photos inside of it. This Scenario is going to be based on an IIS server and everything is set up for a basic HTML page. I will as

Requirements:

  • If a property is not present in the AD account, null or blank, do not display that property.
  • All users must have a photo or a placeholder.
  • The table must be in alphbetical order by the displayname.
  • Required Properities:
    • Photo
    • Display Name
    • Username
    • Job Title
    • Department
    • Mobile Phone
    • Office Phone
    • Fax
    • Manager

The first thing we need to do is grab the data that we need from the active directory. We do that with the get-aduser command.

$users = Get-ADUser -filter {enabled -eq $true} `
-Properties thumbnailPhoto,Displayname,SamAccountName,Title,Department,MobilePhone,OfficePhone,Fax,Manager

There will be two tables that we will be working with. The first table will be two columns and however many rows we will need. The first column will contain the 128×128 active directory thumbnail photo. The second column will contain the Users information. The second column will contain a table with two columns and 8 rows. The first column will be the name of the value and the second column will be the value itself.

I can do this in two ways. I can start the first table inside the here-string or I can create the table before the here-string. I’m going to create the table before the here-string. This way I have a clear image in my head of the table that I am inputting into the here-string. Notice the UserTable is a here-string as well. So we will refer to the Main HTML here-string as the HTML string from here on out.

$UserTable = @"
<table id="myTable">
  <tr class="header">
    <th style="width:20%;visibility: hidden;"></th>
    <th style="width:30%;visibility: hidden;"></th>
  </tr>
"@

Now we have a basic HTML table built. From here on out, we will be creating the rows for this table. Inside each row will be the two columns. The photo column and the data column. The data column will contain the table with the employee data on it. It’s time to start looping through that user’s object we created a while ago. We start off with a simple foreach loop. Inside the loop the logic will go as follows:

  • Grab the photo from the thumbnail property and save it to the image repo using the username.
  • Start the new row. Inside the first column of the new row, we check to see if that photo exists. If it does, then we we create an img tag of the file, if it doesn’t then we target the general image file.
  • The second column we blank out the values for each item.
  • Then we create the table.
  • Then we create if statements that creates each row in table 2. if the users object has the property in question for that user, we set the value and create the <tr> accordingly.
  • Finally we close off the second table and the row.
  • Then after all of the rows on table 1 is created, we close off the table.

Phew. This is going to be crazy.

The first part is to download the photo in question. If the user’s profile has a thumbnail photo we will download it from AD using the set-content command. (PS5)

foreach ($User in $Users) {
    $PhotoPath = "<path>/images/$($user.samaccountname).jpg"
    if ($Null -ne $user.thumbnailPhoto) {
        $user.thumbnailPhoto | Set-Content $PhotoPath -Encoding byte
    }
}

The next step is to determine if the file exists. If it does, we want to create the Image URL towards the new user’s image. If not, we want it to go to the placeholder image. All of this is still within the Loop in question.

if (Test-Path $PhotoPath) { 
    $ImageURL = "Images/$($user.UserPrincipalName).jpg" 
} else { 
    $ImageURL = "Images/Placeholder.png" 
}

Now we need to make a placeholder for each property that we will be using. We do this so the next time the loop processes, we have fresh variables.

$DisplayName = ""
$Username = ""
$JobTitle = ""
$Department = ""
$MobilePhone = ""
$OfficePhone = ""
$Fax = ""
$Manager = ""

Now we need to test these variables and fill them up with HTML code. Each one of these will be a row on the table2. The code below is the same for each of these. All you need to do is replace the <value> with the value in question. The one on the left is what you will replace. The one on the right is an example.

if ($Null -ne $User.<Value>) {
    $<value>= @"
    <tr>
      <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong><value>:</strong>
      </td>
      <td style='vertical-align: top; text-align:left;'>
        $($User.<value>)
      </td>
    </tr>
"@
  } 
if ($Null -ne $User.OfficePhone) {
    $OfficePhone= @"
    <tr>
      <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Office Phone:</strong>
      </td>
      <td style='vertical-align: top; text-align:left;'>
        $($User.OfficePhone)
      </td>
    </tr>
"@
  } 

Once you create a value for each of these items, it’s time to put it together into the User’s table. AKA table2. We start off by using the $usertable from above combined with a here-string. Remember we are now entering the first table again and creating each row. Here is what the code will look like.

  $UserTable = $UserTable + @"
    <tr>
        <td> <center><a href="$ImageURL" target="_blank"><img src='$ImageURL' style='width:125px; height:125px; border-radius:100px;'></a></center></td>
        <td>
            <table>
                $DisplayName
                $UserName
                $JobTitle
                $Department
                $MobilePhone
                $OfficePhone
                $Fax
                $Manager
            </table>
        </td>
    </tr>
"@

We are building this string. This is what you see the $usertable = $usertable + Here-String. All of this is the single row for the table in question. We loop through all of the users using this method and close out the Loop. Once the loop is closed out what we need to do next is close out the table. We do this by adding a final </table> to the end of the table in question.

$UserTable = $UserTable + "</table>"

The final part is placing this table inside an HTML website. It’s as simple as slipping $UserTable between the two bodies.

$HTML = @"
<html>
<head>
<title>Employee List</title>
<style>
</style>
</head>
<body>
$UserTable
</body>
</html>
"@

Now we have an HTML site. We can further edit this and create a nice CSS code with some java-scripts, but I’m not going to get fancy here. The final thing we have to do is export this html page.

$HTML > <pathtosite>\index.html

This process is basically building inside out. We started by the image and then the data. We built accordingly. Now the script itself.

The Script

param (
    $Filepath = (Read-Host "File Path for "),
    $ImagePath = (Read-Host "Image Folder")
)
$users = Get-ADUser -filter {enabled -eq $true} `
-Properties thumbnailPhoto,Displayname,SamAccountName,Title,Department,MobilePhone,OfficePhone,Fax,Manager

$UserTable = @"
<table id="myTable">
  <tr class="header">
    <th style="width:20%;visibility: hidden;"></th>
    <th style="width:30%;visibility: hidden;"></th>
  </tr>
"@
foreach ($User in $Users) {
    $PhotoPath = "$ImagePath/$($user.samaccountname).jpg"
    if ($Null -ne $user.thumbnailPhoto) {
        $user.thumbnailPhoto | Set-Content $PhotoPath -Encoding byte
    }
    if (Test-Path $PhotoPath) { 
        $ImageURL = "Images/$($user.UserPrincipalName).jpg" 
    } else { 
        $ImageURL = "Images/Placeholder.png" 
    }
    $DisplayName = ""
    $Username = ""
    $JobTitle = ""
    $Department = ""
    $MobilePhone = ""
    $OfficePhone = ""
    $Fax = ""
    $Manager = ""
    if ($Null -ne $User.displayname) {
        $DisplayName= @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Display Name:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($User.displayname)
        </td>
    </tr>
"@
    } 
    if ($Null -ne $User.samaccountname) {
        $Username= @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Username:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($User.samaccountname)
        </td>
    </tr>
"@
    } 
    if ($Null -ne $User.title) {
        $JobTitle = @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Job Title:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($User.title)
        </td>
    </tr>
"@
    } 
    if ($Null -ne $User.Department) {
        $Department= @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Department:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($User.Department)
        </td>
    </tr>
"@
    } 
    if ($Null -ne $User.MobilePhone) {
        $MobilePhone = @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Mobile Phone:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($User.MobilePhone)
        </td>
    </tr>
"@
    } 
    if ($Null -ne $User.OfficePhone) {
        $OfficePhone= @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Office Phone:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($User.OfficePhone)
        </td>
    </tr>
"@
    } 
    if ($Null -ne $User.Fax) {
        $Fax = @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Fax:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($User.Fax)
        </td>
    </tr>
"@
    } 
    if ($Null -ne $User.Manager) {
        $ManagerInfo = $User.Manager.split(',')[0] -replace "CN=",''
        $Manager = @"
    <tr>
        <td style='font-size:16px; text-align:justify; width:100px;'>
        <strong>Manager:</strong>
        </td>
        <td style='vertical-align: top; text-align:left;'>
        $($ManagerInfo)
        </td>
    </tr>
"@
    } 
    $UserTable = $UserTable + @"
    <tr>
        <td> <center><a href="$ImageURL" target="_blank"><img src='$ImageURL' style='width:125px; height:125px; border-radius:100px;'></a></center></td>
        <td>
            <table>
                $DisplayName
                $UserName
                $JobTitle
                $Department
                $MobilePhone
                $OfficePhone
                $Fax
                $Manager
            </table>
        </td>
    </tr>
"@
}
$UserTable = $UserTable + "</table>"
$HTML = @"
<html>
<head>
<title>Employee List</title>
<style>
</style>
</head>
<body>
$UserTable
</body>
</html>
"@
$Html > $Filepath

That’s all folk, yall have a great week!

Group Policy Troubleshooting – Stale DNS

Group Policy Troubleshooting – Stale DNS

This one was a fun one that really threw me for a loop. DNS is an issue no matter where you go. Recently facebook showed the world how DNS can take everything down. DNS in your domain is very important to keep alive and healthy. Having items sit in your DNS is deadly to your org. That is why something called DNS Scavenging exists. This story is a story about DNS and how it directly affected group policy.

Scenario – Wrong Server!

A client called and stated that group policy wasn’t applied to a single machine. He said he couldn’t even log into the machine with new accounts, just accounts that were on there from the day before. He went as far as to say that a user that just changed his password had to use his old password. Very interesting combo of items.

Who, What, Where, When, How

  • Who: Anyone using this computer. Users who never signed into the machine, and users who has signed in but changed their passwords recently.
  • What: Login with thier current passwords, Login, Group Policy not applying.
  • Where: This single machine, later on discovered another.
  • When: One week before the issue started. (After DHCP was edited)
  • How: When they log in.

When I first came in, I looked at the machine in question. I ran an IPconfig /all on the machine to get basic information. I marked down the IP, subnet, mac address, DNS servers, and the DHCP server. I then ran the gpresult /r and the command errored out saying the group policy server did not respond. Hum… I pinged the DNS I noted and the ping came back. I ran NSLookup on the dns server’s IP address to get a hostname. The hostname came back as “xxx-bobsmacbook”. Well now, that’s not the DNS server. I asked the client for the DNS server information. He gladly gave it to me. I RDPed into the DNS server. The DNS server was also the DHCP server and the AD server. All the fsmo roles were on this machine. Sigh… Ok, other than that, I deep-dived into DNS because the NSLookup came back as someone’s mac book. Sure enough, there was an entry into DNS from about 2 years before for bobsmacbook at the IP address the machine believed was the DNS server. Infact, every IP address in the subnet was inside there. Most of them were years old.

I looked at the client and asked why their DNS was so full of old records. He replied with, that’s our archive. It took everything in me not to facepalm. I mean, my hand moved instinctively to my face. After explaining the importance of DNS to the client, the client agreed to enable DNS Scavenging. Wouldn’t you know it, after the first rotation, the entire company started to move much quicker. Requests to the IIS server took only seconds instead of minutes. Copying files across the network just generally did better. NSlookup worked. The computer in question group policy was updated correctly. When DNS breaks, everything suffers. In this case, DNS was a young man covered in trash bags.

How to enable DNS Scavenging

DNS Scavenging is an windows feature that finds old stale records and removes them. This ensures environments with DHCP do not detect multiple devices based on bad/multiple DNS entries for the same device. Here are the steps to enable it.

  1. Start > Programs > Administrative tools > DNS > DNS Manager.
  2. Right click the DNS Server
  3. Click set Aging/Scavenging for all zones.
  4. Check box the “Scavenge Stale Resources Records
  5. Select the No-refresh and Refresh intervals totals combined equals to or is less than the DHCP lease. If the lease is 8 days, set the rates at 4 each.
  6. Click Ok.
  7. On the Server Aging/Scavenging Confirmation screen, check box the “Apply these settings to existing active directory intergrated zones.”
    1. Click ok
  8. (Optional) Right click the DNS server and click the “Scavenage State Resource Records” to start the process.

There you have it. The DNS records will be purged when the time comes. This allows DHCP to issue IP addresses with no problems and DNS stays clean.

As always, if you have any questions, feel free to ask.