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

AADServicePrincipal: Adding CustomSecurityAttributes field using the dynamic resource generator #4666

Open
adhodgson1 opened this issue May 13, 2024 · 0 comments

Comments

@adhodgson1
Copy link
Contributor

adhodgson1 commented May 13, 2024

I'm trying to use the Dynamic Resource Generator to create code for the CustomSecurityAttributes field on the AADServicePrincipal resource, as I am unsure how to handle that complex field inside DSC myself. I was hoping to generate the code, and fit the result into the existing resource. I run the DRG using the following command:

New-M365DSCResource -ResourceName AADServicePrincipal -Workload MicrosoftGraph -CmdletNoun 'MgServicePrincipal' -Path $ResourcePath -UnitTestPath $UnitTestPath -ExampleFilePath $ExamplePath -Credential $credential

If we look at the PSM1 file that is generated it looks as if there is usable code for this additional field, however digging into the code a bit more I can see a couple of issues:

function Get-TargetResource
{
[...]
        [Parameter()]
        [Microsoft.Management.Infrastructure.CimInstance]
        $CustomSecurityAttributes,
[...]
        #region resource generator code
        $getValue = Get-MgServicePrincipal -ServicePrincipalId $Id  -ErrorAction SilentlyContinue

        if ($null -eq $getValue)
        {
            Write-Verbose -Message "Could not find an Azure AD Service Principal with Id {$Id}"

            if (-Not [string]::IsNullOrEmpty($DisplayName))
            {
                $getValue = Get-MgServicePrincipal `
                    -Filter "DisplayName eq '$DisplayName'" `
                    -ErrorAction SilentlyContinue | Where-Object `
                    -FilterScript { `
                        $_.AdditionalProperties.'@odata.type' -eq "#microsoft.graph.ServicePrincipal" `
                    }
            }
        }
        #endregion
        if ($null -eq $getValue)
        {
            Write-Verbose -Message "Could not find an Azure AD Service Principal with DisplayName {$DisplayName}"
            return $nullResult
        }
        $Id = $getValue.Id
        Write-Verbose -Message "An Azure AD Service Principal with Id {$Id} and DisplayName {$DisplayName} was found."

This code retrieves the service principal, however I don't believe it is picking up the CustomSecurityAttributes field, as that field doesn't seem to be returned by the underlying Graph API by default, we have to explicitly request it.

Request similar to that used by the DSC tool:

$spn=Get-MgServicePrincipal -ServicePrincipalId $ObjectId
$spn.CustomSecurityAttributes
AdditionalProperties
--------------------
{}

Request including the CustomSecurityAttributes field:

$spn=Get-MgServicePrincipal -ServicePrincipalId $ObjectId -Property CustomSecurityAttributes
$spn.CustomSecurityAttributes
AdditionalProperties
--------------------
{[TestEntAppAttributeSet, System.Collections.Generic.Dictionary`2[System.String,System.Object]]}

Moving onto the handling of this parameter in the code, it looks like for working fields we have code that handles the complex types. Here is code for the AddIns field:

        $complexAddIns = @()
        foreach ($currentaddIns in $getValue.AdditionalProperties.addIns)
        {
            $myaddIns = @{}
            $myaddIns.Add('Id', $currentaddIns.id)
            $complexProperties = @()
            foreach ($currentProperties in $currentaddIns.properties)
            {
                $myProperties = @{}
                $myProperties.Add('Key', $currentProperties.key)
                $myProperties.Add('Value', $currentProperties.value)
                if ($myProperties.values.Where({$null -ne $_}).count -gt 0)
                {
                    $complexProperties += $myProperties
                }
            }
            $myaddIns.Add('Properties',$complexProperties)
            $myaddIns.Add('Type', $currentaddIns.type)
            if ($myaddIns.values.Where({$null -ne $_}).count -gt 0)
            {
                $complexAddIns += $myaddIns
            }
        }

However for the CustomSecurityAttributes field this handling seems to have broken:

        $complexCustomSecurityAttributes = @{}
        if ($complexCustomSecurityAttributes.values.Where({$null -ne $_}).count -eq 0)
        {
            $complexCustomSecurityAttributes = $null
        }

This is always going to return Null as we aren't doing the ForEach that we are doing on the other fields. In addition we aren't retrieving that data anyway. I can see other places in the code where similar work is being done, I suspect that is similarly incorrect.

Moving onto the schema definition I can see this:

[ClassVersion("1.0.0")]
class MSFT_MicrosoftGraphCustomSecurityAttributeValue
{
};

I'm not sure if this is correct or not given the above.

Lastly looking at the permissions:

{
    "resourceName": "AADServicePrincipal",
    "description": "This resource configures an Azure AD Service Principal.",
    "permissions":    {
    "graph":  {
                  "delegated":  {
                                    "read":  [
                                                 {
                                                     "name":  "Application.Read.All"
                                                 },
                                                 {
                                                     "name":  "Directory.Read.All"
                                                 },
                                                 {
                                                     "name":  "Application.ReadWrite.OwnedBy"
                                                 }
                                             ],

This is missing the CustomSecAttributeAssignment.Read|Update.All role required to perform operations on this field.

I could be wrong but I think that the main cause of this issue is that the DRG tool is either running without those additional permission scopes or its missing the attribute type because the field is not being returned from the underlying cmdlet by default. My questions are:

  • Is this an edge case or are there other instances in Graph that follow a similar behaviour?
  • What is the best way of handling this when running the DRG tool if we want a specific field to be captured that won't necessairily be captured on a default run?
  • For this CustomSecurityAttributes field, I am going to write code myself to handle this, but would be grateful of any hints in capturing the complex output returned via DSC.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant