Skip to content

Instantly share code, notes, and snippets.

@Zerg00s
Last active February 6, 2024 08:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save Zerg00s/848e5a7bde46c20254dff377b6a5b6d2 to your computer and use it in GitHub Desktop.
Save Zerg00s/848e5a7bde46c20254dff377b6a5b6d2 to your computer and use it in GitHub Desktop.
Register Azure AD App and Upload a certificate
@ECHO OFF
PowerShell.exe -Command "& '%~dpn0.ps1' -path '%~dp0'"
REM PAUSE
$certificatePassword = 'CERTIFICATE_PASS'
$secureCertificatePass = ConvertTo-SecureString -String $certificatePassword -AsPlainText -Force
Connect-PnPOnline `
-CertificatePath "<PATH>\DeploymentApp.pfx" `
-Tenant <TENANT>.onmicrosoft.com `
-ClientId fff6667e-1141-4bb5-ba3e-eaaf653975c6 `
-Url https://<TENANT>.sharepoint.com `
-CertificatePassword $secureCertificatePass `
-IgnoreSslErrors
<#
.SYNOPSIS
Creates a Self Signed Certificate for use in server to server authentication
.DESCRIPTION
.EXAMPLE
PS C:\> .\Create-SelfSignedCertificate.ps1 -CommonName "MyCert" -StartDate 2015-11-21 -EndDate 2017-11-21
This will create a new self signed certificate with the common name "CN=MyCert". During creation you will be asked to provide a password to protect the private key.
.EXAMPLE
PS C:\> .\Create-SelfSignedCertificate.ps1 -CommonName "MyCert" -StartDate 2015-11-21 -EndDate 2017-11-21 -Password (ConvertTo-SecureString -String "MyPassword" -AsPlainText -Force)
This will create a new self signed certificate with the common name "CN=MyCert". The password as specified in the Password parameter will be used to protect the private key
.EXAMPLE
PS C:\> .\Create-SelfSignedCertificate.ps1 -CommonName "MyCert" -StartDate 2015-11-21 -EndDate 2017-11-21 -Force
This will create a new self signed certificate with the common name "CN=MyCert". During creation you will be asked to provide a password to protect the private key. If there is already a certificate with the common name you specified, it will be removed first.
#>
Param(
[Parameter(Mandatory=$true)]
[string]$CommonName,
[Parameter(Mandatory=$true)]
[DateTime]$StartDate,
[Parameter(Mandatory=$true)]
[DateTime]$EndDate,
[Parameter(Mandatory=$false, HelpMessage="Will overwrite existing certificates")]
[Switch]$Force,
[Parameter(Mandatory=$false)]
[SecureString]$Password
)
# DO NOT MODIFY BELOW
function CreateSelfSignedCertificate(){
#Remove and existing certificates with the same common name from personal and root stores
#Need to be very wary of this as could break something
if($CommonName.ToLower().StartsWith("cn="))
{
# Remove CN from common name
$CommonName = $CommonName.Substring(3)
}
$certs = Get-ChildItem -Path Cert:\LocalMachine\my | Where-Object{$_.Subject -eq "CN=$CommonName"}
if($certs -ne $null -and $certs.Length -gt 0)
{
if($Force)
{
foreach($c in $certs)
{
remove-item $c.PSPath
}
} else {
Write-Host -ForegroundColor Red "One or more certificates with the same common name (CN=$CommonName) are already located in the local certificate store. Use -Force to remove them";
return $false
}
}
$name = new-object -com "X509Enrollment.CX500DistinguishedName.1"
$name.Encode("CN=$CommonName", 0)
$key = new-object -com "X509Enrollment.CX509PrivateKey.1"
$key.ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
$key.KeySpec = 1
$key.Length = 2048
$key.SecurityDescriptor = "D:PAI(A;;0xd01f01ff;;;SY)(A;;0xd01f01ff;;;BA)(A;;0x80120089;;;NS)"
$key.MachineContext = 1
$key.ExportPolicy = 1 # This is required to allow the private key to be exported
$key.Create()
$serverauthoid = new-object -com "X509Enrollment.CObjectId.1"
$serverauthoid.InitializeFromValue("1.3.6.1.5.5.7.3.1") # Server Authentication
$ekuoids = new-object -com "X509Enrollment.CObjectIds.1"
$ekuoids.add($serverauthoid)
$ekuext = new-object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1"
$ekuext.InitializeEncode($ekuoids)
$cert = new-object -com "X509Enrollment.CX509CertificateRequestCertificate.1"
$cert.InitializeFromPrivateKey(2, $key, "")
$cert.Subject = $name
$cert.Issuer = $cert.Subject
$cert.NotBefore = $StartDate
$cert.NotAfter = $EndDate
$cert.X509Extensions.Add($ekuext)
$cert.Encode()
$enrollment = new-object -com "X509Enrollment.CX509Enrollment.1"
$enrollment.InitializeFromRequest($cert)
$certdata = $enrollment.CreateRequest(0)
$enrollment.InstallResponse(2, $certdata, 0, "")
return $true
}
function ExportPFXFile()
{
if($CommonName.ToLower().StartsWith("cn="))
{
# Remove CN from common name
$CommonName = $CommonName.Substring(3)
}
if($Password -eq $null)
{
$Password = Read-Host -Prompt "Enter Password to protect private key" -AsSecureString
}
$cert = Get-ChildItem -Path Cert:\LocalMachine\my | where-object{$_.Subject -eq "CN=$CommonName"}
Export-PfxCertificate -Cert $cert -Password $Password -FilePath "$($CommonName).pfx"
Export-Certificate -Cert $cert -Type CERT -FilePath "$CommonName.cer"
}
function RemoveCertsFromStore()
{
# Once the certificates have been been exported we can safely remove them from the store
if($CommonName.ToLower().StartsWith("cn="))
{
# Remove CN from common name
$CommonName = $CommonName.Substring(3)
}
$certs = Get-ChildItem -Path Cert:\LocalMachine\my | Where-Object{$_.Subject -eq "CN=$CommonName"}
foreach($c in $certs)
{
remove-item $c.PSPath
}
}
if(CreateSelfSignedCertificate)
{
ExportPFXFile
RemoveCertsFromStore
}
# -------------------------------------------------------------------------------
# This script is meant to deploy self-signed certificate to Azure AD
# When running it, you will be asked to login to Office 365 then to Azure.
# This script generates X509 certificates and automatically applies them.
# Refer to comments below to see the entire list of things that the script is doing.
#
# -------------------------------------------------------------------------------
$ErrorActionPreference = "Stop"
# -------------------------------------------------------------------------------
# CHECK IF WE ARE RUNNING THE SCRIPT AS AN ADMIN:
# -------------------------------------------------------------------------------
$IsRunningAsAdmin = [bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match "S-1-5-32-544")
if ($IsRunningAsAdmin -eq $false) {
Write-Host "[ERROR] MAKE SURE YOU RUN THIS SCRIPT AS AN ADMINISTRATOR" -ForegroundColor Red
Read-Host "Press Enter to exit"
return
}
# -------------------------------------------------------------------------------
# Depending on how we run the script, we must find location of the script in different ways.
# We need to set execution location to the current folder before we can use relative paths.
# This gives as an ability to just double-click a .bat file without having to CD to a certain folder on disk.
# -------------------------------------------------------------------------------
if ($psISE) { $scriptLocation = Split-Path -Path $psISE.CurrentFile.FullPath }
else { $scriptLocation = $PSScriptRoot }
Set-Location $scriptLocation
Write-Host $PSScriptRoot
try {
$appName = "DeploymentApp"
# -------------------------------------------------------------------------------
# We name certificate the same name as the AAD Application
# -------------------------------------------------------------------------------
$certificateName = $appName
# -------------------------------------------------------------------------------
# Generate strong passwords for the certificate
# -------------------------------------------------------------------------------
Add-Type -AssemblyName System.Web
# "&" is converted to "\u0026" when using ConvertTo-Json, so we strip this off.
$certificatePassword = [System.Web.Security.Membership]::GeneratePassword(12, 3) -replace "&","!"
$encryptedCertPassword = ConvertTo-SecureString -String $certificatePassword -AsPlainText -Force
# -------------------------------------------------------------------------------
# Generate self-signed .cer and .pfx files
# -------------------------------------------------------------------------------
.\Create-SelfSignedCertificate.ps1 `
-CommonName $certificateName `
-StartDate (Get-Date).AddDays(-1) `
-EndDate "2299-01-01" `
-Password $encryptedCertPassword
Write-Host "[Success] Generated a self-signed certificate: $certificateName" -ForegroundColor Green
# -------------------------------------------------------------------------------
# Logging to Azure Cli using Office 365 Global Tenant Admin
# -------------------------------------------------------------------------------
Write-Host "[Pending user action] Enter Global Office 365 Admin credentials..." -ForegroundColor Yellow
az login --allow-no-subscriptions
Write-Host "[Success] Connected Office 365's Azure tenant" -ForegroundColor Green
# -------------------------------------------------------------------------------
# Select Azure Subscription Associated with Office 365 tenant
# -------------------------------------------------------------------------------
$subscription = az account show | ConvertFrom-Json
$account = az account show | ConvertFrom-Json
# -------------------------------------------------------------------------------
# Delete the app if it already exists
# -------------------------------------------------------------------------------
$apps = az ad app list | ConvertFrom-Json
$app = $apps | Where-Object { $_.displayName -eq $appName }
if ($null -ne $app) {
az ad app delete --id $app.appId
Write-Host "[Success] Deleted existing AAD Application with the name $appName" -ForegroundColor Green
}
# -------------------------------------------------------------------------------
# Find existing application by name
# -------------------------------------------------------------------------------
$apps = az ad app list | ConvertFrom-Json
$app = $apps | Where-Object { $_.displayName -eq $appName }
# -------------------------------------------------------------------------------
# Assign Permissions the app. Permissions are described in the json file.
# We are only asking for one permission: SharePoint Online: Read all site collections
# -------------------------------------------------------------------------------
if ($null -eq $app) {
$app = az ad app create `
--display-name $appName `
--identifier-uris https://$appName.websites.net `
--required-resource-accesses "requiredResourceManifest.json" `
| ConvertFrom-Json
Write-Host "[Success] Registered a new AAD Application with the name $appName" -ForegroundColor Green
}
# -------------------------------------------------------------------------------
# Grant permissions (Admin-consent) programmatically. This commandlet became first available April 04 2019
# -------------------------------------------------------------------------------
az ad app permission admin-consent --id $app.appId
Write-Host "[Success] $appName was granted permissions to access SharePoint Online Site collections for read-only" -ForegroundColor Green
# -------------------------------------------------------------------------------
# Upload a .cer file to the AAD application
# -------------------------------------------------------------------------------
$cerName = $certificateName + ".cer"
az ad app credential reset --id $app.appId --cert `@$cerName
Write-Host "[Success] Uploaded self-signed certificate to the $appName application" -ForegroundColor Green
$fileContentBytes = Get-Content "$certificateName.pfx" -Encoding Byte
$pfxBlob = [System.Convert]::ToBase64String($fileContentBytes)
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import((Get-Location).Path + "\" + $cerName)
$thumbprint = $cert.Thumbprint
# -------------------------------------------------------------------------------
# Preparing an object that will contain all useful information about our Azure AD App
# -------------------------------------------------------------------------------
$o365AppDetails = @{
thumbprint = $thumbprint
pfxBlob = $pfxBlob
appId = $app.appId
tenantId = $account.tenantId
certificatePassword = $certificatePassword
certificateName = $certificateName
appName = $appName
}
# -------------------------------------------------------------------------------
# Save information about the O365 Application in a file
# -------------------------------------------------------------------------------
$o365AppDetails | ConvertTo-Json | Out-File "o365AppDetails.json"
Write-Host "[Success] Saved o365AppDetails.json file about the '$appName' application on disk" -ForegroundColor Green
# -------------------------------------------------------------------------------
# We no longer need to work with Azure associated with Office 365
# -------------------------------------------------------------------------------
az logout
Write-Host "[Success] Logged out from Office 365's Azure tenant" -ForegroundColor Green
}
catch {
$Error
Write-Host "An error occurred. If it was not caused by you cancelling the deployment process - please capture the logs displayed above and contact the deployment specialist." -ForegroundColor Cyan
Read-Host "Press Enter to exit"
return
}
Write-Host "[Success] Application has been deployed." -ForegroundColor Cyan
Read-Host "Press Enter to close this window and exit"
[
{
"resourceAppId": "00000003-0000-0ff1-ce00-000000000000",
"resourceAccess": [
{
"id": "678536fe-1083-478a-9c59-b99265e6b0d3",
"type": "Role"
},
{
"id": "c8e3537c-ec53-43b9-bed3-b2bd3617ae97",
"type": "Role"
},
{
"id": "741f803b-c850-494e-b5df-cde7c675a1ca",
"type": "Role"
}
]
},
{
"resourceAppId": "00000002-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "311a71cc-e848-46a1-bdf8-97ff7156d8e6",
"type": "Scope"
}
]
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment