I am working on a requirement where I want to call ARM Template dynamically from code passing parameters, like Resource Name, Resource Location etc.,
I am able to create ARM template. For example, created ARM Template for storage account and I have parameter file with values to pass to the template. However these values I want to pass from c# code dynamically and provision the resource in Azure.
From Power shell I am able to achieve this but I want the same to be done from c#.
Any suggestions/technical links that I can explore.
I use the following function to deploy an ARM Template using c#
private async Task DeployTemplate(string resourceGroupName, string deploymentName, JObject templateFileContents, JObject parameters)
{
var deployment = new Deployment
{
Properties = new DeploymentProperties
{
Mode = DeploymentMode.Incremental,
Template = templateFileContents,
Parameters = parameters
}
};
var serviceCredentials = await ApplicationTokenProvider.LoginSilentAsync(_tenantId, _clientId, _clientSecret);
var resourceManagementClient = new ResourceManagementClient(serviceCredentials)
{
SubscriptionId = _subscriptionId
};
await resourceManagementClient.Deployments.CreateOrUpdateAsync(resourceGroupName, deploymentName, deployment);
}
The templateFileContents get filled by this function:
private static JObject GetJsonFileContents(string pathToJson)
{
var path = Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), pathToJson);
using (var file = File.OpenText(path))
{
using (var reader = new JsonTextReader(file))
{
return (JObject)JToken.ReadFrom(reader);
}
}
}
This is how I create a dynamic parameters object:
var parameters = JObject.FromObject(
new DbCloneArmParameters
{
databaseName = new ArmParameterValue { Value = databaseName },
serverName = new ArmParameterValue { Value = servername },
location = new ArmParameterValue { Value = "westeurope" } }
});
Helper classes:
public class DbCloneArmParameters
{
public ArmParameterValue databaseName { get; set; }
public ArmParameterValue serverName { get; set; }
public ArmParameterValue location { get; set; }
}
public class ArmParameterValue
{
public string Value { get; set; }
}
I think Microsoft.Azure.Management.Fluent solves your problem.
If you look at Microsoft documentation "Deploy an Azure Virtual Machine using C# and a Resource Manager template", it walks you through deploying a VM using a template file from C#. The documentation could be used as a starting point to deploy any ARM template to Azure.
Related
I am attempting to use the AmazonSimpleEmailService client via the AWS-SDK for .Net, to send a SendBulkTempatedEmailRequest. I have implemented a dedicated handler for actually building the request and making the SendBulkTemplatedEmailAsync call. It is not working as I expect. I think there is a bug with how the request object is serialized and passed to the API.
Here is some sample code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Amazon.SimpleEmail;
using Amazon.SimpleEmail.Model;
using Newtonsoft.Json;
namespace Sample.AWS.SES
{
public class SendEmailService
{
private readonly IAmazonSimpleEmailService _sesClient;
public SendEmailService(IAmazonSimpleEmailService sesClient)
{
_sesClient = sesClient;
}
public async Task<string> SendBulkEmailAsync(SesOrderCreatedBulkTemplate data)
{
var result = string.Empty;
var request = new SendBulkTemplatedEmailRequest
{
Template = data.Template,
ConfigurationSetName = data.ConfigurationSet,
DefaultTemplateData = JsonConvert.SerializeObject(data.DefaultTemplateData),
Source = data.Source,
Destinations = data.Destinations
.Select(d => new BulkEmailDestination
{
Destination = new Destination
{
ToAddresses = d.ToAddresses.ToList(),
},
ReplacementTemplateData = string.Empty
})
.ToList(),
ReplyToAddresses = data.ReplyToAddresses.ToList()
};
try
{
var resp = await _sesClient.SendBulkTemplatedEmailAsync(request);
}
catch (Exception ex)
{
var msgEx = new Exception("Error sending message to SES.", ex);
throw msgEx;
}
return result;
}
public class SesOrderCreatedBulkTemplate
{
public string Source { get; set; }
public string Template { get; set; }
public string ConfigurationSet { get; set; }
public IEnumerable<Destination> Destinations { get; set; }
public MyTemplateData DefaultTemplateData { get; set; }
public IEnumerable<string> ReplyToAddresses { get; set; }
public string ReturnPath { get; set; } = string.Empty;
}
public class DestinationObj
{
public IEnumerable<string> ToAddresses { get; set; }
public MyTemplateData ReplacementTemplateData { get; set; }
public DestinationObj() {}
}
public class MyTemplateData
{
public List<Person> Tenants { get; set; }
}
public class Person
{
public string PersonName { get; set; }
public List<object> PersonData { get; set; }
}
}
}
The properties for SourceArn, TemplateArn and ReturnPathArn are omitted on purpose. According the SES documentation, the SDK wraps the low-level functionality of the Amazon SES API with higher-level data types and function calls that take care of the details for you. When I view the API documentation for sending bulk email, the ARN properties are all list as not required. When I look at the some CLI examples, it is the same. When I look at the documentation for the SDK for .Net v3, it is ambiguous (not marked as required or optional).
Because the SDK supposed to wrap the low-level functionality of the API, I do not believe the ARN values are required (neither the API nor the CLI require them). However, when I attempt to actually use the request object created in the code snippet, I get an error that says InvalidTemplateData.
If I serialize the request object to JSON, then remove the 3 ARN fields from the string, I can use either the API or the CLI to successfully send the message.
In addition to not specifying a value for the ARN's, I have tried (for all 3 ARN values):
specificArn = string.empty;
specificArn = new {};
specificArn = "";
I have also tried explicitly newing-up the object separate from initializing the properties:
var request = new SendBulkTemplatedEmailRequest();, and then individually populating the properties.
If I don't initialize the ARN values, I get an error about NoneType vs StringType when the send method is called. The variations on string initialization that I tried result in InvalidTemplateData errors.
Note, I do know ARN values for Source and ReturnPath. I do not have an ARN value for the template we use. Supposedly, using the CLI, when you create a template you should receive a response back that includes the ARN for the template. I get no response from the CLI when I create a template, but it does get created every time I try. The describe-template CLI command is not valid when you specify SES and responds with an error if I don't specify the workspace (whatever you call the SES space) value.
Does anyone have a suggestion on how to solve this?
From the provided code it's hard to say what you pass into API.
This is how I send bulk emails:
SES configuration
create a template (taken from AWS SES docs) and save it to a file - my-template.json
{
"Template": {
"TemplateName": "my-template",
"SubjectPart": "Greetings, {{name}}!",
"HtmlPart": "<h1>Hello {{name}},</h1><p>Your favorite animal is {{favoriteanimal}}.</p>",
"TextPart": "Dear {{name}},\r\nYour favorite animal is {{favoriteanimal}}."
}
}
create a template via CLI aws ses create-template --cli-input-json file://my-template.json
SES .NET SDK API
async Task SendAsync(string[] receivers)
{
var destinations = receivers
.Select(receiver => new BulkEmailDestination
{
Destination = new Destination(new List<string> { receiver }),
ReplacementTemplateData = ToJson(receiver, "Doggy")
})
.ToList();
var bulkTemplate = new SendBulkTemplatedEmailRequest
{
Source = "your-email#gmail.com", // your email you bulk send from
Template = "my-template", // your template name
DefaultTemplateData = ToJson("<not set>", "<not set>"),
Destinations = destinations
};
await _client.SendBulkTemplatedEmailAsync(bulkTemplate);
}
// Create replacement data by serializing Dictionary
string ToJson(string name, string favoriteanimal)
=> JsonSerializer.Serialize(new Dictionary<string, string>
{
{ "name", name },
{ "favoriteanimal", favoriteanimal }
});
I'm been reading multiples articles about the usage of the AWS S3 in Visual Studio. But all of those are focused on Web implementation.
Has anyone had the need to implement AWS S3 in WinForms - C#?
I implemented a class in C# that interact basically with AWS S3 without having too much configuration.
** I'm using the MS Framework 4.5 **
First of all, remember to use the NuGet Package and install the AWS references (In VS >> Menu >> Tools >> NuGet Package Manager >> Manage NuGet Package Manager for Solution):
AWSSDK - Amazon Simple Storage Service
AWSSDK - Core Runtime
...implemented this class, for my need I only required the access key, secret key, region and the bucket (Exists more methods, but I only required copy, read and delete).
Note: It's not required additional configuration in the app.config file or install/create a profile in the USER folder...
public class clsAwsS3
{
string accessKey { get; set; }
string secretKey { get; set; }
string bucket { get; set; }
RegionEndpoint region { get; set; }
IAmazonS3 client;
public clsAwsS3(string strBucket, string strAccessKey, string strSecretKey, RegionEndpoint region)
{
this.bucket = strBucket;
this.accessKey = strAccessKey;
this.secretKey = strSecretKey;
this.region = region;
login();
}
private void login()
{
client = new AmazonS3Client(accessKey, secretKey, region);
}
public List<string> getItems(string strPrefix = "")
{
List<string> lstResult = new List<string>();
ListObjectsV2Request listRequest;
if (strPrefix == "")
listRequest = new ListObjectsV2Request
{
BucketName = bucket
};
else
listRequest = new ListObjectsV2Request
{
BucketName = bucket,
Prefix = strPrefix
};
ListObjectsV2Response listResponse;
do
{
listResponse = client.ListObjectsV2(listRequest);
foreach (S3Object awsObject in listResponse.S3Objects)
lstResult.Add(awsObject.Key);
listRequest.ContinuationToken = listResponse.NextContinuationToken;
} while (listResponse.IsTruncated);
return lstResult;
}
public string downloadItem(string strItemKey, string strDestination)
{
GetObjectRequest request = new GetObjectRequest
{
BucketName = bucket,
Key = strItemKey
};
using (GetObjectResponse response = client.GetObject(request))
{
response.WriteResponseStreamToFile(strDestination);
}
return strDestination;
}
public void copyItem(string strItemKeySource, string strItemKeyDestination)
{
CopyObjectRequest copyRequest = new CopyObjectRequest
{
SourceBucket = bucket,
SourceKey = strItemKeySource,
DestinationBucket = bucket,
DestinationKey = strItemKeyDestination
};
CopyObjectResponse copyResponse = client.CopyObject(copyRequest);
if (copyResponse.HttpStatusCode != System.Net.HttpStatusCode.OK)
throw new Exception("Item has an error");
}
public void deleteItem(string strItemKey)
{
DeleteObjectRequest deleteObject = new DeleteObjectRequest
{
BucketName = bucket,
Key = strItemKey
};
DeleteObjectResponse deleteResponse = client.DeleteObject(deleteObject);
}
}
Hope this helps to someone else.
I've developed a simple Azure Webapp with C#. And use the table of Azure storage account to save some kinds of records of the website.
It works well on my own computer on both read and write operations, with the real connection string. I can find the record with the "Azure Storage Explorer".
However, after I deploy the app. The cloud version keeps throwing Http 400 error when I try to write the table. But the read operation still works fine. (The local version is always OK.)
The problem is wierd. Please help me with it.
Thanks.
== UPDATE ==
The save & query code is something like this.
public class CodeSnippet: TableEntity
{
public string Title { get; set; }
public string Author { get; set; }
public string FileType { get; set; }
public string Code { get; set; }
public Guid Save()
{
var guid = Guid.NewGuid();
var datetime = DateTimeOffset.UtcNow;
this.RowKey = guid.ToString();
this.PartitionKey = datetime.ToString();
var json = JsonConvert.SerializeObject(this);
var client = AzureStorage.DefaultClient.Instance.GetTableClient();
var table = client.GetTableReference("privateshare");
var insertOp = TableOperation.Insert(this);
table.Execute(insertOp);
return guid;
}
public static CodeSnippet Query(Guid? guid)
{
if (guid == null)
{
return null;
}
var client = AzureStorage.DefaultClient.Instance.GetTableClient();
var table = client.GetTableReference("privateshare");
var query = new TableQuery<CodeSnippet>()
.Where(TableQuery.GenerateFilterCondition(
"RowKey", QueryComparisons.Equal, guid.ToString()))
.Take(1);
var res = table.ExecuteQuery(query);
return res?.First();
}
}
Problem solved.
The error occurs when I take DateTimeOffset.UtcNow.ToString() as the partition key.
After I change it to DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), it eventually works fine.
I don't know how & why the problem happens. But I guess the special characters in the "PartitionKey" may be the reason of the problem.
I am creating Windows Store App based on Split App template. What is the best way to save data from SampleDataSource for later use?
I tried:
Windows.Storage.ApplicationDataContainer roamingSettings = Windows.Storage.ApplicationData.Current.RoamingSettings;
roamingSettings.Values["Data"] = AllGroups;
It throws exception: 'Data of this type is not supported'.
RoamingSettings only supports the runtime data types (with exception of Uri); additionally, there's a limitation as to how much data you can save per setting and in total.
You'd be better off using RoamingFolder (or perhaps LocalFolder) for the storage aspects.
For the serialization aspect you might try the DataContractSerializer. If you have a class like:
public class MyData
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
}
public ObservableCollection<MyData> coll;
then write as follows
var f = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("data.txt");
using ( var st = await f.OpenStreamForWriteAsync())
{
var s = new DataContractSerializer(typeof(ObservableCollection<MyData>),
new Type[] { typeof(MyData) });
s.WriteObject(st, coll);
and read like this
using (var st = await Windows.Storage.ApplicationData.Current.LocalFolder.OpenStreamForReadAsync("data.txt"))
{
var t = new DataContractSerializer(typeof(ObservableCollection<MyData>),
new Type[] { typeof(MyData) });
var col2 = t.ReadObject(st) as ObservableCollection<MyData>;
}
I'm trying to write some activities with C# instead of the designer and XAML. VS2010 has been buggy and very slow for that, and it also has very poor compilation support (for variables names, properties and so on).
So I'm trying to create activities by inheriting from the Activity class directly, but I'm encountering a snag.
Here's my code:
public class TestActivity : Activity
{
public InArgument<string> Username { get; set; }
public InArgument<string> Password { get; set; }
public OutArgument<bool> ValidCredential { get; set; }
public OutArgument<ProvisioningRole> Role { get; set; }
public OutArgument<Guid> Guid { get; set; }
protected override Func<Activity> Implementation
{
get
{
return () =>
{
return new Sequence()
{
Activities =
{
new AuthenticateUserActivity()
{
Username = this.Username,
Password = this.Password,
Guid = this.Guid,
Result = this.ValidCredential
},
new If()
{
Condition = this.ValidCredential,
Then = new GetUserRoleActivity()
{
Username = this.Username,
Password = this.Password,
Result = this.Role
}
},
}
};
};
}
set { base.Implementation = value; }
}
}
The problem is with the If(), the condition. It's supposed to be an InArgument, but this.ValidCredential is an OutArgument. I've tried creating a Variable, assign the value of ValidCredential to it. I also tried to put the result of AuthenticateUserActivity in the variable and then assign it to ValidCredential, but I get an error saying the To property of Assign needs to be specified.
I've looked around for proper tutorials, but all I found was an MSDN article that had a quick and dirty code implementation, and it used literals instead of the passed arguments, so no help from there.
I found out how to do it. You just need to create new InArgument from the original one. There is a constructor that takes an expression for it.
Username = new InArgument<bool>((ActivityContext c) => this.ValidCredential.Get(c))
So I changed my whole activity to
return new CompensableActivity()
{
Body = new Sequence()
{
Activities =
{
new AuthenticateUserActivity()
{
Username = this.Username.In(),
Password = this.Password.In(),
Guid = this.Guid.Out(),
Result = this.ValidCredential.Out()
},
new If(this.ValidCredential.In())
{
Then = new GetUserRoleActivity()
{
Username = this.Username.In(),
Password = this.Password.In(),
Result = this.Role.Out()
},
Else = new Assign<ProvisioningRole>()
{
To = this.Role.Out(),
Value = ProvisioningRole.User
}
}
}
},
};
In and Out being extension methods I wrote:
public static class WorkflowExtensions
{
#region In
public static InArgument<T> In<T>(this InArgument<T> self)
{
return new InArgument<T>(context => self.Get(context));
}
public static InArgument<T> In<T>(this OutArgument<T> self)
{
return new InArgument<T>(context => self.Get(context));
}
#endregion
#region Out
public static OutArgument<T> Out<T>(this InArgument<T> self)
{
return new OutArgument<T>(context => self.Get(context));
}
public static OutArgument<T> Out<T>(this OutArgument<T> self)
{
return new OutArgument<T>(context => self.Get(context));
}
#endregion
}
And now all is well!
You should be able to get this to work. The basic approach should be to use a Variable to store data, use an OutArgument to get data out of activities into the Variable and InArguments to get data from a Variable into an activity.
Also note that the expressions to tie InArguments to Variables are VisualBasicValue expressions. So something like:
Condition = new VisualBasicValue("System.DateTime.Now.Hour < 12")
This blog post isn't about using arguments and variables but shows a couple of examples.
Going to shamelessly plug my own library that I ended up making for this:
http://code.google.com/p/system-transactions/
Allows basic compensation of code without the ginormous hassle of WF. Also, compiles properly and is easily debuggable.