Add Config warning when creating a resource with aliases where slug is missing.
Use new ReconcileAliases function from opslevel-go
[X] List your changes here
[x] Make a changie entry
Tophatting
With this Terraform config:
resource "opslevel_infrastructure" "example" {
aliases = []
schema = "Database"
owner = opslevel_team.example.id
provider_data = {
account = "dev2"
}
data = jsonencode({
name = "my-database"
})
}
resource "opslevel_service" "example" {
name = "foo25"
aliases = []
description = "The test service"
framework = "something fancy"
language = "elixir"
lifecycle_alias = "beta"
tier_alias = "tier_2"
owner = opslevel_team.example.id
api_document_path = "/swagger.json"
preferred_api_document_source = "PULL" //or "PUSH"
}
resource "opslevel_team" "example" {
name = "foo4"
aliases = []
responsibilities = "Responsible for foo frontend and backend"
member {
email = "david+pat@opslevel.com"
role = "contributor"
}
member {
email = "taimoor+pat@opslevel.com"
role = "contributor"
}
member {
email = "kyle+pat@opslevel.com"
role = "manager"
}
}
Create resources with aliases = [], terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# opslevel_infrastructure.example will be created
+ resource "opslevel_infrastructure" "example" {
+ aliases = []
+ data = jsonencode(
{
+ name = "my-database"
}
)
+ id = (known after apply)
+ owner = (known after apply)
+ provider_data = {
+ account = "dev2"
}
+ schema = "Database"
}
# opslevel_service.example will be created
+ resource "opslevel_service" "example" {
+ aliases = []
+ api_document_path = "/swagger.json"
+ description = "The test service"
+ framework = "something fancy"
+ id = (known after apply)
+ language = "elixir"
+ lifecycle_alias = "beta"
+ name = "foo25"
+ owner = (known after apply)
+ preferred_api_document_source = "PULL"
+ tier_alias = "tier_2"
}
# opslevel_team.example will be created
+ resource "opslevel_team" "example" {
+ aliases = []
+ id = (known after apply)
+ name = "foo4"
+ responsibilities = "Responsible for foo frontend and backend"
+ member {
+ email = "david+pat@opslevel.com"
+ role = "contributor"
}
+ member {
+ email = "kyle+pat@opslevel.com"
+ role = "manager"
}
+ member {
+ email = "taimoor+pat@opslevel.com"
+ role = "contributor"
}
}
Plan: 3 to add, 0 to change, 0 to destroy.
opslevel_team.example: Creating...
opslevel_team.example: Creation complete after 0s [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTUxOA]
opslevel_infrastructure.example: Creating...
opslevel_service.example: Creating...
opslevel_infrastructure.example: Creation complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NTUwNTg]
opslevel_service.example: Creation complete after 2s [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU3MjI]
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Destroy resources, terraform destroy ✅
On create, Warn users that aliases are created and how they can work around an infinite apply loop
With config aliases set, but without value in name
resource "opslevel_team" "example" {
name = "foo4"
aliases = ["newnewnew"] # <-- slug is missing here
responsibilities = "Responsible for foo frontend and backend"
member {
email = "david+pat@opslevel.com"
role = "contributor"
}
member {
email = "taimoor+pat@opslevel.com"
role = "contributor"
}
member {
email = "kyle+pat@opslevel.com"
role = "manager"
}
}
Run terraform apply, note missing alias foo4_11
# ... truncated output
Plan: 1 to add, 0 to change, 0 to destroy.
opslevel_team.example: Creating...
╷
│ Warning: opslevel client error
│
│ with opslevel_team.example,
│ on main.tf line 29, in resource "opslevel_team" "example":
│ 29: resource "opslevel_team" "example" {
│
│ warning while reconciling team aliases: '[newnewnew]'
│ OpsLevel API Errors:
│ - 'alias' slug is locked, it cannot be deleted
│
│ OpsLevel API Errors:
│ - 'alias' Alias 'newnewnew' has already been taken
│
╵
╷
│ Warning: Config warning
│
│ with opslevel_team.example,
│ on main.tf line 29, in resource "opslevel_team" "example":
│ 29: resource "opslevel_team" "example" {
│
│ On create, OpsLevel API creates a new alias for teams. If this causes issues, create team with empty 'aliases'. Then update team with 'aliases'
╵
╷
│ Error: Config error
│
│ with opslevel_team.example,
│ on main.tf line 29, in resource "opslevel_team" "example":
│ 29: resource "opslevel_team" "example" {
│
│ default aliases from API need to be added to config: [foo4_11 newnewnew]
Add missing alias, terraform apply again (still fails - needs alias foo4_12 now)
# ... truncated output
│ Warning: Config warning
│
│ with opslevel_team.example,
│ on main.tf line 29, in resource "opslevel_team" "example":
│ 29: resource "opslevel_team" "example" {
│
│ On create, OpsLevel API creates a new alias for teams. If this causes issues, create team with empty 'aliases'. Then update team with 'aliases'
╵
╷
│ Error: Config error
│
│ with opslevel_team.example,
│ on main.tf line 29, in resource "opslevel_team" "example":
│ 29: resource "opslevel_team" "example" {
│
│ default aliases from API need to be added to config: [foo4_12 foo4_11 newnewnew]
Happy path
With this config, note each resource has aliases = []
resource "opslevel_infrastructure" "example" {
aliases = []
schema = "Database"
owner = opslevel_team.example.id
provider_data = {
account = "dev2"
}
data = jsonencode({
name = "my-database"
})
}
resource "opslevel_service" "example" {
name = "foo25"
aliases = []
description = "The test service"
framework = "something fancy"
language = "elixir"
lifecycle_alias = "beta"
tier_alias = "tier_2"
owner = opslevel_team.example.id
api_document_path = "/swagger.json"
preferred_api_document_source = "PULL" //or "PUSH"
}
resource "opslevel_team" "example" {
name = "foo4"
aliases = []
responsibilities = "Responsible for foo frontend and backend"
member {
email = "david+pat@opslevel.com"
role = "contributor"
}
member {
email = "taimoor+pat@opslevel.com"
role = "contributor"
}
member {
email = "kyle+pat@opslevel.com"
role = "manager"
}
}
Create resources, terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# opslevel_infrastructure.example will be created
+ resource "opslevel_infrastructure" "example" {
+ aliases = []
+ data = jsonencode(
{
+ name = "my-database"
}
)
+ id = (known after apply)
+ owner = (known after apply)
+ provider_data = {
+ account = "dev2"
}
+ schema = "Database"
}
# opslevel_service.example will be created
+ resource "opslevel_service" "example" {
+ aliases = []
+ api_document_path = "/swagger.json"
+ description = "The test service"
+ framework = "something fancy"
+ id = (known after apply)
+ language = "elixir"
+ lifecycle_alias = "beta"
+ name = "foo25"
+ owner = (known after apply)
+ preferred_api_document_source = "PULL"
+ tier_alias = "tier_2"
}
# opslevel_team.example will be created
+ resource "opslevel_team" "example" {
+ aliases = []
+ id = (known after apply)
+ name = "foo4"
+ responsibilities = "Responsible for foo frontend and backend"
+ member {
+ email = "david+pat@opslevel.com"
+ role = "contributor"
}
+ member {
+ email = "kyle+pat@opslevel.com"
+ role = "manager"
}
+ member {
+ email = "taimoor+pat@opslevel.com"
+ role = "contributor"
}
}
Plan: 3 to add, 0 to change, 0 to destroy.
opslevel_team.example: Creating...
opslevel_team.example: Creation complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTUzOA]
opslevel_infrastructure.example: Creating...
opslevel_service.example: Creating...
opslevel_infrastructure.example: Creation complete after 0s [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NTY2NTc]
opslevel_service.example: Creation complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU3NDA]
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Update config, aliases are set but slug is missing
resource "opslevel_infrastructure" "example" {
aliases = ["infratest"]
schema = "Database"
owner = opslevel_team.example.id
provider_data = {
account = "dev2"
}
data = jsonencode({
name = "my-database"
})
}
resource "opslevel_service" "example" {
name = "foo25"
aliases = ["servicetest"]
description = "The test service"
framework = "something fancy"
language = "elixir"
lifecycle_alias = "beta"
tier_alias = "tier_2"
owner = opslevel_team.example.id
api_document_path = "/swagger.json"
preferred_api_document_source = "PULL" //or "PUSH"
}
resource "opslevel_team" "example" {
name = "foo4"
aliases = ["teamtest"]
responsibilities = "Responsible for foo frontend and backend"
member {
email = "david+pat@opslevel.com"
role = "contributor"
}
member {
email = "taimoor+pat@opslevel.com"
role = "contributor"
}
member {
email = "kyle+pat@opslevel.com"
role = "manager"
}
}
Attempt to updated resources, needs slugs raised by Config error
# ...truncted output
│ Error: Config error
│
│ with opslevel_team.example,
│ on main.tf line 29, in resource "opslevel_team" "example":
│ 29: resource "opslevel_team" "example" {
│
│ default aliases from API need to be added to config: [foo4_13]
### Add `foo4_13` to `aliases` then `terraform apply`. Infra and Team are created, service has same alias issue
```tf
opslevel_team.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTUzOA]
opslevel_infrastructure.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NTY2NTc]
opslevel_service.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU3NDA]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# opslevel_infrastructure.example will be updated in-place
~ resource "opslevel_infrastructure" "example" {
~ aliases = [
+ "infratest",
]
id = "Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NTY2NTc"
# (4 unchanged attributes hidden)
}
# opslevel_service.example will be updated in-place
~ resource "opslevel_service" "example" {
~ aliases = [
+ "servicetest",
]
id = "Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU3NDA"
name = "foo25"
# (8 unchanged attributes hidden)
}
# opslevel_team.example will be updated in-place
~ resource "opslevel_team" "example" {
+ aliases = [
+ "foo4_13",
+ "teamtest",
]
id = "Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTUzOA"
name = "foo4"
# (1 unchanged attribute hidden)
+ member {
+ email = "david+pat@opslevel.com"
+ role = "contributor"
}
+ member {
+ email = "kyle+pat@opslevel.com"
+ role = "manager"
}
+ member {
+ email = "taimoor+pat@opslevel.com"
+ role = "contributor"
}
}
Plan: 0 to add, 3 to change, 0 to destroy.
opslevel_team.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTUzOA]
opslevel_team.example: Modifications complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTUzOA]
opslevel_infrastructure.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NTY2NTc]
opslevel_service.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU3NDA]
opslevel_infrastructure.example: Modifications complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NTY2NTc]
╷
│ Warning: opslevel client error
│
│ with opslevel_service.example,
│ on main.tf line 13, in resource "opslevel_service" "example":
│ 13: resource "opslevel_service" "example" {
│
│ Unable to reconcile service aliases: '[servicetest]'
│ OpsLevel API Errors:
│ - 'alias' slug is locked, it cannot be deleted
│
╵
╷
│ Error: Config error
│
│ with opslevel_service.example,
│ on main.tf line 13, in resource "opslevel_service" "example":
│ 13: resource "opslevel_service" "example" {
│
│ default aliases from API need to be added to config: [foo25]
Fix service alias, aliases = ["servicetest", "foo25"], terraform apply
opslevel_team.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTUzOA]
opslevel_infrastructure.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NTY2NTc]
opslevel_service.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU3NDA]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# opslevel_service.example will be updated in-place
~ resource "opslevel_service" "example" {
~ aliases = [
+ "foo25",
+ "servicetest",
]
id = "Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU3NDA"
name = "foo25"
# (8 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
opslevel_service.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU3NDA]
opslevel_service.example: Modifications complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU3NDA]
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
Unset aliases (aliases = null) for all resources then terraform apply
opslevel_team.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTUzOA]
opslevel_service.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU3NDA]
opslevel_infrastructure.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NTY2NTc]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# opslevel_infrastructure.example will be updated in-place
~ resource "opslevel_infrastructure" "example" {
- aliases = [
- "infratest",
] -> null
id = "Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NTY2NTc"
# (4 unchanged attributes hidden)
}
# opslevel_service.example will be updated in-place
~ resource "opslevel_service" "example" {
- aliases = [
- "foo25",
- "servicetest",
] -> null
id = "Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU3NDA"
name = "foo25"
# (8 unchanged attributes hidden)
}
# opslevel_team.example will be updated in-place
~ resource "opslevel_team" "example" {
- aliases = [
- "foo4_13",
- "teamtest",
] -> null
id = "Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTUzOA"
name = "foo4"
# (1 unchanged attribute hidden)
# (3 unchanged blocks hidden)
}
Plan: 0 to add, 3 to change, 0 to destroy.
opslevel_team.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTUzOA]
opslevel_team.example: Modifications complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTUzOA]
opslevel_infrastructure.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NTY2NTc]
opslevel_service.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU3NDA]
opslevel_infrastructure.example: Modifications complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NTY2NTc]
opslevel_service.example: Modifications complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU3NDA]
╷
│ Warning: opslevel client error
│
│ with opslevel_team.example,
│ on main.tf line 29, in resource "opslevel_team" "example":
│ 29: resource "opslevel_team" "example" {
│
│ warning while reconciling team aliases: '[]'
│ OpsLevel API Errors:
│ - 'alias' slug is locked, it cannot be deleted
│
╵
Apply complete! Resources: 0 added, 3 changed, 0 destroyed.
Issues
Combine reconcileAliases
Changelog
Add
Config warning
when creating a resource withaliases
where slug is missing. Use new ReconcileAliases function from opslevel-gochangie
entryTophatting
With this Terraform config:
Create resources with
aliases = []
,terraform apply
Destroy resources,
terraform destroy
✅On create, Warn users that aliases are created and how they can work around an infinite apply loop
With config
aliases
set, but without value inname
Run
terraform apply
, note missing aliasfoo4_11
Add missing alias,
terraform apply
again (still fails - needs aliasfoo4_12
now)Happy path
With this config, note each resource has
aliases = []
Create resources,
terraform apply
Update config, aliases are set but slug is missing
Attempt to updated resources, needs slugs raised by
Config error
Fix service alias,
aliases = ["servicetest", "foo25"]
,terraform apply
Unset aliases (
aliases = null
) for all resources thenterraform apply
terraform destroy
✅