Create a PFX file from separate private and public key certificate files

Whilst installing ADFS I needed to import my wildcard certificate. This proved fiddly as Microsoft need it to be in pfx format, and I only had separate private .key and public .crt files.

PFX is a Microsoft certificate format that combines the public and private keys into one file, but is different from the combined format used by (e.g.) OpenSSL. I found that I had to go through several steps to achieve this.

Your CRT public key file should start with the following (open it in notepad to check):

-----BEGIN CERTIFICATE-----

Convert the public key into a PVK file

If you open your public key file (e.g. in notepad) and it says

-----BEGIN RSA PRIVATE KEY-----

then you’ll need to convert it into a PVK file. a PVK file is a binary file, so it’ll look have lots of non-alphanumeric characters in it. To do the conversion, I used a utility called pvk written by Dr Stephen N Henson (thank you!). The command line for pvk.exe is:

pvk.exe -in rcmtech_private.key -out rcmtech_private.pvk -topvk

You’ll be prompted for a password, and you do need to specify one or the next step in the process will fail.

Download the Windows SDK

Now you need a utility called pvk2pfx. Annoyingly this only comes as part of the Windows SDK, so you need to download that first – you just need to tick the Windows Software Development Kit option when selecting the features to install. Once you’ve pulled it down (it is several GB), you’ll find pvk2pfx in the following folder (you are using a 64-bit OS, right?):

C:\Program Files (x86)\Windows Kits\10\bin\x64\pvk2pfx.exe

Create the PFX file

The command line for pvk2pfx.exe is:

pvk2pfx.exe /pvk rcmtech_private.pvk /spc rcmtech_public.crt /pfx rcmtech.pfx

You’ll get a pop up asking for your pvk file password, which you specified when creating it using pvk.exe. (If you didn’t specify a password, and just hit enter on this popup you’ll just get an error: ERROR: Password incorrect. (Error Code = 0x80070056).)

You should now find yourself with a PFX file, which you can use to import into Windows (e.g. ADFS config wizard).

Posted in Security, Windows | Tagged , , , , , , , , , , , , , | 2 Comments

Send email via Yahoo with PowerShell

Quick reference for sending email via a Yahoo! mail account and their SMTP servers from within a PowerShell script.

You’ll need the Yahoo SMTP server details, which require you to use a specific port (587 – I couldn’t get 465 to work) and an encrypted connection.

I also set up a Yahoo “app password” for PowerShell, which means I don’t have to use my regular password in plain text in a script, which is a good thing. To do this click the settings cog from within Yahoo Mail, go to Account info, then when that opens go to Account Security. From there you’ll find Manage app passwords and can add a new one called (e.g.) PowerShell. Copy and paste the password generated into the PowerShell script. Note that the generated password is shown in four blocks of four letters with spaces in between but when you paste it it’ll come out with no spaces – and this is correct – you do not need the spaces displayed on the Yahoo web page.

Here’s the sample script:

$Username = "something@yahoo.co.uk"
$Password = "abcdefghijklmnop"

$SecurePassword = $Password | ConvertTo-SecureString -AsPlainText -Force
$Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $Username, $SecurePassword

$RcptTo = "somebody@mailserver.com"
$Subject = "Yahoo Test"
$Body = "This is a test message"
Send-MailMessage -From $Username -To $RcptTo -Subject $Subject -Body $Body -SmtpServer smtp.mail.yahoo.com -Port 587 -UseSsl -Credential $Credentials

And here’s the same thing as a function:

function Send-YahooMail ($Username, $Password, $RcptTo, $Subject, $Body){
    $SecurePassword = $Password | ConvertTo-SecureString -AsPlainText -Force
    $Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $Username, $SecurePassword
    Send-MailMessage -From $Username -To $RcptTo -Subject $Subject -Body $Body -SmtpServer smtp.mail.yahoo.com -Port 587 -UseSsl -Credential $Credentials
}

If you don’t want any kind of password in plain text in a script then you need to also look at this post of mine too.

Posted in PowerShell, Security | Tagged , , , , , , , , , , , , | 1 Comment

Connection State Announcer

I’ve been having some very annoying problems with my home broadband where the connection keeps dropping every few minutes. I wrote this quick script to monitor a site on the internet and tell me when it can’t be reached anymore, but it has plenty of other uses. I’m using PowerShell’s ability to speak because I wanted to leave this running whilst I had a web browser open over the top or was away from the computer.
It was also a fun coding exercise to write something that would detect a state change and only report when the connection came up or down, not the entire time it was down. I did this using a two position array and comparing the contents between subsequent tests of the internet site. If the test results were the same, the connection hadn’t changed state, if they were different it had.

# Configure the test
$TestSubject = "www.bbc.co.uk"
$TestInterval = 2
$TriesPerTest = 2

# Initialise speech
Add-Type -AssemblyName System.Speech
$Speak = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer

# Initialise an array to hold state information
$Connected = New-Object System.Collections.ArrayList
# Assume the connection is down, write this to the first two positions in the array
$Connected.Add($false) | Out-Null
$Connected.Add($false) | Out-Null

# Loop forever
while($true){
    # Test the connection, store the result in the second position in the array
    if(Test-Connection -ComputerName $TestSubject -Count $TriesPerTest -ErrorAction SilentlyContinue){
        $Connected[1] = $true
    }else{
        $Connected[1] = $false
    }
    # Compare the first and second positions to give us a state change trigger
    if($Connected[0] -ne $Connected[1]){
        # State has changed, write timestamp to screen
        Write-Host ("`n"+(Get-Date -Format s)+" ") -ForegroundColor Gray
        if($Connected[1] -eq $true){
            # Announce that the connection is now up
            $Speak.Speak($TestSubject+" is up!")
        }else{
            # Announce that the connection is now down
            $Speak.Speak($TestSubject+" is down.")
        }
        # Copy the test result into the first position in the array, ready for comparison with the next test result
        $Connected[0] = $Connected[1]
    }
    # Print something colourful to the screen to show relative up/down times
    if($Connected[0] -eq $true){
        Write-Host "#" -ForegroundColor Green -NoNewline
    }else{
        Write-Host "#" -ForegroundColor Red -NoNewline
    }
    # Pause before next test
    Start-Sleep -Seconds $TestInterval
}
Posted in Networking, PowerShell, Windows | Tagged , , , , | Leave a comment

VeeamZip Hyper-V Backup

Veeam Backup & Replication drops down into VeeamZip mode if you don’t have a licence key. But it’ll still provide some handy functionality – I’m using it to take a basic backup of all the VMs on a Hyper-V host at a remote site, just in case the (rather crusty old) hardware dies.

There’s no scheduling in the free Veeam backup product, but you can write a PowerShell script and use Windows Task Scheduler to run it, which is what I’ve done. I’m backing up all powered on VMs on a particular host to a UNC path on a physical server.

Add-PSSnapin VeeamPSSnapin
$VMs = Find-VBRHvEntity -Name * -Server HVHost01
$VMs.Count
foreach($VM in $VMs){
    if($VM.PowerState -eq "PoweredOn"){
        Write-Host ("Backing up "+$VM.Name+"...") -ForegroundColor Gray -NoNewline
        try{
            $Result = Start-VBRZip -Entity $VM -Folder "\\FileServer\Veeam\Backups" -Compression 5 -AutoDelete In1Week
            if($Result.Result -ne "Failed"){
                Write-Host "Done" -ForegroundColor Green
                # You could use Send-MailMessage here to get email notification of a successful backup
            }
            else{
                Write-Host "Failed" -ForegroundColor Red
            }
        }
        catch{
            Write-Host "Error" -ForegroundColor Red
            $Error[0]
        }
    }
}

I created a domain admin account in Active Directory to use as a service account for Veeam, and used this to run the scheduled task too. The task needs to be “Run with highest privileges”.

If you get the error:

06/07/2016 10:13:10 :: Error: Access is denied.
--tr:Error code: 0x00000005
Cannot create folder. Folder path: [\\FileServer\Veeam\Backups].
--tr:FC: Failed to create directory. Directory path: [\\FileServer\Veeam\Backups].
--tr:Failed to call DoRpc. CmdName: [FcCreateDir].
Access is denied.
Cannot create folder. Folder path: [\\FileServer\Veeam\Backups].

It’s probably because the Veeam B&R server authenticates to the UNC path using its local system account, so you need to grant that computer account access to the share and NTFS permissions on the server holding the shared folder, e.g. VBRServer$

Posted in PowerShell, veeam | Tagged , , , , , , , | Leave a comment

SMTP email diagnostics and info – troubleshoot email not received

Every now and then somebody will contact you via a method that they wouldn’t normally use and say something like “did you get the email I sent to you a few days ago”. If the answer to this is “no, I did not receive your email” then the following might be useful.

SMTP (Simple Mail Transport Protocol) servers, classically, should never blackhole email. To blackhole an email message, years ago, was a bad thing and meant that your mail server, or the recipient’s mail server was probably malfunctioning. A receiving SMTP server should either accept and deliver an email, or return a message to the sending server explaining why it was unable to deliver the email. Likewise, a sending SMTP server should either successfully transmit an email to a destination mail server, or deliver an NDR (non-delivery report) back to the user who tried to send the email.

Of course that was before spam became a problem. Now it is standard practice to silently drop emails in certain circumstances. However this does occasionally lead to the problems described at the start of this article. Spam is simply such a huge volume of email now that it would take significant extra server resource to deal with it according to the original “proper” SMTP methodology. You also don’t necessarily want a spammer’s mail server to know if an address they’ve tried to send to is legitimate or not – if they know it exists, or just that a particular internet server is an SMTP server, they’ll likely just send more junk to it.

Diagnosis and troubleshooting

So how to diagnose the “never received” email problem?

Firstly, ask the sender if they received an NDR. If they did, their mail system will usually have provided a log of the conversation when it was trying to talk to your mail system, and this will often be quite helpful.

If they never received an NDR, check your mail system logs to see if you can see any trace of their mail system trying to connect to your mail system. Many companies route all their email though a third party spam/malware filter (e.g. Symantec Email Security.cloud, Microsoft Exchange Online Protection), so this is a good place to start – if you can’t see any emails being received from your sender’s email address there then you can rule out any problems with your internal mail servers.

You can also use online tools such a the Microsoft Remote Connectivity Analyser or MX Toolbox to check that your mail servers (or a third party’s) are configured correctly. From a command prompt (on Windows) you can also get your MX records:

nslookup -type=mx rcmtech.co.uk 8.8.8.8

where 8.8.8.8 is a Google public DNS server, you can change this to use any DNS server you like.

Has the sender been blacklisted?

If a sender’s domain sends spam, or too much spam, it might be blacklisted. You can check this by using MX Toolbox and/or talking to your third party spam filter vendor. If a domain is blacklisted, spam filtering systems will frequently silently drop all (or most) mail send out from it.

Not all failures are bad

When using these tools, it helps to know a bit about how MX record preference values work, and tricks that mail filtering companies use to try and cut down on the amount of spam they have to process. Your MX (mail exchanger) DNS record will usually have more than one entry, because you’ll (ideally) have a primary mail server (or cluster of servers) and one or more lower priority servers in case your primary server is unavailable.

A sending SMTP server will use the lowest preference number server first. It will only use a server with a higher preference number if all lower preference number servers are unavailable or reject the message/communication attempt.

In the case of MessageLabs, the server addresses are actually server clusters. Also, the clusterna names are all spam traps, and will never accept any email. This is because spammers apparently often deliberately send to higher preference number servers because historically they might have no or less spam filtering applied to them, and they would normally only be used on the rare occasions when the primary server (lowest/lower preference number) was unavailable. No correctly configured/normally functioning sending mail server would ever pick one of the “a” servers. This is important to know because some SMTP testing tools will test all servers in the MX records for a domain, and so now we know that in the case of messagelabs.com servers, we can expect a failure from the clusterna server, but not from the clustern one!

Posted in Exchange | Tagged , , , , , , , , , , , , , , | Leave a comment

PowerShell: Change Hyper-V VM VLAN after Live Migration

One of my old Hyper-V hosts has its virtual switch uplink NICs connected to access ports rather than trunk ports. My other hosts all have trunked uplinks, which means that the VMs need to have a VLAN ID specified to connect them onto the correct VLAN.

I’m gradually moving VMs off the old host, but want to minimise downtime and down’t want to do it out of hours. The problem is that as soon as the VM has finished its Live Migration onto the new host, it stops being able to talk to the network, due to the VLAN ID not being set.

So I wrote this script to monitor the new host for the presence of the VM that’s being migrated, and as soon as it sees it, to set the VLAN ID to the correct value.

$HVHost = "RCMHV01"
$VMName = "OldVM01"
$SwitchName = "LAN Switch"
$VlanID = 250

$Finished = $false

Write-Host "Waiting for $VMName to move onto $HVHost"
while($Finished -ne $true){
    $VMs = Get-VM -ComputerName $HVHost
    if($VMs.Name -contains $VMName){
        Write-Host "Migrated, waiting for Status to be OK"
        while((Get-VM -ComputerName $HVHost -Name $VMName).PrimaryOperationalStatus -ne "Ok"){
            Start-Sleep -Seconds 1
        }
        $VM = Get-VM -ComputerName $HVHost -Name $VMName
        $VMNICs = Get-VMNetworkAdapter -VM $VM
        foreach($VMNIC in $VMNICs){
            if($VMNIC.SwitchName -eq $SwitchName){
                Write-Host "Configure VMNIC"
                Set-VMNetworkAdapterVlan -VMNetworkAdapter $VMNIC -VlanId $VlanID -Access
                $Finished = $true
            }
        }

    }
}
Posted in Hyper-V, PowerShell | Tagged , , , , , , | Leave a comment

Send notification email from SCCM 2012 Task Sequence

I wanted to be able to send an email from a step in a SCCM 2012 OS Deployment (OSD) task sequence. This was so that I could be notified when the task sequence had completed successfully.

The account used to send the email needed to be a specific account, as otherwise Exchange would reject the message. I’d already created a sccm.notifications@rcmtech.co.uk account in Active Directory and a mailbox for it in Exchange.

I thought I’d just use a Run Command Line step, and run PowerShell.exe specifying a very basic script file on the command line containing a Send-MailMessage cmdlet. That fails to run with an error though, and the Run PowerShell Script step doesn’t have a “Run this step as the following account” option.

So I reverted to “good old” VBScript. This is a script that sends an email using CDO.Message:

Const cdoNTLM = 2 'NTLM
Set oMessage = CreateObject("CDO.Message") 
Dim sComputerName, sOSDComputerName, sSubject, sTextBody
Dim oShell
Set oShell = CreateObject("WScript.Shell")
sSubject = "Build Complete %COMPUTERNAME%"
sSubject = oShell.ExpandEnvironmentStrings(sSubject)
oMessage.Subject = sSubject
sTextBody = "Build Complete "&Now
oMessage.TextBody = sTextBody
oMessage.From = "sccm.notifications@rcmtech.co.uk"
oMessage.To = "robin@rcmtech.co.uk"
oMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
oMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "mail.rcmtech.co.uk"
oMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
oMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = cdoNTLM
oMessage.Configuration.Fields.Update
oMessage.Send

Save that into a file called EmailBuildComplete.vbs.

I then put this script file into an SCCM Package, and distributed it to my distribution points. At the end of the OSD task sequence, I then added a Run Command Line step called Send Email Notification, containing the following command line:

cscript.exe //nologo EmailBuildComplete.vbs

and set the step to run as my sccm.notifications account.

Posted in sccm, Scripting, Windows | Tagged , , , , , , , , , | Leave a comment

SCCM 2012 PXE-E55: ProxyDHCP service did not reply to request on port 4011

Had this today, didn’t have any of #60, #66 or #67 DHCP options set that might have been conflicting (per Microsoft guidelines).

I was able to build Hyper-V VMs at the site where the problem distribution point was, but PCs weren’t building, giving the error:

PXE-E55: ProxyDHCP service did not reply to request on port 4011

I then checked the SMSPXE.log file which is found on the distribution point on the path D:\SMS_DP$\sms\logs (drive letter might be different, hopefully you’re not using the C drive…)

I saw warnings and messages similar to:

Warning: Matching Processor Architecture Boot Image (0) not found
F8:CA:B8:22:A6:53, 4C4C4544-0051-3610-8030-B3C04F443732: Not serviced.

I checked the two boot images in the SCCM console under Software Library – Overview – Operating Systems – Boot Images. The Boot Image (x64) was distributed to all my DPs, but Boot Image (x86) was missing from the one at the site with the PXE problems.

I distributed the x86 boot image to the distribution point it was missing from, and once that had completed I saw it being picked up in smspxe.log:

Found new image RCM000BF
Opening image file D:\RemoteInstall\SMSImages\RCM000BF\boot.RCM000BF.wim
Found Image file: D:\RemoteInstall\SMSImages\RCM000BF\boot.RCM000BF.wim
PackageID: RCM000BF
ProductName: Microsoft® Windows® Operating System
Architecture: 0
Description: Microsoft Windows PE (x86)
Version:
Creator:
SystemDir: WINDOWS
Closing image file D:\RemoteInstall\SMSImages\RCM000BF\boot.RCM000BF.wim

PXE boot of the failing clients is now working.

Posted in sccm | Tagged , , , , , , , , , , , , , , , | Leave a comment

Standard user launching a command with elevated credentials

I needed standard users to be able to run certain executables with administrator credentials, but ideally without wanting to give them an administrator account and password, and certainly without adding them to the Administrators group. I also needed a log of the commands run with elevated credentials, which is what led me to this method.

I’m using the Application event log and the Task Scheduler. The problem with this method is that when Task Scheduler launches a process as a different user to that currently logged on on the console, the launched process cannot interact with the desktop. Sadly there’s no way around this that I’m away of, which is a shame.

This method might still be useful though if the users just need to run command line utilities and can redirect the output to a text or log file.

Step One – Create a new event source

This allows us to filter out events easily later, and makes the logged commands easy to find. From an Administrator PowerShell prompt issue the following command:

New-EventLog -LogName Application -Source "RunElevated"

Step Two – Create a scheduled task

Open Task Scheduler, create a basic task called RunElevated.
The trigger is When a specific event is logged.
The log is Application, the source is RunElevated, the Event ID is 1.
The action is Start a program.
For Program/script we’re using the full path to powershell.exe: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
The argument is a PowerShell one liner to pick the event Message data out of the most recent RunElevated event to be logged to the Application event log, and execute the data:

&(Get-WinEvent -FilterHashtable @{logname='application'; providername='RunElevated'} -MaxEvents 1).Message

Finally, edit the new scheduled task and choose the administrator account that you want the task to run as, select Run whether user is logged on or not and Run with highest privileges. When you click OK you’ll be prompted for the account’s password.

Step three

Now, as a regular user, we’re going to write a command line to the Application event log using PowerShell:

Write-EventLog -LogName Application -Source "RunElevated" -EntryType Information -EventId 1 -Message "C:\windows\system32\notepad.exe"

This gives us an event that looks like this:

Log Name:      Application
Source:        RunElevated
Date:          31/05/2016 14:29:55
Event ID:      1
Task Category: (1)
Level:         Information
Keywords:      Classic
User:          N/A
Computer:      Laptop001.rcmtech.co.uk
Description:
C:\windows\system32\notepad.exe

The task should trigger and you’ll see notepad running (hidden) as the admin user you specified when creating the scheduled task. Note terribly handy, but somebody might have a use for this method!

Posted in PowerShell, Windows | Tagged , , , , , , , , , , , , , , , , , | Leave a comment

PowerShell: Active Directory User to SID and SID to User

I was recently asked to find the username associated with a particular Active Directory SID (technically I was given the RID).

This is actually pretty easy in PowerShell, and quite intuitive using basic AD cmdlets.

Get the user for a given RID:

Get-ADUser -Filter * | Select-Object -Property SID,Name | Where-Object -Property SID -like "*-6640"

Get the SID for a given user:

Get-ADUser -Identity ad.user | Select-Object -Property Name,SID
Posted in PowerShell, Windows | Tagged , , , , , , , | 1 Comment