I am registering AAD Applications through the following code
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(serviceRoot, async () => await GetAppTokenAsync());
Application application = new Application()
{
AvailableToOtherTenants = false,
DisplayName = appName,
ErrorUrl = null,
GroupMembershipClaims = null,
Homepage = "http://"+appName,
IdentifierUris = new List<string>() { "https://"+appName },
KeyCredentials = new List<KeyCredential>(),
KnownClientApplications = new List<Guid>(),
LogoutUrl = null,
Oauth2AllowImplicitFlow = false,
Oauth2AllowUrlPathMatching = false,
Oauth2Permissions = new List<OAuth2Permission>(),
Oauth2RequirePostResponse = false,
// PasswordCredentials = new List<PasswordCredential>(),
PasswordCredentials = new List<PasswordCredential>(),
PublicClient = false,
ReplyUrls = new List<string>(),
// RequiredResourceAccess = new List<RequiredResourceAccess>(),
RequiredResourceAccess = new List<RequiredResourceAccess>(),
SamlMetadataUrl = null,
ExtensionProperties = new List<ExtensionProperty>(),
Manager = null,
ObjectType = "Application",
DeletionTimestamp = null,
CreatedOnBehalfOf = null,
CreatedObjects = new List<DirectoryObject>(),
DirectReports = new List<DirectoryObject>(),
Members = new List<DirectoryObject>(),
MemberOf = new List<DirectoryObject>(),
Owners = new List<DirectoryObject>(),
OwnedObjects = new List<DirectoryObject>(),
Policies = new List<DirectoryObject>()
};
I also have an object of type Microsoft.Azure.ActiveDirectory.GraphClient.User which contains all the information of a User that I want to add as owner of the application.
How can I do that?
The way I was trying that is by doing this
activeDirectoryClient.Applications.AddApplicationAsync(application).Wait();
ServicePrincipal newServicePrincpal = new ServicePrincipal();
if (application != null)
{
newServicePrincpal.DisplayName = application.DisplayName;
newServicePrincpal.AccountEnabled = true;
newServicePrincpal.AppId = application.AppId;
newServicePrincpal.Owners.Add(user);
try
{
activeDirectoryClient.ServicePrincipals.AddServicePrincipalAsync(newServicePrincpal).Wait();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
But when I navigate to the application manually in the Azure Portal, the only owner that appears is my own account and not also the other account I got in the user variable
Any idea how to add other owners to the application?
I can reproduce this issue too. The root cause for this issue is that the Azure AD Graph library doesn't provide the owner info when it try to create the service principal.
If you want to add the owner for the service principal, you can use code below after you creating the service principal:
var activeDirectoryClient = GraphHelper.CreateGraphClient();
var sp = (ServicePrincipal)activeDirectoryClient.ServicePrincipals.GetByObjectId("4af8365b-1b49-481c-8c47-7b3fab5611fc").ExecuteAsync().Result;
var user = new Users().GetUserByUserName(activeDirectoryClient, "user2#adfei.onmicrosoft.com").Result;
sp.Owners.Add(user);
sp.UpdateAsync();
And if you want to add the owner of application, here is the code for you reference:
var activeDirectoryClient = GraphHelper.CreateGraphClient();
var app = (Application)activeDirectoryClient.Applications.GetByObjectId("bd87934b-dd4f-446a-a025-7675d1b2464a").ExecuteAsync().Result;
var user = new Users().GetUserByUserName(activeDirectoryClient, "user2#adfei.onmicrosoft.com").Result;
app.Owners.Add(user);
app.UpdateAsync();
More detail about the difference between application and service principal please check this document.
And if you want the Graph client library to support adding the owner when creating the them, you can try to submit the feedback from here.
Update
public static ActiveDirectoryClient CreateGraphClient()
{
string accessToken = "";
string tenantId= "";
string graphResourceId = "https://graph.windows.net";
Uri servicePointUri = new Uri(graphResourceId);
Uri serviceRoot = new Uri(servicePointUri, tenantId);
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(serviceRoot, async () => await Task.FromResult(accessToken));
return activeDirectoryClient;
}
Add a runalbe code sample to add an owner for the service principal:
https://github.com/VitorX/AddServicePrincipalWithOwner
Update2
After you run the code sample in the above, you could capture the result using the Fiddler like below. And we can get the object id of service principal via the response of creating the service principal:
Then we can check the owners of principals via the REST like figure below:
Related
I am trying to crate a site using . This is the code:
using PnP.Framework;
TeamSiteCollectionCreationInformation modernteamSiteInfo = new TeamSiteCollectionCreationInformation
{
Description = siteDescription,
DisplayName = siteTitle,
Alias = siteUrl.AbsoluteUri,
IsPublic = true,
Lcid = (uint)lcid,
};
var createModernSite = await clientContext.CreateSiteAsync(modernteamSiteInfo);
I am not really sure how to initiate the ClientContext because I think it should use https://tenant-admin.sharepoint.com. How can I get the ClientContext object?
I tried to use the same object from the current ClientContext, but I got this error message:
App-Only is currently not supported, unless you provide a Microsoft
Graph Access Token.. StackTrace: at
PnP.Framework.Sites.SiteCollection.CreateAsync(ClientContext
clientContext, TeamSiteCollectionCreationInformation
siteCollectionCreationInformation, Int32 delayAfterCreation, Int32
maxRetryCount, Int32 retryDelay, Boolean noWait, String
graphAccessToken, AzureEnvironment azureEnvironment) at
Microsoft.SharePoint.Client.ClientContextExtensions.CreateSiteAsync(ClientContext
clientContext, TeamSiteCollectionCreationInformation
siteCollectionCreationInformation)
You can Create Sharepoint site using pnp core sdk like this.Before that You need to authentication for your application.
var context = await _pnpContextFactory.CreateAsync("Default");
var teamSiteToCreate = new TeamSiteOptions("Demo", "Demo")
{
Description = "My site description",
Language = Language.English,
Owners = new string[] { "test#xyz.onmicrosoft.com" },
};
SiteCreationOptions siteCreationOptions = new SiteCreationOptions()
{
UsingApplicationPermissions = true
};
var create = await context.GetSiteCollectionManager().CreateSiteCollectionAsync(teamSiteToCreate, siteCreationOptions);
return Ok(Create.Uri);
I am trying to create Resource Group and Virtual Machine (and other components) programmatically using C#. I want to use
SdkContext.AzureCredentialsFactory.FromServicePrincipal(clientId, clientSecrete, tenantId, environment);
But I don't know my client Secrete. How to get it or generate it? Or how to generate the bearer access token of current Azure user credential for restful api call?
The file %userprofile%.azure\accessTokens.json contains bearer access token. But it is generated by Azure Cli. Is there a way to generate the token via C# code?
This article
https://learn.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal shows a way but it needs to register an app and grant it certain permission (I don't have the permission to grant the permission to app).
The link you provided is the correct way to use SdkContext.AzureCredentialsFactory.FromServicePrincipal, if you just have a user account rather than a valid service principal, the best way in this case is to use Azure.Identity to auth, make sure your user account has the permission to create the VM, i.e. RBAC role at the scope you want to create the VM.
using Azure.Identity;
using Azure.ResourceManager.Resources;
using Azure.ResourceManager.Resources.Models;
using Azure.ResourceManager.Compute;
using Azure.ResourceManager.Compute.Models;
using Azure.ResourceManager.Network;
using Azure.ResourceManager.Network.Models;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
namespace CreateVMSample
{
public class Program
{
protected static string AdminUsername = "<username>";
protected static string AdminPassword = "<password>";
static async Task Main(string[] args)
{
var subscriptionId = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
var resourceClient = new ResourcesManagementClient(subscriptionId, new DefaultAzureCredential());
// Create Resource Group
Console.WriteLine("--------Start create group--------");
var resourceGroups = resourceClient.ResourceGroups;
var location = "westus2";
var resourceGroupName = "QuickStartRG";
var resourceGroup = new ResourceGroup(location);
resourceGroup = await resourceGroups.CreateOrUpdateAsync(resourceGroupName, resourceGroup);
Console.WriteLine("--------Finish create group--------");
// Create a Virtual Machine
await Program.CreateVmAsync(subscriptionId, "QuickStartRG", location, "quickstartvm");
// Delete resource group if necessary
//Console.WriteLine("--------Start delete group--------");
//await (await resourceGroups.StartDeleteAsync(resourceGroupName)).WaitForCompletionAsync();
//Console.WriteLine("--------Finish delete group--------");
//Console.ReadKey();
}
public static async Task CreateVmAsync(
string subscriptionId,
string resourceGroupName,
string location,
string vmName)
{
var computeClient = new ComputeManagementClient(subscriptionId, new DefaultAzureCredential());
var networkClient = new NetworkManagementClient(subscriptionId, new DefaultAzureCredential());
var virtualNetworksClient = networkClient.VirtualNetworks;
var networkInterfaceClient = networkClient.NetworkInterfaces;
var publicIpAddressClient = networkClient.PublicIPAddresses;
var availabilitySetsClient = computeClient.AvailabilitySets;
var virtualMachinesClient = computeClient.VirtualMachines;
// Create AvailabilitySet
Console.WriteLine("--------Start create AvailabilitySet--------");
var availabilitySet = new AvailabilitySet(location)
{
PlatformUpdateDomainCount = 5,
PlatformFaultDomainCount = 2,
Sku = new Azure.ResourceManager.Compute.Models.Sku() { Name = "Aligned" }
};
availabilitySet = await availabilitySetsClient.CreateOrUpdateAsync(resourceGroupName, vmName + "_aSet", availabilitySet);
// Create IP Address
Console.WriteLine("--------Start create IP Address--------");
var ipAddress = new PublicIPAddress()
{
PublicIPAddressVersion = Azure.ResourceManager.Network.Models.IPVersion.IPv4,
PublicIPAllocationMethod = IPAllocationMethod.Dynamic,
Location = location,
};
ipAddress = await publicIpAddressClient.StartCreateOrUpdate(resourceGroupName, vmName + "_ip", ipAddress)
.WaitForCompletionAsync();
// Create VNet
Console.WriteLine("--------Start create VNet--------");
var vnet = new VirtualNetwork()
{
Location = location,
AddressSpace = new AddressSpace() { AddressPrefixes = new List<string>() { "10.0.0.0/16" } },
Subnets = new List<Subnet>()
{
new Subnet()
{
Name = "mySubnet",
AddressPrefix = "10.0.0.0/24",
}
},
};
vnet = await virtualNetworksClient
.StartCreateOrUpdate(resourceGroupName, vmName + "_vent", vnet)
.WaitForCompletionAsync();
// Create Network Interface
Console.WriteLine("--------Start create Network Interface--------");
var nic = new NetworkInterface()
{
Location = location,
IpConfigurations = new List<NetworkInterfaceIPConfiguration>()
{
new NetworkInterfaceIPConfiguration()
{
Name = "Primary",
Primary = true,
Subnet = new Subnet() { Id = vnet.Subnets.First().Id },
PrivateIPAllocationMethod = IPAllocationMethod.Dynamic,
PublicIPAddress = new PublicIPAddress() { Id = ipAddress.Id }
}
}
};
nic = await networkInterfaceClient
.StartCreateOrUpdate(resourceGroupName, vmName + "_nic", nic)
.WaitForCompletionAsync();
// Create VM
Console.WriteLine("--------Start create VM--------");
var vm = new VirtualMachine(location)
{
NetworkProfile = new Azure.ResourceManager.Compute.Models.NetworkProfile { NetworkInterfaces = new[] { new NetworkInterfaceReference() { Id = nic.Id } } },
OsProfile = new OSProfile
{
ComputerName = vmName,
AdminUsername = Program.AdminUsername,
AdminPassword = Program.AdminPassword,
LinuxConfiguration = new LinuxConfiguration { DisablePasswordAuthentication = false, ProvisionVMAgent = true }
},
StorageProfile = new StorageProfile()
{
ImageReference = new ImageReference()
{
Offer = "UbuntuServer",
Publisher = "Canonical",
Sku = "18.04-LTS",
Version = "latest"
},
DataDisks = new List<DataDisk>()
},
HardwareProfile = new HardwareProfile() { VmSize = VirtualMachineSizeTypes.StandardB1Ms },
AvailabilitySet = new Azure.ResourceManager.Compute.Models.SubResource() { Id = availabilitySet.Id }
};
var operation = await virtualMachinesClient.StartCreateOrUpdateAsync(resourceGroupName, vmName, vm);
var result = (await operation.WaitForCompletionAsync()).Value;
Console.WriteLine("VM ID: " + result.Id);
Console.WriteLine("--------Done create VM--------");
}
}
}
Source - https://github.com/Azure-Samples/azure-samples-net-management/blob/master/samples/compute/create-virtual-machine/Program.cs
This sample uses DefaultAzureCredential of Azure.Identity to auth, it will try the following credentials in this doc to auth automatically, one of them is VisualStudioCredential, means it will use the logged user account of your VS. You can also use it directly, replace all the DefaultAzureCredential with VisualStudioCredential in the sample code.
Besides, if you still want to get the token to call the REST API manually, you could use the code below.
var tokenCredential = new VisualStudioCredential();
var accessToken = await tokenCredential.GetTokenAsync("https://management.azure.com");
Trying to get all azure subscriptions for all the tenants' user is associated, I am using an azure sample solution which I got from GitHub but the solution is not able to get all the subscription.
Sample Solution Link.
When looping through each tenant I am getting error after calling Azure Resource Management List Subscription API
The Below Try Code block throughs exception for all tenants other than tenant in claims
Failed to acquire token silently. Call method AcquireToken
string tenantId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
and the exception is handled in the catch block where authContext.AcquireToken is called instead of authContext.AcquireTokenSilent
public static List<Subscription> GetUserSubscriptions(string organizationId)
{
List<Subscription> subscriptions = null;
//string tenantId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
string signedInUserUniqueName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Name).Value.Split('#')[ClaimsPrincipal.Current.FindFirst(ClaimTypes.Name).Value.Split('#').Length - 1];
try
{
// Aquire Access Token to call Azure Resource Manager
ClientCredential credential = new ClientCredential(ConfigurationManager.AppSettings["ida:ClientID"],
ConfigurationManager.AppSettings["ida:Password"]);
// initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's EF DB
AuthenticationContext authContext = new AuthenticationContext(
string.Format(ConfigurationManager.AppSettings["ida:Authority"], organizationId),
new ADALTokenCache(signedInUserUniqueName));
AuthenticationResult result =
authContext.AcquireTokenSilent(
ConfigurationManager.AppSettings["ida:AzureResourceManagerIdentifier"], credential,
new UserIdentifier(signedInUserUniqueName, UserIdentifierType.RequiredDisplayableId));
subscriptions = new List<Subscription>();
// Get subscriptions to which the user has some kind of access
string requestUrl = string.Format("{0}/subscriptions?api-version={1}",
ConfigurationManager.AppSettings["ida:AzureResourceManagerUrl"],
ConfigurationManager.AppSettings["ida:AzureResourceManagerAPIVersion"]);
// Make the GET request
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = client.SendAsync(request).Result;
if (response.IsSuccessStatusCode)
{
string responseContent = response.Content.ReadAsStringAsync().Result;
dynamic subscriptionsResult = (Json.Decode(responseContent)).value;
foreach (dynamic subscription in subscriptionsResult)
{
subscriptions.Add(new Subscription()
{
Id = subscription.subscriptionId,
DisplayName = subscription.displayName,
OrganizationId = organizationId
});
}
}
}
below catch block is able to get the token after changing the tenant id(organisation Id) dynamically but getting the below error when using the token with REST API
{"error":{"code":"InvalidAuthenticationToken","message":"The received access token is not valid: at least one of the claims 'puid' or 'altsecid' or 'oid' should be present. If you are accessing as application please make sure service principal is properly created in the tenant."}}
catch (AdalException ex)
{
if (ex.ErrorCode == "failed_to_acquire_token_silently")
{
ClientCredential credential = new ClientCredential(ConfigurationManager.AppSettings["ida:ClientID"],
ConfigurationManager.AppSettings["ida:Password"]);
// initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's EF DB
AuthenticationContext authContext = new AuthenticationContext(
string.Format(ConfigurationManager.AppSettings["ida:Authority"], organizationId), new ADALTokenCache(signedInUserUniqueName));
//List<TokenCacheItem> items = authContext.TokenCache.ReadItems().ToList();
string resource = ConfigurationManager.AppSettings["ida:AzureResourceManagerIdentifier"];
AuthenticationResult result = authContext.AcquireToken(resource, credential);
subscriptions = new List<Subscription>();
// Get subscriptions to which the user has some kind of access
string requestUrl = string.Format("{0}/subscriptions?api-version={1}", ConfigurationManager.AppSettings["ida:AzureResourceManagerUrl"],
ConfigurationManager.AppSettings["ida:AzureResourceManagerAPIVersion"]);
// Make the GET request
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = client.SendAsync(request).Result;
if (response.IsSuccessStatusCode)
{
string responseContent = response.Content.ReadAsStringAsync().Result;
var subscriptionsResult = (Json.Decode(responseContent)).value;
foreach (var subscription in subscriptionsResult)
subscriptions.Add(new Subscription()
{
Id = subscription.subscriptionId,
DisplayName = subscription.displayName,
OrganizationId = organizationId
});
}
else
{
string errMessage = response.Content.ReadAsStringAsync().Result;
}
}
}
return subscriptions;
}```
I want to send sign in request for IdP for every unauthenticated request to my Web Application. I am using Sustainsys.Saml2.Owin. I am using the default StubIdp.
In the configuration method, I have added a filter which checks if incoming request is authenticated. If it is not, I issue an OWIN challenge.
My problem is that, the OWIN challenge is not redirecting the application to StubIdp login page. What am I missing here?
Following is the code for Startup.Auth.cs class
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieName = "MyJobSizeToken",
CookieSecure = CookieSecureOption.Always,
CookieHttpOnly = true,
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseSaml2Authentication(CreateSaml2Options());
app.Use((context, next) =>
{
if (!context.Authentication.User.Identity.IsAuthenticated)
{
//context.Environment.Add("saml2.idp", new EntityId("https://stubidp.sustainsys.com/Metadata"));
//context.Authentication.Challenge("https://stubidp.sustainsys.com/Metadata");
HttpContext.Current.GetOwinContext().Authentication.Challenge(new
AuthenticationProperties { RedirectUri = "/" },
"KentorAuthServices"); // This line does not redirect to the stubidp
}
return next.Invoke();
});
}
private static Saml2AuthenticationOptions CreateSaml2Options()
{
var spOptions = CreateSPOptions();
var Saml2Options = new Saml2AuthenticationOptions(false)
{
SPOptions = spOptions
};
var idp = new IdentityProvider(new EntityId("https://stubidp.sustainsys.com/Metadata"), spOptions)
{
AllowUnsolicitedAuthnResponse = true,
Binding = Saml2BindingType.HttpRedirect,
SingleSignOnServiceUrl = new Uri("https://stubidp.sustainsys.com")
};
idp.SigningKeys.AddConfiguredKey(
new X509Certificate2(
HostingEnvironment.MapPath(
"~/App_Data/stubidp.sustainsys.com.cer")));
Saml2Options.IdentityProviders.Add(idp);
// It's enough to just create the federation and associate it
// with the options. The federation will load the metadata and
// update the options with any identity providers found.
new Federation("http://localhost:52071/Federation", true, Saml2Options);
return Saml2Options;
}
private static SPOptions CreateSPOptions()
{
var nz = CultureInfo.GetCultureInfo("en-nz");
var organization = new Organization();
organization.Names.Add(new LocalizedName("Flink Solutions", nz));
organization.DisplayNames.Add(new LocalizedName("Flink Solutions", nz));
organization.Urls.Add(new LocalizedUri(new Uri("http://www.Sustainsys.se"), nz));
var spOptions = new SPOptions
{
EntityId = new EntityId("https://localhost:44339/Saml2"),
ReturnUrl = new Uri("http://localhost:44339/"),
DiscoveryServiceUrl = new Uri("http://localhost:44339/DiscoveryService"),
Organization = organization
};
var techContact = new ContactPerson
{
Type = ContactType.Technical
};
techContact.EmailAddresses.Add("Saml2#example.com");
spOptions.Contacts.Add(techContact);
var supportContact = new ContactPerson
{
Type = ContactType.Support
};
supportContact.EmailAddresses.Add("support#example.com");
spOptions.Contacts.Add(supportContact);
var attributeConsumingService = new AttributeConsumingService("Saml2")
{
IsDefault = true,
};
attributeConsumingService.RequestedAttributes.Add(
new RequestedAttribute("urn:someName")
{
FriendlyName = "Some Name",
IsRequired = true,
NameFormat = RequestedAttribute.AttributeNameFormatUri
});
attributeConsumingService.RequestedAttributes.Add(
new RequestedAttribute("Minimal"));
spOptions.AttributeConsumingServices.Add(attributeConsumingService);
spOptions.ServiceCertificates.Add(new X509Certificate2(
AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "/App_Data/Sustainsys.Saml2.Tests.pfx"));
return spOptions;
}
If I issue the request from a cshtml page like;
<script type="text/javascript">
window.location.href = "/Saml2/Signin?idp=https://stubidp.sustainsys.com/Metadata";
</script>
then it works. Why can't I issue the challenge from the OWIN filter?
The second param to Challenge() must be the authentication scheme as set in the Saml2 options. The default is now Saml2, so replace your KentorAuthServices scheme to Saml2.
I am trying to allocate a resource to a task in Dynamics 365 programmatically. The code is:
private static void AssignResourceToTask(XrmServiceContext xrm)
{
Guid TaskId = new Guid("e0bdf7c7-6a14-e711-8114-e0071b6ac161");
Guid ResourceId = new Guid("cd01ae07-b9cd-e611-80e7-c4346bac0910");
Guid ProjectId = new Guid("7982feea-a0f2-e611-8130-e0071b6a92f1");
var AssignResource = new msdyn_resourceassignment
{
msdyn_resourceassignmentId = Guid.NewGuid(),
msdyn_bookableresourceid = new Microsoft.Xrm.Client.CrmEntityReference("bookableresource", ResourceId),
msdyn_taskid = new Microsoft.Xrm.Client.CrmEntityReference("msdyn_projecttask", TaskId),
msdyn_projectid = new Microsoft.Xrm.Client.CrmEntityReference("msdyn_project", ProjectId),
};
AssignResource.msdyn_bookableresourceid.Name = "Amy Alberts";
xrm.AddObject(AssignResource);
xrm.SaveChanges();
}
But I am unable to assign a resource to a task using the above code.
Have you tried Microsoft.Crm.Sdk.AssignRequest? Your code would look like:
_serviceProxy.EnableProxyTypes();
_service = (IOrganizationService)_serviceProxy;
var assignRequest = new AssignRequest
{
Assignee = new EntityReference("contact", ResourceId),
Target = new EntityReference("task", TaskId)
};
_service.Execute(assignRequest);
Microsoft have an example here.