Best Practices Guide for Creating Infrastructure Plugins
In this page, you will find best practices guide for creating Infrastructure Plugins.
When creating Infrastructure Plugins in StackSpot, you should follow some best practices to avoid execution issues and ensure the consistency and security of your Application. See the main guidelines below:
1. Avoid using inputs or Jinja manipulations in resource or module names
Avoid using inputs or Jinja expressions directly in the names of resources or modules in Terraform files.
If you change an input that defines the name of these resources, Terraform may lose track of the resource, resulting in unnecessary destruction and recreation of your Infrastructure.
Not recommended
resource "aws_lambda_function" "{{ lambda_name }}" {
...
}
resource "aws_lambda_function" "stk_{{ lambda_name }}" {
...
}
module "ecr_{{ app_name }}" {
...
}
Recommended
resource "aws_lambda_function" "stackspot_lambda" { // no inputs
filename = "{{ lambda_zip }}"
function_name = "{{ lambda_name }}"
....
}
2. Do not use conditionals, loops, or interpolation in Jinja with Connection Interfaces
When using Connection Interfaces in Terraform templates, do not use Jinja logic (conditionals, loops, interpolation, or filters) to manipulate data from these interfaces. Excessive use of Jinja can cause inconsistencies, make maintenance harder, and limit resource reusability.
Always prefer native Terraform strategies and features to handle variables, lists, and objects from Connection Interfaces.
Not recommended
resource "aws_sqs_queue" "stackspot_queue_deadletter" {
...
{% if "delayed" in connections.father_queue.queue_name %}
delay_seconds = 90
{% else %}
delay_seconds = 0
{% endif %}
}
{% for queue in connections.queues -%}
resource "aws_sns_topic_subscription" "queues_subscription-" {
protocol = "sqs"
raw_message_delivery = true
topic_arn = aws_sns_topic.orders.arn
endpoint = {{ queue.arn }}
}
{% endfor %}
{% for queue in connections.queues -%}
resource "local_file" "appspec_file" {
content = <EOT
{{ aws_codedeploy_appspec_conn.connection.json_spec | from_json | tojson(indent=4) }}
EOT
filename = "${path.module}/deploy-aws/deploys/appspec.json"
}
{% endfor %}
resource "aws_sqs_queue" "stackspot_queue_deadletter" {
name = "{{ connections.father_queue.queue_name }}_deadletter"
}
Recommended
- Use Terraform conditional expressions.
resource "aws_sqs_queue" "stackspot_queue_deadletter" {
...
delay_seconds = strcontains({{ connections.father_queue.queue_name }}, "delayed") ? 90 : 0
}
- Use the Terraform for_each meta-argument.
variable "queues" {
type = list(object({
arn = string
topic_name = string
}))
default = {{ connections.queues }}
}
resource "aws_sns_topic_subscription" "orders_to_process_subscription" {
for_each = var.queues
protocol = "sqs"
raw_message_delivery = true
topic_arn = aws_sns_topic.orders.arn
endpoint = each.value.arn
}
{% for queue in connections.queues -%}
resource "local_file" "appspec_file" {
content = jsonencode(jsondecode({{ aws_codedeploy_appspec_conn.connection.json_spec }}))
filename = "./deploy-aws/deploys/appspec.json"
}
{% endfor %}
resource "aws_sqs_queue" "stackspot_queue_deadletter" {
name = join("_", ["{{ connections.father_queue.queue_name }}", "deadletter"])
}
3. Do not use Jinja with Connection Interfaces outside Terraform (.tf) files
When using Connection Interfaces, avoid interpolating values directly with Jinja in files that are not Terraform (for example, YAML, JSON, etc). When Connection Interfaces are generated within the same Application (by other applied Plugins), their values are resolved at runtime and should be handled as Terraform variables.
Not recommended
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ connection.eks-conn.cluster_name }}
...
Recommended
- Use Terraform itself to resolve these values.
apiVersion: v1
kind: ServiceAccount
metadata:
name: ${ cluster_name }
...
resource "kubernetes_manifest" "manifest" {
manifest = yamldecode(templatefile("./manifest.tpl", {
cluster_name = {{ connection.eks-conn.cluster_name }}
}))
}
4. Do not use Connection Interfaces in tfvars
Do not use Connection Interfaces directly in the tfvars file. This practice can cause inconsistencies and security issues, such as:
- Exposure of sensitive data: Connection Interfaces may contain critical information such as ARNs, bucket names, or other resource identifiers. Storing this data directly in
tfvarsincreases the risk of unnecessary exposure. - Abstraction breakdown: This practice can compromise Plugin modularity, making it harder to reuse and integrate Plugins or Stacks with other components.
5. Be careful with Workflow Deployments
Workflow Deploy applies all Terraform from the Plugins of an Application or Infrastructure simultaneously. This may require more resources and cause issues in some cases. Check the main points of attention:
- Complex infrastructures: An Application or Infrastructure with many Infrastructure Plugins that require high memory usage, caching, or file reading should use more powerful runners to avoid performance issues.
- Provider version conflicts: Using Plugins with different version constraints for the same provider can cause conflicts during Terraform execution.
Example:
In an Infrastructure with both an SQS Plugin and an SNS Plugin applied:
- The SQS Plugin restricts the AWS provider version to
">= 1.2.0, < 2.0.0". - The SNS Plugin restricts the AWS provider version to
">= 4.0.0".
Since there is no version that satisfies both conditions, Terraform will throw an error during initialization.
Solution: Make sure that all provider versions for all Plugins in a Stack are compatible. Also, check the compatibility of additional Plugins in the Workspace.
By following these guidelines, you will ensure greater consistency and security when creating and running Infrastructure Plugins in StackSpot.