Access Storage Account Key from a resource outside the ARM template - arm

I'm using several ARM templates in our project, each of them meant for a different component, and there's also a Common ARM template that includes all resources that most of the elements need to work, as SqlServer, Storage Accounts, Redis cache(only one of this resources for all the elements)
The thing is, as Storage account is in a separate ARM template(Common infrastructure ARM template), I'm not able to access the Storage account keys from the component template. I need this to properly set the value of the connection string for the component to use it. If I include the storage account resource in the component template, I can access it via:
[concat('DefaultEndpointsProtocol=https;AccountName=',
variables('YFO.StorageAccount.Name'), ';AccountKey=',
listKeys(resourceId('Microsoft.Storage/storageAccounts',
variables('YFO.StorageAccount.Name')), providers('Microsoft.Storage',
'storageAccounts').apiVersions[0]).keys[0].value)]
But when I remove it from the component template, as it should be, then I get the following error:
New-AzureRmResourceGroupDeployment :
Error: Code=InvalidTemplate; Message=Deployment template validation failed: 'The template reference '**********' is not valid: could not
find template resource or resource copy with this name. Please see
https://aka.ms/arm-template-expressions/#reference for usage details.'
It seems listKeys won't do the work if the resource is outside the template you are trying to deploy
For the AppInsights component, I was able to do this with:
[reference(concat('Microsoft.Insights/components/',
variables('AppInsightsName'))).InstrumentationKey]
even if the AppInsights resource is located out of the component ARM template, but I cannot do it with Storage account as the object returned for Storage Account in reference function is as following:
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Allow"
},
"supportsHttpsTrafficOnly": false,
"encryption": {
"services": {
"file": {
"enabled": true,
"lastEnabledTime": "2018-08-18T06:05:57.3069884Z"
},
"blob": {
"enabled": true,
"lastEnabledTime": "2018-08-18T06:05:57.3069884Z"
}
},
"keySource": "Microsoft.Storage"
},
"provisioningState": "Succeeded",
"creationTime": "2018-08-18T06:05:56.8228127Z",
"primaryEndpoints": {
"blob": "https://yfomormonttest.blob.core.windows.net/",
"queue": "https://yfomormonttest.queue.core.windows.net/",
"table": "https://yfomormonttest.table.core.windows.net/",
"file": "https://yfomormonttest.file.core.windows.net/"
},
"primaryLocation": "westeurope",
"statusOfPrimary": "available",
"secondaryLocation": "northeurope",
"statusOfSecondary": "available"
}
Any clue?
Thanks and regards.

The problem was in providers('Microsoft.Storage', 'storageAccounts').apiVersions[0] to get the API version that listkeys needs as a parameter. Setting directly the parameter as 2018-07-01 did work

Related

Use parametrized values with Azure Logic App

I'm working with an Azure LogicApp where the workflow have fixed values. Our DevOps tool is VSTS (Visual Studio Team Services) and because we have multiple environments to handle, I have to do some refactoring (using parametrized values) so in VSTS we'll be able to provide environment-specific values.
Thanks to 2 websites I found on Internet I managed to understand that there are 3 kind of parameters :
To understand my problem, here's my action :
I want to have my string "/work/documents" in a parameter value. So in the "Code view" I managed to use a parameter instead of a hard-coded value :
"triggers": {
"When_a_file_is_added_or_modified": {
"type": "ApiConnection",
"inputs": {
"host": {
"connection": {
"name": "#parameters('$connections')['sftp_1']['connectionId']"
}
},
"method": "get",
"path": "/datasets/default/triggers/onupdatedfile",
"queries": {
"folderId": "#{parameters('pathToRootFolder')}"
}
},
"recurrence": {
"frequency": "Hour",
"interval": 1
}
}
}
And in my parameters.json :
"sftp_1_path_root_folder": {
"value": "/work/documents"
}
Here's the final result in Visual Studio:
Am I missing something ? Why isn't the value displayed in the Designer ? Thank you for your help in advance !
When you are using Logic Apps parameters inside your Logic App definition, they are not resolved at design-time, but at run-time. Thus, you are not supposed to see them in the designer. If you run the workflow, you should be able to see the actual value at run-time.
If you want to resolve those parameters at deployment-time, then you would need to write directly from the ARM template using ARM parameters into the workflow definition. This is possible, but in some cases, it can become a bit more complex. That's why I prefer to make use of Logic Apps parameters as described here.

Need to read cookies value by it's name angularjs

I've cookie in my application and I need to read it using angularJS ngCookies.
When I exported the cookies from browser extension it looks like following json :
[
{
"domain": "localhost",
"hostOnly": true,
"httpOnly": false,
"name": "JSESSIONID",
"sameSite": "no_restriction",
"secure": true,
"session": true,
"storeId": "0",
"value": "00FC04BF082458FFE6F175C7E03F5712",
"id": 18
}
]
there can be more objects in this JSON along with 'JSESSIONID'. so I want to read only JSESSIONID's value.
my Code :
var jsessionCookie = $cookies.get('JSESSIONID');
console.log(" Cookies 'JSESSIONID' : "+jsessionCookie);
I'm getting undefined object.
The code you have seems fine, but the underlying issue is different.
You have your cookie marked as
"httpOnly": true
This means that cookie cannot be accessed by client side code including Angular.js.
The only way to access it is to change the code that generates the code so the cookie is not marked as httpOnly. There are some security considerations for making the change, so make sure you understand what you are doing.
You can read more about HttpOnly at OWASP web site.
To store a cokkie,
$cookies.put("cookie_name","cookie_value",{"expires":expireDate,"domain":"domain_name"});
To get a stored cookie
var cookieValue = $cookies.get("cookie_name);

Is including additional information in the output object a good idea?

I'm experimenting with a Conversation where I would like to modify the output in a couple of different ways:
different output for speech or text
different output depending on the tone of the conversation
It looks like I can add extra output details which make it through to the client ok. For example, adding speech alongside text...
{
"output": {
"speech": {
"Hi. Please see my website for details."
},
"link": "http://www.example.com",
"text": {
"Hi. Please see http://www.example.com for details."
}
}
}
For the tone, I wondered about making up a custom selection policy, unfortunately it seems to treat it the same as a random selection policy. For example...
{
"output": {
"text": {
"values": [
"Hello. Please see http://www.example.com for more details.",
"Hi. Please see http://www.example.com for details."
]
},
"append": false,
"selection_policy": "tone"
}
}
I could just add a separate tone-sensitive object to output though so that's not a big problem.
Would there be any issues adding things to output in this way?
You can definitely use the output field to specify custom variables you want your client app to see with the benefit that these variables will not persist across multiple dialog rounds (which they would if you would add them to the context field).
Now currently there is no "easy" way how to define your custom selection policy (apart from the random and sequential supported by the runtime right now) - but you could still return an array of possible answers to the client app with some attribute telling the client app which selection policy to use and you would implement this policy in the client app.

String concatenation issue with Azure Logic Apps

I'm creating an ARM template that deploys an Web App (an Mvc Api) and a Logic App.
I'm attempting to define an HTTP Action within the Logic App such that it dynamically concatenates the base Uri of the Api as well as a property of the current item using splitOn and #triggerBody(). The base Uri itself is concatenated from a set of parameters in the ARM template into a variable variables('hockeyAppAPISettings').Uri.
Here's the relevant snipped of the action definition:
"actionName": {
"conditions": [ ],
"inputs": {
"authentication": {
"audience": "[variables('apiSettings').Authentication.Audience]",
"clientId": "[variables('apiSettings').Authentication.ClientId]",
"secret": "[variables('apiSettings').Authentication.Secret]",
"tenant": "[variables('apiSettings').Authentication.Tenant]",
"type": "ActiveDirectoryOAuth"
},
"method": "patch",
"uri": "[concat(variables('apiSettings').Uri, '/#{triggerBody()['Id']}/ScanningInProgress')]"
//"uri": "[concat(variables('apiSettings').Uri, '//#{triggerBody()[/'Id/']}//ScanningInProgress')]"
//"uri": "[concat(variables('apiSettings').Uri, '//##{triggerBody()[/'Id/']}//ScanningInProgress')]"
},
"type": "Http"
},
The "uri" section is what i'm struggling with. I've sprinkled various escape characters (\ and #) in differing patterns through out this.
I either can't get the deployment to succeed w/deployment errors like:
Unable to parse template language expression
'concat(variables('apiSettings').Uri,
'//#{triggerBody()[/'Id/']}//ScanningInProgress')': expected token
'RightParenthesis' and actual 'Identifier'. Please see
http://aka.ms/arm-template-expressions for usage details..'.
Or if I get the deployment working and then look at the code in the portal after deployment, the string concatenation doesn't seem to work properly. The variable doesn't get converted to its value.
I have validated that if I edit the Uri directly (via the portal HTML editor) using this: "uri" : "https://edited.azurewebsites.net/api/Packages/#{triggerBody()['Id']}/ScanningInProgress" the Logic App will make a patch call for each item that comes from the HTTP trigger.
What am I doing wrong?
You need to escape the inner single quotes, i.e. try
"uri": "[concat(variables('apiSettings').Uri, '/#{triggerBody()[''Id'']}/ScanningInProgress')]"
Alternatively you can use the dot notation to reference the property, i.e.
"uri": "[concat(variables('apiSettings').Uri, '/#{triggerBody().Id}/ScanningInProgress')]"
For me changing this
"uri": "[concat(parameters('APIMUrl_param'), '/sales-management/differential-reference-codes/v1/?instance=', parameters('APIDRCInstance_param'), '&filter=differentialReferenceCode%20eq%27', variables('varDRC'), '%27')]",
to this has worked
"uri": "#concat(parameters('APIMUrl_param'), '/sales-management/differential-reference-codes/v1/?instance=', parameters('APIDRCInstance_param'), '&filter=differentialReferenceCode%20eq%27', variables('varDRC'), '%27')",

Include roles in User.login() and User.getCurrent() Angular SDK

I'm currently writing an administrative interface using the Loopback Angular SDK. After having dug through the documentation and code, I'm still no wiser as to how to include the user's roles in the response. It's causing me real headaches on the frontend because I'm not yet experienced enough with Angular to figure out how to enforce a role check on each of my states (I'm using UI-Router).
client: /auth.js
// Log the user in
$scope.doAuth = function() {
$scope.hasError = false;
$scope.busy = true;
$scope.loginResult = User.login({include: 'roles'}, $scope.credentials,
function wasSuccessfulAuth(authResponse) {
$scope.busy = true;
$rootScope.isAuthenticated = true;
$rootScope.user = authResponse.user;
$location.path('dashboard');
},
function wasFailedAuth(authResponse) {
$timeout(function() {
$scope.hasError = true;
$scope.authError = authResponse.data.error.message || 'Unknown error';
$scope.busy = false;
}, 1000);
}
)
}
server: /common/models/user.json
{
"name": "user",
"plural": "Users",
"base": "User",
"properties": {
},
"relations": {
"roles": {
"type": "belongsTo",
"model": "RoleMapping",
"foreignKey": "principalId"
}
},
"acls": [],
"methods": []
}
So this works in the API explorer, I have the routes I'd expect with an object that has a relation, but I can't seem to get any further than that... All that gets returned is the standard user login stuff (id, accessToken, email, etc) The docs seem to run cold when I get this far but I'd have thought this would have been a common use case?
This is a bit of a showstopper for me.
It's actually surprisingly easy to solve this problem using LoopBack: this is where "model scopes" come in very handy -- including the default scope which I find extremely useful for this type of situation.
First, a brief explanation of model scopes:
A model scope is like a saved query or "view", that allows you to specify a built-in filter for any query for that scope. For example, if you set the default scope to a valid filter, every single query (of any kind) against your model will have this filter applied!
This can get you in a heap of trouble, but there's one use-case that's pretty safe (all other things equal) and actually addresses your question perfectly: when I said a scope lets you give a valid filter, it turns out filters aren't just where clauses, but also include, limit, etc.
So to solve your problem, you simply need a default scope on your User model that includes whatever you need to include. For your example:
Simply add a scope object to your common/models/user.json:
{
"name": "user",
"plural": "Users",
"base": "User",
"scope": {
"include": [
"roles"
]
},
"properties": {
},
"relations": {
"roles": {
"type": "belongsTo",
"model": "RoleMapping",
"foreignKey": "principalId"
}
},
"acls": [],
"methods": []
}
By adding a default scope with an "include", LoopBack will automatically embed the object(s) of the related model based on the named relation (just like if you added it in your query -- which as discussed, is not possible, or at least not easy, in this case).
One caveat: since Role and RoleMapping are built-in models and are surely marked as non-public, I am not actually sure whether you can include them directly (but for a different reason than above). I haven't worked enough with ACLs (yet), but presumably there's more complexity around access controls especially in related models.
So, whereas my example code above explain the mechanics of doing the include here, the policy concern may slow you down (I'd be curious to know if they do).
That said, however, I guess you could add a derived model (from RoleMapping) that you make public (just like you did for user), and use it instead everywhere (including in the relation to/from user) -- hopefully that's clear (let me know if not).
In conclusion: If you add a default scope to your derived user model, to do the include for you, the AngularJS service wrapper (built by lb-ng) will be none the wiser (the include all happens on the backend):
$scope.loginResult = User.login($scope.credentials,
function wasSuccessfulAuth(authResponse) {
console.log('Related models are here: ', authResponse.roles,
authResponse.user);
...
In other words, the resulting model will contain an array, .roles[], containing the related roles to this user (based on your relation definition), and .user with the entire user model right there!
This latter point is unclear but I'm confident in that, because I did exactly the above but with a different related model that I know works. And to my surprise, user was included as well, since that's explicitly requested by the LoopBack $resource wrapper (the one created by lb-ng). So, in fact, you don't have do a separate query to get the user -- it's already there! There's no API that I see when using the $service wrapper, to get at that built-in include and change it; I guess that's what was posted in a comment above.
Hope this is helpful.
Steve
User.login returns AccessToken instance. To get user role, you should make separate request to fetch user, including role.

Resources