Server Timezone Script
Time for another one of my quick powershell scripts.
This script was created because we discovered we had servers at the Brisbane site, who were running with Daylight Saving time, and also found some servers in Sydney who were not.
For a list of computer names, this script will check that the timezone is "Brisbane" and output the ones who are not to a file.
Creating 1.5 million users in AD
Ever needed to create A LOT of user accounts?
Try this script: (uses Quest CmdLets)
Service Monitoring and Recovery with PowerShell
Recently, Symantec Backup Exec was crashing quite regularly on me.Every Saturday at 4am, the main Backup Exec service would coem crashing down, taking the weekly backups with it and then causing my Monday morning to be painful (unless whoever was on call managed to see that the backups had stopped).
I know that this sort of monitoring should be done with something like SCOM, but that wasn't something I could do in this situation.
The solution was a PowerShell script running hourly (or more or less depending on the needs) that would look at the running state of all the Backup Exec services and start any that were not running, as well as send the person on call an email to tell them something bad was happening.
Using my standard template (see previous postings), I added one parameter to the script, $query, which is simply the query string that I would pass to get the services I want to monitor. For example, a query string of "backup*" will return all the Backup Exec processes, a string of "spooler" would return the print spooler.
The body of the script is pretty simple:
Windows DNS and BIND Server together
Another one of my bulk DNS scripts.
This was a weird request however there were a number of requirements:
- Allow BIND servers running on Linux/Unix to take zone transfers of all production zones (forward and reverse), this should be limited to specific servers.
- BIND servers should only be allowed to request one transfers from specific servers
- Only authorised BIND servers should be permitted
- BIND servers SHOULD be listed in a name server query for a particular zone (that is, BIND servers should have an NS record)
The script is pretty simple:
Bulk import of DNS PTR Records
Due to a number of issues, I was once required to delete some reverse lookup zones in DNS and then recreate them. There were two reasons I had to do this, firstly there were some conflicting replications configurations, secondly I was merging some DNS servers and finally I was making the reverse look zones class B instead of 2 dozen class C zones.
Before I start, there is one limitation with this script. All of entries you are importing must belong to the same reverse lookup zone. For example, this script would handle importing entries of 10.0.0.23, 10.0.0.55 into a reverse lookup zone of 10.0.0.0/24 or even 10.0.0.0/16; however it will fail if you try to import those entries into a zone of 172.16.0.0/24.
This script I wrote for these sort of situations, and many others. Whenever doing this work, I have always had a resulting CSV file with my entries that I need to end up back in DNS. This file has had the following format:
host, ip
hostname1.domain.local, 10.0.0.1
hostname2.domain.local, 10.0.0.2
Once you have a CSV file of the above format, point the following code at it:
$filename = read-host "CSV filename"
$dnsserver = Read-Host "dnsserver"
$namespace = read-host "namespace - format 201.168.192.in-addr.arpa"
$entries = import-csv $filename
foreach ($entry in $entries) {
dnscmd $dnsserver /recordadd $namespace $entry.ip "ptr" $entry.host
}
Bulk DNS PTR record creation
Many years ago, a poorly informed technician on a vendor support line claimed that because we had "GAPS" (his words not mine) in our reverse look up zones that their application was running poorly. Missing entries in your reverse lookup zone can be caused by computers not registering their PTR records, the DHCP server not doing so, or in some cases the DNS server not creating the records correctly; for the record, our "gaps" were because on that subnet, only a dozen or so clients were residing and hence, only a few entries existed (and they were all correct too).
To prove the vendor wrong, i went a created "filler" entries in that subnet using PowerShell. See below:
$dnsserver = <your dns server >
$namespace = <namespace - format 201.168.192.in-addr.arpa>
$startip = <if you want to create a whole standard class C subnet, this is 1>
$endip = <if you want to create a whole standard class C subnet, this is 255>for ($ii=$startip; $ii -lt $endip; $ii++) {
dnscmd $dnsserver /recordadd $namespace $ii "ptr" "FakeDummyAddress"
}
DNS Cleanup - Removing an old DNS Server
The script that is outlined below was written very quickly one night. The issue was we had several old decomissioned/dead DNS servers in the environment, and a lot of DNS namespaces to remove them from (aproximately 10 forward and 20 reverse lookup zones). It should be noted that this script assumes we can make a change on a single master server and replication (hopefully AD Integrated) will take care of the rest.
Enjoy.
$masterdns = "<Primary DNS FQDN>"
$olddnshost = read-host "Enter new host name (FQDN)"
$enumzones = get-WMIObject -Computer $masterdns -Namespace "root\MicrosoftDNS" `
-Class "MicrosoftDNS_Zone"foreach ($zone in $enumzones)
{
if ($zone.zonetype -eq 1)
{
write-host ""
$name = $zone.namednscmd $masterdns /recorddelete $name "@" NS $olddnshost
Write-Host "NS Record for "$olddnshost " deleted from "$name
}}
Encrypting a string using certificates and PowerShell
I recently had the need to encrypt some strings using a public and private key and then store it for later use. The public key was stored as part of a certificate issued by the internal CA, the private key was to be held offline for later use. If you were using this process to exchange data with another party, you would require the other parties public certificate (and hence their public key).
The encryption process was pretty simple; however the decryption process was another story.
Encrypting a string using a key stored in a public key requires only one prerequisite component, and that is the certificate containing the key you are going to use. This certificate in my case was stored within the Windows certificate store for the local machine, in the Trusted People folder. My encryption function will need two items of input, the string to encrypt, and the certificate we will be encrypting against as System.Security.Cryptography.X509Certificates.X509Certificate2.
You may have just wondered, or swore, “How the hell do I get a System.Security.Cryptography.X509Certificates.X509Certificate2???!!!??”. Well if you remember the power of PowerShell then you will remember that it allows you to interact with the certificate store in the same way you would the registry or the file system.
To see all the Trusted Root Certification Authorities using PowerShell:
Dir cert:\localmachine\Root
Look at the output, does it look familiar? This is the equivalent to opening the MMC, adding the Certificates snap-in for the local machine and browsing to Trusted Root Certification Authorities.
What about the certificates for my AD account?
Dir cert:\currentuser\my
This is the equivalent to opening the MMC, adding the Certificates snap-in for the current user and browsing to Personal.
If you perform a get-member on what is returned, you will notice a the objects being returned are the right type for what my function will require.
For what I was doing, I knew that the certificate with the thumbprint of 5F507E471772839A953406A00537F609955AFCD7 which was stored the Trusted People folder for the local machine. I could get the certificate using
Gi cert:\localmachine\trustedpeople\5F507E471772839A953406A00537F609955AFCD7
The other thing to note, is that my encryption function actually returns a base64 encoded envelope which not only contains the message/data which was encrypted but also information regarding the certificate used to encrypt the data (and which information will be used later to decrypt the data).
The encryption function looks like this:
Function encrypt-envelope ($unprotectedcontent, $cert)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
$utf8content = [Text.Encoding]::UTF8.GetBytes($unprotectedcontent)
$content = New-Object Security.Cryptography.Pkcs.ContentInfo `
-argumentList (,$utf8content)
$env = New-Object Security.Cryptography.Pkcs.EnvelopedCms $content
$recpient = (New-Object System.Security.Cryptography.Pkcs.CmsRecipient($cert))
$env.Encrypt($recpient)
$base64string = [Convert]::ToBase64String($env.Encode())
return $base64string
}
Decryption is a little more complicated, but it isn’t overly complicated when you sit down and actually think about it. To work out the reverse method, I simply worked with PowerShell and the MSDN library to reverse the process laid out in the encryption method.
For this to work, you need to have the private key for the corresponding certificate installed in either the Local Computer or User certificate stores.
function decrypt-envelope ($base64string)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
$content = [Convert]::FromBase64String($base64string)
$env = New-Object Security.Cryptography.Pkcs.EnvelopedCms
$env.Decode($content)
$env.Decrypt()
$utf8content = [text.encoding]::UTF8.getstring($env.ContentInfo.Content)
return $utf8content
}
Note this will return the string correctly formatted as it originally entered.
I will be posting some other scripts using this code in the coming days.