Skip to content

Commit

Permalink
Fixed GetBicepParamResources exception when expanding Microsoft Graph A…
Browse files Browse the repository at this point in the history
  • Loading branch information
BernieWhite authored Oct 11, 2024
1 parent 25a6389 commit 194b235
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 1 deletion.
6 changes: 6 additions & 0 deletions docs/CHANGELOG-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers

## Unreleased

What's changed since pre-release v1.39.0:

- Bug fixes:
- Fixed `GetBicepParamResources` exception when expanding `Microsoft.Graph/groups` resource by @BernieWhite.
[#3062](https://github.com/Azure/PSRule.Rules.Azure/issues/3062)

## v1.39.0

What's changed since pre-release v1.38.0:
Expand Down
17 changes: 17 additions & 0 deletions src/PSRule.Rules.Azure/Common/JsonExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal static class JsonExtensions
private const string PROPERTY_TYPE = "type";
private const string PROPERTY_FIELD = "field";
private const string PROPERTY_EXISTING = "existing";
private const string PROPERTY_IMPORT = "import";
private const string PROPERTY_SCOPE = "scope";
private const string PROPERTY_SUBSCRIPTION_ID = "subscriptionId";
private const string PROPERTY_RESOURCE_GROUP = "resourceGroup";
Expand Down Expand Up @@ -508,11 +509,27 @@ internal static bool IsEmpty(this JToken value)
(value.Type == JTokenType.String && string.IsNullOrEmpty(value.Value<string>()));
}

/// <summary>
/// Resources with the <c>existing</c> property set to <c>true</c> are references to existing resources.
/// </summary>
/// <param name="o">The resource <seealso cref="JObject"/>.</param>
/// <returns>Returns <c>true</c> if the resource is a reference to an existing resource.</returns>
internal static bool IsExisting(this JObject o)
{
return o != null && o.TryBoolProperty(PROPERTY_EXISTING, out var existing) && existing != null && existing.Value;
}

/// <summary>
/// Resources with an <c>import</c> property set to a non-empty string refer to extensibility provided resources.
/// For example, Microsoft Graph.
/// </summary>
/// <param name="o">The resource <seealso cref="JObject"/>.</param>
/// <returns>Returns <c>true</c> if the resource is imported from an extensibility provider.</returns>
internal static bool IsImport(this JObject o)
{
return o != null && o.TryStringProperty(PROPERTY_IMPORT, out var s) && !string.IsNullOrEmpty(s);
}

internal static bool TryResourceType(this JObject o, out string type)
{
type = default;
Expand Down
2 changes: 1 addition & 1 deletion src/PSRule.Rules.Azure/Data/Template/TemplateVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1231,7 +1231,7 @@ private void Resources(TemplateContext context, JObject resources)
{
Reference(context, p.Name, resource);
}
else
else if (!resource.IsImport())
{
r.AddRange(ResourceExpand(context, p.Name, resource));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// Test case for https://github.com/Azure/PSRule.Rules.Azure/issues/3062

extension microsoftGraphV1_0

var appRoleId = 'appRoleId'
var certKey = 'certKey'

resource resourceApp 'Microsoft.Graph/[email protected]' = {
uniqueName: 'ExampleResourceApp'
displayName: 'Example Resource Application'
appRoles: [
{
id: appRoleId
allowedMemberTypes: ['User', 'Application']
description: 'Read access to resource app data'
displayName: 'ResourceAppData.Read.All'
value: 'ResourceAppData.Read.All'
isEnabled: true
}
]
}

resource resourceSp 'Microsoft.Graph/[email protected]' = {
appId: resourceApp.appId
}

resource clientApp 'Microsoft.Graph/[email protected]' = {
uniqueName: 'ExampleClientApp'
displayName: 'Example Client Application'
keyCredentials: [
{
displayName: 'Example Client App Key Credential'
usage: 'Verify'
type: 'AsymmetricX509Cert'
key: certKey
}
]
}

resource clientSp 'Microsoft.Graph/[email protected]' = {
appId: clientApp.appId
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"languageVersion": "2.1-experimental",
"contentVersion": "1.0.0.0",
"metadata": {
"_EXPERIMENTAL_WARNING": "This template uses ARM features that are experimental. Experimental features should be enabled for testing purposes only, as there are no guarantees about the quality or stability of these features. Do not enable these settings for any production usage, or your production environment may be subject to breaking.",
"_EXPERIMENTAL_FEATURES_ENABLED": [
"Extensibility"
],
"_generator": {
"name": "bicep",
"version": "0.30.23.60470",
"templateHash": "2001503425732761903"
}
},
"variables": {
"appRoleId": "appRoleId",
"certKey": "certKey"
},
"imports": {
"MicrosoftGraph": {
"provider": "MicrosoftGraph",
"version": "0.1.8-preview"
}
},
"resources": {
"resourceApp": {
"import": "MicrosoftGraph",
"type": "Microsoft.Graph/[email protected]",
"properties": {
"uniqueName": "ExampleResourceApp",
"displayName": "Example Resource Application",
"appRoles": [
{
"id": "[variables('appRoleId')]",
"allowedMemberTypes": [
"User",
"Application"
],
"description": "Read access to resource app data",
"displayName": "ResourceAppData.Read.All",
"value": "ResourceAppData.Read.All",
"isEnabled": true
}
]
}
},
"resourceSp": {
"import": "MicrosoftGraph",
"type": "Microsoft.Graph/[email protected]",
"properties": {
"appId": "[reference('resourceApp').appId]"
},
"dependsOn": [
"resourceApp"
]
},
"clientApp": {
"import": "MicrosoftGraph",
"type": "Microsoft.Graph/[email protected]",
"properties": {
"uniqueName": "ExampleClientApp",
"displayName": "Example Client Application",
"keyCredentials": [
{
"displayName": "Example Client App Key Credential",
"usage": "Verify",
"type": "AsymmetricX509Cert",
"key": "[variables('certKey')]"
}
]
}
},
"clientSp": {
"import": "MicrosoftGraph",
"type": "Microsoft.Graph/[email protected]",
"properties": {
"appId": "[reference('clientApp').appId]"
},
"dependsOn": [
"clientApp"
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"experimentalFeaturesEnabled": {
"optionalModuleNames": true,
"extensibility": true
},
"extensions": {
"microsoftGraphV1_0": "br:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:0.1.8-preview"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@
<None Update="Template.Policy.WithDeployment.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Bicep\ExtensibilityTestCases\Tests.Bicep.1.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
15 changes: 15 additions & 0 deletions tests/PSRule.Rules.Azure.Tests/TemplateVisitorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1351,6 +1351,21 @@ public void ProcessTemplate_WhenParented_ScopeIsExpected()
Assert.Equal("/subscriptions/ffffffff-ffff-ffff-ffff-ffffffffffff/resourceGroups/ps-rule-test-rg/providers/Microsoft.Sql/servers/server01/databases/db02", actual["scope"].Value<string>());
}

/// <summary>
/// Test case for https://github.com/Azure/PSRule.Rules.Azure/issues/3062
/// </summary>
[Fact]
public void ProcessTemplate_WhenMicrosoftGraphType_ShouldIgnoreExtensibilityResources()
{
var resources = ProcessTemplate(GetSourcePath("Bicep/ExtensibilityTestCases/Tests.Bicep.1.json"), null, out _);

Assert.Single(resources);

var actual = resources[0];
Assert.Equal("Microsoft.Resources/deployments", actual["type"].Value<string>());
Assert.Equal("ps-rule-test-deployment", actual["name"].Value<string>());
}

#region Helper methods

private static string GetSourcePath(string fileName)
Expand Down

0 comments on commit 194b235

Please sign in to comment.