AD FS, Azure Stack, Uncategorized Danny McDermott AD FS, Azure Stack, Uncategorized Danny McDermott

Creating an Azure Stack AD FS SPN for use with az CLI

Following on from my previous blog post on filling in the gaps for AD FS on Azure Stack integrated systems, here are some more complete instructions on creating a Service Principal on Azure Stack systems using AD FS as the identity provider. Why do you need this? Well, check out the following scenarios as taken from https://docs.microsoft.com/en-us/azure/azure-stack/azure-stack-integrate-identity#spn-creation:

There are many scenarios that require the use of a service principal name (SPN) for authentication. The following are some examples:

  • CLI usage with AD FS deployment of Azure Stack

  • System Center Management Pack for Azure Stack when deployed with AD FS

  • Resource providers in Azure Stack when deployed with AD FS

  • Various third party applications

  • You require a non-interactive logon

azsADFS.png

Following on from my previous blog post on filling in the gaps for AD FS on Azure Stack integrated systems, here are some more complete instructions on creating a Service Principal on Azure Stack systems using AD FS as the identity provider. Why do you need this? Well, check out the following scenarios as taken from https://docs.microsoft.com/en-us/azure/azure-stack/azure-stack-integrate-identity#spn-creation:

There are many scenarios that require the use of a service principal name (SPN) for authentication. The following are some examples:

  • CLI usage with AD FS deployment of Azure Stack

  • System Center Management Pack for Azure Stack when deployed with AD FS

  • Resource providers in Azure Stack when deployed with AD FS

  • Various third party applications

  • You require a non-interactive logon

I’ve highlighted the first point ‘CLI usage with AD FS deployment of Azure Stack’. This is significant as AD FS only supports interactive login. At this point in time, the AZ CLI does not support interactive mode, so you must use a service principal.

There are a few areas that weren’t clear to me at first, so I worked it all out and tried to simplify the process.

At a high level, these are the tasks:

  • Create an X509 certificate (or use an existing one) to use for authentication

  • Create a new Service Principal (Graph Application) on the internal Azure Stack domain via PEP PowerShell session

  • Return pertinent details, such as Client ID, cert thumbprint, Tenant ID and relevant external endpoints for the Azure Stack instance

  • Export the certificate as PFX (for use on clients using PowerShell) and PEM file including private certificate (for use with Azure CLI)

  • Give the Service Principal permissions to the subscription

Here’s the link to the official doc’s: https://docs.microsoft.com/en-gb/azure/azure-stack/azure-stack-create-service-principals#create-service-principal-for-ad-fs

I’ve automated the process by augmenting the script provided in the link above. It creates a self-signed cert, AD FS SPN and files required to connect. It needs to be run on a system that has access to the PEP and also has the Azure Stack PowerShell module installed.

The script includes the steps to export the PFX (so you can use it with PowerShell on other systems) and PEM files, plus output ALL the relevant info you will need to connect via AZ CLI/ PoSh


# Following code taken from https://github.com/mongodb/support-tools/blob/master/ssl-windows/Convert-PfxToPem.ps1

Add-Type @'
   using System;
   using System.Security.Cryptography;
   using System.Security.Cryptography.X509Certificates;
   using System.Collections.Generic;
   using System.Text;
   public class Cert_Utils
   {
      public const int Base64LineLength = 64;
      private static byte[] EncodeInteger(byte[] value)
      {
         var i = value;
         if (value.Length > 0 && value[0] > 0x7F)
         {
            i = new byte[value.Length + 1];
            i[0] = 0;
            Array.Copy(value, 0, i, 1, value.Length);
         }
         return EncodeData(0x02, i);
      }
      private static byte[] EncodeLength(int length)
      {
         if (length < 0x80)
            return new byte[1] { (byte)length };
         var temp = length;
         var bytesRequired = 0;
         while (temp > 0)
         {
            temp >>= 8;
            bytesRequired++;
         }
         var encodedLength = new byte[bytesRequired + 1];
         encodedLength[0] = (byte)(bytesRequired | 0x80);
         for (var i = bytesRequired - 1; i >= 0; i--)
            encodedLength[bytesRequired - i] = (byte)(length >> (8 * i) & 0xff);
         return encodedLength;
      }
      private static byte[] EncodeData(byte tag, byte[] data)
      {
         List result = new List();
         result.Add(tag);
         result.AddRange(EncodeLength(data.Length));
         result.AddRange(data);
         return result.ToArray();
      }
       
      public static string RsaPrivateKeyToPem(RSAParameters privateKey)
      {
         // Version: (INTEGER)0 - v1998
         var version = new byte[] { 0x02, 0x01, 0x00 };
         // OID: 1.2.840.113549.1.1.1 - with trailing null
         var encodedOID = new byte[] { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
         List privateKeySeq = new List();
         privateKeySeq.AddRange(version);
         privateKeySeq.AddRange(EncodeInteger(privateKey.Modulus));
         privateKeySeq.AddRange(EncodeInteger(privateKey.Exponent));
         privateKeySeq.AddRange(EncodeInteger(privateKey.D));
         privateKeySeq.AddRange(EncodeInteger(privateKey.P));
         privateKeySeq.AddRange(EncodeInteger(privateKey.Q));
         privateKeySeq.AddRange(EncodeInteger(privateKey.DP));
         privateKeySeq.AddRange(EncodeInteger(privateKey.DQ));
         privateKeySeq.AddRange(EncodeInteger(privateKey.InverseQ));
         List privateKeyInfo = new List();
         privateKeyInfo.AddRange(version);
         privateKeyInfo.AddRange(encodedOID);
         privateKeyInfo.AddRange(EncodeData(0x04, EncodeData(0x30, privateKeySeq.ToArray())));
         StringBuilder output = new StringBuilder();
         var encodedPrivateKey = EncodeData(0x30, privateKeyInfo.ToArray());
         var base64Encoded = Convert.ToBase64String(encodedPrivateKey, 0, (int)encodedPrivateKey.Length);
         output.AppendLine("-----BEGIN PRIVATE KEY-----");
         for (var i = 0; i < base64Encoded.Length; i += Base64LineLength)
            output.AppendLine(base64Encoded.Substring(i, Math.Min(Base64LineLength, base64Encoded.Length - i)));
         output.Append("-----END PRIVATE KEY-----");
         return output.ToString();
      }
      public static string PfxCertificateToPem(X509Certificate2 certificate)
      {
         var certBase64 = Convert.ToBase64String(certificate.Export(X509ContentType.Cert));
         var builder = new StringBuilder();
         builder.AppendLine("-----BEGIN CERTIFICATE-----");
         for (var i = 0; i < certBase64.Length; i += Cert_Utils.Base64LineLength)
            builder.AppendLine(certBase64.Substring(i, Math.Min(Cert_Utils.Base64LineLength, certBase64.Length - i)));
         builder.Append("-----END CERTIFICATE-----");
         return builder.ToString();
      }
   }
'@

# Credential for accessing the ERCS PrivilegedEndpoint typically domain\cloudadmin 
$creds = Get-Credential 
$pepIP = "172.16.101.224"
$date = (get-date).ToString("yyMMddHHmm")
$appName = "appSPN"

$PEMFile = "c:\temp\$appName-$date.pem"
$PFXFile = "c:\temp\$appName-$date.pfx"

# Creating a PSSession to the ERCS PrivilegedEndpoint 
$session = New-PSSession -ComputerName $pepIP -ConfigurationName PrivilegedEndpoint -Credential $creds

 # This produces a self signed cert for testing purposes. It is preferred to use a managed certificate for this. 
$cert = New-SelfSignedCertificate -CertStoreLocation "cert:\CurrentUser\My" -Subject "CN=$appName" -KeySpec KeyExchange 
$ServicePrincipal = Invoke-Command -Session $session {New-GraphApplication -Name $args[0] -ClientCertificates $args[1]} -ArgumentList $appName,$cert
$AzureStackInfo = Invoke-Command -Session $session -ScriptBlock { get-azurestackstampinformation } 
$session|remove-pssession 

# For Azure Stack development kit, this value is set to https://management.local.azurestack.external. We will read this from the AzureStackStampInformation output of the ERCS VM. 
$ArmEndpoint = $AzureStackInfo.TenantExternalEndpoints.TenantResourceManager 
$AdminEndpoint = $AzureStackInfo.AdminExternalEndpoints.AdminResourceManager 
# For Azure Stack development kit, this value is set to https://graph.local.azurestack.external/. We will read this from the AzureStackStampInformation output of the ERCS VM. 
$GraphAudience = "https://graph." + $AzureStackInfo.ExternalDomainFQDN + "/" 
# TenantID for the stamp. We will read this from the AzureStackStampInformation output of the ERCS VM. 
$TenantID = $AzureStackInfo.AADTenantID 
# Register an AzureRM environment that targets your Azure Stack instance 
Add-AzureRMEnvironment ` -Name "azurestacktenant" ` -ArmEndpoint $ArmEndpoint 
Add-AzureRMEnvironment ` -Name "azurestackadmin" ` -ArmEndpoint $AdminEndpoint 

# Set the GraphEndpointResourceId value 
Set-AzureRmEnvironment ` -Name "azurestacktenant" -GraphAudience $GraphAudience -EnableAdfsAuthentication:$true 
    
Add-AzureRmAccount -EnvironmentName "azurestacktenant" `
 -ServicePrincipal ` -CertificateThumbprint $ServicePrincipal.Thumbprint `
  -ApplicationId $ServicePrincipal.ClientId `
   -TenantId $TenantID
 

# Output details required to pass to PowrShell or AZ CLI 
write-host "ClientID          : $($ServicePrincipal.ClientId)"
write-host "Cert Thumbprint   : $($ServicePrincipal.Thumbprint)"
write-host "Application Name  : $($ServicePrincipal.ApplicationName)"
write-host "TenantID          : $TenantID"
write-host "ARM EndPoint      : $ArmEndpoint"
write-host "Admin Endpoint    : $AdminEndpoint"
write-host ""
write-host "PEM Cert path     : $PEMFile"
write-host "PFX Cert Path     : $PFXFile"


# Export the Cert to a pem file for user with Azure CLI
$result = [Cert_Utils]::PfxCertificateToPem($cert)

$parameters = ([Security.Cryptography.RSACryptoServiceProvider] $cert.PrivateKey).ExportParameters($true)
$result += "`r`n" + [Cert_Utils]::RsaPrivateKeyToPem($parameters);

$result | Out-File -Encoding ASCII -ErrorAction Stop  $PEMFile


# Now Export the cert to PFX
$pw = Read-Host "Enter PFX Certificate Password" -AsSecureString
Export-PfxCertificate -cert $cert -FilePath $PFXFile -Password $pw

Here is an example of the output produced:

Next, connect to the Tenant Portal and give the Service Principal access to the subscription you want it to have access to:

Once you’ve done the above, here are the high-level steps to use the Service Principal account with Azure CLI:

  • Trust the Azure Stack CA Root Certificate (if using Enterprise CA / ASDK) within AZ CLI (Python). This is a one-time operation per system you’re running AZ CLI on.

  • Register Azure Stack environment (either tenant/user or admin)

  • Set the active cloud environment for CLI

  • Set the CLI to use Azure Stack compatible API version

  • Sign into the Azure Stack environment with service principal account

For reference, here are the official links with the information on how to do it. It works well, so just follow those:

https://docs.microsoft.com/en-us/azure/azure-stack/user/azure-stack-version-profiles-azurecli2


 
az cloud register -n AzureStackUser --endpoint-resource-manager 'https://management.' --suffix-storage-endpoint '' --suffix-keyvault-dns '.vault.'   

az cloud register -n AzureStackAdmin --endpoint-resource-manager 'https://adminmanagement.' --suffix-storage-endpoint '' --suffix-keyvault-dns '.vault.'

az cloud set -n AzureStackUser

az cloud update --profile 2017-03-09-profile

az login --tenant   --service-principal  -u  -p 

Read More
Linux, Uncategorized Mike DeLuca Linux, Uncategorized Mike DeLuca

Openshift 3.5 installation headaches (and where to find the aspirin)

I've been in the container business for over a year now, and have worked with Docker and Kubernetes. So when the opportunity arose to work with Openshift, I figured it would be a great skill to add, and should be pretty easy since it runs on Kubernetes, and I'm already familiar with that.

OpenShift-LogoType.svg_-281x300.png

I've been in the container business for over a year now, and have worked with Docker and Kubernetes. So when the opportunity arose to work with Openshift, I figured it would be a great skill to add, and should be pretty easy since it runs on Kubernetes, and I'm already familiar with that.

Well, the headaches started almost immediately. This isn't due to the nature of the product, but rather the installation documentation. If you go to Red Hat and look up the quickstart guide, it gives instructions for installing Openshift 3.0. Well, I wanted 3.5, so I decided to try changing things up to make it work.

If you follow the instructions, you're likely to end up with errors regarding openvswitch during the atomic-openshift-installer process. It all comes back to our old friend, dependencies.

Here's a basic outline of what you need to do to install Openshift. I'm not going into details, except to call out where things need to be done differently from the documentation:

Ensure your hosts are running RHEL 7.5 and have either XFS for overlay storage, or have a dedicated block device you can use for Openshift storage.

Ensure DNS resolution

Install prerequisites - This is the important part:

subscription-manager attach --pool=<your pool id here> subscription-manager repos --disable=* subscription-manager repos --enable=rhel-7-server-rpms --enable=rhel-7-server-optional-rpms --enable=rhel-7-server-extras-rpms --enable=rhel-7-server-ose-3.5-rpms --enable=rhel-7-fast-datapath-rpms yum -y update

Notice the last repo, rhel-7-fast-datapath-rpms. This repo contains the current version of openvswitch.

Create a root ssh key (or a key for a service account) and copy it to all nodes.

Install docker and configure docker storage

Install dependency packages:

yum -y install atomic-openshift-docker-excluder atomic-openshift-excluder atomic-Openshift-utils bridge-utils git iptables-services net-tools wget openvswitch

Notice the excluders in the list. These are handy for preventing yum from updating Openshift or docker before you're ready to update them. These are also optional and won't prevent the Openshift install if they are omitted.

Install Openshift:

atomic-openshift-excluder unexclude atomic-openshift-installer install

The excluder line is only necessary if you installed the excluder in the first place. If you unexclude openshift, make sure you exclude it again when you're done.

Read More
Linux, Uncategorized Mike DeLuca Linux, Uncategorized Mike DeLuca

Adding your custom images to MaaS

n my last two posts, we covered creating custom Windows and RHEL images for MaaS. Now we'll deal with uploading them for use. The process is relatively simple:

1. Upload your image to the MaaS server

maas-logo.png

In my last two posts, we covered creating custom Windows and RHEL images for MaaS. Now we'll deal with uploading them for use. The process is relatively simple:

1. Upload your image to the MaaS server

2. Import the image to MaaS

The first step is easy enough, just use SCP (or a program like WinSCP in Windows) to upload the file:

Capture1-300x193.png

To Import the image in MaaS, use the following command:

maas $profile boot-resources create name=custom/$imagedisplayname architecture=amd64/generic content=@$tgzfilepath

In the code, I've used variables to make explanation simpler. In real life, I'd just input the actual values. Following is an explanation of each value:

$profile - When using the maas command, you must first log in. This variable represents your maas profile name.

$imagedisplayname - You can put any value here. Preceding it by custom/ means that the image will show up under custom images in MaaS with the name specified following the /

$tgzfilepath - This is the full path to the image file on the MaaS server. If you are admin2 and you kept an image namede my-image.tgz in a directory called images under your home directory, this value would be /home/admin2/images/my-image.tgz

Read More
PowerShell, Uncategorized Mike DeLuca PowerShell, Uncategorized Mike DeLuca

Creating a custom Windows image for MaaS

Continuing on the Maas theme from yesterday, I thought I'd put up a post about my experience with Windows imaging for MaaS. The Windows Openstack Imaging Tools are great for creating Windows images for Maas. They provide support for creating gold images, custom drivers, and pretty much anything else you'd want for custom image creation.

index.jpg

Continuing on the Maas theme from yesterday, I thought I'd put up a post about my experience with Windows imaging for MaaS. The Windows Openstack Imaging Tools are great for creating Windows images for Maas. They provide support for creating gold images, custom drivers, and pretty much anything else you'd want for custom image creation.

Included is support for UEFI or BIOS boot options. This is where I ran into issues. I found that Hyper-V wasn't having secureboot turned off for the VM, causing BIOS builds to fail. I found the problem and fixed it relatively easily. After successfully creating a BIOS image, and finding it wouldn't install on most of our equipment due to it being configured for UEFI, I decided to create a UEFI image.

Creating a UEFI image errored out, and so the hunt for the issue began. I won't bore you with the tedium of digging through code to figure out where the problem lay, but there are two changes that need to be made to the PS module that does most of the heavy lifting in the solution.

In the install directory, there is a file called WinImageBuilder.psm1. The first fix is to insert the following code right after line 902.


if ($DiskLayout -eq "UEFI")         {         $Drive = $Drive[1]         }

The solution doesn't treat the disk layout as an array if UEFI is selected, so naturally the disk optomization phase fails.

Following that, the next change is after line 1006. Insert the following code:


if ($DiskLayout -ne "UEFI")         {         Set-VMFirmware -VMName (Get-VM).Name -EnableSecureBoot Off         }

Turning off secureboot enables BIOS builds to succeed.

Once these fixes are in place, you can set your variables and have a successful build .

Read More
Linux, Uncategorized Mike DeLuca Linux, Uncategorized Mike DeLuca

Creating a custom RHEL image for MAAS

A little change from our typical programming, today's post is about how to add an image to an Ubuntu MaaS instance. So what is a Sr Consultant, specializing in Azure and containerization doing working with Linux in an on-premises environment?

index.jpg

A little change from our typical programming, today's post is about how to add an image to an Ubuntu MaaS instance. So what is a Sr Consultant, specializing in Azure and containerization doing working with Linux in an on-premises environment?

As far-removed as it seems from the norm of what we do at Avanade, there is still a lot of demand for on-prem solutions. Many cases are like ours; we have a large lab full of equipment we've already paid for and it makes fiscal sense to do much of our dev work there.

In our lab, we utilize MAAS (Metal as a Service) to manage our hardware. Out of the box (for the free version of MAAS at least), MAAS comes with images for Ubuntu and CENTOS. With the paid version of MAAS, you get Windows and RHEL images as well.

A brief glance around the internet showed that there used to be a tool called MAAS-image-builder that allows for the custom creation of RHEL images. Ubuntu seems to have burried many of the references to this once they started using images as a differentiation between free and paid versions. There are actually a few branches of this, none of which worked out of the box.

The version we eventually got to work was this one: https://code.launchpad.net/~ltrager/maas-image-builder/update_ks

To make it work, you must build it as per the instructions, and once installed, make a few changes:

1. Add the following to /usr/lib/maas-image-builder/contrib/rhel/rhel7-amd64.ks in the %packages section:

Capture-215x300.png

Without the python2-oauthlib.noarch and python2-requests-oauthlib.noarch packages, the install will complete, but MAAS will register it as a failed deployment.

Additionally, as anyone familiar with kickstart files will probably tell you, it's advisable to create a standard user account, and register your subscription in addition to any other customization you want to add to your image.

One caveat however: Keep in mind that no matter what you do with the part command in your kickstart, it won't actually make a difference to the final install process in MAAS. MAAS manages storage layouts on its own, and won't allow a custom storage layout for any OS other than Ubuntu.

Read More