-
Notifications
You must be signed in to change notification settings - Fork 31
Add New-EntraServicePrincipalKeyCredential #1487
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c906d57
37be4b6
f8e26c2
cca8030
47367dc
d57bc74
eb436cb
722d14c
ecb7f5b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
function New-EntraServicePrincipalKeyCredential { | ||
[CmdletBinding()] | ||
param ( | ||
[Parameter(Mandatory = $true, HelpMessage = "Specifies the unique identifier (ObjectId) of the service principal to which the key credential will be added.")] | ||
[Alias("ObjectId")] | ||
[ValidateNotNullOrEmpty()] | ||
[System.String]$ServicePrincipalId, | ||
|
||
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, | ||
HelpMessage = "Specifies the value for the public key encoded in Base64.")] | ||
[System.String]$Value, | ||
|
||
[Parameter(Mandatory = $true, ValueFromPipeline = $true, HelpMessage = "Specifies the type of key credential (e.g., AsymmetricX509Cert, Symmetric).")] | ||
[ValidateSet('AsymmetricX509Cert', 'X509CertAndPassword')] | ||
[System.String]$Type, | ||
|
||
[Parameter(Mandatory = $true, ValueFromPipeline = $true, HelpMessage = "Specifies the usage of the key credential (e.g., Sign, Verify).")] | ||
[ValidateSet('Sign', 'Verify')] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this the only set? |
||
[System.String]$Usage, | ||
|
||
[Parameter(Mandatory = $true, ValueFromPipeline = $true, HelpMessage = "A self-signed JWT token used as a proof of possession of the existing keys. This JWT token must be signed with a private key that corresponds to one of the existing valid certificates associated with the servicePrincipal.")] | ||
[System.String]$Proof, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This param is not needed |
||
|
||
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "Contain the password for the key. This property is required for keys of type X509CertAndPassword.")] | ||
[System.String]$PasswordCredential, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This param is not needed.
Below are the params for |
||
|
||
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "Specifies a custom identifier for the key credential.")] | ||
[System.String] $CustomKeyIdentifier, | ||
|
||
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "Specifies the time when the key becomes valid as a DateTime object.")] | ||
[System.Nullable[System.DateTime]] $StartDate, | ||
|
||
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, | ||
HelpMessage = "Specifies the time when the key becomes invalid as a DateTime object.")] | ||
[System.Nullable[System.DateTime]] $EndDate | ||
) | ||
|
||
begin { | ||
# Ensure connection to Microsoft Entra | ||
if (-not (Get-EntraContext)) { | ||
$errorMessage = "Not connected to Microsoft Graph. Use 'Connect-Entra -Scopes User.Read.All' to authenticate." | ||
Write-Error -Message $errorMessage -ErrorAction Stop | ||
return | ||
} | ||
} | ||
|
||
PROCESS { | ||
|
||
$customHeaders = New-EntraCustomHeaders -Command $MyInvocation.MyCommand | ||
$customHeaders['Content-Type'] = 'application/json' | ||
$baseUri = '/v1.0/servicePrincipals' | ||
$URI = "$baseUri/$ServicePrincipalId/addKey" | ||
|
||
$params = @{ | ||
keyCredential = @{ | ||
type = $Type | ||
usage = $Usage | ||
key = $Value | ||
dateTimeStart = $StartDate | ||
dateTimeEnd = $EndDate | ||
customKeyIdentifier = $CustomKeyIdentifier | ||
} | ||
passwordCredential = $null | ||
proof = $Proof | ||
} | ||
|
||
if ($Type -eq 'X509CertAndPassword') { | ||
if ([string]::IsNullOrWhiteSpace($PSBoundParameters["PasswordCredential"])) { | ||
$errorMessage = "The 'CustomKeyIdentifier' property is required for keys of type X509CertAndPassword" | ||
Write-Error -Message $errorMessage -ErrorAction Stop | ||
} | ||
$params["PasswordCredential"] = @{ | ||
secretText = $PSBoundParameters["PasswordCredential"] | ||
} | ||
} | ||
|
||
Write-Debug("============================ TRANSFORMATIONS ============================") | ||
$params.Keys | ForEach-Object { "$_ : $($params[$_])" } | Write-Debug | ||
Write-Debug("=========================================================================`n") | ||
|
||
try { | ||
$response = Invoke-GraphRequest -Headers $customHeaders -Uri $URI -Method "POST" -Body ($params | ConvertTo-Json -Depth 4) | ||
|
||
$targetTypeList = @() | ||
foreach ($data in $response) { | ||
$target = New-Object Microsoft.Graph.PowerShell.Models.MicrosoftGraphKeyCredential | ||
$data.PSObject.Properties | ForEach-Object { | ||
$propertyName = $_.Name.Substring(0, 1).ToUpper() + $_.Name.Substring(1) | ||
$propertyValue = $_.Value | ||
$target | Add-Member -MemberType NoteProperty -Name $propertyName -Value $propertyValue -Force | ||
} | ||
$targetTypeList += $target | ||
} | ||
$targetTypeList | ||
} | ||
catch { | ||
Write-Error "Failed to add key credential: $($_.Exception.Message)" | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
function New-EntraBetaServicePrincipalKeyCredential { | ||
[CmdletBinding()] | ||
param ( | ||
[Parameter(Mandatory = $true, HelpMessage = "Specifies the unique identifier (ObjectId) of the service principal to which the key credential will be added.")] | ||
[Alias("ObjectId")] | ||
[ValidateNotNullOrEmpty()] | ||
[System.String]$ServicePrincipalId, | ||
|
||
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, | ||
HelpMessage = "Specifies the value for the public key encoded in Base64.")] | ||
[System.String]$Value, | ||
|
||
[Parameter(Mandatory = $true, ValueFromPipeline = $true, HelpMessage = "Specifies the type of key credential (e.g., AsymmetricX509Cert, Symmetric).")] | ||
[ValidateSet('AsymmetricX509Cert', 'X509CertAndPassword')] | ||
[System.String]$Type, | ||
|
||
[Parameter(Mandatory = $true, ValueFromPipeline = $true, HelpMessage = "Specifies the usage of the key credential (e.g., Sign, Verify).")] | ||
[ValidateSet('Sign', 'Verify')] | ||
[System.String]$Usage, | ||
|
||
[Parameter(Mandatory = $true, ValueFromPipeline = $true, HelpMessage = "A self-signed JWT token used as a proof of possession of the existing keys. This JWT token must be signed with a private key that corresponds to one of the existing valid certificates associated with the servicePrincipal.")] | ||
[System.String]$Proof, | ||
|
||
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "Contain the password for the key. This property is required for keys of type X509CertAndPassword.")] | ||
[System.String]$PasswordCredential, | ||
|
||
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "Specifies a custom identifier for the key credential.")] | ||
[System.String] $CustomKeyIdentifier, | ||
|
||
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "Specifies the time when the key becomes valid as a DateTime object.")] | ||
[System.Nullable[System.DateTime]] $StartDate, | ||
|
||
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, | ||
HelpMessage = "Specifies the time when the key becomes invalid as a DateTime object.")] | ||
[System.Nullable[System.DateTime]] $EndDate | ||
) | ||
|
||
begin { | ||
# Ensure connection to Microsoft Entra | ||
if (-not (Get-EntraContext)) { | ||
$errorMessage = "Not connected to Microsoft Graph. Use 'Connect-Entra -Scopes User.Read.All' to authenticate." | ||
Write-Error -Message $errorMessage -ErrorAction Stop | ||
return | ||
} | ||
} | ||
|
||
PROCESS { | ||
|
||
$customHeaders = New-EntraBetaCustomHeaders -Command $MyInvocation.MyCommand | ||
$customHeaders['Content-Type'] = 'application/json' | ||
$baseUri = '/beta/servicePrincipals' | ||
$URI = "$baseUri/$ServicePrincipalId/addKey" | ||
|
||
$params = @{ | ||
keyCredential = @{ | ||
type = $Type | ||
usage = $Usage | ||
key = $Value | ||
dateTimeStart = $StartDate | ||
dateTimeEnd = $EndDate | ||
customKeyIdentifier = $CustomKeyIdentifier | ||
} | ||
passwordCredential = $null | ||
proof = $Proof | ||
} | ||
|
||
if ($Type -eq 'X509CertAndPassword') { | ||
if ([string]::IsNullOrWhiteSpace($PSBoundParameters["PasswordCredential"])) { | ||
$errorMessage = "The 'CustomKeyIdentifier' property is required for keys of type X509CertAndPassword" | ||
Write-Error -Message $errorMessage -ErrorAction Stop | ||
} | ||
$params["PasswordCredential"] = @{ | ||
secretText = $PSBoundParameters["PasswordCredential"] | ||
} | ||
} | ||
|
||
Write-Debug("============================ TRANSFORMATIONS ============================") | ||
$params.Keys | ForEach-Object { "$_ : $($params[$_])" } | Write-Debug | ||
Write-Debug("=========================================================================`n") | ||
|
||
try { | ||
$response = Invoke-GraphRequest -Headers $customHeaders -Uri $URI -Method "POST" -Body ($params | ConvertTo-Json -Depth 4) | ||
$targetTypeList = @() | ||
foreach ($data in $response) { | ||
$target = New-Object Microsoft.Graph.Beta.PowerShell.Models.MicrosoftGraphKeyCredential | ||
$data.PSObject.Properties | ForEach-Object { | ||
$propertyName = $_.Name.Substring(0, 1).ToUpper() + $_.Name.Substring(1) | ||
$propertyValue = $_.Value | ||
$target | Add-Member -MemberType NoteProperty -Name $propertyName -Value $propertyValue -Force | ||
} | ||
$targetTypeList += $target | ||
} | ||
$targetTypeList | ||
} | ||
catch { | ||
Write-Error "Failed to add key credential: $($_.Exception.Message)" | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# ------------------------------------------------------------------------------ | ||
# Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. | ||
# ------------------------------------------------------------------------------ | ||
BeforeAll { | ||
if ((Get-Module -Name Microsoft.Entra.Applications) -eq $null) { | ||
Import-Module Microsoft.Entra.Applications | ||
} | ||
Import-Module (Join-Path $PSScriptRoot "..\..\Common-Functions.ps1") -Force | ||
|
||
$response = @{ | ||
"@odata.context" = 'https://graph.microsoft.com/v1.0/$metadata#microsoft.graph.keyCredential' | ||
"customKeyIdentifier" = $null | ||
"endDateTime" = "01/15/2027 14:22:00" | ||
"key" = $null | ||
"keyId" = "aaaaaaaa-0b0b-1c1c-2d2d-333333" | ||
"startDateTime" = "01/15/2025 14:22:00" | ||
"type" = "AsymmetricX509Cert" | ||
"usage" = "Verify" | ||
} | ||
|
||
Mock -CommandName Invoke-MgGraphRequest -MockWith { $response } -ModuleName Microsoft.Entra.Applications | ||
Mock -CommandName Get-EntraContext -MockWith { @{Scopes = @("Application.ReadWrite.All") } } -ModuleName Microsoft.Entra.Applications | ||
} | ||
|
||
Describe "New-EntraServicePrincipalKeyCredential" { | ||
Context "Test for New-EntraServicePrincipalKeyCredential" { | ||
|
||
It "Should fail when ServicePrincipalId is null" { | ||
{ New-EntraServicePrincipalKeyCredential -ServicePrincipalId -Value "U29mdHdhcmU=" -Type "AsymmetricX509Cert" -Usage "Verify" -Proof "c29tZVByb29m" } | Should -Throw "Missing an argument for parameter 'ServicePrincipalId'*" | ||
} | ||
|
||
It "Should fail when ServicePrincipalId is empty" { | ||
{ New-EntraServicePrincipalKeyCredential -ServicePrincipalId ""} | Should -Throw "Cannot validate argument on parameter 'ServicePrincipalId'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again." | ||
} | ||
|
||
It "Should return created service principal key credential when ServicePrincipalId is valid and all required parameters are provided" { | ||
$result = New-EntraServicePrincipalKeyCredential -ServicePrincipalId "aaaaaaaa-2222-1111-1111-cccccccccccc" -Value "U29mdHdhcmU=" -Type "AsymmetricX509Cert" -Usage "Verify" -Proof "c29tZVByb29m" | ||
$result | Should -Not -BeNullOrEmpty | ||
|
||
Should -Invoke -CommandName Invoke-MgGraphRequest -ModuleName Microsoft.Entra.Applications -Times 1 | ||
} | ||
|
||
It "Should fail when key type is X509CertAndPassword and PasswordCredential hasn't been provided" { | ||
{ New-EntraServicePrincipalKeyCredential -ServicePrincipalId "aaaaaaaa-2222-1111-1111-cccccccccccc" -Value "U29mdHdhcmU=" -Type "X509CertAndPassword" -Usage "Sign" -Proof "c29tZVByb29m" } | Should -Throw "The 'CustomKeyIdentifier' property is required for keys of type X509CertAndPassword" | ||
} | ||
|
||
It "Should return created service principal key credential when PasswordCredential is provided for key type X509CertAndPassword" { | ||
$result = New-EntraServicePrincipalKeyCredential -ServicePrincipalId "aaaaaaaa-2222-1111-1111-cccccccccccc" -Value "U29mdHdhcmU=" -Type "X509CertAndPassword" -Usage "Sign" -Proof "c29tZVByb29m" -PasswordCredential "MySecretPassword" | ||
$result | Should -Not -BeNullOrEmpty | ||
|
||
Should -Invoke -CommandName Invoke-MgGraphRequest -ModuleName Microsoft.Entra.Applications -Times 1 | ||
} | ||
|
||
It "Should contain 'User-Agent' header" { | ||
$userAgentHeaderValue = "PowerShell/$psVersion EntraPowershell/$entraVersion New-EntraServicePrincipalKeyCredential" | ||
$result = New-EntraServicePrincipalKeyCredential -ServicePrincipalId "aaaaaaaa-2222-1111-1111-cccccccccccc" -Value "U29mdHdhcmU=" -Type "AsymmetricX509Cert" -Usage "Verify" -Proof "c29tZVByb29m" | ||
$result | Should -Not -BeNullOrEmpty | ||
$userAgentHeaderValue = "PowerShell/$psVersion EntraPowershell/$entraVersion New-EntraServicePrincipalKeyCredential" | ||
Should -Invoke -CommandName Invoke-GraphRequest -ModuleName Microsoft.Entra.Applications -Times 1 -ParameterFilter { | ||
$Headers.'User-Agent' | Should -Be $userAgentHeaderValue | ||
$true | ||
} | ||
} | ||
|
||
It "Should execute successfully without throwing an error" { | ||
# Disable confirmation prompts | ||
$originalDebugPreference = $DebugPreference | ||
$DebugPreference = 'Continue' | ||
try { | ||
# Act & Assert: Ensure the function doesn't throw an exception | ||
{ New-EntraServicePrincipalKeyCredential -ServicePrincipalId "aaaaaaaa-2222-1111-1111-cccccccccccc" -Value "U29mdHdhcmU=" -Type "AsymmetricX509Cert" -Usage "Verify" -Proof "c29tZVByb29m" -Debug } | Should -Not -Throw | ||
} | ||
finally { | ||
# Restore original confirmation preference | ||
$DebugPreference = $originalDebugPreference | ||
} | ||
} | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the only set?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes -> https://learn.microsoft.com/en-us/graph/api/serviceprincipal-addkey?view=graph-rest-1.0&tabs=http
Supported key types are:
AsymmetricX509Cert: The usage must be Verify.
X509CertAndPassword: The usage must be Sign