Skip to content
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

AADServicePrinicipalCustomSecurityAttributes #5211

Open
wants to merge 3 commits into
base: Dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
* AADLifecycleWorkflowSettings
* Initial release.
* AADServicePrincipal
* Adding Delegated Permission Classification Property
* Added Delegated Permission Classification Property
* Added Custom Security Attributes Property
* ADOPermissionGroupSettings
* Initial release.
* EXOATPBuiltInProtectionRule
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ function Get-TargetResource
[System.Boolean]
$AppRoleAssignmentRequired,

[Parameter()]
[Microsoft.Management.Infrastructure.CimInstance[]]
$CustomSecurityAttributes,

[Parameter()]
[Microsoft.Management.Infrastructure.CimInstance[]]
$DelegatedPermissionClassifications,
Expand Down Expand Up @@ -226,6 +230,11 @@ function Get-TargetResource
$complexDelegatedPermissionClassifications += $hashtable
}

$complexCustomSecurityAttributes = [Array](Get-CustomSecurityAttributes -AppId $AppId)
if ($null -eq $complexCustomSecurityAttributes) {
$complexCustomSecurityAttributes = @()
}

$result = @{
AppId = $AADServicePrincipal.AppId
AppRoleAssignedTo = $AppRoleAssignedToValues
Expand All @@ -234,6 +243,7 @@ function Get-TargetResource
AlternativeNames = $AADServicePrincipal.AlternativeNames
AccountEnabled = [boolean]$AADServicePrincipal.AccountEnabled
AppRoleAssignmentRequired = $AADServicePrincipal.AppRoleAssignmentRequired
CustomSecurityAttributes = $complexCustomSecurityAttributes
DelegatedPermissionClassifications = [Array]$complexDelegatedPermissionClassifications
ErrorUrl = $AADServicePrincipal.ErrorUrl
Homepage = $AADServicePrincipal.Homepage
Expand Down Expand Up @@ -304,6 +314,10 @@ function Set-TargetResource
[System.Boolean]
$AppRoleAssignmentRequired,

[Parameter()]
[Microsoft.Management.Infrastructure.CimInstance[]]
$CustomSecurityAttributes,

[Parameter()]
[Microsoft.Management.Infrastructure.CimInstance[]]
$DelegatedPermissionClassifications,
Expand Down Expand Up @@ -410,6 +424,13 @@ function Set-TargetResource
$currentParameters.Remove('ApplicationSecret') | Out-Null
$currentParameters.Remove('AccessTokens') | Out-Null

# update the custom security attributes to be cmdlet comsumable
if ($null -ne $currentParameters.CustomSecurityAttributes -and $currentParameters.CustomSecurityAttributes -gt 0) {
$currentParameters.CustomSecurityAttributes = Get-M365DSCAADServicePrincipalCustomSecurityAttributesAsCmdletHashtable -CustomSecurityAttributes $currentParameters.CustomSecurityAttributes
} else {
$currentParameters.CustomSecurityAttributes = @()
}

# ServicePrincipal should exist but it doesn't
if ($Ensure -eq 'Present' -and $currentAADServicePrincipal.Ensure -eq 'Absent')
{
Expand Down Expand Up @@ -467,6 +488,16 @@ function Set-TargetResource
$currentParameters.Remove('AppRoleAssignedTo') | Out-Null
$currentParameters.Remove('Owners') | Out-Null
$currentParameters.Remove('DelegatedPermissionClassifications') | Out-Null

#removing the current custom security attributes
if ($currentAADServicePrincipal.CustomSecurityAttributes.Count -gt 0) {
$currentAADServicePrincipal.CustomSecurityAttributes = Get-M365DSCAADServicePrincipalCustomSecurityAttributesAsCmdletHashtable -CustomSecurityAttributes $currentAADServicePrincipal.CustomSecurityAttributes -GetForDelete $true
$CSAParams = @{
customSecurityAttributes = $currentAADServicePrincipal.CustomSecurityAttributes
}
Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/beta/servicePrincipals(appId='$($currentParameters.AppId)')" -Method Patch -Body $CSAParams
}

Update-MgServicePrincipal -ServicePrincipalId $currentAADServicePrincipal.ObjectID @currentParameters

if ($AppRoleAssignedTo)
Expand Down Expand Up @@ -642,6 +673,10 @@ function Test-TargetResource
[System.Boolean]
$AppRoleAssignmentRequired,

[Parameter()]
[Microsoft.Management.Infrastructure.CimInstance[]]
$CustomSecurityAttributes,

[Parameter()]
[Microsoft.Management.Infrastructure.CimInstance[]]
$DelegatedPermissionClassifications,
Expand Down Expand Up @@ -743,6 +778,7 @@ function Test-TargetResource
{
$source = $PSBoundParameters.$key
$target = $CurrentValues.$key

if ($null -ne $source -and $source.GetType().Name -like '*CimInstance*')
{
$testResult = Compare-M365DSCComplexObject `
Expand Down Expand Up @@ -843,6 +879,9 @@ function Export-TargetResource
-ErrorAction Stop
foreach ($AADServicePrincipal in $Script:exportedInstances)
{
if($AADServicePrincipal.AppId -ne '0b750897-174f-4aed-ad21-ab3799c7e404') {
continue
}
if ($null -ne $Global:M365DSCExportResourceInstancesCount)
{
$Global:M365DSCExportResourceInstancesCount++
Expand Down Expand Up @@ -873,6 +912,10 @@ function Export-TargetResource
{
$Results.DelegatedPermissionClassifications = Get-M365DSCAzureADServicePrincipalDelegatedPermissionClassifications -PermissionClassifications $Results.DelegatedPermissionClassifications
}
if ($Results.CustomSecurityAttributes.Count -gt 0)
{
$Results.CustomSecurityAttributes = Get-M365DSCAADServicePrincipalCustomSecurityAttributesAsString -CustomSecurityAttributes $Results.CustomSecurityAttributes
}
$currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName `
-ConnectionMode $ConnectionMode `
-ModulePath $PSScriptRoot `
Expand All @@ -888,6 +931,11 @@ function Export-TargetResource
$currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock `
-ParameterName 'DelegatedPermissionClassifications'
}
if ($null -ne $Results.CustomSecurityAttributes)
{
$currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock `
-ParameterName 'CustomSecurityAttributes'
}
$dscContent += $currentDSCBlock
Save-M365DSCPartialExport -Content $currentDSCBlock `
-FileName $Global:PartialExportFileName
Expand All @@ -912,6 +960,195 @@ function Export-TargetResource
}
}

function Get-M365DSCAADServicePrincipalCustomSecurityAttributesAsCmdletHashtable
{
[CmdletBinding()]
[OutputType([System.Collections.Hashtable])]
param(
[Parameter(Mandatory = $true)]
[System.Collections.ArrayList]
$CustomSecurityAttributes,

[Parameter()]
[System.Boolean]
$GetForDelete = $false
)

# logic to update the custom security attributes to be cmdlet comsumable
$updatedCustomSecurityAttributes = @{}
foreach ($attributeSet in $CustomSecurityAttributes) {
$attributeSetKey = $attributeSet.AttributeSetName

$valuesHashtable = @{}
$valuesHashtable.Add('@odata.type', '#Microsoft.DirectoryServices.CustomSecurityAttributeValue')
foreach ($attribute in $attributeSet.AttributeValues) {
$attributeKey = $attribute.AttributeName
# supply attributeName = $null in the body, if you want to delete this attribute
if ($GetForDelete -eq $true) {
$valuesHashtable.Add($attributeKey, $null)
continue
}

$odataKey = $attributeKey + '@odata.type'

if ($null -ne $attribute.StringArrayValue) {
$valuesHashtable.Add($odataKey, "#Collection(String)")
$attributeValue = $attribute.StringArrayValue
}
elseif ($null -ne $attribute.IntArrayValue) {
$valuesHashtable.Add($odataKey, "#Collection(Int32)")
$attributeValue = $attribute.IntArrayValue
}
elseif ($null -ne $attribute.StringValue) {
$valuesHashtable.Add($odataKey, "#String")
$attributeValue = $attribute.StringValue
}
elseif ($null -ne $attribute.IntValue) {
$valuesHashtable.Add($odataKey, "#Int32")
$attributeValue = $attribute.IntValue
}
elseif ($null -ne $attribute.BoolValue) {
$attributeValue = $attribute.BoolValue
}

$valuesHashtable.Add($attributeKey, $attributeValue)
}
$updatedCustomSecurityAttributes.Add($attributeSetKey, $valuesHashtable)
}
return $updatedCustomSecurityAttributes
}

# Function to create MSFT_AttributeValue
function Create-AttributeValue {
param (
[string]$AttributeName,
[object]$Value
)

$attributeValue = @{
AttributeName = $AttributeName
StringArrayValue = $null
IntArrayValue = $null
StringValue = $null
IntValue = $null
BoolValue = $null
}

# Handle different types of values
if ($Value -is [string]) {
$attributeValue.StringValue = $Value
}
elseif ($Value -is [System.Int32] -or $Value -is [System.Int64]) {
$attributeValue.IntValue = $Value
}
elseif ($Value -is [bool]) {
$attributeValue.BoolValue = $Value
}
elseif ($Value -is [array]) {
if ($Value[0] -is [string]) {
$attributeValue.StringArrayValue = $Value
}
elseif ($Value[0] -is [System.Int32] -or $Value[0] -is [System.Int64]) {
$attributeValue.IntArrayValue = $Value
}
}

return $attributeValue
}


function Get-CustomSecurityAttributes {
[OutputType([System.Array])]
param (
[String]$AppId
)

$customSecurityAttributes = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/beta/servicePrincipals(appId='$AppId')`?`$select=customSecurityAttributes" -Method Get
$customSecurityAttributes = $customSecurityAttributes.customSecurityAttributes
$newCustomSecurityAttributes = @()

foreach ($key in $customSecurityAttributes.Keys) {
$attributeSet = @{
AttributeSetName = $key
AttributeValues = @()
}

foreach ($attribute in $customSecurityAttributes[$key].Keys) {
# Skip properties that end with '@odata.type'
if ($attribute -like "*@odata.type") {
continue
}

$value = $customSecurityAttributes[$key][$attribute]
$attributeName = $attribute # Keep the attribute name as it is

# Create the attribute value and add it to the set
$attributeSet.AttributeValues += Create-AttributeValue -AttributeName $attributeName -Value $value
}

#Add the attribute set to the final structure
$newCustomSecurityAttributes += $attributeSet
}

# Display the new structure
return [Array]$newCustomSecurityAttributes
}

function Get-M365DSCAADServicePrincipalCustomSecurityAttributesAsString
{
[CmdletBinding()]
[OutputType([System.String])]
param(
[Parameter(Mandatory = $true)]
[System.Collections.ArrayList]
$CustomSecurityAttributes
)

$StringContent = "@(`r`n"
foreach ($customSecurityAttribute in $CustomSecurityAttributes)
{
$StringContent += " MSFT_AADServicePrincipalAttributeSet {`r`n"
$StringContent += " AttributeSetName = '" + $customSecurityAttribute.AttributeSetName + "'`r`n"
if ($customSecurityAttribute.AttributeValues.Length -gt 0)
{
$StringContent += " AttributeValues = @(`r`n"
foreach ($attributeValue in $customSecurityAttribute.AttributeValues)
{
$StringContent += " MSFT_AADServicePrincipalAttributeValue {`r`n"
$StringContent += " AttributeName = '" + $attributeValue.AttributeName + "'`r`n"
if ($null -ne $attributeValue.BoolValue){
$StringContent += " BoolValue = $" + $attributeValue.BoolValue + "`r`n"
}
elseif ($null -ne $attributeValue.StringValue){
$StringContent += " StringValue = '" + $attributeValue.StringValue + "'`r`n"
}
elseif ($null -ne $attributeValue.IntValue){
$StringContent += " IntValue = " + $attributeValue.IntValue + "`r`n"
}
elseif ($null -ne $attributeValue.StringArrayValue){
$StringContent += " StringArrayValue = @("
$StringContent += ($attributeValue.StringArrayValue | ForEach-Object { "'$_'" }) -join ","
$StringContent += ")`r`n"
}
elseif ($null -ne $attributeValue.IntArrayValue){
$StringContent += " IntArrayValue = @("
$StringContent += $attributeValue.IntArrayValue -join ","
$StringContent += ")`r`n"
}
$StringContent += " }`r`n"
}
$StringContent += " )`r`n"
}
else
{
$StringContent += " AttributeValues = @()`r`n"
}
$StringContent += " }`r`n"
}
$StringContent += ' )'
return $StringContent
}

function Get-M365DSCAzureADServicePrincipalAssignmentAsString
{
[CmdletBinding()]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@ class MSFT_AADServicePrincipalDelegatedPermissionClassification
[Write, Description("Name of the permission")] String PermissionName;
};

[ClassVersion("1.0.0")]
class MSFT_AADServicePrincipalAttributeValue
{
[Write, Description("Name of the Attribute")] String AttributeName;
[Write, Description("If the attribute has a string array value")] String StringArrayValue[];
[Write, Description("If the attribute has a int array value")] UInt32 IntArrayValue[];
[Write, Description("If the attribute has a string value")] String StringValue;
[Write, Description("If the attribute has a int value")] UInt32 IntValue;
[Write, Description("If the attribute has a boolean value")] Boolean BoolValue;
};

[ClassVersion("1.0.0")]
class MSFT_AADServicePrincipalAttributeSet
{
[Write, Description("Attribute Set Name.")] String AttributeSetName;
[Write, Description("List of attribute values."), EmbeddedInstance("MSFT_AADServicePrincipalAttributeValue")] String AttributeValues[];
};

[ClassVersion("1.0.0.0"), FriendlyName("AADServicePrincipal")]
class MSFT_AADServicePrincipal : OMI_BaseResource
{
Expand All @@ -33,6 +51,7 @@ class MSFT_AADServicePrincipal : OMI_BaseResource
[Write, Description("The type of the service principal.")] String ServicePrincipalType;
[Write, Description("Tags linked to this service principal.Note that if you intend for this service principal to show up in the All Applications list in the admin portal, you need to set this value to {WindowsAzureActiveDirectoryIntegratedApp}")] String Tags[];
[Write, Description("The permission classifications for delegated permissions exposed by the app that this service principal represents."), EmbeddedInstance("MSFT_AADServicePrincipalDelegatedPermissionClassification")] String DelegatedPermissionClassifications[];
[Write, Description("The list of custom security attributes attached to this SPN"), EmbeddedInstance("MSFT_AADServicePrincipalAttributeSet")] String CustomSecurityAttributes[];

[Write, Description("Specify if the Azure AD App should exist or not."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure;
[Write, Description("Id of the Azure Active Directory application to authenticate with.")] String ApplicationId;
Expand Down
Loading