New-NpsRadiusClientWithTemplate.ps1

#requires -Version 2.0


<#PSScriptInfo
 
        .VERSION 0.1
 
        .GUID 59c4a6c3-5bd5-43cd-913b-f4e533f6ef73
 
        .AUTHOR Chris Masters
 
        .COMPANYNAME Chris Masters
 
        .COPYRIGHT (c) 2019 Chris Masters. All rights reserved.
 
        .TAGS NPS RADIUS SecretKey SharedSecret
 
        .LICENSEURI
 
        .PROJECTURI https://www.powershellgallery.com/profiles/masters274/
 
        .ICONURI
 
        .EXTERNALMODULEDEPENDENCIES
 
        .REQUIREDSCRIPTS
 
        .EXTERNALSCRIPTDEPENDENCIES
 
        .RELEASENOTES
        Initial release, with very little testing. I know it works for adding. And when you check the GUI, it shows
        the device as using the template. However, I have not gone so far as to setup a dev env so I could change
        the keys we use for our various SharedSecretTemplates.
 
        Some notes on the process (observed only, not research)...
 
        The IAS (Network Policy Server) service stores its files in $env:SystemRoot\System32\IAS\
 
        When you edit data, it gets entered into ias_converted.xml and ias.xml.
 
        I'm not sure of the process or reason for the two different files, as they're identical (SHA256 hashed).
        Maybe the changes are entered into the converted files, checked for proper formatting, then copied to
        ias.xml...?
 
        Either way, I don't think it would be a good idea to edit these files directly, because stuff happens.
 
        PROCESS:
         
        - First the script creates a session with your NPS server.
        - Creates a new NPS radius client
        - Exports the configuration (messy, I know. Yet will be worth it when you need to change the key later)
        - Changes the GUID for the template used on that client
        - Then imports the changed config
        - And optionaly restarts the service. I've noticed that it doesn't take affect until a service restart when
        editing via the command line.
 
 
        Also of note.... it wasn't 3am when I wrote this, so not my best work... but it was pretty late, so maybe
        it's not so bad.
 
        .PRIVATEDATA
#>
 


<#
        .Synopsis
        Adds a new NPS RADIUS client, and applies an authentication template to the client
 
        .DESCRIPTION
        Why Microsoft doesn't give you the ability to apply a template when creating a new client puzzles me.
        This script aims to solve this problem, as many organizations have a lot of devices, and a password
        policy that requires changing RADIUS secrets. You don't want to have to update all the clients, just the
        template. That why we use them...!
 
        This may not be the best way to solve this problem, however it does work.
 
        .EXAMPLE
        Example of how to use this cmdlet
 
        .EXAMPLE
        Another example of how to use this cmdlet
 
        .NOTES
        I would suggest that you add default parameter values into your profile. You don't want to put in values
        that don't change often, every time.
 
        $PSDefaultParameterValues = @{
        ComputerName = 'myNpsServer.domain.name'
        AuthenticationTemplate = 'Cisco_Template'
        Vendor = 'Cisco'
        }
#>



[CmdLetBinding()]
Param
(
    [Parameter(Mandatory=$true, position=0, HelpMessage='Client name', ValueFromPipeline=$true, 
    ValueFromPipelineByPropertyName=$true)]
    [String] $ClientName,

    [Parameter(Mandatory=$true, position=1, HelpMessage='Client IP', ValueFromPipelineByPropertyName=$true)]
    [IPAddress] $IPAddress,

    [Parameter(Mandatory=$true, HelpMessage='Name of computer to manage', ValueFromPipelineByPropertyName=$true)]
    [String] $AuthenticationTemplate,

    [Parameter(Mandatory=$false, HelpMessage='Radius client vendor name. i.e cisco', ValueFromPipelineByPropertyName=$true)]
    [String] $Vendor = 'RADIUS Standard',

    [Parameter(Mandatory=$true, HelpMessage='Name of NPS server(s)')]
    [String[]] $ComputerName,

    [Switch] $Restart, # The service needs to be restarted before the settings take effect

    [Parameter(Mandatory=$false, HelpMessage='Credentials for administering NPS')]
    [System.Management.Automation.Credential()]
    [PSCredential] $Credential,

    [ValidateSet('Default','Basic','CredSSP','Digest','Kerberos','Negotiate','NegotiateWithImplicitCredential')]
    [String] $Authentication = 'Kerberos'
)
    
Begin
{
    # If you don't want ur stuff to work, don't restart
    If (!$Restart)
    {
        Write-Warning -Message 'You need to restart before settings take affect'
    }
    
    # Constants
       
    $Params = @{
        ComputerName = $ComputerName
        Authentication = $Authentication
    }
    
    If ($Credential)
    {
        $Params += @{
            Credential = $Credential
        }
    }
    
    # We only need to make our connection once
    $s = New-PSSession @Params
}
    
Process
{
    # Variables
    
    [ScriptBlock] $sbCommand = {
    
        Param
        (
            $ClientName,
            
            $IPAddress,
            
            $AuthenticationTemplate,
            
            $Vendor,
            
            $Restart
        )
        
        
        Try
        {
            # IAS templates are stored in a file on the NPS server.
            [String] $strIASFile = $env:SystemRoot + '\System32\ias\iastemplates.xml'
          
            [xml] $objIAS = Get-Content -Path $strIASFile
          
            [String] $strTemplateGuid = $objIAS.GetElementsByTagName($AuthenticationTemplate).Properties.Template_Guid.InnerText
            
            [String] $strSharedSecret = $objIAS.GetElementsByTagName($AuthenticationTemplate).Properties.RADIUS_Shared_Secret.InnerText
  
            # We need to make the object firt, then we'll apply the auth template
            New-NpsRadiusClient -Name $ClientName -Address ($IPAddress.ToString()) -AuthAttributeRequired $False -VendorName $Vendor -SharedSecret $strSharedSecret
          
            # we now need to export the config, update the affected object, then re-import
            $strTempExportFile = [System.IO.Path]::GetTempPath() + [Guid]::NewGuid().guid + '.xml'
            $strTempImportFile = [System.IO.Path]::GetTempPath() + [Guid]::NewGuid().guid + '.xml'
          
            Export-NpsConfiguration -Path $strTempExportFile 
          
            [xml] $objNPSExport = Get-Content $strTempExportFile
        }
        Catch
        {
            "Error was $_"
            $line = $_.InvocationInfo.ScriptLineNumber
            "Error was in Line $line"
        }
        
        
        Try
        {
            $cleanedClientName = $ClientName -replace '\s|\-','_'
            
            $objNPSExport.GetElementsByTagName($cleanedClientName).properties.Client_Secret_Template_Guid.'#text' = $strTemplateGuid
        }
        Catch
        {
            # No need to keep it, if we can't find it later.
            Remove-NpsRadiusClient -Name $ClientName -ErrorAction SilentlyContinue
            
            # clean up the temp file
            Remove-Item -Path $strTempExportFile
            
            Write-Error -Message 'Unable to find your object. Please only use uderscores "_", instead of spaces or dashes "-"'
            
            # exiting
            return
        }
        
        
        $objNPSExport.Save($strTempImportFile)
        
        Import-NpsConfiguration -Path $strTempImportFile
        
        # Clean up
        remove-item -Path $strTempExportFile -ErrorAction SilentlyContinue
        Remove-Item -Path $strTempImportFile -ErrorAction SilentlyContinue
        
        # when making changes to clients, the service has to be restarted.
        If ($Restart)
        {
            Restart-Service -Name IAS
        }
    }
    
    Invoke-Command -Session $s -ScriptBlock $sbCommand -ArgumentList $ClientName, $IPAddress, $AuthenticationTemplate, $Vendor, $Restart
}
    
End
{
    Remove-PSSession -Session $s
}