Get oauth2_permissions from azuread_application using Terraform - azure-active-directory

I have an app registration which defines two oauth2_permissions blocks, e.g. (other details elided)
resource "azuread_application" "myapp" {
oauth2_permissions {
is_enabled = true
type = "User"
value = "Permission.One"
}
oauth2_permissions {
is_enabled = true
type = "User"
value = "Permission.Two"
}
}
Which, when applied,works just fine. I then want to refer to those permissions in another app registration, e.g.
resource "azuread_application" "myotherapp" {
required_resource_access {
resource_app_id = azuread_application.myapp.application_id
resource_access {
id = ??
type = "Scope"
}
}
}
For the id here, I have tried:
id = lookup(azuread_application.myapp.oauth2_permissions[0], "id")
which gives This value does not have any indices. As does
id = azuread_application.myapp.oauth2_permissions.0.id
I can define a data block and get the output of oauth2_permissions from myapp:
data "azuread_application" "myapp" {
application_id = azuread_application.myapp.application_id
}
output "myapp-perms" {
value = data.azuread_application.myapp.oauth2_permissions
}
And on apply, that will correctly show an array of the two permission blocks. If I try to refer to the data block instead of the application block, i.e.
id = lookup(data.azuread_application.myapp.oauth2_permissions[0], "id")
This gives me a different error: The given key does not identify an element in this collection value
If I apply those two permissions manually on the console, everything works fine. From reading around I was fairly sure that at least one of the above methods should work but I am clearly missing something.
For completeness, provider definition:
provider "azurerm" {
version = "~> 2.12"
}
provider "azuread" {
version = "~> 0.11.0"
}

Based on comments.
The solution is to use tolist. The reason is that the multiple oauth2_permissions blocks will be represented as sets of objects, which can't be accessed using indices.
id = tolist(azuread_application.myapp.oauth2_permissions)[0].id
However, the sets don't have guaranteed order. Thus a special attention should be payed to this.

Related

How can I associate NSG's and Subnets being created by loops in Terraform?

Here is the code I am using to create subnets and nsgs I want to associate the NSG and subnet in the same script but I am unable to understand how can I get subnet IDs and NSG IDs which are being produced here and use them in the association resource. Thanks in advance for the help !
First part of code this is being used to create n no of Subnets and NSGs depends upon the parameter
provider "azurerm" {
version = "2.0.0"
features {}
}
resource "azurerm_resource_group" "new-rg" {
name = var.rg_name
location = "West Europe"
}
resource "azurerm_virtual_network" "new-vnet" {
name = var.vnet_name
address_space = ["${var.vnet_address_space}"]
location = azurerm_resource_group.new-rg.location
resource_group_name = azurerm_resource_group.new-rg.name
}
resource "azurerm_subnet" "test" {
count = "${length(var.subnet_prefix)}"
name = "${element(var.subnet_subnetname, count.index)}"
resource_group_name = azurerm_resource_group.new-rg.name
virtual_network_name = azurerm_virtual_network.new-vnet.name
address_prefix = "${element(var.subnet_prefix, count.index)}"
}
resource "azurerm_network_security_group" "new-nsg" {
count = "${length(var.subnet_prefix)}"
name = "${element(var.subnet_subnetname, count.index)}-nsg"
location = azurerm_resource_group.new-rg.location
resource_group_name = azurerm_resource_group.new-rg.name
}
Below is the resource where i have to pass the parameters to create the association for the above subnets and nsgs being created.
Second Part of code Need to make the below code usable for above solution for n no of associations.
resource "azurerm_subnet_network_security_group_association" "example" {
subnet_id = azurerm_subnet.example.id
network_security_group_id = azurerm_network_security_group.example.id
}
How can associate the n number of subnets and nsgs being created by using 2nd part of code, I cant find my way to that
This seems like a good case for for_each. Here is some code I'm using for AWS (the same logic applies as far as I can tell)-
(var.nr_azs is just an int, formatlist is used because for_each only likes strings)
locals {
az_set = toset(formatlist("%s", range(var.nr_azs))) # create a list of numbers and convert them to strings)
}
resource "aws_subnet" "private" {
for_each = local.az_set
availability_zone = random_shuffle.az.result[each.key]
cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 8, each.key)
vpc_id = aws_vpc.main.id
map_public_ip_on_launch = false
}
resource "aws_eip" "nat_gw" {
vpc = true
}
resource "aws_nat_gateway" "gw" {
for_each = aws_subnet.private
allocation_id = aws_eip.nat_gw.id
subnet_id = each.value.id
}
resource "aws_route_table" "private_egress" {
for_each = aws_nat_gateway.gw
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = each.value.id
}
}
resource "aws_route_table_association" "private" {
for_each = local.az_set
subnet_id = aws_subnet.private[each.key].id
route_table_id = aws_route_table.private_egress[each.key].id
}
So i was able to solve the issue mentioned by me above the following code contains the solution for the mentioned scenario for the problem.
resource "azurerm_subnet_network_security_group_association" "snet-nsg-association" {
count = length(var.subnet_subnetname)
subnet_id = element(azurerm_subnet.multi-snet.*.id, count.index)
network_security_group_id = element(azurerm_network_security_group.new-nsg.*.id, count.index)
}

concatenate filepath prefix and file name in terraform code

I'm trying to create policies in aws with terraform.
variable "path" {
type = "string"
}
variable "policies" {
type = list(object ({
name = string
plcyfilename = string
asmplcyfilename = string
desc = string
ownner = string}))
default = []
}
resource "aws_iam_policy" "policy" {
count = length(var.policies)
name = lookup(var.policies[count.index], "name")
policy = file(lookup(var.policies[count.index], concat("var.path","plcyfilename")))
description = "Policy for ${lookup(var.policies[count.index], "desc")}"
}
and this is how my tfvars looks like:
path = "./../t2/scripts/"
policies = [
{name = "cwpolicy", plcyfilename = "cw.json" , asmplcyfilename ="csasm.json", desc ="vpcflowlogs", ownner ="vpc"},
]
The error that is thrown while I do this is like this:
Error: Invalid function argument
on main.tf line 13, in resource "aws_iam_policy" "policy":
13: policy = file(lookup(var.policies[count.index], "${concat("${var.path}","plcyfilename")}"))
Invalid value for "seqs" parameter: all arguments must be lists or tuples; got
string.
I'm using terraform 0.12.
It works as expected if I change the variable to have complete file path:plcyfilename=./../t2/scripts/cw.json.
However I want to isolate the file path from the file names.
Can someone point me where I am going wrong.
The concat function is for concatenating lists, not for concatenating strings.
To concatenate strings in Terraform, we use template interpolation syntax:
policy = file("${var.path}/${var.policies[count.index].policy_filename}")
Since your collection of policies is not a sequence where the ordering is significant, I'd recommend also changing this to use resource for_each, which will ensure that Terraform tracks the policies using the policy name strings rather than using the positions in the list:
variable "policies" {
type = map(object({
policy_filename = string
assume_policy_filename = string
description = string
owner = string
}))
default = {}
}
resource "aws_iam_policy" "policy" {
for_each = var.policies
name = each.key
policy = file("${var.path}/${each.value.policy_filename}")
description = "Policy for ${each.value.description}"
}
In this case the policies variable is redefined as being a map, so you'd now present the name of each policy as the key within the map rather than as one of the attributes:
policies = {
cw = {
policy_filename = "cw.json"
assume_policy_filename = "csasm.json"
description = "vpcflowlogs"
owner = "vpc"
}
# ...
}
Because the for_each value is the policies map, each.key inside the resource block is a policy name and each.value is the object representing that policy, making the resulting expressions easier to read and understand.
By using for_each, we will cause Terraform to create resource instance addresses like aws_iam_policy.policy["cw"] rather than like aws_iam_policy.policy[1], and so adding and removing elements from the map will cause Terraform to add and remove corresponding instances from the resource, rather than try to update instances in-place to respect the list ordering as it would've done with your example.

'504 - Gateway Timeout' when Indexing the items in Episerver Find

When Indexing the items, it fails sometimes and it gives,
The remote server returned an error: (504) Gateway Timeout. [The remote server returned an error: (504) Gateway Timeout.]
The Indexing logic is here as below,
var client = EPiServer.Find.Framework.SearchClient.Instance;
List<ItemModel> items = getItems(); // Get more than 1000 items
List<ItemModel> tempItems = new List<ItemModel>();
//Index 50 items at a time
foreach(var item in items)
{
tempItems.Add(item);
if (tempItems.Count == 50)
{
client.Index(tempItems);
tempItems.Clear();
}
}
What causes this to happen ?
Note: The above mentioned ItemModel is a custom model which is not implemented interfaces (such as IContent). And the items is a list of ItemModel objects.
Additional info:
EPiServer.Find.Framework version 13.0.1
EPiServer.CMS.Core version 11.9.2
I always figured the SearchClient to be a bit sketchy when manipulating data in Find, as far as I figured (but I have to check this) the SearchClient obey under the request limitation of Episerver Find and when doing bigger operations in loops it tends to time out.
Instead, use the ContentIndexer, i.e.
// Use this or injected parameter
var loader = ServiceLocator.Current.GetInstance<IContentLoader>();
// Remove all children or not
var cascade = true;
ContentReference entryPoint = ...where you want to start
// Get all indexable languages from Find
Languages languages = SearchClient.Instance.Settings.Languages;
// Remove all current instances of all languages below the selected content node
//languages.ForEach(x => ContentIndexer.Instance.RemoveFromIndex(entryPoint, cascade.Checked, x.FieldSuffix));
foreach (var lang in languages)
{
if (cascade)
{
var descendents = loader.GetDescendents(entryPoint);
foreach (ContentReference descendent in descendents)
{
ContentIndexer.Instance.RemoveFromIndex(descendent, false, lang.FieldSuffix);
}
}
// Try delete the entrypoint
var entryTest = loader.Get<IContent>(entryPoint, new CultureInfo(lang.FieldSuffix));
if (entryTest != null)
{
var delRes = ContentIndexer.Instance.Delete(entryTest);
}
}
This is the most bulletproof way to delete stuff from the index as far as I figured.

Laravel 5.4 - Array returning NULL instead of a numeric value

Here's a problem that is bothering me for a while.
I have a service provider to pass data to all views, everytime the sidebar is rendered. like this:
`
public function boot()
{
$userrole = array (DB::table('users')->where('id','=', Auth::id())->value('role'));
$menucase1 = [3,4,9,10];
$menucase2 = [1,2,3,10];
$menucase3 = [1,3,4,9,10];
$menucase4 = [4,9];
$commondata = compact('userrole','menucase1','menucase2','menucase3','menucase4');
view()->share('commondata', $commondata);
View::composer('sidebar', function ($view) {
$userrole = array (DB::table('users')->where('id','=', Auth::id())->value('role'));
$menucase1 = [3,4,9,10];
$menucase2 = [1,2,3,10];
$menucase3 = [1,3,4,9,10];
$menucase4 = [4,9];
$commondata = compact('userrole','menucase1','menucase2','menucase3','menucase4');
$view->with('commondata', $commondata);
});
}`
Doing a {{ dd($commondata) }} returns the correct values for the menucase arrays, but NULL for the $userrole
If i declare the same $userrole variable in a controller and call the variable in the view, the received data is correct.
Why is this happening?
Thanks in advance
Can't understand what are you actually trying to do.
If you want get user role as array, you can using pluck method:
$userRole = User::where('id', Auth::id())->pluck('role')->toArray();
But for current user you can just get the role
$userRole = [Auth::user()->role];
UPD: you also can do it in view without any sharing
{{ Auth::user()->role }}
If your user has many roles from a different table, and you have the relationship defined, you could do
$userrole = Auth::user()->roles->pluck('name');
//will return all the roles names in an array
//Replace name by the actual column you want from 'roles' table.

In Firebase, is there a way to get the number of children of a node without loading all the node data?

You can get the child count via
firebase_node.once('value', function(snapshot) { alert('Count: ' + snapshot.numChildren()); });
But I believe this fetches the entire sub-tree of that node from the server. For huge lists, that seems RAM and latency intensive. Is there a way of getting the count (and/or a list of child names) without fetching the whole thing?
The code snippet you gave does indeed load the entire set of data and then counts it client-side, which can be very slow for large amounts of data.
Firebase doesn't currently have a way to count children without loading data, but we do plan to add it.
For now, one solution would be to maintain a counter of the number of children and update it every time you add a new child. You could use a transaction to count items, like in this code tracking upvodes:
var upvotesRef = new Firebase('https://docs-examples.firebaseio.com/android/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes');
upvotesRef.transaction(function (current_value) {
return (current_value || 0) + 1;
});
For more info, see https://www.firebase.com/docs/transactions.html
UPDATE:
Firebase recently released Cloud Functions. With Cloud Functions, you don't need to create your own Server. You can simply write JavaScript functions and upload it to Firebase. Firebase will be responsible for triggering functions whenever an event occurs.
If you want to count upvotes for example, you should create a structure similar to this one:
{
"posts" : {
"-JRHTHaIs-jNPLXOQivY" : {
"upvotes_count":5,
"upvotes" : {
"userX" : true,
"userY" : true,
"userZ" : true,
...
}
}
}
}
And then write a javascript function to increase the upvotes_count when there is a new write to the upvotes node.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.countlikes = functions.database.ref('/posts/$postid/upvotes').onWrite(event => {
return event.data.ref.parent.child('upvotes_count').set(event.data.numChildren());
});
You can read the Documentation to know how to Get Started with Cloud Functions.
Also, another example of counting posts is here:
https://github.com/firebase/functions-samples/blob/master/child-count/functions/index.js
Update January 2018
The firebase docs have changed so instead of event we now have change and context.
The given example throws an error complaining that event.data is undefined. This pattern seems to work better:
exports.countPrescriptions = functions.database.ref(`/prescriptions`).onWrite((change, context) => {
const data = change.after.val();
const count = Object.keys(data).length;
return change.after.ref.child('_count').set(count);
});
```
This is a little late in the game as several others have already answered nicely, but I'll share how I might implement it.
This hinges on the fact that the Firebase REST API offers a shallow=true parameter.
Assume you have a post object and each one can have a number of comments:
{
"posts": {
"$postKey": {
"comments": {
...
}
}
}
}
You obviously don't want to fetch all of the comments, just the number of comments.
Assuming you have the key for a post, you can send a GET request to
https://yourapp.firebaseio.com/posts/[the post key]/comments?shallow=true.
This will return an object of key-value pairs, where each key is the key of a comment and its value is true:
{
"comment1key": true,
"comment2key": true,
...,
"comment9999key": true
}
The size of this response is much smaller than requesting the equivalent data, and now you can calculate the number of keys in the response to find your value (e.g. commentCount = Object.keys(result).length).
This may not completely solve your problem, as you are still calculating the number of keys returned, and you can't necessarily subscribe to the value as it changes, but it does greatly reduce the size of the returned data without requiring any changes to your schema.
Save the count as you go - and use validation to enforce it. I hacked this together - for keeping a count of unique votes and counts which keeps coming up!. But this time I have tested my suggestion! (notwithstanding cut/paste errors!).
The 'trick' here is to use the node priority to as the vote count...
The data is:
vote/$issueBeingVotedOn/user/$uniqueIdOfVoter = thisVotesCount, priority=thisVotesCount
vote/$issueBeingVotedOn/count = 'user/'+$idOfLastVoter, priority=CountofLastVote
,"vote": {
".read" : true
,".write" : true
,"$issue" : {
"user" : {
"$user" : {
".validate" : "!data.exists() &&
newData.val()==data.parent().parent().child('count').getPriority()+1 &&
newData.val()==newData.GetPriority()"
user can only vote once && count must be one higher than current count && data value must be same as priority.
}
}
,"count" : {
".validate" : "data.parent().child(newData.val()).val()==newData.getPriority() &&
newData.getPriority()==data.getPriority()+1 "
}
count (last voter really) - vote must exist and its count equal newcount, && newcount (priority) can only go up by one.
}
}
Test script to add 10 votes by different users (for this example, id's faked, should user auth.uid in production). Count down by (i--) 10 to see validation fail.
<script src='https://cdn.firebase.com/v0/firebase.js'></script>
<script>
window.fb = new Firebase('https:...vote/iss1/');
window.fb.child('count').once('value', function (dss) {
votes = dss.getPriority();
for (var i=1;i<10;i++) vote(dss,i+votes);
} );
function vote(dss,count)
{
var user='user/zz' + count; // replace with auth.id or whatever
window.fb.child(user).setWithPriority(count,count);
window.fb.child('count').setWithPriority(user,count);
}
</script>
The 'risk' here is that a vote is cast, but the count not updated (haking or script failure). This is why the votes have a unique 'priority' - the script should really start by ensuring that there is no vote with priority higher than the current count, if there is it should complete that transaction before doing its own - get your clients to clean up for you :)
The count needs to be initialised with a priority before you start - forge doesn't let you do this, so a stub script is needed (before the validation is active!).
write a cloud function to and update the node count.
// below function to get the given node count.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.userscount = functions.database.ref('/users/')
.onWrite(event => {
console.log('users number : ', event.data.numChildren());
return event.data.ref.parent.child('count/users').set(event.data.numChildren());
});
Refer :https://firebase.google.com/docs/functions/database-events
root--|
|-users ( this node contains all users list)
|
|-count
|-userscount :
(this node added dynamically by cloud function with the user count)

Resources