AWS API Gateway - Call GET Method with C# SDK - c#

I have an API Gateway that uses IAM authorization. I have a C# application that I'm hoping to call the API with. I started with a GetMethodRequest but I don't see anyway to set the PathPart parameter.
var userId = _remoteCredentials.UserId;
var key = _remoteCredentials.Key;
var client = new AmazonAPIGatewayClient(userId, key, Amazon.RegionEndpoint.USEast2);
GetMethodRequest getMethodRequest = new GetMethodRequest();
getMethodRequest.HttpMethod = HttpMethod.Get.ToString();
getMethodRequest.ResourceId = "4abcde";
getMethodRequest.RestApiId = "aasfasdfs";
var task = Task.Run(async () => await client.GetMethodAsync(getMethodRequest).ConfigureAwait(false));
I was expecting something like the Test-AGInvokeMethod in the Powershell SDK which allows me to set the query string and the path.
$response = Test-AGInvokeMethod
-RestApiId aasfasdfs
-ResourceId 4abcde
-HttpMethod GET
-PathWithQueryString '/etl/upload_url'
Any help is greatly appreciated.
EDIT Below is something of a solution that I ended up using the AWS4RequestSigner is a library that I found on Github
var signer = new AWS4RequestSigner(userId, key);
var destinationUrl = string.Format("https://ad9vxabc123.execute-api.us-east-2.amazonaws.com/dev/etl/summary/latest?tms_id={0}&model_id={1}", _tmsId, _modelId);
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(destinationUrl),
};
var signed = Task.Run(async () => await signer.Sign(request, "execute-api", "us-east-2").ConfigureAwait(false));
var signedResult = signed.Result;

The AmazonAPIGatewayClient is for managing your API Gateway e.g. adding new stages or deleting API keys.
You're looking to invoke a method on your API Gateway, like Test-AGInvokeMethod does.
To invoke your API gateway, you need to call the deployed API endpoint using a HTTP client.
.NET's in-built HttpClient is a good start.

Related

Trigger cancellation of Azure build pipeline from code from c#

I'm building an azure function that works as a approval checker for azure builds to be deployed, in the image below, you can see how it's being called for a pipeline that is trying to deploy.
What I need to do in C#, in some scenarios, is to CANCEL the pipeline that is calling my function to stop it from calling.
What I'm thinking to use is a PATCH method that Azure itself uses to cancel them, but I don't know how to call it with the proper headers and payload
This is what gets called when you hit the CANCEL button in a pipeline:
I'm building a request with a HTTPClient with this address, what else should I send as a param or header to make it work?
var _baseAddress = new Uri("https://dev.azure.com/myprojectname/");
var msg = new HttpRequestMessage(HttpMethod.Patch, new Uri(_baseAddress, $"{myProjectId}/_apis/build/builds/{buildRunId}"));
var serializedDoc = JsonConvert.SerializeObject(new { status = 4 });
msg.Content = new StringContent(serializedDoc, Encoding.UTF8);
msg.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
msg.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json-patch+json");
var result = await _httpClient.SendAsync(msg);
I could not find any other similar question or implementation, so any answer is welcome

LibGit2Sharp: How to push a local repo commit to Azure DevOps remote repo using a Personal Access Token inside a custom HTTP authentication header?

I am trying to push a commit I made on my local repository to a remote counterpart, hosted on a private Azure DevOps server, using LibGit2Sharp programmatically.
As per the Azure documentation, the HTTPS OAuth enabled Personal Access Token needs to sent with the request in a custom Authentication header as 'Basic' with the Base64 encoded token:
var personalaccesstoken = "PATFROMWEB";
using (HttpClient client = new HttpClient()) {
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(Encoding.ASCII.GetBytes($":{personalaccesstoken}")));
using (HttpResponseMessage response = client.GetAsync(
"https://dev.azure.com/{organization}/{project}/_apis/build/builds?api-version=5.0").Result) {
response.EnsureSuccessStatusCode();
}
}
The LibGit2Sharp.CloneOptions class has a FetchOptions field which in turn has a CustomHeaders array that can be used to inject the authentication header during the clone operation, like the following (as mentioned in this issue):
CloneOptions cloneOptions = new() {
CredentialsProvider = (url, usernameFromUrl, types) => new UsernamePasswordCredentials {
Username = $"{USERNAME}",
Password = $"{ACCESSTOKEN}"
},
FetchOptions = new FetchOptions {
CustomHeaders = new[] {
$"Authorization: Basic {encodedToken}"
}
}
};
Repository.Clone(AzureUrl, LocalDirectory, cloneOptions);
And the clone process succeeds (I tested it as well as checked the source code :) )
However, the LibGit2Sharp.PushOptions does not have any such mechanism to inject authentication headers. I am limited to the following code:
PushOptions pushOptions = new()
{
CredentialsProvider = (url, usernameFromUrl, types) => new UsernamePasswordCredentials
{
Username = $"{USERNAME}",
Password = $"{PASSWORD}"
}
};
This is making my push operation fail with the following message:
Too many redirects or authentication replays
I checked the source code for Repository.Network.Push() on Github.
public virtual void Push(Remote remote, IEnumerable<string> pushRefSpecs, PushOptions pushOptions)
{
Ensure.ArgumentNotNull(remote, "remote");
Ensure.ArgumentNotNull(pushRefSpecs, "pushRefSpecs");
// Return early if there is nothing to push.
if (!pushRefSpecs.Any())
{
return;
}
if (pushOptions == null)
{
pushOptions = new PushOptions();
}
// Load the remote.
using (RemoteHandle remoteHandle = Proxy.git_remote_lookup(repository.Handle, remote.Name, true))
{
var callbacks = new RemoteCallbacks(pushOptions);
GitRemoteCallbacks gitCallbacks = callbacks.GenerateCallbacks();
Proxy.git_remote_push(remoteHandle,
pushRefSpecs,
new GitPushOptions()
{
PackbuilderDegreeOfParallelism = pushOptions.PackbuilderDegreeOfParallelism,
RemoteCallbacks = gitCallbacks,
ProxyOptions = new GitProxyOptions { Version = 1 },
});
}
}
As we can see above, the Proxy.git_remote_push method call inside the Push() method is passing a new GitPushOptions object, which indeed seems to have a CustomHeaders field implemented. But it is not exposed to a consumer application and is being instantiated in the library code directly!
It is an absolute necessity for me to use the LibGit2Sharp API, and our end-to-end testing needs to be done on Azure DevOps repositories, so this issue is blocking me from progressing further.
My questions are:
Is it possible to use some other way to authenticate a push operation on Azure from LibGit2Sharp? Can we leverage the PushOptions.CredentialsProvider handler so that it is compatible with the auth-n method that Azure insists on?
Can we cache the credentials by calling Commands.Fetch by injecting the header in a FetchOptions object before carrying out the Push command? I tried it but it fails with the same error.
To address the issue, is there a modification required on the library to make it compatible with Azure Repos? If yes, then I can step up and contribute if someone could give me pointers on how the binding to the native code is made :)
I will provide an answer to my own question as we have fixed the problem.
The solution to this is really simple; I just needed to remove the CredentialsProvider delegate from the PushOptions object, that is:
var pushOptions = new PushOptions();
instead of,
PushOptions pushOptions = new()
{
CredentialsProvider = (url, usernameFromUrl, types) => new UsernamePasswordCredentials
{
Username = $"{USERNAME}",
Password = $"{PASSWORD}"
}
};
¯\(ツ)/¯
I don't know why it works, but it does. (Maybe some folks from Azure can clarify it to us.)
It turns out that this works on windows (push options with no credentials provider). Perhaps because somewhere a native call the OS resolves the credentials using some other means. But in Linux / container environment, the issue persists.
"There was a problem pushing the repo: remote authentication required but no callback set"
I think as you mentioned, minimally the CustomHeaders implementation must be exposed for this to work.
Image of error on console

Passing parameters to a Twilio via CallResource.CreateAsync to programmatically add a bot to a conference call

I'm trying to create a conference call with a Moderator and several participants one of which is a bot.
The bot is controlled by the Moderator via a back channel so it can Say things etc to the conference call.
I setup the conference call when the Moderator calls in from a web client and then dial in the other participants and the bot using CallResource.CreateAsync with a callback url so I know which conference to add them to.
The bot needs to get a parameter so it knows which moderator to listen to for instructions.
However, I can't seem to pass any parameters to the bot (which is currently being triggered via another TwiML app) from the C# API using CallResource.CreateAsync.
Adding a participant to the call (callbackUrl adds the connected call to the conference) - this works fine:
var to = new PhoneNumber(callData.PhoneNumber);
var from = new PhoneNumber(_twilioSettings.PhoneNumber);
var callbackUrl = GetConnectConferenceUrl(callData.CallToken);
var url = new Uri(callbackUrl);
var participantCallResource = await CallResource.CreateAsync(to, from, url: url);
Adding the bot to call (Phone number is setup in Twilio as a TwiML app with a webhook back to my server) - how do I pass parameters to the TwiML app?
var toBot = new PhoneNumber(botNumber);
var fromBot = new PhoneNumber(_twilioSettings.PhoneNumber);
var botCallbackUrl = GetConnectConferenceUrl(callData.CallToken, isBot: true);
var botUrl = new Uri(botCallbackUrl);
var botCallResource = await CallResource.CreateAsync(toBot, fromBot, url: botUrl)
How do I pass parameters to a TwiML bin or Webhook or phonenumber from C#?
Do I need to add the bot in to the call a different way?
Parameters are passed either on the To or on the Url per https://www.twilio.com/docs/voice/how-share-information-between-your-applications
I believe your use case would put it on the Url. Assuming you have a moderatorId:
var botCallbackUrl = GetConnectConferenceUrl(callData.CallToken, isBot: true, moderatorId);
var botUrl = new Uri(botCallbackUrl);
where GetConnectConferenceUrl adds the parameter you need.
I've used it on the To line for a Client endpoint, so that looked like
var toBot = $"{new PhoneNumber(botNumber)}?moderatorId={moderatorId}";

Awssdk works on .net dotnetcore (IAM and GatewayAPI) but throws Amazon.CognitoIdentity.Model.NotAuthorizedException on Xamarin Android APP

I have a problem with Awssdk lib from Amazon that I can't understand.
I made an easy Class to authorized and obtain resources from Amazon.
It uses the configuration from user sessionConfig: clientId, identitypoolId,userpoolId, username, password, secret.
And also the request config (signRequest) host, absolutpath, method, region.
var client = AmazonClient(sessionConfig, requestconfig);
With this I can easyly
client.GetClientTokens();
That makes a call to CognitoAuth userpools:
var cred = new CognitoAWSCredentials(_sessionConfig.IdentityPoolId,` RegionEndpoint.EUCentral1);
var provider = new AmazonCognitoIdentityProviderClient(cred, RegionEndpoint.EUCentral1);
CognitoUserPool userPool = new CognitoUserPool(_sessionConfig.UserPoolId, _sessionConfig.ClientId, provider);
CognitoUser user = new CognitoUser(_sessionConfig.UserPoolId, _sessionConfig.ClientId, userPool, provider, _sessionConfig.Secret, _sessionConfig.UserName);
var authRequest = new InitiateSrpAuthRequest()
{
Password = _sessionConfig.Password
};
AuthFlowResponse authResponse = await user.StartWithSrpAuthAsync(authRequest).ConfigureAwait(false);
Then I just call
client.GetApiResource(absolutpath);
And I can get with this auth info the resource from the api.
_requestConfig.AbsolutePath = absolutePath;
//Signmethod from Amazon
GetSignedRequest();
var responses = _webRequest.GetResponse();
var result = responses.GetResponseStream();
var data = string.Empty;
using (var sr = new StreamReader(result))
{
data = sr.ReadToEnd();
}
return data;
This code works like a charm on my dotnetcore console app, I become tokens access data and user or other api resources.
When I want to use it on a Xamarin.Android solution.
I become, when trying to get the credentials:
user.StartWithSrpAuthAsync(authRequest).ConfigureAwait(false);
Amazon.CognitoIdentity.Model.NotAuthorizedException: Access to
Identity 'eu-central-1:xxxxxxxxxxxxxxxxxxxxx' is forbidden.
System.Net.HttpStatusCode.BadRequest
errorCode "NotAuthorizedException"
The only thing I could see it is different is the UserAgent from provider config:
console program:
aws-sdk-dotnet-coreclr/3.3.11.22 aws-sdk-dotnet-core/3.3.29.12 .NET_Core/4.6.26606.02 OS/Microsoft_Windows_10.0.14393
Xamarin.Android app:
aws-sdk-dotnet-pcl/3.3.4.3 aws-sdk-dotnet-core/3.3.29.13 Mono/5.10.1(tarball) OS/ANDROID_7.0 PCL/Xamarin.Android
Console works xamarin throw this exception. Any ideas?

Calling MailChimp API v3.0 with .Net

I'm trying to access our MailChimp account via the new 3.0 REST API. I've done the following:
using(var http = new HttpClient())
{
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("username:mailchimpapikey-us1"));
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
string content = await http.GetStringAsync(#"https://us1.api.mailchimp.com/3.0/lists");
Console.WriteLine(content);
}
However, when I run this code, I get a 401 error with the following json details:
{"type":"http://kb.mailchimp.com/api/error-docs/401-api-key-invalid","title":"API Key Invalid","status":401,"detail":"Your API key may be invalid, or you've attempted to access the wrong datacenter.","instance":"a9fe4028-519e-41d6-9f77-d2caee4d4683"}
The datacenter I'm using in my URI (us1 in this example) matches the dc on my API key. My API key works if I use the MailChimp SDK so I know my key isn't invalid. Also, using Fiddler, I can see that the MailChimp SDK is calling the same dc as I'm doing in my URI.
Any Ideas as to why I am having trouble Authenticating?
EDIT
As noted in the question, I'm asking specifically about accessing the new 3.0 REST API. I'm trying to do this directly as opposed to using a third party wrapper.
The new API is composed of http calls so it should be pretty straight forward. I'm simply having trouble with the authentication piece.
So I was able to finally chat with a super tech support person at MailChimp.
The MailChimp docs state the following
The easiest way to authenticate is using HTTP Basic Auth. Enter any string
as the username and supply your API Key as the password.
Your HTTP library should have built-in support for basic authorization.
Their documentation is a bit misleading. Typically the Auth header for Basic Auth would look like what I was sending:
Authorization: Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
where the row of x would represent the base64 encoded username:password.
However, talking with the support tech, the actual implementation they use is:
Authorization: username keyid
No base64 encoding, no Basic keyword. Username doesn't even have to be your username.
So, here is the working code:
using(var http = new HttpClient())
{
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", mailchimpapikey-us1);
string content = await http.GetStringAsync(#"https://us1.api.mailchimp.com/3.0/lists");
Console.WriteLine(content);
}
EDIT
Note the comments. TooMuchPete was correct in that the normal HTTP Basic Auth headers do work. Apparently I was hitting some old code or something on the MailChimp side.
I'm leaving the post as a reference for anyone who is trying to call the new 3.0 API.
I wrote an article on a simple way up adding subscribers to a list using:
Dim mailchimp As New ZmailChimp
Dim ListId$ = "9b2e63f0b9" 'List Sage' List
Dim email$ = "samsmith20#anymail.com" '"sam19#postcodelite.com"
Dim fieldListOnAdd = "FNAME,Sam,LNAME,Smith,MTYPE,User,MID,631637"
Dim fieldListOnUpdate = "FNAME,Sam,LNAME,Smith,MID,631637" 'Don't change MTYPE
'Put on 'Sage One' and 'Sage 50' group
Dim groupList = "407da9f47d,05086211ba"
With mailchimp
.API$ = "46cMailChimpAPIKeyd1de-us14" 'MailChimp API key
.dataCenter$ = "us14" 'Last 4 letters of API key
.password$ = "Password!"
MsgBox(.addSubscriber(ListId$, email, fieldListOnAdd, fieldListOnUpdate, groupList))
End With
mailchimp = Nothing
see:http://www.codeproject.com/Tips/1140339/Mail-Chimp-Add-Update-e-mail-to-List-and-Subscribe
this may save someone some time
Mailchimp Ecommerce
var mcorder = new Standup.Ecomm.MailChimpManager(ConfigurationManager.AppSettings["MailChimpApiKey"]);
var orders = new MailOrder();
orders.CampaignId = ConfigurationManager.AppSettings["MailChimpCampaignId"];
orders.EmailId = ConfigurationManager.AppSettings["MailChimpEmailId"];
orders.Id = orderNumber;
orders.StoreId = "abcde";
orders.StoreName = "E-Commerce Store";
orders.Total = Convert.ToDouble(orderTotal);
orders.Tax = Convert.ToDouble(tax);
orders.Items = new List<MailOrderItem>();
foreach (var orderItem in orderItemList)
{
var item = new MailOrderItem();
item.ProductId = orderItem.OrderNumber;
item.ProductName = orderItem.Title;
item.SKU = orderItem.Sku;
item.CategoryId = 0;
item.CategoryName = " ";
item.Quantity = orderItem.Quantity;
item.Cost = Convert.ToDouble(orderItem.ProductCost);
orders.Items.Add(item);
}
mcorder.AddOrder(orders);

Categories