Terraform Digitalocean: move resources under project - terraform

I want to create N droplets on DigitalOcean and assign them to a DigitalOcean project (that is not yet existing).
First I'm creating a project and I assign the droplets to the project using the resources item. I'm also creating the two droplets.
resource "digitalocean_project" "project" {
name = "playground"
count = "2"
description = "Description"
purpose = "Description purposes"
environment = "Development"
resources = [
digitalocean_droplet.myserver[count.index].urn
]
}
resource "digitalocean_droplet" "myserver" {
count = "2"
name = "server-${count.index}"
image = "ubuntu-18-04-x64"
size = "1gb"
region = "${var.region}"
}
The droplets are created successfully. One droplet is moved to the newly created project, while the other droplet remains in my default project.
The error message is below is clear. It tries to create a second project with the same name.
Error: Error creating Project: POST
https://api.digitalocean.com/v2/projects: 409 name is already in use
(duplicate)
on create_server.tf line 1, in resource "digitalocean_project"
"project": 1: resource "digitalocean_project" "project" {
How can I assign the two droplets to my project (which I want to create dynamically)?

If you want one project with multiple resources then you need to only create a single project and assign the list of resources to it. To do this you'll want to remove the count parameter from the digitalocean_project resource (this would create multiple projects) and then use the splat expression of the digitalocean_droplet resources to pass a list of the resources to the project.
So you want something that looks a little like this:
resource "digitalocean_project" "project" {
name = "playground"
description = "Description"
purpose = "Description purposes"
environment = "Development"
resources = digitalocean_droplet.myserver[*].urn
}
resource "digitalocean_droplet" "myserver" {
count = "2"
name = "server-${count.index}"
image = "ubuntu-18-04-x64"
size = "1gb"
region = var.region
}

Related

I have 2 or more instances starting with an identical name, I want to run terraform script for all those

resource "google_compute_instance_iam_binding" "vm-access" {
project = var.project_id
instance_name=
role = "roles/compute.osLogin"
members = ["group", ]
condition {
title = "vm-access"
description = "allows read-only access to fmis instances only"
expression = "resource.type == \"compute.googleapis.com/Instance\" && resource.name.startsWith(\"instance\")"
}
}
I want that instance_name to be filled in such a way that, I can apply this to all vms starting with the name instance.
You can use for_each to apply this to all VM.
Details available to below link
https://developer.hashicorp.com/terraform/language/meta-arguments/for_each

Provisioning Teams in GitHub using Terraform - team hierarchy

I am attempting to use the github terraform provider to simplify how we manage teams in our GitHub Organization. To reduce the amount of changes to the Terraform code I wanted to use a solution like https://learn.hashicorp.com/tutorials/terraform/github-user-teams where contributors would only need to change values in a csv file.
The example does not include a solution for having teams be members of teams. Looking at the provider documentation https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team, it requires parent_team_id be provided.
My current approach is to have two CSV files, one with parent team name and one without, and build a local mapping. The issue is that when I apply I get the error
The "for_each" value depends on resource attributes that cannot be determined
until apply, so Terraform cannot predict how many instances will be created.
To work around this, use the -target argument to first apply only the
resources that the for_each depends on.
Can someone point out where I am going wrong, it will be something simple but I cannot see what.
NOTE: I am aware (after posting) that I am not actually using the parent_team_id in the second team resource, thats a simple fix once I get the local working.
Resources
resource "github_team" "l0" {
for_each = {
for team in csvdecode(file("teams.csv")) :
team.name => team
}
name = each.value.name
description = each.value.description
privacy = each.value.privacy
create_default_maintainer = true
}
resource "github_team" "l1" {
for_each = {
for team in local.l1_mapping : team.name => team
}
name = each.value.name
description = each.value.description
privacy = each.value.privacy
create_default_maintainer = true
depends_on = [
github_team.l0
]
}
Locals
# Create local values to retrieve items from CSVs
locals {
l1_team_plus = {
for file2 in csvdecode(file("l1_teams.csv")) :
file2.name => file2
}
l1_mapping = flatten([
for team2 in local.l1_team_plus : [
for t in github_team.l0 : {
id = t.id
slug = t.slug
team2 = team2
} if t.slug == team2.name
]
])
}

Editing Application Insight Terraform Resource Name

I have this code in TF for creating App Insight Resource.
The name for creating the App Insight is being done by our internal names module.
The output of the name for App Insight is of the format -->
(Resource Group Name)-(Your own custom value)-dev-cus-001
Now we have 3 resources for App Insight with 3 custom values defined in the Locals block as shown below:
locals {
application-insights = [
"masterdata",
"inboundintegration",
"intgr-func",
]
}
module "names-application-insights" {
version = "~> 1.0"
source = "My Module URL"
for_each = toset(local.application-insights)
resource_type = "application-insights"
resource_location = var.resource_group_location
resource_environment = var.resource_group_environment
resource_name = format("%s-%s", var.resource_group_name, each.value)
resource_count = var.resource_group_count
}
resource "azurerm_application_insights" "default" {
for_each = toset(local.application-insights)
location = data.azurerm_resource_group.default.location
resource_group_name = data.azurerm_resource_group.default.name
name = module.names-application-insights[each.value].results
application_type = "web"
}
Now i have been asked to remove the custom value "masterdata" from the name of the first App Insight resource : (rg)-masterdata-dev-cus-001
So how do i remove the "masterdata" value from just one App Insights and ensure the other two App Insights that are created with two Locals : "inboundintegration" & "intgr-func" are not affected
If i remove the first custom value "masterdata" from the Locals block, it will throw error right if i run the code after removing that value from Locals?
How to edit this resource name then.
As I see you use for_each to create the application insights. Then the only thing you can do is to remove the custom value masterdata from the Locals and it won't affect the other two resources. It means you will only create two application insights with the names "inboundintegration" & "intgr-func".

How to use shared_image plan data like publisher, offer and sku in virtual_machine resource in Terraform

Using Terraform v0.12.9, azurerm provider v1.36.1
Scenario:
You have created an image based on a third-party marketplace image.
You have published this image to a shared image gallery.
You have been able to do so in an automated fashion using Terraform and Azure Devops pipelines. (ex. CIS hardened images)
For each of these previous steps you have provided plan information data. if not, you would not be able to create a VM from this image, because there is an extra cost associated with using the marketplace image.
Now you want other teams be able to use your image from the Shared Image Gallery through Terraform Automation.
How to get the plan information from the shared image without hard-coding it?
Well, I thought to just retreive the data using a data source using:
data "azurerm_shared_image" "image" {
name = var.image_name
gallery_name = var.gallery_name
resource_group_name = var.rsg_name
}
and using the necessary blocks inside the virtual_machine resource like so:
storage_image_reference {
id = data.azurerm_shared_image.image.id
}
plan {
name = data.azurerm_shared_image.image.sku
publisher = data.azurerm_shared_image.image.publisher
product = data.azurerm_shared_image.image.offer
}
However, I get an error stating:
Error: Unsupported attribute
This object has no argument, nested block, or exported attribute named "sku".
for each sku, publisher and offer.
We can get a better picture of what we are dealing with to add an output for testing purposes, like so:
output "imagedata" {
value = data.azurerm_shared_image.image
}
We get an important insight:
imagedata = {
"description" = ""
"eula" = ""
"gallery_name" = [removed]
"id" = [removed]
"identifier" = [
{
"offer" = "cis-centos-7-l1"
"publisher" = "center-for-internet-security-inc"
"sku" = "cis-centos75-l1"
},
]
"location" = [removed]
"name" = [removed]
"os_type" = "Linux"
"privacy_statement_uri" = ""
"release_note_uri" = ""
"resource_group_name" = [removed]
"tags" = {}
}
Ah now using data.azurerm_shared_image.image.identifier.sku won't just work here either. As far as Terraform is concerned, the identifier block is only one element in an array.
Solution:
We retrieve the data still the same way:
data "azurerm_shared_image" "image" {
name = var.image_name
gallery_name = var.gallery_name
resource_group_name = var.rsg_name
}
And we reference it in our virtual_machine resource like so:
storage_image_reference {
id = data.azurerm_shared_image.image.id
}
plan {
name = data.azurerm_shared_image.image.identifier[0].sku
publisher = data.azurerm_shared_image.image.identifier[0].publisher
product = data.azurerm_shared_image.image.identifier[0].offer
}

How to iterate multiple resources over the same list?

New to Terraform here. I'm trying to create multiple projects (in Google Cloud) using Terraform. The problem is I've to execute multiple resources to completely set up a project. I tried count, but how can I tie multiple resources sequentially using count? Here are the following resources I need to execute per project:
Create project using resource "google_project"
Enable API service using resource "google_project_service"
Attach the service project to a host project using resource "google_compute_shared_vpc_service_project" (I'm using shared VPC)
This works if I want to create a single project. But, if I pass a list of projects as input, how can I execute all the above resources for each project in that list sequentially?
Eg.
Input
project_list=["proj-1","proj-2"]
Execute the following sequentially:
resource "google-project" for "proj-1"
resource "google_project_service" for "proj-1"
resource "google_compute_shared_vpc_service_project" for "proj-1"
resource "google-project" for "proj-2"
resource "google_project_service" for "proj-2"
resource "google_compute_shared_vpc_service_project" for "proj-2"
I'm using Terraform version 0.11 which does not support for loops
In Terraform, you can accomplish this using count and the two interpolation functions, element() and length().
First, you'll give your module an input variable:
variable "project_list" {
type = "list"
}
Then, you'll have something like:
resource "google_project" {
count = "${length(var.project_list)}"
name = "${element(var.project_list, count.index)}"
}
resource "google_project_service" {
count = "${length(var.project_list)}"
name = "${element(var.project_list, count.index)}"
}
resource "google_compute_shared_vpc_service_project" {
count = "${length(var.project_list)}"
name = "${element(var.project_list, count.index)}"
}
And of course you'll have your other configuration in those resource declarations as well.
Note that this pattern is described in Terraform Up and Running, Chapter 5, and there are other examples of using count.index in the docs here.
A small update to this question/answer (terraform 0.13 and above). The count or length is not advisable to use anymore due to the way that terraforms works, let's imagine the next scenario:
Suppose you have an array with 3 elements: project_list=["proj-1","proj-2","proj-3"], once you apply that if you want to delete the "proj-2" item from your array once you run the plan, terraform will modify your second element to "proj-3" instead of removing It from the list (more info in this good post). The solution to get the proper behavior is to use the for_each function as follow:
variable "project_list" {
type = list(string)
}
resource "google_project" {
for_each = toset(var.project_list)
name = each.value
}
resource "google_project_service" {
for_each = toset(var.project_list)
name = each.value
}
resource "google_compute_shared_vpc_service_project" {
for_each = toset(var.project_list)
name = each.value
}
Hope this helps! 👍

Resources