We are a shop that uses C# , Team Foundation Server and Rally as our main item tracking.
We would like to use Rally Item ChangeSets to follow TFS Changesets. Using Rally's C# RestApi seems a little different than the one made in Java.
Is there a way to do the same thing as described in this article using the Rally C# RestApi?
Rally update Changeset data from Java using Java Toolkit for Rally REST API
You can definitely do the same in .NET. A perk is that it's a lot less verbose than the Java equivalent. Here's an example:
// System Libraries
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Web;
// Rally REST API Libraries
using Rally.RestApi;
using Rally.RestApi.Response;
namespace RestExample_AddChangesetToUserStory
{
class Program
{
static void Main(string[] args)
{
// Set user parameters
String userName = "user#company.com";
String userPassword = "topsecret";
// Set Rally parameters
String rallyURL = "https://rally1.rallydev.com";
String rallyWSAPIVersion = "1.40";
//Initialize the REST API
RallyRestApi restApi;
restApi = new RallyRestApi(userName,
userPassword,
rallyURL,
rallyWSAPIVersion);
// Changeset Owner Username
String changesetOwner = "scm_integration#company.com";
// SCM Repository Name
String scmRepositoryName = "MySCMRepo";
// FormattedID of Artifact to associate to
String storyFormattedID = "US14";
// Create Request for User
Request userRequest = new Request("user");
userRequest.Fetch = new List<string>()
{
"UserName",
"Subscription",
"DisplayName"
};
// Add a Query to the Request
userRequest.Query = new Query("UserName", Query.Operator.Equals, changesetOwner);
// Query Rally
QueryResult queryUserResults = restApi.Query(userRequest);
// Grab resulting User object and Ref
DynamicJsonObject myUser = new DynamicJsonObject();
myUser = queryUserResults.Results.First();
String myUserRef = myUser["_ref"];
//Set our Workspace and Project scopings
String workspaceRef = "/workspace/12345678910";
String projectRef = "/project/12345678911";
bool projectScopingUp = false;
bool projectScopingDown = true;
// Get handle to SCM Repository
Request scmRequest = new Request("SCMRepository");
scmRequest.Fetch = new List<string>()
{
"ObjectID",
"Name",
"SCMType"
};
// Add query
scmRequest.Query = new Query("Name", Query.Operator.Equals, scmRepositoryName);
// Query Rally
QueryResult querySCMResults = restApi.Query(scmRequest);
DynamicJsonObject myRepository = new DynamicJsonObject();
myRepository = querySCMResults.Results.First();
// Find User Story that we want to add Changeset to
// Tee up Story Request
Request storyRequest = new Request("hierarchicalrequirement");
storyRequest.Workspace = workspaceRef;
storyRequest.Project = projectRef;
storyRequest.ProjectScopeDown = projectScopingDown;
storyRequest.ProjectScopeUp = projectScopingUp;
// Fields to Fetch
storyRequest.Fetch = new List<string>()
{
"Name",
"FormattedID",
"Changesets"
};
// Add a query
storyRequest.Query = new Query("FormattedID", Query.Operator.Equals, storyFormattedID);
// Query Rally for the Story
QueryResult queryResult = restApi.Query(storyRequest);
// Pull reference off of Story fetch
var storyObject = queryResult.Results.First();
String storyReference = storyObject["_ref"];
// Pull existing Changesets off of Story
var existingChangesets = storyObject["Changesets"];
Console.WriteLine("Story: " + storyFormattedID);
Console.WriteLine("Number of Existing Changesets: " + existingChangesets.Count);
// DynamicJSONObject for New Changeset
DynamicJsonObject newChangeset = new DynamicJsonObject();
// Commit Time Stamp
String commitTimeStamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
// Populate Changeset Attributes
newChangeset["SCMRepository"] = myRepository;
newChangeset["Author"] = myUserRef;
newChangeset["Revision"] = "2451";
newChangeset["Uri"] = "https://svnrepo.company.com:8001";
newChangeset["CommitTimestamp"] = commitTimeStamp;
// Artifacts list
var changeSetArtifacts = new ArrayList();
changeSetArtifacts.Add(storyObject);
// Update attribute on Changeset
newChangeset["Artifacts"] = changeSetArtifacts;
try
{
// Create the Changeset
Console.WriteLine("Creating Rally Changeset...");
CreateResult myChangesetCreateResult = restApi.Create("ChangeSet", newChangeset);
String myChangesetRef = myChangesetCreateResult.Reference;
Console.WriteLine("Successfully Created Rally Changeset: " + myChangesetRef);
List<string> createWarnings = myChangesetCreateResult.Warnings;
for (int i = 0; i < createWarnings.Count; i++)
{
Console.WriteLine(createWarnings[i]);
}
List<string> createErrors = myChangesetCreateResult.Errors;
for (int i = 0; i < createErrors.Count; i++)
{
Console.WriteLine(createErrors[i]);
}
}
catch (Exception e)
{
Console.WriteLine("Exception occurred creating Rally Changeset: " + e.StackTrace);
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
}
}
Related
I have a Service Hook integrated with a Service Bus that triggers when stage is waiting for approval, in the message i get the group assigned for approval and with that info i need to get the mail address of all members of that group.
For now i can only get the details of the group but nothing about the members.
I am also trying to use GraphHttpClient of Microsoft.VisualStudio.Services library.
If the team you mean is the concept in DevOps service, then you can use the below code to get the team member in C#.
using Newtonsoft.Json.Linq;
using RestSharp;
using System;
namespace GetTeamMembers
{
class Program
{
static void Main(string[] args)
{
string OrgName = "xxx";
string wanted_projectname = "xxx";
string wanted_teamname = "xxx";
string PAT = "xxx";
var client1 = new RestClient("https://dev.azure.com/"+OrgName+"/_apis/teams?api-version=6.0-preview.3");
var request1 = new RestRequest("https://dev.azure.com/"+OrgName+"/_apis/teams?api-version=6.0-preview.3", Method.Get);
request1.AddHeader("Authorization", "Basic "+PAT);
var response1 = client1.Execute(request1);
JObject json_response1 = JObject.Parse(response1.Content);
int count1 = (int)JObject.Parse(response1.Content)["count"];
for (int i=0;i<count1;i++) {
var team_name = (string)json_response1["value"][i]["name"];
var project_name = (string)json_response1["value"][i]["projectName"];
var team_id = (string)json_response1["value"][i]["id"];
var project_id = (string)json_response1["value"][i]["projectId"];
if (project_name==wanted_projectname&&team_name==wanted_teamname) {
var client2 = new RestClient("https://dev.azure.com/"+OrgName+"/_apis/projects/"+project_id+"/teams/"+team_id+"/members?api-version=6.0");
var request2 = new RestRequest("https://dev.azure.com/"+OrgName+"/_apis/projects/"+project_id+"/teams/"+team_id+"/members?api-version=6.0", Method.Get);
request2.AddHeader("Authorization", "Basic "+PAT);
var response2 = client2.Execute(request2);
Console.WriteLine(response2.Content);
}
}
}
}
}
I'm attempting to initiate a call with the Microsoft Graph SDK Create call API using the code sample below. The attempt fails with a Not Found exception.
I have registered the bot application, added the API call permissions and I am able to receive incoming calls from Teams.
It's not clear from the Microsoft documentation whether Teams users can be called directly or whether they have to be allocated a VoIP number. Has anyone been able to use the Graph SDK to call a Teams User? Is there some special configuration a User needs to have in order to be able to receive a call?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Graph.Communications.Common.Telemetry;
using Microsoft.Extensions.Logging;
using Microsoft.Graph;
using Microsoft.Graph.Communications.Calls;
using Microsoft.Graph.Communications.Calls.Media;
using Microsoft.Graph.Communications.Client;
using Microsoft.Skype.Bots.Media;
namespace sipbotcaller
{
class Program
{
private static string APP_NAME = "";
private static string APP_ID = "";
private static string APP_SECRET = "";
private static string TENANT_ID = "";
private static string CALLBACK_URI = "";
private static string CERTIFICATE_THUMBPRINT = "";
private static int MEDIA_PORT = 10000;
private static string PUBLIC_IP = "";
private static string HOSTNAME = "";
static async Task Main(string[] args)
{
Console.WriteLine("Teams Call Console:");
GraphLogger graphLogger = new GraphLogger(APP_NAME);
graphLogger.DiagnosticLevel = System.Diagnostics.TraceLevel.Verbose;
ILogger logger = new ConsoleLogger(graphLogger);
AuthenticationProvider authProvider = new AuthenticationProvider(
APP_NAME,
APP_ID,
APP_SECRET,
TENANT_ID,
graphLogger);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var users = await graphClient.Users.Request().GetAsync();
foreach (var user in users)
{
Console.WriteLine($"user Id: {user.Id}.");
Console.WriteLine($"user Display Name: {user.DisplayName}.");
}
var mediaPlatformSettings = new MediaPlatformSettings()
{
MediaPlatformInstanceSettings = new MediaPlatformInstanceSettings()
{
CertificateThumbprint = CERTIFICATE_THUMBPRINT,
InstanceInternalPort = MEDIA_PORT,
InstancePublicIPAddress = IPAddress.Parse(PUBLIC_IP),
InstancePublicPort = MEDIA_PORT,
ServiceFqdn = HOSTNAME,
},
ApplicationId = APP_ID,
};
var builder = new Microsoft.Graph.Communications.Client.CommunicationsClientBuilder(
APP_NAME,
APP_ID,
graphLogger);
builder
.SetAuthenticationProvider(authProvider)
.SetNotificationUrl(new Uri(CALLBACK_URI))
.SetMediaPlatformSettings(mediaPlatformSettings)
.SetServiceBaseUrl(new Uri(CALLBACK_URI));
var client = builder.Build();
AudioSocketSettings audioSockSettings = new AudioSocketSettings {
CallId = Guid.NewGuid().ToString(),
SupportedAudioFormat = AudioFormat.Pcm16K,
StreamDirections = StreamDirection.Sendrecv
};
AudioSocket audioSock = new AudioSocket(audioSockSettings);
var mediaConfig = MediaPlatform.CreateMediaConfiguration(audioSock);
Console.WriteLine($"media config: {mediaConfig}");
Console.WriteLine($"Attempting to call {users.First().DisplayName}.");
var call = new Call
{
CallbackUri = CALLBACK_URI,
TenantId = TENANT_ID,
Targets = new List<InvitationParticipantInfo>()
{
new InvitationParticipantInfo
{
Identity = new IdentitySet
{
User = new Identity
{
DisplayName = users.First().DisplayName,
Id = users.First().Id
},
}
}
},
RequestedModalities = new List<Modality>()
{
Modality.Audio
},
MediaConfig = new AppHostedMediaConfig()
{
Blob = mediaConfig.ToString(Newtonsoft.Json.Formatting.None)
},
};
var callResult = await client.Calls().AddAsync(call);
Console.WriteLine($"Call result {callResult.Id}.");
Console.WriteLine("Finished.");
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
}
}
Result:
<snip>
StatefulCall: Verbose
StatefulCall: Info
StatefulCall: Verbose
StatefulCall: Info
StatefulCall: Info
StatefulCall: Error {
"error": {
"code": "itemNotFound",
"message": "Unexpected exception returned from the service.\r\nStatus Code: NotFound"
}
}
StatefulCall: Info
I am trying to get the list of users in ADO using .NET clients. I am referring to this git repository:
https://github.com/microsoft/azure-devops-dotnet-samples/blob/master/ClientLibrary/Quickstarts/dotnet/GraphQuickStarts/Samples/EnumerateUsers.cs
I tried same thing but still it shows error that GetUsersAsync needs assembly reference. I have tried all the references. I am getting GetUserAsync but that is for one user. I need to fetch all the users.
Instead of using GetUsersAsync, please use ListUsersAsync:
PagedGraphUsers users = graphClient.ListUsersAsync().Result;
In the following two code samples i'm returning the users' emails. You can use Azure DevOps' userentitlements to return the information you need like the license details.
SDK
//string organization = ...
//string userName = ...
//string pat = ...
List<string> users = new List<string>();
var uri = new Uri($"https://vsaex.dev.azure.com/{organization}");
var credentials = new VssBasicCredential(userName, pat);
using (var connection = new VssConnection(uri, credentials))
using (var client = connection.GetClient<MemberEntitlementManagementHttpClient>())
{
string continuationToken = null;
do
{
var data = await client.SearchUserEntitlementsAsync(continuationToken);
continuationToken = data.ContinuationToken;
foreach (var member in data.Members)
{
string email = member.User.MailAddress.ToLower();
users.Add(email);
}
}
while (continuationToken != null);
}
Rest API
//string organization = ...
// HttpClient client = ...
List<string> users = new List<string>();
string baseUrl = $"https://vsaex.dev.azure.com/{organization}/_apis/userentitlements?api-version=6-preview.3";
string url = baseUrl;
string continuationToken = string.Empty;
do
{
var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
dynamic data = await response.Content.ReadAsAsync<object>();
foreach (var member in data.members)
{
users.Add(member.user.mailAddress.ToString());
}
continuationToken = HttpUtility.UrlEncode(data.continuationToken.ToString());
url = baseUrl + "&continuationToken=" + continuationToken;
}
}
while (!string.IsNullOrWhiteSpace(continuationToken));
I was having as tough time getting Microsoft.Azure.Cosmos.Table to automatically initialise the SecondaryUri when parsing a connection string that used a SAS token.
So I ended up explicitly specifying the TableSecondaryEndpoint in the connection string, that works but I'm unable to query the secondary because the SDK throws an Exception before even attempting the request.
In my testing, I have identified that this is a regression not present in Microsoft.WindowsAzure.Storage.Table 8.7.0 (The basis for Microsoft.Azure.Cosmos.Table 1.0.6)
Expert opinions very welcome that this point. Thank you.
Project code for this Exception here (also copied below): https://github.com/golfalot/SOshowAzureTableBug
Side issue detailing the SecondaryUri initialisation problem raised here: https://github.com/Azure/azure-cosmos-table-dotnet/issues/36
using System;
using System.Collections.Generic;
using LEGACY_STORAGE = Microsoft.WindowsAzure.Storage;
using LEGACY_RETRY = Microsoft.WindowsAzure.Storage.RetryPolicies;
using LEGACY_TABLE = Microsoft.WindowsAzure.Storage.Table; //8.7.0 because this is the base for 1.0.6
using NEWEST_TABLE = Microsoft.Azure.Cosmos.Table; // version 1.0.6
using Microsoft.Azure.Cosmos.Table; // had to add this to get access CreateCloudTableClient extension method
using System.Diagnostics;
namespace SOshowAzureTableBug
{
class Program
{
// the SAS token is immaterial in reproducing the problem
const string connectionTableSAS = "TableSecondaryEndpoint=http://127.0.0.1:10002/devstoreaccount1-secondary;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;SharedAccessSignature=immaterial";
static void Main(string[] args)
{
/* Legacy Table SDK */
var storageAccountLegacy = LEGACY_STORAGE.CloudStorageAccount.Parse(connectionTableSAS);
var tableClientLegacy = storageAccountLegacy.CreateCloudTableClient();
Debug.Assert(tableClientLegacy.StorageUri.SecondaryUri != null); // demonstrate SecondaryUri initialised
var tableRequestOptionsLegacy = new LEGACY_TABLE.TableRequestOptions () { LocationMode = LEGACY_RETRY.LocationMode.SecondaryOnly };
tableClientLegacy.DefaultRequestOptions = tableRequestOptionsLegacy;
var tableLegacy = tableClientLegacy.GetTableReference("foo"); // don't need table to exist to show the issue
var retrieveOperation = LEGACY_TABLE.TableOperation.Retrieve(string.Empty, string.Empty, new List<string>() { "bar" });
var tableResult = tableLegacy.Execute(retrieveOperation);
Console.WriteLine("Legacy PASS");
/* Newset Table SDK */
var storageAccountNewest = NEWEST_TABLE.CloudStorageAccount.Parse(connectionTableSAS);
var tableClientNewest = storageAccountNewest.CreateCloudTableClient(new TableClientConfiguration());
Debug.Assert(tableClientNewest.StorageUri.SecondaryUri != null); // demonstrate SecondaryUri initialised
var tableRequestOptionsNewest = new NEWEST_TABLE.TableRequestOptions() { LocationMode = NEWEST_TABLE.LocationMode.SecondaryOnly };
tableClientNewest.DefaultRequestOptions = tableRequestOptionsNewest;
var tableNewset = tableClientNewest.GetTableReference("foo"); // don't need table to exist to show the issue
var retrieveOperationNewset = NEWEST_TABLE.TableOperation.Retrieve(string.Empty, string.Empty, new List<string>() { "bar" });
/* throws Microsoft.Azure.Cosmos.Table.StorageException
* Exception thrown while initializing request: This operation can only be executed against the primary storage location
*/
var tableResultNewset = tableNewset.Execute(retrieveOperationNewset);
Console.WriteLine("Press any key to exit");
Console.Read();
}
}
}
I believe you've encountered a bug with the SDK.
When I try the following code, I get the same error as you:
var account = CloudStorageAccount.Parse(connectionString);
var requestOptions = new TableRequestOptions()
{
LocationMode = LocationMode.SecondaryOnly
};
var client = account.CreateCloudTableClient();
client.DefaultRequestOptions = requestOptions;
var table = client.GetTableReference("myTable");
var op = TableOperation.Retrieve("", "");
var result1 = table.Execute(op);
I decompiled the library code and found the culprit source code:
if (commandLocationMode == CommandLocationMode.PrimaryOnly)
{
if (restCMD.LocationMode == LocationMode.SecondaryOnly)
{
throw new InvalidOperationException("This operation can only be executed against the primary storage location.");//This is the error that gets thrown.
}
Logger.LogInformational(executionState.OperationContext, "This operation can only be executed against the primary storage location.", Array.Empty<object>());
executionState.CurrentLocation = StorageLocation.Primary;
restCMD.LocationMode = LocationMode.PrimaryOnly;
}
However, if I don't set DefaultRequestOptions at client level and specify it below in Execute method, I don't get the error but then it's because the primary endpoint is hit instead of secondary (I checked that in Fiddler).
var account = CloudStorageAccount.Parse(connectionString);
var requestOptions = new TableRequestOptions()
{
LocationMode = LocationMode.SecondaryOnly
};
var client = account.CreateCloudTableClient();
var table = client.GetTableReference("myTable");
var op = TableOperation.Retrieve("", "");
var result1 = table.Execute(op, requestOptions);
Workaround
If your objective is to query entities from secondary location, then you can use ExecuteQuery method on CloudTable like shown below. This works (Again, I checked in Fiddler).
var account = CloudStorageAccount.Parse(connectionString);
var requestOptions = new TableRequestOptions()
{
LocationMode = LocationMode.SecondaryOnly
};
var client = account.CreateCloudTableClient();
client.DefaultRequestOptions = requestOptions;
var table = client.GetTableReference("myTable");
TableQuery query = new TableQuery();
var result = table.ExecuteQuery(query).ToList();
I have the following controller:
[Authorize]
public class SetupController : ApiController
{
[HttpPost]
public Task async SetupPartnerPackAsync(SetupInformation info)
{
if (info.SslCertificateGenerate)
{
SetupManager.CreateX509Certificate(info);
}
else
{
SetupManager.LoadX509Certificate(info);
}
info.SslCertificateThumbprint = SetupManager.GetX509CertificateThumbprint(info);
info.AzureAppKeyCredential = SetupManager.GetX509CertificateInformation(info);
await SetupManager.RegisterAzureADApplication(info);
}
}
But I have the following 2 error which seems simple:
Severity Code Description Project File Line Suppression State
Error CS1520 Method must have a return
type InnovationInABoxWebApi H:\InnovationInABoxWebApi\InnovationInABoxWebApi\Controllers\SetupController.cs 24 Active
Severity Code Description Project File Line Suppression State
Error CS4033 The 'await' operator can only be used within an async
method. Consider marking this method with the 'async' modifier and
changing its return type to
'Task'. InnovationInABoxWebApi H:\InnovationInABoxWebApi\InnovationInABoxWebApi\Controllers\SetupController.cs 39 Active
However I am not sure how to fix this, as the operation can take some time to complete, it really needs to be asybnc
and the setupmanager
using CERTENROLLLib;
using Microsoft.Identity.Client;
using Microsoft.Online.SharePoint.TenantAdministration;
using Microsoft.SharePoint.Client;
using Newtonsoft.Json;
using OfficeDevPnP.Core;
using OfficeDevPnP.Core.Entities;
using OfficeDevPnP.Core.Framework.Provisioning.Model;
using OfficeDevPnP.Core.Framework.Provisioning.ObjectHandlers;
using OfficeDevPnP.Core.Framework.Provisioning.Providers.Xml;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Resources;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Xml.Linq;
namespace InnovationInABoxWebApi.Components
{
public static class SetupManager
{
public static String GetX509CertificateThumbprint(SetupInformation info)
{
var certificate = info.AuthenticationCertificate;
return (certificate.Thumbprint.ToUpper());
}
public static String GetX509CertificateInformation(SetupInformation info)
{
// var basePath = String.Format(#"{0}..\..\..\..\Scripts\", AppDomain.CurrentDomain.BaseDirectory);
var certificate = info.AuthenticationCertificate;
//var certificate = new X509Certificate2();
//if (info.SslCertificateGenerate)
//{
// certificate.Import($#"{basePath}{info.SslCertificateCommonName}.cer");
//}
//else
//{
// certificate = new X509Certificate2(info.SslCertificateFile, info.SslCertificatePassword);
//}
var rawCert = certificate.GetRawCertData();
var base64Cert = System.Convert.ToBase64String(rawCert);
var rawCertHash = certificate.GetCertHash();
var base64CertHash = System.Convert.ToBase64String(rawCertHash);
var KeyId = System.Guid.NewGuid().ToString();
var keyCredential =
"{" +
"\"customKeyIdentifier\": \"" + base64CertHash + "\"," +
"\"keyId\": \"" + KeyId + "\"," +
"\"type\": \"AsymmetricX509Cert\"," +
"\"usage\": \"Verify\"," +
"\"key\": \"" + base64Cert + "\"" +
"}";
return (keyCredential);
}
public static void CreateX509Certificate(SetupInformation info)
{
var certificate = CreateSelfSignedCertificate(info.SslCertificateCommonName.ToLower(),
info.SslCertificateStartDate, info.SslCertificateEndDate, info.SslCertificatePassword);
SaveCertificateFiles(info, certificate);
}
public static void LoadX509Certificate(SetupInformation info)
{
var certificate = new X509Certificate2(info.SslCertificateFile, info.SslCertificatePassword);
info.AuthenticationCertificate = certificate;
info.SslCertificateCommonName = certificate.SubjectName.Name;
}
public static void SaveCertificateFiles(SetupInformation info, X509Certificate2 certificate)
{
info.AuthenticationCertificate = certificate;
//var basePath = String.Format(#"{0}..\..\..\..\Scripts\", AppDomain.CurrentDomain.BaseDirectory);
//info.SslCertificateFile = $#"{basePath}{info.SslCertificateCommonName}.pfx";
//var pfx = certificate.Export(X509ContentType.Pfx, info.SslCertificatePassword);
//System.IO.File.WriteAllBytes(info.SslCertificateFile, pfx);
//var cer = certificate.Export(X509ContentType.Cert);
//System.IO.File.WriteAllBytes($#"{basePath}{info.SslCertificateCommonName}.cer", cer);
}
public static X509Certificate2 CreateSelfSignedCertificate(string subjectName, DateTime startDate, DateTime endDate, String password)
{
// Create DistinguishedName for subject and issuer
var name = new CX500DistinguishedName();
name.Encode("CN=" + subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE);
// Create a new Private Key for the certificate
CX509PrivateKey privateKey = new CX509PrivateKey();
privateKey.ProviderName = "Microsoft RSA SChannel Cryptographic Provider";
privateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE;
privateKey.Length = 2048;
privateKey.SecurityDescriptor = "D:PAI(A;;0xd01f01ff;;;SY)(A;;0xd01f01ff;;;BA)(A;;0x80120089;;;NS)";
privateKey.MachineContext = true;
privateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG;
privateKey.Create();
// Define the hashing algorithm
var serverauthoid = new CObjectId();
serverauthoid.InitializeFromValue("1.3.6.1.5.5.7.3.1"); // Server Authentication
var ekuoids = new CObjectIds();
ekuoids.Add(serverauthoid);
var ekuext = new CX509ExtensionEnhancedKeyUsage();
ekuext.InitializeEncode(ekuoids);
// Create the self signing request
var cert = new CX509CertificateRequestCertificate();
cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, privateKey, String.Empty);
cert.Subject = name;
cert.Issuer = cert.Subject;
cert.NotBefore = startDate;
cert.NotAfter = endDate;
cert.X509Extensions.Add((CX509Extension)ekuext);
cert.Encode();
// Enroll the certificate
var enroll = new CX509Enrollment();
enroll.InitializeFromRequest(cert);
string certData = enroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64HEADER);
enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate,
certData, EncodingType.XCN_CRYPT_STRING_BASE64HEADER, String.Empty);
var base64encoded = enroll.CreatePFX(password, PFXExportOptions.PFXExportChainWithRoot);
// Instantiate the target class with the PKCS#12 data
return new X509Certificate2(
System.Convert.FromBase64String(base64encoded), password,
System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.Exportable);
}
public async static Task RegisterAzureADApplication(SetupInformation info)
{
// Fix the App URL
if (!info.AzureWebAppUrl.EndsWith("/"))
{
info.AzureWebAppUrl = info.AzureWebAppUrl + "/";
}
// Load the App Manifest template
//Stream stream = typeof(SetupManager)
// .Assembly
// .GetManifestResourceStream("OfficeDevPnP.PartnerPack.Setup.Resources.azure-ad-app-manifest.json");
using (StreamReader sr = new StreamReader("Resources\azure-ad-app-manifest.json"))
{
// Get the JSON manifest
var jsonApplication = sr.ReadToEnd();
var application = JsonConvert.DeserializeObject<AzureAdApplication>(jsonApplication);
var keyCredential = JsonConvert.DeserializeObject<KeyCredential>(info.AzureAppKeyCredential);
application.displayName = info.ApplicationName;
application.homepage = info.AzureWebAppUrl;
application.identifierUris = new List<String>();
application.identifierUris.Add(info.ApplicationUniqueUri);
application.keyCredentials = new List<KeyCredential>();
application.keyCredentials.Add(keyCredential);
application.replyUrls = new List<String>();
application.replyUrls.Add(info.AzureWebAppUrl);
// Generate the Application Shared Secret
var startDate = DateTime.Now;
Byte[] bytes = new Byte[32];
using (var rand = System.Security.Cryptography.RandomNumberGenerator.Create())
{
rand.GetBytes(bytes);
}
info.AzureAppSharedSecret = System.Convert.ToBase64String(bytes);
application.passwordCredentials = new List<object>();
application.passwordCredentials.Add(new AzureAdApplicationPasswordCredential
{
CustomKeyIdentifier = null,
StartDate = startDate.ToString("o"),
EndDate = startDate.AddYears(2).ToString("o"),
KeyId = Guid.NewGuid().ToString(),
Value = info.AzureAppSharedSecret,
});
// Get an Access Token to create the application via Microsoft Graph
var office365AzureADAccessToken = await AzureManagementUtility.GetAccessTokenSilentAsync(
AzureManagementUtility.MicrosoftGraphResourceId,
ConfigurationManager.AppSettings["O365:ClientId"]);
var azureAdApplicationCreated = false;
// Create the Azure AD Application
try
{
await CreateAzureADApplication(info, application, office365AzureADAccessToken);
azureAdApplicationCreated = true;
}
catch (ApplicationException ex)
{
var graphError = JsonConvert.DeserializeObject<GraphError>(((HttpException)ex.InnerException).Message);
if (graphError != null && graphError.error.code == "Request_BadRequest" &&
graphError.error.message.Contains("identifierUris already exists"))
{
// We need to remove the existing application
// Thus, retrieve it
String jsonApplications = await HttpHelper.MakeGetRequestForStringAsync(
String.Format("{0}applications?$filter=identifierUris/any(c:c+eq+'{1}')",
AzureManagementUtility.MicrosoftGraphBetaBaseUri,
HttpUtility.UrlEncode(info.ApplicationUniqueUri)),
office365AzureADAccessToken);
var applications = JsonConvert.DeserializeObject<AzureAdApplications>(jsonApplications);
var applicationToUpdate = applications.Applications.FirstOrDefault();
if (applicationToUpdate != null)
{
// Remove it
await HttpHelper.MakeDeleteRequestAsync(
String.Format("{0}applications/{1}",
AzureManagementUtility.MicrosoftGraphBetaBaseUri,
applicationToUpdate.Id),
office365AzureADAccessToken);
// And add it again
await CreateAzureADApplication(info, application, office365AzureADAccessToken);
azureAdApplicationCreated = true;
}
}
}
if (azureAdApplicationCreated)
{
// TODO: We should upload the logo
// property mainLogo: stream of the application via PATCH
}
}
}
public static async Task CreateAzureADApplication(SetupInformation info, AzureAdApplication application, string office365AzureADAccessToken)
{
String jsonResponse = await HttpHelper.MakePostRequestForStringAsync(
String.Format("{0}applications",
AzureManagementUtility.MicrosoftGraphBetaBaseUri),
application,
"application/json", office365AzureADAccessToken);
var azureAdApplication = JsonConvert.DeserializeObject<AzureAdApplication>(jsonResponse);
info.AzureAppClientId = azureAdApplication.AppId.HasValue ? azureAdApplication.AppId.Value : Guid.Empty;
}
}
}
You are defining the method with async word after the return type Task, async must be before Task.
public async Task SetupPartnerPackAsync(SetupInformation info)
{
.
.
.