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

initial checkin for Azure.Provisioning.CloudMachine (work in progress) #46077

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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
11 changes: 11 additions & 0 deletions sdk/provisioning/Azure.Provisioning.CloudMachine/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Release History

## 1.0.0-beta.1 (Unreleased)

### Features Added

### Breaking Changes

### Bugs Fixed

### Other Changes
55 changes: 55 additions & 0 deletions sdk/provisioning/Azure.Provisioning.CloudMachine/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Azure Provisioning client library for .NET

Azure.Provisioning.CloudMachine simplifies declarative resource provisioning in .NET for CloudMachine resources.

## Getting started

### Install the package

Install the client library for .NET with [NuGet](https://www.nuget.org/ ):

```dotnetcli
dotnet add package Azure.Provisioning.CloudMachine
```

### Prerequisites

> You must have an [Azure subscription](https://azure.microsoft.com/free/dotnet/).

### Authenticate the Client

## Key concepts

This library allows you to specify your infrastructure in a declarative style using dotnet. You can then use azd to deploy your infrastructure to Azure directly without needing to write or maintain bicep or arm templates.

## Troubleshooting

- File an issue via [GitHub Issues](https://github.com/Azure/azure-sdk-for-net/issues).
- Check [previous questions](https://stackoverflow.com/questions/tagged/azure+.net) or ask new ones on Stack Overflow using Azure and .NET tags.

## Next steps

## Contributing

For details on contributing to this repository, see the [contributing
guide][cg].

This project welcomes contributions and suggestions. Most contributions
require you to agree to a Contributor License Agreement (CLA) declaring
that you have the right to, and actually do, grant us the rights to use
your contribution. For details, visit <https://cla.microsoft.com>.

When you submit a pull request, a CLA-bot will automatically determine
whether you need to provide a CLA and decorate the PR appropriately
(for example, label, comment). Follow the instructions provided by the
bot. You'll only need to do this action once across all repositories
using our CLA.

This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For
more information, see the [Code of Conduct FAQ][coc_faq] or contact
<opencode@microsoft.com> with any other questions or comments.

<!-- LINKS -->
[cg]: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/resourcemanager/Azure.ResourceManager/docs/CONTRIBUTING.md
[coc]: https://opensource.microsoft.com/codeofconduct/
[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Azure.Provisioning.CloudMachine
{
public partial class CloudMachineInfrastructure : Azure.Provisioning.Infrastructure
{
public CloudMachineInfrastructure(string? name = "cm") : base (default(string)) { }
public Azure.Provisioning.BicepParameter PrincipalIdParameter { get { throw null; } }
public Azure.Provisioning.BicepParameter PrincipalNameParameter { get { throw null; } }
public Azure.Provisioning.BicepParameter PrincipalTypeParameter { get { throw null; } }
public override Azure.Provisioning.ProvisioningPlan Build(Azure.Provisioning.ProvisioningContext? context = null) { throw null; }
}
}
11 changes: 11 additions & 0 deletions sdk/provisioning/Azure.Provisioning.CloudMachine/nuget.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
christothes marked this conversation as resolved.
Show resolved Hide resolved
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="Azure SDK for .NET Dev Feed" value="https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-net/nuget/v3/index.json" />
</packageSources>
<disabledPackageSources>
<clear />
</disabledPackageSources>
</configuration>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Description>Azure.Provisioning.CloudMachine simplifies declarative resource provisioning in .NET.</Description>
<Version>1.0.0-beta.1</Version>
<TargetFrameworks>$(RequiredTargetFrameworks)</TargetFrameworks>
<LangVersion>12</LangVersion>

<!-- Disable warning CS1591: Missing XML comment for publicly visible type or member -->
<NoWarn>CS1591</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Provisioning.Storage" VersionOverride="1.0.0-alpha.20240918.3" />
<PackageReference Include="Azure.Provisioning.ServiceBus" VersionOverride="1.0.0-alpha.20240918.3" />
<PackageReference Include="Azure.Provisioning.EventGrid" VersionOverride="1.0.0-alpha.20240918.1" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Azure.Provisioning.Authorization;
using Azure.Provisioning.EventGrid;
using Azure.Provisioning.Expressions;
using Azure.Provisioning.Resources;
using Azure.Provisioning.Roles;
using Azure.Provisioning.ServiceBus;
using Azure.Provisioning.Storage;

namespace Azure.Provisioning.CloudMachine;
christothes marked this conversation as resolved.
Show resolved Hide resolved

public class CloudMachineInfrastructure : Infrastructure
{
private readonly string _name;
private UserAssignedIdentity _identity;
private StorageAccount _storage;
private BlobService _blobs;
private BlobContainer _container;
private ServiceBusNamespace _serviceBusNamespace;
private ServiceBusNamespaceAuthorizationRule _serviceBusNamespaceAuthorizationRule;
private ServiceBusTopic _serviceBusTopic_main;
private ServiceBusTopic _serviceBusTopic_app;
private ServiceBusSubscription _serviceBusSubscription_main;
private ServiceBusSubscription _serviceBusSubscription_app;
private SystemTopic _eventGridTopic_Blobs;
private SystemTopicEventSubscription _systemTopicEventSubscription;

/// <summary>
/// The common principalId parameter.
/// </summary>
public BicepParameter PrincipalIdParameter => new BicepParameter("principalId", typeof(string));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these public while everything else is private? Just a point in time thing or ...?


/// <summary>
/// The common principalType parameter.
/// </summary>
public BicepParameter PrincipalTypeParameter => new BicepParameter("principalType", typeof(string));

/// <summary>
/// The common principalName parameter.
/// </summary>
public BicepParameter PrincipalNameParameter => new BicepParameter("principalName", typeof(string));

public CloudMachineInfrastructure(string? name = "cm") : base(name!)
{
_name = name ?? "cm";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels a little weird to make it nullable but give it a default but then add an explicit override here. I think this would be cleaner with a non-nullable param that defaults to "cm".

_identity = new($"{_name}_identity");
var managedServiceIdentity = new ManagedServiceIdentity
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style nit - most of this file is doing Foo f = new() rather than var f = new Foo().

{
ManagedServiceIdentityType = ManagedServiceIdentityType.UserAssigned,
UserAssignedIdentities = { { BicepFunction.Interpolate($"{_identity!.Id}").Compile().ToString(), new UserAssignedIdentityDetails() } }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need the ! in _identity! after instantiating it above?

};

_storage = StorageResources.CreateAccount($"{_name}_sa");
_storage.Identity = managedServiceIdentity;

_blobs = new($"{_name}_blobs")
{
Parent = _storage,
};
_container = new BlobContainer($"{_name}_container", "2023-01-01")
{
Parent = _blobs,
Name = "default"
};

_serviceBusNamespace = new($"{_name}_sb")
{
Sku = new ServiceBusSku
{
Name = ServiceBusSkuName.Standard,
Tier = ServiceBusSkuTier.Standard
},
};
_serviceBusNamespaceAuthorizationRule = new($"{_name}_sb_auth_rule", "2021-11-01")
{
Parent = _serviceBusNamespace,
Rights = [ServiceBusAccessRight.Listen, ServiceBusAccessRight.Send, ServiceBusAccessRight.Manage]
};
_serviceBusTopic_main = new($"{_name}_sb_topic_main", "2021-11-01")
{
Parent = _serviceBusNamespace,
// Name = "default",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this commented out? I don't know if I'm aware of whatever's blocking this.

MaxMessageSizeInKilobytes = 256,
DefaultMessageTimeToLive = new StringLiteral("P14D"),
RequiresDuplicateDetection = false,
EnableBatchedOperations = true,
SupportOrdering = true,
Status = ServiceBusMessagingEntityStatus.Active
};
_serviceBusSubscription_main = new($"{_name}_sb_sub_main", "2021-11-01")
{
Parent = _serviceBusTopic_main,
IsClientAffine = false,
LockDuration = new StringLiteral("PT30S"),
RequiresSession = false,
DefaultMessageTimeToLive = new StringLiteral("P14D"),
DeadLetteringOnFilterEvaluationExceptions = true,
DeadLetteringOnMessageExpiration = true,
MaxDeliveryCount = 10,
EnableBatchedOperations = true,
Status = ServiceBusMessagingEntityStatus.Active
};
_serviceBusTopic_app = new($"{_name}_sb_topic_app", "2021-11-01")
{
Parent = _serviceBusNamespace,
// Name = "default",
MaxMessageSizeInKilobytes = 256,
DefaultMessageTimeToLive = new StringLiteral("P14D"),
RequiresDuplicateDetection = false,
EnableBatchedOperations = true,
SupportOrdering = true,
Status = ServiceBusMessagingEntityStatus.Active
};
_serviceBusSubscription_app = new($"{_name}_sb_sub_app", "2021-11-01")
{
Parent = _serviceBusTopic_app,
IsClientAffine = false,
LockDuration = new StringLiteral("PT30S"),
RequiresSession = false,
DefaultMessageTimeToLive = new StringLiteral("P14D"),
DeadLetteringOnFilterEvaluationExceptions = true,
DeadLetteringOnMessageExpiration = true,
MaxDeliveryCount = 10,
EnableBatchedOperations = true,
Status = ServiceBusMessagingEntityStatus.Active
};
_eventGridTopic_Blobs = new($"{_name}_eg_blob", "2022-06-15")
{
TopicType = "Microsoft.Storage.StorageAccounts",
Source = _storage.Id,
Identity = managedServiceIdentity
};
_systemTopicEventSubscription = new($"{_name}_eg_blob_sub", "2022-06-15")
{
Parent = _eventGridTopic_Blobs,
DeliveryWithResourceIdentity = new DeliveryWithResourceIdentity
{
Identity = new EventSubscriptionIdentity
{
IdentityType = EventSubscriptionIdentityType.UserAssigned,
UserAssignedIdentity = _identity.Id
},
Destination = new EventHubEventSubscriptionDestination
{
ResourceId = _serviceBusTopic_main.Id
}
},
Filter = new EventSubscriptionFilter
{
IncludedEventTypes =
[
"Microsoft.Storage.BlobCreated",
"Microsoft.Storage.BlobDeleted",
christothes marked this conversation as resolved.
Show resolved Hide resolved
"Microsoft.Storage.BlobRenamed"
],
IsAdvancedFilteringOnArraysEnabled = true
},
EventDeliverySchema = EventDeliverySchema.EventGridSchema,
RetryPolicy = new EventSubscriptionRetryPolicy
{
MaxDeliveryAttempts = 30,
EventTimeToLiveInMinutes = 1440
}
};
}

public override ProvisioningPlan Build(ProvisioningContext? context = null)
{
// Always add a default location parameter.
// azd assumes there will be a location parameter for every module.
// The Infrastructure location resolver will resolve unset Location properties to this parameter.
Add(new BicepParameter("location", typeof(string))
{
Description = "The location for the resource(s) to be deployed.",
Value = BicepFunction.GetResourceGroup().Location
});

Add(PrincipalIdParameter);
Add(PrincipalTypeParameter);
Add(PrincipalNameParameter);

Add(_identity);
Add(_storage);
Add(_storage.AssignRole(StorageBuiltInRole.StorageBlobDataContributor, RoleManagementPrincipalType.User, PrincipalIdParameter));
Add(_storage.AssignRole(StorageBuiltInRole.StorageTableDataContributor, RoleManagementPrincipalType.User, PrincipalIdParameter));
Add(_container);
Add(_blobs);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be better to parent this to the storage account here to prepare for when things open up.

Add(_serviceBusNamespace);
Add(_serviceBusNamespace.AssignRole(ServiceBusBuiltInRole.AzureServiceBusDataOwner, RoleManagementPrincipalType.User, PrincipalIdParameter));
Add(_serviceBusNamespaceAuthorizationRule);
Add(_serviceBusTopic_main);
Add(_serviceBusTopic_app);
Add(_serviceBusSubscription_main);
Add(_serviceBusSubscription_app);

// This is necessary until SystemTopic adds an AssignRole method.
var role = ServiceBusBuiltInRole.AzureServiceBusDataOwner;
RoleAssignment roleAssignment = new RoleAssignment(_eventGridTopic_Blobs.ResourceName + "_" + _identity.ResourceName + "_" + ServiceBusBuiltInRole.GetBuiltInRoleName(role));
roleAssignment.Name = BicepFunction.CreateGuid(_eventGridTopic_Blobs.Id, _identity.Id, BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", role.ToString()));
roleAssignment.Scope = new IdentifierExpression(_eventGridTopic_Blobs.ResourceName);
roleAssignment.PrincipalType = RoleManagementPrincipalType.ServicePrincipal;
roleAssignment.RoleDefinitionId = BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", role.ToString());
roleAssignment.PrincipalId = _identity.PrincipalId;
Add(roleAssignment);
Add(_systemTopicEventSubscription);
Add(_eventGridTopic_Blobs);

// Placeholders for now.
Add(new BicepOutput($"storage_name", typeof(string)) { Value = _storage.Name });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want the name or the primary endpoint like _storage.PrimaryEndpoints.Value!.BlobUri?

Add(new BicepOutput($"servicebus_name", typeof(string)) { Value = _serviceBusNamespace.Name });

return base.Build(context);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Diagnostics.CodeAnalysis;

[assembly: Experimental("AZPROVISION001")]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<LangVersion>12</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\core\Azure.Core.TestFramework\src\Azure.Core.TestFramework.csproj" />
<ProjectReference Include="..\src\Azure.Provisioning.CloudMachine.csproj" />
</ItemGroup>
</Project>
18 changes: 18 additions & 0 deletions sdk/provisioning/Azure.Provisioning.sln
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Provisioning.WebPubSu
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Provisioning.WebPubSub.Tests", "Azure.Provisioning.WebPubSub\tests\Azure.Provisioning.WebPubSub.Tests.csproj", "{015670AB-881C-464F-8545-E686C9A92C4F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Azure.Provisioning.CloudMachine", "Azure.Provisioning.CloudMachine", "{DB2FC0CB-7103-4D3C-A737-8C6AB61168F8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Provisioning.CloudMachine", "Azure.Provisioning.CloudMachine\src\Azure.Provisioning.CloudMachine.csproj", "{F54CA64F-3BB8-49D5-AE3B-3408AE324632}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Provisioning.CloudMachine.Tests", "Azure.Provisioning.CloudMachine\tests\Azure.Provisioning.CloudMachine.Tests.csproj", "{94642992-7CC0-4649-B58F-8E5E3F134C8A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -308,11 +314,23 @@ Global
{015670AB-881C-464F-8545-E686C9A92C4F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{015670AB-881C-464F-8545-E686C9A92C4F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{015670AB-881C-464F-8545-E686C9A92C4F}.Release|Any CPU.Build.0 = Release|Any CPU
{F54CA64F-3BB8-49D5-AE3B-3408AE324632}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F54CA64F-3BB8-49D5-AE3B-3408AE324632}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F54CA64F-3BB8-49D5-AE3B-3408AE324632}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F54CA64F-3BB8-49D5-AE3B-3408AE324632}.Release|Any CPU.Build.0 = Release|Any CPU
{94642992-7CC0-4649-B58F-8E5E3F134C8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{94642992-7CC0-4649-B58F-8E5E3F134C8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{94642992-7CC0-4649-B58F-8E5E3F134C8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{94642992-7CC0-4649-B58F-8E5E3F134C8A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7CB2D417-323C-4A66-9355-26459881303F}
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F54CA64F-3BB8-49D5-AE3B-3408AE324632} = {DB2FC0CB-7103-4D3C-A737-8C6AB61168F8}
{94642992-7CC0-4649-B58F-8E5E3F134C8A} = {DB2FC0CB-7103-4D3C-A737-8C6AB61168F8}
EndGlobalSection
EndGlobal
Loading
Loading