functions/New-ServicePrincipalObject.ps1
Function New-ServicePrincipalObject { <# .SYNOPSIS PowerShell module for creating, retrieving and removing Azure registered / enterprise applications and service principals. .DESCRIPTION This module will create creating, retrieving and remove Azure registered / enterprise applications and service principals objects that can be used for automation tasks. Enterprise applications created will also have a mirror service principal objects created. Azure registered applications will be linked to a new service principal which can then be use as an authentication mechanism for connecting applications and PowerShell session to an Office 365 and Azure tenant. The PSServicePrincipal logging provider is based on PSFramework: All messages are logged by default 'Documents\PowerShell Script Logs' on Windows and 'Documents/PowerShell Script Logs' on MacOS. There are two logging streams (output and debug). Both streams have respective folders for analysis: You can run Get-LogFolder -LogFolder [OutputLoggingFolder] and [DebugLoggingFolder] to access either logging stream directory. For more information on PSFramework please visit: https://psframework.org/ PSFramework Logging: https://psframework.org/documentation/quickstart/psframework/logging.html PSFramework Configuration: https://psframework.org/documentation/quickstart/psframework/configuration.html PSGallery - PSFramework module - https://www.powershellgallery.com/packages/PSFramework/1.0.19 .PARAMETER CreateSelfSignedCertificate Used when creating a single self-signed certificate to be used with registered and enterprise applications for certificate based connections. .PARAMETER CreateSingleObject Used when creating a single default enterprise application (service principal). .PARAMETER CreateBatchObjects Used when creating a batch of service principals from a text file. .PARAMETER CreateSPNWithAppID Used when creating a service principal and a registered Azure ApplicationID. .PARAMETER CreateSPNWithPassword Used when creating a service principal and a registered Azure application with a user supplied password. .PARAMETER CreateSPNsWithNameAndCert Used when creating a service principal and a registered Azure application using a display name and certificate. .PARAMETER Cba Used to create a registered application, self-signed certificate, upload to the application, applies the correct application roll assignments. .PARAMETER UploadCertToApp Switch used to create a registered application, self-signed certificate, upload to the application, applies the correct application roll assignments. .PARAMETER EnableException Disables user-friendly warnings and enables the throwing of exceptions. This is less user friendly, but allows catching exceptions in calling scripts. .PARAMETER RegisteredApp Switch used to create an Azure registered application. .PARAMETER Reconnect Used when forcing a new connection to an Azure tenant subscription. .PARAMETER ApplicationID Unique ApplicationId for a service principal in a tenant. Once created this property cannot be changed. .PARAMETER Certificate This parameter is the value of the "asymmetric" credential type. It represents the base 64 encoded certificate. .PARAMETER DisplayName DisplayName of the objects you are retrieving. .PARAMETER NameFile Name of the file that contains the list of service principals being passed in for creation. .PARAMETER ObjectID ObjectId for a service principal in a tenant. Once created this property cannot be changed. .Parameter DumpCerts Switch indicating to display both certificate store .EXAMPLE PS c:\> New-ServicePrincipalObject -Reconnect Force a reconnect to a specific Azure tenant. .EXAMPLE PS c:\> New-ServicePrincipalObject -CreateSelfSignedCertificate Create a basic self-signed certificate to be used with registered and enterprise applications for certificate-based connections. .EXAMPLE PS c:\> New-ServicePrincipalObject -DisplayName CompanySPN -CreateSingleObject Create a new service principal with a display name of 'CompanySPN' and password (an autogenerated GUID) and creates the service principal based on the application just created. The start date and end date are added to password credential. .EXAMPLE PS c:\> New-ServicePrincipalObject -DisplayName CompanySPN -CreateSingleObject -CreateSPNWithPassword Create a new Enterprise Application and service principal with a display name of 'CompanySPN' and a (user supplied password) and creates the service principal based on the application just created. The start date and end date are added to password credential. .EXAMPLE PS c:\> New-ServicePrincipalObject -DisplayName CompanyApp -CreateSingleObject -RegisteredApp -Cba Create a registered application with a display name of 'CompanyApp', a self-signed certificate which is uploaded to the application and applies the correct appRoll assignments. .EXAMPLE PS c:\> New-ServicePrincipalObject -ApplicationID 34a23ad2-dac4-4a41-bc3b-d12ddf90230e -CreateSingleObject -CreateSPNWithAppID Create a new Enterprise Application and service principal with the ApplicationID '34a23ad2-dac4-4a41-bc3b-d12ddf90230e'. .EXAMPLE PS c:\> New-ServicePrincipalObject -CreateBatchObjects -NameFile c:\temp\YourDataFile.txt Connect to an Azure tenant and creates a batch of enterprise applications and service principal objects from a file passed in. .EXAMPLE PS c:\> New-ServicePrincipalObject -DisplayName CompanySPN -CreateSPNsWithNameAndCert -CreateSingleObject -Certificate <public certificate as base64-encoded string> Create a new Enterprise Application and service principal with a display name of 'CompanySPN' and certificate and creates the service principal based on the application just created. The end date is added to key credential. .NOTES When passing in the application ID it is the Azure ApplicationID from your registered application. WARNING: If you do not connect to an Azure tenant when you run Import-Module Az.Resources you will be logged in interactively to your default Azure subscription. After signing in, you will see information indicating which of your Azure subscriptions is active. If you have multiple Azure subscriptions in your account and want to select a different one, get your available subscriptions with Get-AzSubscription and use the Set-AzContext cmdlet with your subscription id. INFORMATION: The default parameter set uses default values for parameters if the user does not provide any. For more information on the default values used, please see the description for the given parameters below. This cmdlet can assign a role to the service principal with the Role 'Contributor'. Roles are applid at the end of the service principal creation. For more information on Azure RBAC roles please see: https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles Microsoft TechNet Documentation: https://docs.microsoft.com/en-us/powershell/module/az.resources/new-azadserviceprincipal?view=azps-3.8.0 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] [CmdletBinding(DefaultParameterSetName = 'Default')] [OutputType('System.Boolean')] [OutputType('System.String')] param( [parameter(ParameterSetName = "Reconnect")] [switch] $Reconnect, [parameter(ParameterSetName = "DisplayName", HelpMessage = "Display name used to create or delete an SPN or application")] [ValidateNotNullOrEmpty()] [string] $DisplayName = "Default", [parameter(ParameterSetName = "DisplayName", HelpMessage = "Switch indicating to create a single service principal or application")] [switch] $CreateSingleObject, [parameter(ParameterSetName = "DisplayName", HelpMessage = "Switch indicating to display both certificate stores")] [switch] $DumpCerts, [parameter(ParameterSetName = "DisplayName", HelpMessage = "Switch for indicating to create self-signed certificate and upload it to the Azure application")] [switch] $Cba, [parameter(ParameterSetName = "DisplayName", HelpMessage = "Switch for indicating to create self-signed certificate and upload it to the Azure application")] [switch] $UploadCertToApp, [parameter(ParameterSetName = "DisplayName", HelpMessage = "Switch for indicating we want to create a Registered Azure application")] [switch] $RegisteredApp, [parameter(ParameterSetName = "CreateSelfSignedCertificate")] [switch] $CreateSelfSignedCertificate, [parameter(Mandatory = $True, ParameterSetName = 'CreateBatchObjects', HelpMessage = "Switch used to create batch objects")] [switch] $CreateBatchObjects, [parameter(Mandatory = $True, ParameterSetName = 'CreateBatchObjects', HelpMessage = "Name file used to create a batch of spn's")] [ValidateNotNullOrEmpty()] [string] $NameFile, [parameter(ParameterSetName = "ID", HelpMessage = "ApplicationID used to create or delete an SPN or application")] [ValidateNotNullOrEmpty()] [Guid] $ApplicationID, [parameter(ParameterSetName = "ID", HelpMessage = "Switch for creating spn with an ApplicationID")] [switch] $CreateSPNWithAppID, [parameter(ParameterSetName = "DisplayName", HelpMessage = "Switch for creating spn with a certificate and Base64 string")] [switch] $CreateSPNsWithNameAndCert, [parameter(ParameterSetName = "DisplayName", HelpMessage = "Switch for creating spn with a password")] [switch] $CreateSPNWithPassword, [parameter(HelpMessage = "Certificate parameter for a created spn")] [ValidateNotNullOrEmpty()] [string] $Certificate, [parameter(HelpMessage = "Enable exception logging")] [switch] $EnableException ) process { # Adding admin check for Windows Version 2004 which needs admin access due to UAC if (-NOT (Test-PSFPowerShell -Elevated)) { Write-PSFMessage -Level Host -Message "PSServicePrincipal needs PowerShell to run as an administrator if you are running on Windows 10 version 2004 or trying to save a certificate to the local machine certificate store. Exiting" -StringValues $module return } $script:certCounter = 0 $script:certExportedCounter = 0 $script:runningOnCore = $false $script:AzSessionFound = $false $script:AdSessionFound = $false $script:AzSessionInfo = $null $script:AdSessionInfo = $null $script:roleListToProcess = New-Object -Type System.Collections.ArrayList $requiredModules = @("AzureAD", "Az.Accounts", "Az.Resources") Foreach ($module in $requiredModules) { if (-NOT (Get-Module -Name $module)) { Import-Module $module; Write-PSFMessage -Level Verbose -Message "Importing required modules {0}" -StringValues $module } } $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Reconnect Write-PSFMessage -Level Host -Message "Starting script run: {0}" -StringValues (Get-Date) if ($DumpCerts) { Write-PSFMessage -Level Host -Message "Opening CurenntUser and LocalMachine Certificate Stores" Get-ChildItem Cert:\CurrentUser\My | Sort-Object Get-ChildItem Cert:\LocalMachine\My | Sort-Object return } if ($CreateSelfSignedCertificate) { Invoke-PSFProtectedCommand -Action "Attempting to create self-signed certificate!" -Target $parameter.Values -ScriptBlock { New-SelfSignedCert @parameter -EnableException -ErrorAction Stop } -EnableException $EnableException -PSCmdlet $PSCmdlet } Invoke-PSFProtectedCommand -Action "Attempting to Connecting to cloud!" -Target $parameter.Values -ScriptBlock { Connect-ToCloudTenant @parameters -EnableException return } -EnableException $EnableException -PSCmdlet $PSCmdlet if ($CreateBatchObjects) { Write-PSFMessage -Level Host -Message "Testing access to {0}" -StringValues $NameFile if (-NOT (Test-Path -Path $NameFile)) { Stop-PSFFunction -Message "ERROR: File problem. Exiting" -EnableException $EnableException -Cmdlet $PSCmdlet -ErrorRecord $_ return } else { Write-PSFMessage -Level Host -Message "{0} accessable. Reading file contents" -StringValues $NameFile $objectsToCreate = Get-Content $NameFile | Where-Object { $_ -ne "" } # Validate that we have data and if we dont we exit out if (0 -eq $objectsToCreate.Length) { Stop-PSFFunction -Message "Error with imported content. Exiting" -EnableException $EnableException -Cmdlet $PSCmdlet -ErrorRecord $_ return } } # Check to make sure we have the list of objects to process if (-NOT $objectsToCreate) { Write-PSFMessage -Level Warning "ERROR: No list of objects found!" return } Write-PSFMessage -Level Host -Message "Object list DETECTED! Staring batch creation of SPN's" if ($RegisteredApp) { Write-PSFMessage -Level Host -Message "Creating batch registered Applications" if (-NOT $script:runningOnCore) { foreach ($DisplayName in $objectsToCreate) { try { $newApp = New-AzureADApplication -DisplayName $DisplayName -ErrorAction SilentlyContinue -ErrorVariable ProcessError } catch { Stop-PSFFunction -Message "ERROR creating batch spn's" -EnableException $EnableException -Cmdlet $PSCmdlet -ErrorRecord $_ return } if ($newApp) { Write-PSFMessage -Level Host -Message "Registered Application created: DisplayName: {0} - ApplicationID {1}" -StringValues $newApp.DisplayName, $newApp.AppId $script:roleListToProcess.Add($newApp) # Since we only create an AzureAD application we need to create the matching service principal try { New-ServicePrincipal -ApplicationID $newApp.AppID } catch { Stop-PSFFunction -Message "ERROR creating batch spn's" -EnableException $EnableException -Cmdlet $PSCmdlet -ErrorRecord $_ return } } elseif ($ProcessError) { Write-PSFMessage -Level Warning "WARNING: {0}" -StringValues $ProcessError.Exception.Message } } if ($script:roleListToProcess.Count -gt 0) { Add-RoleToSPN -spnToProcess $script:roleListToProcess } } else { Write-PSFMessage -Level Host -Message "At this time AzureAD PowerShell module does not work on PowerShell Core. Please use PowerShell version 5 or 6 to create Registered Applications." } } else { Write-PSFMessage -Level Host -Message "Creating batch entperise service principal and applications" foreach ($spn in $objectsToCreate) { New-ServicePrincipal -DisplayName $spn } if ($roleListToProcess.Count -gt 0) { Add-RoleToSPN -spnToProcess $script:roleListToProcess } } } if ($CreateSingleObject) { try { if ($RegisteredApp) { Write-PSFMessage -Level Host -Message "Creating registered applications" if (-NOT $script:runningOnCore) { if ($Cba) { Write-PSFMessage -Level Host -Message "Creating a new self-signed certificate for Exchange CBA" New-SelfSignedCert -CertificateName $DisplayName -SubjectAlternativeName $DisplayName -Cba -RegisteredApp -EnableException } if ($UploadCertToApp) { Write-PSFMessage -Level Host -Message "Creating a new self-signed certificate for registered Azure application" New-SelfSignedCert -CertificateName $DisplayName -SubjectAlternativeName $DisplayName -FriendlyName $DisplayName -UploadCertToApp -RegisteredApp -EnableException } else { $newApp = New-AzureADApplication -DisplayName $DisplayName -ErrorAction SilentlyContinue -ErrorVariable ProcessError } if ($newApp) { Write-PSFMessage -Level Host -Message "Registered Application created: DisplayName: {0} - ApplicationID {1}" -StringValues $newApp.DisplayName, $newApp.AppId New-ServicePrincipal -ApplicationID $newApp.AppId -RegisteredApp # Since we only create an AzureAD application we need to create the matching service principal } elseif ($ProcessError) { Write-PSFMessage -Level Warning "WARNING: $($ProcessError[0].Exception.Message)" } } else { Write-PSFMessage -Level Host -Message "At this time AzureAD PowerShell module does not work on PowerShell Core. Please use PowerShell version 5x." } } elseif ($DisplayName -and $CreateSPNWithPassword) { New-ServicePrincipal -DisplayName $DisplayName -CreateSPNWithPassword } elseif (($DisplayName) -and (-NOT $RegisteredApp) -and (-NOT $Cba) -and (-NOT $CreateSPNsWithNameAndCert)) { New-ServicePrincipal -DisplayName $DisplayName } if ($script:roleListToProcess.Count -gt 0) { Add-RoleToSPN -spnToProcess $script:roleListToProcess if ($Cba) { Add-ExchangePermsToSPN.ps1 -DisplayName $DisplayName } } } catch { Stop-PSFFunction -Message "ERROR: Creating a simple SPN failed" -EnableException $EnableException -Cmdlet $PSCmdlet -ErrorRecord $_ } } if ($CreateSPNWithAppID) { try { New-ServicePrincipal -ApplicationID $ApplicationID } catch { Stop-PSFFunction -Message "ERROR creating an spn by application id" -EnableException $EnableException -Cmdlet $PSCmdlet -ErrorRecord $_ return } } if ($CreateSPNsWithNameAndCert) { try { if ((-NOT $Certificate)) { Stop-PSFFunction -Message "ERROR: No service principal specified. Exiting" -EnableException $EnableException -Cmdlet $PSCmdlet return } else { Write-PSFMessage -Level Host -Message "Creating new SPN {0} and with auto generated certificate key" -StringValues $DisplayName $endDate = Get-Date; $endDate = $currentDate.AddYears(1) $newSPN = New-AzADServicePrincipal -DisplayName $DisplayName -CertValue $Certificate -EndDate $endDate -ErrorAction Stop Add-RoleToSPN -spnToProcess $newSPN } } catch { Stop-PSFFunction -Message "ERROR: No certificate as base64-encoded string specified. Exiting" -EnableException $EnableException -Cmdlet $PSCmdlet -ErrorRecord $_ return } } } end { Write-PSFMessage -Level Host -Message 'Completed. Log saved to: "{0}". ' -StringValues $script:loggingFolder } } |