rewrite data null_data_source to locals - rewrite count to for_each - count

data "aws_iam_role" "extra_iam_role" {
count = length(var.s3_permission_extra_roles)
name = var.s3_permission_extra_roles[count.index]
}
data "null_data_source" "extra_iam" {
count = length(var.s3_permission_extra_roles)
inputs = {
role_id = "${data.aws_iam_role.extra_iam_role[count.index].unique_id}:*"
}
}
I want to rewrite that code like this:
data "aws_iam_role" "extra_iam_role" {
for_each = var.s3_permission_extra_roles
name = each.value
}
# is that correct ?
data "null_data_source" "extra_iam" {
for_each = var.s3_permission_extra_roles
inputs = {
role_id = data.aws_iam_role.extra_iam_role????????
#what code should be in previous line
}
}
i am clueless here....please help
i want to get rid of that count.index because that cant be used by locals...
i am not sure how to rewrite that count.index into for_each similarity

#kovokilla I believe 'var.s3_permission_extra_roles' is of type set and in that case, in your first code you should do something similar to below
data "aws_iam_role" "extra_iam_role" {
for_each = toset(var.s3_permission_extra_roles)
name = each.value
}
and for second block, you can write something similar to below
data "null_data_source" "extra_iam" {
for_each = toset(var.s3_permission_extra_roles)
inputs = {
role_id = data.aws_iam_role.extra_iam_role[each.key].unique_id
}
}
Remember to define your 's3_permission_extra_roles' as type set in your variables.tf file
variable "s3_permission_extra_roles" {
description = "s3 permission roles"
default = ["role1","role2"]
type = set(string)
}

Related

Terraform setproduct with inner loop

I'm trying to create a CloudWatch alarm that will cycle through instances defined in data.tf and for each on of these to cycle through the volume id's
data.tf
data "aws_instances" "instance_cloudwatch" {
instance_tags = {
Type = var.type
}
}
data "aws_ebs_volumes" "cw_volumes" {
tags = {
Name = var.name
}
}
data "aws_ebs_volume" "cw_volume" {
for_each = toset(data.aws_ebs_volumes.cw_volumes.ids)
filter {
name = "volume-id"
values = [each.value]
}
}
In the resource I created
locals {
vol_map = {
for pair in setproduct(data.aws_instances.instance_cloudwatch.ids,data.aws_ebs_volume.cw_volume.*.id) : "${pair[0]}-${pair[1]}" => {
id = pair[0]
vol = pair[1]
}
}
}
And then I try to use these pairs in the alarm dimensions
resource "aws_cloudwatch_metric_alarm" "some_alarm" {
for_each = local.vol_map
...
dimensions = {
InstanceId = each.value.id
VolumeId = each.value.vol
}
When I run terraform apply I get this error
Error: Unsupported attribute
for pair in setproduct(data.aws_instances.instance_cloudwatch.ids,data.aws_ebs_volume.cw_volume..id) : "${pair[0]}-${pair[1]}" => {*
This object does not have an attribute named "id" I tried volume_id and got the same error
The issue is that you can't use the .*. syntax (in data.aws_instances.instance_cloudwatch.ids,data.aws_ebs_volume.cw_volume.*.id) on a resource that you created with for_each. The .*. syntax only works when you use count. This is because it only works with arrays/lists, and for_each creates a map.
Try values(data.aws_instances.instance_cloudwatch.ids,data.aws_ebs_volume.cw_volume).*.id. This will get the data.aws_ebs_volume.cw_volume values as a list instead of a map, so you can then use .*. on them.

list of objects (blocks for network)

In openstack_compute_instance_v2, Terraform can attach the existing networks, while I have 1 or n network to attach, in module:
...
variable "vm_network" {
type = "list"
}
resource "openstack_compute_instance_v2" "singlevm" {
name = "${var.vm_name}"
image_id = "${var.vm_image}"
key_pair = "${var.vm_keypair}"
security_groups = "${var.vm_sg}"
flavor_name = "${var.vm_size}"
network = "${var.vm_network}"
}
in my .tf file:
module "singlevm" {
...
vm_network = {"name"="NETWORK1"}
vm_network = {"name"="NETWORK2"}
}
Terraform returns expected object, got invalid error.
What am I doing wrong here?
That's not how you specify a list in your .tf file that sources the module.
Instead you should have something more like:
variable "vm_network" { default = [ "NETWORK1", "NETWORK2" ] }
module "singlevm" {
...
vm_network = "${var.vm_network}"
}

Dictionary and condition

I have been working on an assignment in which I have to upload some records from a file to Dictionary and manipulate.
Actually file have number of record with same invoice number and different tax values and I have to add all those values and make it only one invoice
what I'm trying to do
I'm passing values from a foreach loop to a function which check if the dictionary is empty so it will simply add first record and on second call it will check weather any record in dictionary have same invoice number so it will sum and update current tax value to one already added,
what I'm getting
when I pass 2nd value (and so on) the previous value of last entry in dictionary some how update itself with current value before even comparing which I don't want.
public jd_records jd = new jd_records();
Dictionary<int, jd_records> jdValues = new Dictionary<int, jd_records>();
//Calling values with loop while jd is a publicly declared object of class jd_records
foreach (DataRow dr in jddt.Rows)
{
//jd_records jdPass = new jd_records();
jd.supplierName = dr["Supplier"].ToString();
jd.supplierNTN = dr["Supplier NTN"].ToString();
jd.invoiceNo = dr["JDE Invoice Number"].ToString();
jd.invoiceDate = DateTime.Parse(dr["JDE Invoice Date"].ToString());
if (dr["Taxable Amount"].ToString().Equals(""))
{ jd.taxable = 0; }
else
{ jd.taxable = float.Parse(dr["Taxable Amount"].ToString()); }
if (dr["Tax To Pay"].ToString().Equals(""))
{ jd.tax = 0; }
else
{ jd.tax = float.Parse(dr["Tax To Pay"].ToString()); }
jdRecordCheck();
}
called function
public void jdRecordCheck()
{
if (jdValues.Count < 1)
{
jdValues.Add(0, jd);
}
else //previous record values (at key 0) changes to new jd value when come to this else part on execution
{
foreach (KeyValuePair<int,jd_records> jdVal in jdValues)
{
if ((jdVal.Value.supplierNTN.Equals(jd.supplierNTN)) && (jdVal.Value.invoiceNo.Equals(jd.invoiceNo)))
{
jdVal.Value.tax = jdVal.Value.tax + jd.tax;
jdVal.Value.taxable = jdVal.Value.taxable + jd.taxable;
jdValues[jdVal.Key] = jdVal.Value;
}
else
{
jdValues.Add(jdVal.Key + 1, jd);
}
}
}
}
I'll be very thankful if anyone helps.
Seems to me this would be much easier to do with Linq, which contains functions for grouping and summing.
List<jd_records> result = jddt.Rows
.GroupBy(jd => jd.invoiceNo)
.Select(jd => new jd_records
{
invoiceNo = jd.invoiceNo,
totalTax = jd.Sum(d => d.tax)
}).ToList();

replace DescriptionAttr with Dictionary or smth

I have a enum that's contain Description Attribute(for audit):
public enum ActivityType
{
[NotExist("Not Assign")]
[Description("Change Level")]
LevelChanged,
[NotExist("Not Assign")]
[Description("Change Skill Level")]
SkillLevelChanged
}
And all had been great until i needed to put Desription in Resource files(attribute didn't support them), so i need Dictionary or something like this.The question is: How implement this feature without big changing in all another logic?Something like this:
private static readonly Dictionary<ActivityType, String> ActivityDescription = new Dictionary<ActivityType, String>()
{
{ActivityType.LevelChanged, "Change"},
{ActivityType.SkillLevelChanged, "SkillChange"}
}
The solution that requires the minimal amount of code change for you would be to alter your descriptions to be the resource keys in your resource file. Then you could read these dynamically by doing something like:
[Description("Change_Level")]
Then your resource key/value would be:
Change_Level Change Level
And to read it you can do:
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute attribute = value.GetType()
.GetField(value.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.SingleOrDefault() as DescriptionAttribute;
if (attribute != null)
{
var resManager = new ResourceManager(typeof(MyResources));
return resManager.GetString(attribute.Description);
}
else
{
return value.ToString();
}
If however you're wanting a nicer solution with the option to pass in the resource file, you can hijack the Display attribute:
[Display(ResourceType = typeof(MyResources), Name = "Change_Level")]
Then you can do:
FieldInfo fi = value.GetType().GetField(value.ToString());
DisplayAttribute attribute = value.GetType()
.GetField(value.ToString())
.GetCustomAttributes(typeof(DisplayAttribute), false)
.SingleOrDefault() as DisplayAttribute;
if (attribute != null)
{
var resManager = new ResourceManager(attribute.ResourceType);
return resManager.GetString(attribute.Name);
}
else
{
return value.ToString();
}

Exjts4 grid remote summary (no grouping)

I've created Ext.grid.Panel and now I need to make a summary with data calculated on server. And I have no grouping in that grid.
In feature ftype: 'summary' there is no such property like 'remoteRoot'.
Is there any opportunity to create this summary withou grouping?
You have to extend / override class. This is an example based on AbstractSummary.js and should be optimised.
// usage in grid:
{
ftype : 'summary',
remoteRoot : 'summary'
}
//response from server
{
data : [] // our standard data
summary : {
summaryField : 123123
}
}
// our class
Ext.define('w3desApp.grid.feature.Summary', {
override : 'Ext.grid.feature.Summary',
getSummary: function(store, type, field, group) {
var reader = store.proxy.reader;
if (this.remoteRoot && reader.rawData) {
// reset reader root and rebuild extractors to extract summaries data
root = reader.root;
reader.root = this.remoteRoot;
reader.buildExtractors(true);
summaryRow = reader.getRoot(reader.rawData);
// restore initial reader configuration
reader.root = root;
reader.buildExtractors(true);
if (typeof summaryRow[field] != 'undefined') {
return summaryRow[field];
}
return '';
}
return this.callParent(arguments);
}
});

Resources