How to Add CNAME in Azure DNS via C# code - c#

I have requirement to have multiple subdomain.
My site is hosted in virtual machine with name test.mydomain.com
I have manually added CNAME Testing1 in azureDNS and configure that in IIS site bindings Testing1.mydomain.com. and this is working
But i want to make it automatic.intead of manual process. need to do it from code or from powershell.
Anyone having knowledge about this please help me
Thanks

You'll need to start by creating a Service Principal account since it's the authority you'll use to authenticate via the SDK into Azure.
Here's the documentation to create a service principal:
Authenticating with a password
Authenticating with a certificate
Install the following packages to your C# project:
Microsoft.Azure.Management.Dns
Microsoft.Rest.ClientRuntime.Azure.Authentication
Microsoft.Azure.Management.ResourceManager (note this is a preview package)
You'll need to populate the following variables for the sample below:
tenantID: The ID of the tenant your Service Principal and DNS resource are on
clientId: The Service Principal identifier
secret: Whatever your secret mechanism is for the Service Principal you picked above
subscriptionId: The ID of the Azure subscription your DNS resource is on
resourceGroupName: The name of the resource group your DNS resource is located in
zoneName: The name of the DNS zone you're modifying (the host name)
recordSetName: A name to assign to the record set
Then you'll use the following logic to create the necessary CNAMEs necessary in Azure DNS using the Service Principal values.
var dnsClient = new DnsManagementClient(serviceCreds);
dnsClient.SubscriptionId = subscriptionId;
// Create record set parameters
var recordSetParams = new RecordSet();
recordSetParams.TTL = 3600;
recordSetParams.CnameRecord = new CnameRecord
{
Cname = "<Your CNAME Value>"
};
//Create the record in Azure DNS
await dnsClient.RecordSets.CreateOrUpdateAsync(resourceGroupName, zoneName, recordSetName, RecordType.Cname, recordSetParams);

Related

How do I get my multi-tenant Azure Function to access resources in another tenant?

I'm fairly confident I set everything up right for a multi-tenant app.
In Tenant A:
I created an Azure function and enabled a system managed identity. I granted the Managed Identity permissions to the Graph API. I confirmed my API can obtain an access token for the Managed Identity and access the Graph API with it. I added Azure AD authentication to the app and created an App Registration for the app. I configured the necessary Graph API permissions in the API Permissions settings of the app registration. I also enabled the various options to enable multi-tenant access.
In Tenant B:
I accessed the special URL to start the admin consent process for the app in Tenant A. I confirmed that I was prompted to consent to the permissions that I specified in the app registration in Tenant A. I can see that a couple new enterprise application entries were created in Tenant B for the app in Tenant A.
So far so good. However, no matter what I do, I cannot obtain a token to access the graph API in the context of Tenant B. It ALWAYS gives me a token for the managed identity in Tenant A, and accesses the info in Tenant A.
The code the retrieves an access token using the managed identity is here:
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { AdditionallyAllowedTenants = { "*" }, TenantId = "<Tenant B ID>" });
var token = credential.GetToken(
new Azure.Core.TokenRequestContext(
new[] { "https://graph.microsoft.com/.default" }, null, null, "<Tenant B ID"));
var accessToken = token.Token;
Now, I have tried all sorts of different combinations here. But, from everything I read, I am supposed to be able to specify the Tenant ID of another tenant in these methods and obtain an access token for that specific tenant. However, even when I specify Tenant B's ID, I always get a token back for the managed identity in Tenant A. If I use the token, of course, I end up accessing all of Tenant A's information.
Ironically, if I remove the DefaultAzureCredentialOptions and include the tenantID in the GetToken request I get an error telling me that I'm trying to obtain a token for a different tenant (than my own) and that I need to add the AdditionallyAllowedTenants option. It clears the error up when I add that, but then it doesn't obtain a token for the other tenant.
Perhaps I am still approaching this wrong. I want to host a multi-tenant Azure Function that runs in the context of the Tenant where the request originated from to access information within that tenant using a managed identity that exists within that tenant. I can obtain the Tenant ID context from the claims sent to the Azure function during authentication, but no matter how I try to specify that Tenant ID in my code, it will not get a token for that Tenant.
UPDATE and SOLUTION:
Thanks to Philippe's great write up, I did finally get this. I had some confusion around the mechanisms at play here and want to add this note for clarity on how I solved it.
If you've followed most of the documents you have an Azure Function app in your "host" tenant A, with an associated app registration in tenant A. The app registration has certain API permissions configured, and you have consented to this app and permissions in Tenant B.
To access the resources in Tenant B, you need to create a secret key or certificate on the app registration in Tenant A. You then need to authenticate using the Client ID, and secret key/certificate of the app registration in Tenant A, but you will request a token for Tenant B. Here it is in code using a certificate for authentication:
var appToken = new ClientCertificateCredential(tenantID, appID, appCert, new ClientCertificateCredentialOptions { AdditionallyAllowedTenants = { "*" } });
var graphServiceClient = new GraphServiceClient(appToken);
Here, we assign the following to the variables:
tenantID = Tenant B's ID
appID = Tenant A's App registration client ID
appCert = Tenant A's App registration cert
We have to include the AdditionallyAllowedTenants config parameter, and the * authorizes a token from any tenant. We then take that credential and use it to build a new GraphServiceClient that is connected to Tenant B.
You've created two separate and independent identities for your service:
The managed identity: You don't need to deal with credentials, but this identity can only be used within the same tenant. You can't use your managed identity to directly access another tenant, and you can't (currently) use your managed identity to authenticate as your app registration.
The app registration: This identity can be used to access data in another tenant, but (currently) you do need to deal with credentials.
The DefaultAzureCredential will attempt different ways of authenticating until it finds one that works:
First, it first tries to authenticate using an EnvironmentCredential. This will look for certain environment variables needed to authenticate. Depending on the variables it finds, it will end up creating a ClientSecretCredential (to authenticate using the app's client secret) or ClientCertificateCredential (using an app's client certificate).
Next, if no suitable environment variables were found, it tries to authenticate using ManagedIdentityCredential, which (as the name suggests) will attempt to use a managed identity. For you, this will always succeed (because there is a managed identity available to be used).
Next, if no managed identity was available, it will continue trying with various other options. This is all described in detail in the documentation.
For your scenario, you basically have two options today.
Option 1, using DefaultAzureCredential and environment variables
Place the credentials for your multi-tenant app registration in environment variables and your existing code will "just work".
For example:
AZURE_TENANT_ID - the target tenant where you want to obtain the token
AZURE_CLIENT_ID - your multi-tenant app registration's app ID
AZURE_CLIENT_SECRET - a client secret for your multi-tenant app registration
Note: In general, it is better to use a certificate than a secret.
Option 2, using ClientSecretCredential or ClientCertificateCredential
Instead of using DefaultAzureCredential, you can directly create a ClientSecretCredential or a ClientCertificateCredential. You'll need to store the credential somewhere safe.
For example, on approach you could follow which would avoid any credentials in environment variable or in code:
Store credentials for your multi-tenant app in a key vault.
Allow your function's managed identity to access the credential in the key vault.
In your function, use your managed identity to retrieve the credential from Key Vault (e.g. using ManagedIdentityCredential), in your tenant.
Use that credential to authenticate as your multi-tenant app registration, in the target tenant (i.e. ClientSecretCredential or ClientCertificateCredential)
https://learn.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme
Managed Identities can only get tokens within the tenant that they exist in.
You'd need a "traditional" multi-tenant app registration + client certificate/secret for this case.

Getting Registered App Display Name from an App Id

I have an Azure App Id which I have obtained from a token. I would like to get the display name of the application using C# so that I can add it to logs and Application Insights custom properties for a request. It is obviously more human-readable than an Id hence my need.
When I go into the portal I can go to the app registrations and type the AppId in and it shows me the name, so clearly it can be obtained (I can see from the URL https://graph.windows.net/myorganization/applications/?api-version=2.0&$top=40&$filter=appId%20eq%20%27MYAPPID%27), but how do I get this information programmatically in C#?
I could maintain my own lookup table somewhere which converts app ids to names, but this seems pointless when the information is obviously available at runtime.
If you want to get the information about your application in your c# application, you can use Microsoft.Graph.Beta to call the API. For example
Register a new application using the Azure portal
Sign in to the Azure portal using either a work or school account or
a personal Microsoft account.
If your account gives you access to more than one tenant, select your account in the top right corner, and set your portal session to the Azure AD tenant that you want.
In the left-hand navigation pane, select the Azure Active Directory service, and then select App registrations > New registration.
Configure Microsoft Graph permissions you need for your application
Code
/*
install Microsoft.Graph.Beta
install Microsoft.Graph.COre
install Microsoft.Graph.Beta.Auth
*/
string clientId = "your application id";
string appKey = "your client secret";
string tenantId = "your tenant id";
IConfidentialClientApplication confidentialClientApplication =
ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantId)
.WithClientSecret(appKey)
.Build();
ClientCredentialProvider authProvider = new
ClientCredentialProvider(confidentialClientApplication);
var apps = graphClient.Applications.Request().Filter("appId eq \'you application id\'").GetAsync().Result;
foreach(var app in apps){
Console.WriteLine(app.DisplayName);
}
For more details, please refer to the document

New Azure AD application doesn't work until updated through management portal

I have created a new application in Azure AD using the AAD Graph API. (code)
Unfortunately it doesn't let my client access the requested resources until I have been to the application's configuration page in the Azure management portal and made a cosmetic change, and then saved it. After removing the change and saving again, it still works.
The application manifest files before the change + change back steps and after them are completely identical (as in diff.exe says they are the same).
When comparing the JWT tokens returned when the application authenticates, it shows that the post-change access token includes the "roles" section. The entire "roles" section is not present in the access token returned before saving the application in the management portal.
So it seems the Azure management portal does "something" to the application when saving changes. The question is what it is, and can I do the same using the AAD graph API?
There were several issues. Some bugs in the backend on Azure, which have now been fixed, and also some missing calls to the API which I didn't know were necessary.
Thanks to some very helpful people at MS Support, we were able to get it to work.
When creating an application, you need to do the following:
Create an application object.
Setup the RequiredResourceAccess for the application, ie. which permissions the appliation has to Azure Graph API etc. This is what is configured in the portal's "permissions to other applications" settings. You can get the necessary GUIDs by configuring the permissions manually, and then looking in the application's AAD manifest file.
Create a service principal for the application.
Add AppRoleAssignments to the service principal.
The final part is what I was missing before. Even though you have configured RequiredResourceAccess on the application object, the service principal still needs the AppRoleAssignments to actually have permission to access the resources.
When creating the AppRoleAssignments it is a little bit tricky to figure out which PrincipalId to assign, since that is the AAD ObjectId of the service principal for the other resource.
Here is a snippet for adding the AppRoleAssignment to access the Azure AD Graph API. client is an ActiveDirectoryClient instance, and sp is the ServicePrincipal for my application:
// find the azure ad service principal
var aadsp =
client.ServicePrincipals.Where(csp => csp.AppId == "00000002-0000-0000-c000-000000000000")
.ExecuteSingleAsync().Result;
// create the app role assignment
var azureDirectoryReadAssignment = new AppRoleAssignment
{
PrincipalType = "ServicePrincipal",
PrincipalId = Guid.Parse(sp.ObjectId), //
Id = Guid.Parse("5778995a-e1bf-45b8-affa-663a9f3f4d04"), // id for Directory.Read
// azure active directory resource ID
ResourceId = Guid.Parse(aadsp.ObjectId) // azure active directory resource ID
};
// add it to the service principal
sp.AppRoleAssignments.Add(azureDirectoryReadAssignment);
// update the service principal in AAD
await sp.UpdateAsync();
My experience is that you need to wait a short time, maybe 2-3 minutes, before the newly created objects are valid in AAD, and then you can authenticate using the new application.
Apart from RasmusW's answer above, there a few more things that you might have to do depending on what you are trying to achieve.
If you want delegated permissions to work, you also need to add an Oauth2PermissionGrant into Oauth2PermissionGrants collection at the root level. This should have clientId of caller's SPN ObjectId, ResourceId of called SPN's object Id. The Scope value of the Oauth2PermissionGrant is key. It should have space separated values. Each value here comes from the 'Value' property of the Oauth2Permission object on the target SPN.
Additionally you may also need to be in appropriate DirectoryRole e.g. Directory Readers.

How to add subdomain to Azure WebSite via API

My website is hosted as Azure WebSite and I need to allow users create their own subdomains.
I already implemented this on local machine using DNS and CNAME option, but I can't do it in Azure. When I try to add Azure host name for the site I receive error that hostname must ends with '.azurewebsites.net'.
Here is my code:
var client = new WebSiteManagementClient(new CertificateCloudCredentials(ConfigurationManager.AppSettings["AzureSubscriptionId"],
new X509Certificate2(Path.Combine(AppDomain.CurrentDomain.RelativeSearchPath, "{certificate}"), "{password}")));
var configuration = client.WebSites.Get(webSpaceName, webSiteName, new WebSiteGetParameters());
if (configuration.StatusCode != HttpStatusCode.OK)
throw new Exception("AzureGet: " + subdomain);
configuration.WebSite.HostNames.Add(hostName);
var response = client.WebSites.Update(webSpaceName, webSiteName, new WebSiteUpdateParameters());
Is there any other methods to add custom HostName like 'sub.mydomain.net'?
You could use a wildcard custom domain name for your website, and then check the hostname in your application code by inspecting the headers (host or X-Forwarded-Host) of the incoming request.
i.e. your domain name would point *.mydomain.net to your web app, and then your DB keeps track of the user registered domains, and then your web app does the routing.

Openstack.Net SDK cannot access services

We've setup an OpenStack system on our own hardware installing all components, with everything seemingly fine as we've created networks and VMs through the web interface.
I'm trying to use openstack.net SDK to do things programatically. I seem to be able to Authenticate fine using a username and password, but when it comes to accessing other services that are installed, we get errors suggesting the API endpoints aren't available to the user.
The code we're using is below which works fine until the CreateServer line at which point I get the error
"Unable to authenticate user and retrieve authorized service endpoints."
Uri baseUrl = new Uri("http://mycloudip:5000/v2.0");
CloudIdentity cloudId = new CloudIdentity()
{
Username = userName,
Password = password
};
CloudIdentityProvider cip = new CloudIdentityProvider(cloudId, baseUrl);
UserAccess ua = cip.Authenticate(cloudId);
CloudServersProvider provider = new CloudServersProvider(cloudId);
Metadata metaData = new Metadata(); // Add some metadata just because we can
metaData.Add("Description", "Example 4 - Getting Started");
string serverName = "Example4";
string imageId = "48df4181-040e-4821-8723-d9e4ba908d2f";
string flavorId = "3";
NewServer newServer = provider.CreateServer(serverName, imageId, flavorId, DiskConfiguration.Manual, metaData);
I can see all the service urls in the Access and Security >> API Endpoints section whilst logged on as the same user in the dashboard, but UserAccess.ServiceCatalog doesn't seem to be populated with anything.
Any help or pointers much appreciated.
The default IIdentityProvider used by the CloudServersProvider implementation in openstack.net SDK 1.3.2.0 is designed around the authentication requirements for Rackspace. In order to authenticate against a different OpenStack-compatible installation, you'll need to follow the steps described in the following documentation:
OpenStack Authentication (openstack.net API Reference Documentation)
The following is an excerpt of the current documentation:
This page describes the process for authenticating against reference OpenStack installations, including but not limited to DevStack and the Rackspace Private Cloud.
Usage Notes
Client authentication against a reference OpenStack installation requires the following.
Create an instance of CloudIdentityWithProject and initialize its properties with the desired authentication credentials. The CloudIdentityWithProject credentials class allows the tenantName and tenantId properties described in the OpenStack documentation to be defined.
Create an instance of OpenStackIdentityProvider, and pass the previously created credentials to the constructor.
When creating a service provider instance, such as CloudFilesProvider or CloudQueuesProvider, pass null for the CloudIdentity parameter and the identity provider from the previous step as the IIdentityProvider parameter.
Limitations
The OpenStackIdentityProvider only supports authentication using username and password credentials, along with optionally specifying the tenant name and/or tenant ID (referred to as the project name and ID starting with the Identity Service API v3).

Categories