I have the following terraform :
data "terraform_remote_state" "stack" {
backend = "local"
config {
path = "terraform.tfstate"
}
}
output "diditwork" {
value = "${data.terraform_remote_state.stack.aws_autoscaling_group.main.id}"
}
and I have a terraform.tfstate file in the same folder :
{
"version": 3,
"terraform_version": "0.9.3",
"serial": 14,
"lineage": "dc16a61f-72dd-435b-ba3f-5e36e14aace2",
"modules": [
{
"path": [
"root"
],
"outputs": {},
"resources": {
"aws_autoscaling_group.main": {
"type": "aws_autoscaling_group",
"depends_on": [
"aws_launch_configuration.lc"
],
"primary": {
"id": "djin-sample-asg-stag",
"attributes": {
"arn": "arn:aws:autoscaling:us-east-1:174120285419:autoScalingGroup:04c470fa-45f8-4711-aa31-b3ede40d6…
but for some reason when I do a terraform apply my output doesn't print anything for the autoscaling group id. The apply is successful and it doesn't even throw any error. What am i missing ?
This is wrong.
value = "${data.terraform_remote_state.stack.aws_autoscaling_group.main.id}"
You can only get root level outputs in data.
https://www.terraform.io/docs/providers/terraform/d/remote_state.html#root-outputs-only
Only the root level outputs from the remote state are accessible.
Outputs from modules within the state cannot be accessed. If you want
a module output to be accessible via a remote state, you must thread
the output through to a root output.
So, you will first need to output your autoscaling id, something like:
output "asg_id" {
value = "${aws_autoscaling_group.main.id}"
}
And then in the data get, you will do,
output "diditwork" {
value = "${data.terraform_remote_state.stack.asg_id}"
}
Also, doing a remote data source on the same location will be a bad idea in general for backends that support locking (or for scenarios where you really want to use it). I am assuming your are doing the remote data source from the same location only for experimental basis, so no harm done, but in real usage, you should just use the value as used in the output above.
Related
Whenever a new release pipeline is ran in Azure DevOps, the URL Is changed.. currently my ARM template has a hard-coded URL which can be annoying to keep on adding in manually.
"cors": {
"allowedOrigins": [
"[concat('https://',parameters('storage_account_name'),'.z10.web.core.windows.net')]"
}
The only thing that changes is the 10 part in the z10 so essentially i want it to be something like
[concat('https://',parameters('storage_account_name'),'.z', '*', '.web.core.windows.net')] I dont know if something like that is valid but essentially its so that the cors policy will accept the URL regardless of the z number.
Basically speaking this is not possible, because of the CORS standard (see docs).
which allows only for exact origins, wildcard, or null.
For instance, ARM for Azure Storage is also following this pattern allowing you to put a list of exact origins or a wildcard (see ARM docs)
However, if you know your website name, in your ARM you can receive the full host and use it in your CORS:
"[reference(resourceId('Microsoft.Web/sites', parameters('SiteName')), '2018-02-01').defaultHostName]"
The same with a static website (which is your case I guess) if you know the storage account name:
"[reference(concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName')), '2019-06-01', 'Full').properties.primaryEndpoints.web]"
Advance reference output manipulation
Answering on comment - if you would like to replace some characters in the output from the reference function the easiest way is to use build-in replace function (see docs)
In case you need a more advanced scenario I am pasting my solution by introducing a custom function which is removing https:// and / from the end so https://contonso.com/ is transformed to contonso.com:
"functions": [
{
"namespace": "lmc",
"members": {
"replaceUri": {
"parameters": [
{
"name": "uriString",
"type": "string"
}
],
"output": {
"type": "string",
"value": "[replace(replace(parameters('uriString'), 'https://',''), '/','')]"
}
}
}
}
],
# ...(some code)...
"resources": [
# ... (some resource)...:
"properties": {
"hostName": "[lmc.replaceUri(reference(variables('storageNameCdn')).primaryEndpoints.blob)]"
}
]
I have defined an azurerm_resource_group_template_deployment my_rm which has ARM template source:
{
...
"parameters": {... },
"resources": [ ... ],
"outputs": {
"db_name": {
"type": "string",
"value": "test_value"
}
}
}
I would like to use this output in terraform, like:
output "db_name" {
value = azurerm_resource_group_template_deployment.my_rm.output_content["db_name"]
}
Unfortunately above definition returns empty value.
What is the correct way to define the output in terraform?
The output_content exports the JSON Content of the Outputs of the ARM Template Deployment.
After my validation, you could output the content with
output "db_name" {
value = azurerm_resource_group_template_deployment.my_rm.output_content
}
Then run terraform apply, you will see the output result, then you can change to filter the result with
output "db_name" {
value = jsondecode(azurerm_resource_group_template_deployment.my_rm.output_content).db_name.value
}
Please note that the db_name is not the same declaration db_name in your terraform code, it really should match the output JSON key in the first above step.
For example,
I have below terraform configuration for cognito client:
data "aws_cognito_user_pools" "re_user_pool" {
name = "${var.cognito_user_pool_name}"
}
resource "aws_cognito_user_pool_client" "app_client" {
name = "re-app-client"
user_pool_id = data.aws_cognito_user_pools.re_user_pool.id
depends_on = [data.aws_cognito_user_pools.re_user_pool]
explicit_auth_flows = ["USER_PASSWORD_AUTH"]
prevent_user_existence_errors = "ENABLED"
allowed_oauth_flows_user_pool_client = true
allowed_oauth_flows = ["code"]
allowed_oauth_scopes = ["phone", "openid", "email", "profile", "aws.cognito.signin.user.admin"]
supported_identity_providers = ["COGNITO", "Google"]
callback_urls = ["https://scnothzsf0.execute-api.ap-southeast-2.amazonaws.com/staging/signup"]
}
I references the cognito user pool which already exists on AWS. The error happens on the line user_pool_id = data.aws_cognito_user_pools.re_user_pool.id when it uses the user pool id in aws_cognito_user_pool_client.
I will get the error
Error: Error creating Cognito User Pool Client: InvalidParameterException: 1 validation error detected: Value 're-user' at 'userPoolId' failed to satisfy constraint: Member must satisfy regular expression pattern: [\w-]+_[0-9a-zA-Z]+
on infra/cognito.tf line 5, in resource "aws_cognito_user_pool_client" "app_client":
5: resource "aws_cognito_user_pool_client" "app_client" {`
It seems the format of the ID is not correct. I have read this document https://www.terraform.io/docs/providers/aws/d/cognito_user_pools.html and it has a reference attribute ids - The list of cognito user pool ids.. I wonder why it gives a list of user pool id. How can I reference this ID?
I also tried to reference it as user_pool_id = data.aws_cognito_user_pools.re_user_pool.ids[0] but got an error:
Error: Invalid index
on infra/cognito.tf line 8, in resource "aws_cognito_user_pool_client" "app_client":
8: user_pool_id = data.aws_cognito_user_pools.re_user_pool.ids[0]
This value does not have any indices.
The re_user_pool referenced above is defined here:
resource "aws_cognito_user_pool" "re_user_pool" {
name = "re-user"
}
I came across your question while working through this same problem. I see the question is several months old, but I'm still going to add an answer for anyone else that ends up here like I did.
First, the solution is to convert the ids value from a set to a list via the tolist function and then access it as you would any terraform list.
Caveat: In my case, I have ensured I only have one user pool for a given name, but you could get multiple user pools if you haven't followed this convention. This solution will not be a complete solution for that situation, but perhaps it will still point in the right direction.
Example code:
data "aws_cognito_user_pools" "test" {
name = "a_name"
}
output "test" {
value = "${tolist(data.aws_cognito_user_pools.test.ids)[0]"
}
Second, how I arrived at it:
I added an output block so I could see what I was working with and I commented out the problematic lines in my terraform file so I could successfully execute terraform apply. Next I ran terraform apply followed by terraform output --json (note: the apply must be successful for output to have the latest values).
Example temporary output block:
output "test" {
value = "${data.aws_cognito_user_pools.test}" // output top-level object for debugging
}
Relevant terraform apply output:
test = {
"arns" = [
"<redacted>",
]
"id" = "a_name"
"ids" = [
"us-east-1_<redacted>",
]
"name" = "a_name"
}
Relevant terraform output --json output:
"test": {
"sensitive": false,
"type": [
"object",
{
"arns": [
"set",
"string"
],
"id": "string",
"ids": [
"set",
"string"
],
"name": "string"
}
],
"value": {
"arns": [
"<redacted>"
],
"id": "a_name",
"ids": [
"us-east-1_<redacted>"
],
"name": "a_name"
}
}
As you can see, the ids portion is a set of type string. I decided to try converting ids to a list to see if I could then access the 0 index and it worked. I feel like this could be a terraform bug, but I haven't filed an issue yet.
In my ARM template I have a variable called "subnets" which can be of 3 types.
If it is of typeA then I want 4 subnets of the given names and addresses; if it's typeB then 2 subnets, and so on.
"variables": {
"subnets" : {
"typeA" : {
"network" : "3.0/24",
"directory" : "5.0/24",
"documents" : "8.0/24",
"security" : "10.0/24",
},
"typeB" : {
"directory" : "10.0/24",
"database" : "11.0/24",
},
"dmz" : {
"directory" : "12.0/24",
"database" : "15.0/24", }
}
}
In the ARM template I have a parameter which tells me what type to use. So I have a segment like the below which uses a condition to match on the subnetType being typeA and creates a virtual network accordingly.
{
"type": "Microsoft.Network/virtualNetworks",
"condition" : "[contains(parameters('subnetType'), 'typeA')]",
"apiVersion": "2018-10-01",
...
"copy" : [ {
"name" : "subnets",
"count" : "[length(array(variables('subnets').typeA))]",
"input": {
"name": "...",
"properties": {
"addressPrefix": "..."
}
}
} ]
}
}
As you can see above, I have a copy block within this VirtualNetwork resource, and I want to create the various subnets for the typeA network. I figure I could convert subnets.typeA to an array and get the length of it to loop over (that's the idea, I don't know if it actually works) but I am not clear how to extract the subnet name and addressprefix from my variable above.
so there are 2 issues here:
no way to loop object keys in arm templates
use of different resources in the template to create subnets
there is no way to work around the first limitation that I know of, whereas the second limitation is mostly due to you trying to work around the first one. I'd go for a completely different approach:
"networks": [
{
"name": "typeA",
"subnets": [
{
"name": "network",
"addressSpace": "3.0/24"
},
{
"name": "directory",
"addressSpace": "5.0/24"
},
{
"name": "documents",
"addressSpace": "8.0/24"
},
{
"name": "security",
"addressSpace": "10.0/24"
}
]
},
{
// second virtual network
},
{
// x virtual network
}
]
the main downside here - you'd have to have a nested deployment, because you cannot actually iterate array inside array, so you'd have to feed each object inside array into a deployment that would create a virtual network that can contain various subnets.
You can consult this link for an example of this exact approach or the official Azure Building Blocks thingie way of doing this (which is quite similar in the approach, but the implementation is different).
You could, get away with different resources instead of iterations, but that means you are less flexible and each time you make a change to the input everything breaks or just doesnt work like you think it would (your way of doing this would fall apart if dmz doesnt exist in that variable, you'll get a compilation error, similarly if you add another key to the object, say applicationgateway it will work, but that virtual network won't get created)
I have created a role (commonrole) and applied to multiple nodes.
Now I want to override one of the attributes on 1 particular node to change to a different value.
So , created 1 more role (noderole) and applied that role after "commonrole "to this node but my node does not picks the new value (-Xmx2048m as mentioned below).
Sample common role-
{
"name": "commonrole",
"description": "Manages all nodes",
"run_list": [
"recipe[abc]"
],
"default_attributes": {
"catalina_opts": [
"-Dfile.encoding=UTF-8"
]
}
Sample noderole-
{
"name": "noderole",
"description": "Manages particular node",
"run_list": [
"role[commonrole]"
],
"default_attributes": {
"catalina_opts": [
"-Dfile.encoding=UTF-8",
"-Xmx2048m"
]
}
}
Am I missing something?
Arrays in node attributes are kind of weird. I've got a full write up on my site but basically this should result in the merged value being:
[
"-Dfile.encoding=UTF-8",
"-Dfile.encoding=UTF-8",
"-Xmx2048m"
]
or something similar. Also remember you won't see the attribute change immediately in the knife node show output, only after a successful converge.