Intro
The Scenario
Thanks to the success of Infrastructure-as-Code tools, more organizations are allowing their developers to deploy infrastructure resources themselves. Examples of these resources could be AWS S3 buckets for storage, containers for their applications, serverless functions, and anything else needed. While this paradigm shift can reduce the overall load on the infrastructure developers, you must make considerations when shifting that load.
- Productivity waste due to context switching.
- Security risks due to expanded access to infrastructure.
- Reliability and stability risks due to misconfigurations.
Developing a robust self-service infrastructure platform using Resourcely's Blueprints and Guardrails can address all of these concerns with proper planning.
Let's examine a simple AWS S3 deployment to see how Resourcely's features can empower your developers to deploy their infrastructure securely.
Prerequisites
To integrate Resourcely into your deployment pipeline, you'll need to configure a few things. In this example, I'll use a GitHub Actions Pipeline.
GitHub Repository
You'll need to configure a GitHub repository and provide access to Resourcely. If you haven't already done this, you can find more information here: https://docs.resourcely.io/integrate/source-code-management/github.
Terraform code
Start with some basic Terraform code. We'll add more to this, but this is to verify the workflow in the subsequent steps works:
main.tf
data "aws_region" "current" {}
output "current_region" {
value = data.aws_region.current.name
}
Terraform backend
You'll need a backend configured for Terraform. You can use any backend you wish, such as one using S3 and DynamoDB:
versions.tf
terraform {
required_version = "~> 1.9.5"
backend "s3" {
bucket = "resourcely-tf-backend"
key = "terraform.tfstate"
region = "us-east-1"
dynamodb_table = "ResourcelyTerraformLocks"
}
}
GitHub Actions Workflow
You'll then need to configure a GitHub Actions Workflow that authenticates to AWS (I prefer OIDC), deploy Terraform, and allow Resourcely to analyze the state. You can find more information on configuring your workflow and the necessary role here: https://docs.resourcely.io/integrate/terraform-integration/github-actions/local-plan/aws-with-openid-connect
An example of the workflow is below. Ensure you set the role-to-assume
secret:
.github/workflows/main.yml
name: Plan and Apply Terraform
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
permissions:
id-token: write
contents: read
jobs:
terraform:
name: 'Terraform'
runs-on: ubuntu-latest
environment: production
defaults:
run:
shell: bash
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
aws-region: ${{ vars.AWS_REGION }}
- name: Checkout
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
- name: Terraform Init
run: terraform init
- name: Terraform Plan
run: terraform plan -out=plan.raw
- name: Convert the plan to JSON
id: planToJson
run: terraform show -json plan.raw
- name: Save JSON to a file
using: fishcharlie/CmdToFile@v1.0.0
with:
data: ${{ steps.planToJson.outputs.stdout }}
output: plan.json
- name: Upload Terraform Plan Output
uses: actions/upload-artifact@v4
with:
name: plan-file
path: plan.json
- name: Terraform Apply
if: GitHub.ref == 'refs/heads/main' && github.event_name == 'push'
run: terraform apply -auto-approve -input=false
resourcely-ci:
Needs: terraform
if: GitHub.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download Terraform Plan Output
uses: actions/download-artifact@v4
with:
name: plan-file
path: tf-plan-files/
- name: Resourcely CI
uses: Resourcely-Inc/resourcely-action@v1
with:
resourcely_api_token: ${{ secrets.RESOURCELY_API_TOKEN }}
resourcely_api_host: "https://api.resourcely.io"
tf_plan_directory: "tf-plan-files"
Once everything is in place and you've tested the workflow, let's start building with Resourcely!
Blueprints
What are blueprints?
Resourcely's Blueprints provide a practical way to simplify and standardize cloud resource deployment using customizable templates that generate Terraform configurations. With Blueprints, teams can create consistent, secure, and compliant infrastructure setups, making the deployment process more efficient and improving collaboration across projects.
Configure blueprints
In this tutorial, we'll import an existing module to start our Blueprint. We'll use Resourcely's "Foundry" to author the Blueprint.
Choose the S3 Bucket
option. That should add code similar to the one below to your console:
---
constants:
__name: "{{ bucket }}_{{ __guid }}"
---
resource "aws_s3_bucket" "{{ __name }}" {
bucket = "{{ bucket }}"
}
resource "aws_s3_bucket_public_access_block" "{{ __name }}" {
bucket = aws_s3_bucket.{{ __name }}.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_ownership_controls" "{{ __name }}" {
bucket = aws_s3_bucket.{{ __name }}.id
rule {
object_ownership = "BucketOwnerEnforced"
}
}
resource "aws_s3_bucket_versioning" "{{ __name }}" {
bucket = aws_s3_bucket.{{ __name }}.id
versioning_configuration {
status = "{{ versioning_configuration_status }}"
}
}
The templating syntax helps ensure each deployment's bucket names and resource IDs are unique. The "constants" section is similar to a "locals" block in HCL. In this case, it generates a unique name by appending the user-defined bucket to the system-defined "GUID."
Developer Experience
When a user deploys a Blueprint, they're not required to modify any code directly. Instead, Resourcely presents users with a clean UI that allows them to define the template's variables easily. To define the bucket in this example, click the "Developer Experience" tab. Resourcely presents you with a text input field for the Bucket
and a dropdown for the "Versioning configuration status":
Once you define these items, you can preview the Terraform code that will be created by clicking on the "Terraform" tab:
The GUID of the deployment is appended to all resource IDs, enabling the resources to be deployed multiple times without creating overlapping bucket names. For more information about authoring Blueprints and available variables, see here.
Modifying the Blueprint
Blueprints are straightforward to modify manually, but some very cool features simplify the process of maximizing the developer-friendliness of your blueprints. One of these quality-of-life features is the ability to generate "tags" dynamically based on value type. If you highlight an attribute of one of the resources, you can click the "Use Selection" dropdown followed by the "Generate tag" option to generate a tag. You can also simply right-click the attribute to do the same. In the following image, I select the block_public_acls
attribute and generate a tag for it:
Once you've done this, Resourcely creates a new variable. Feel free to edit this as you wish:
The "Developer Experience" tab now has the new boolean added:
While this is a limited example, the ability to create a simple developer experience from your even complicated Terraform code could not be more effortless. Typically, you won't parameterize every single attribute in your code as that would not make for a great experience, so we'll add some guardrails to ensure no one makes changes that could cause your deployment to become non-compliant.
Guardrails
What are guardrails?
Resourcely's Guardrails are infrastructure policies seamlessly integrated into developer workflows. They allow you to define rules enforced within your existing CI pipeline, ensuring compliance during deployment. Guardrails can be customized to consider specific contexts and route any Terraform configurations that violate these rules for approval. By integrating Guardrails with Blueprints, developers receive immediate feedback and guidance during configuration, promoting secure and efficient deployments.
The "Really" Policy Language
You create guardrails using the innovative "Really" policy language. If you've used other policy languages, such as Rego, you'll find that the Really policy language is a fantastic alternative. I won't dive too deep into the differences since Travis McPeak already has this fantastic post: Announcing Really. The Really policy language is one of the first things that drew me to Resourcely. I've written countless lines of Rego in my Open Policy Agent policies, and I would have loved to cut those lines down significantly with Really's more concise syntax. Check out the Really docs here.
Add guardrails
To author a guardrail, it's easiest to begin in the Foundry. Click on the "Author a Guardrail" tab, "Select a Guardrail starter" from the dropdown menu, and choose "[S3] Bucket Versioning Enabled." Once you've done that, you will see the generated Really policy-as-code language:
You can choose an approver to authorize overrides for the Guardrail. If you haven't created any, "default" will work just fine. Once you set this, click "Create Guardrail" at the top left of the screen, decide if you want this Guardrail to be "Active," "Inactive," or "Evaluate Only," then click "Yes, create Guardrail," choose "Use Guardrail in Blueprint" on the following screen, and this Guardrail is ready to go!
Create A Custom Guardrail
Creating a Guardrail from a starter is a great way to get things kicked off, but there certainly won't be a starter Guardrail for every attribute you need to enforce. Let's create a custom Guardrail that will enforce the "object_ownership" attribute within the "aws_s3_ownership_controls" resource to be enforced to "BucketOwnerEnforced":
resource "aws_s3_bucket_ownership_controls" "{{ __name }}" {
bucket = aws_s3_bucket.{{ __name }}.id
rule {
object_ownership = "BucketOwnerEnforced"
}
}
First, select the "object_ownership" attribute within the resource, click on the "Use Selection" dropdown, and select "Generate Guardrail."
Voila! That simple action generated everything needed to enforce that "object_ownership" setting. Feel free to change it if you so desire, but otherwise, click on "Define Metadata" and complete the fields:
Once that's finished, click the "Create Guardrail" button again and choose "Use Guardrail in Blueprint" to add it to the list of usable Guardrails.
Managing Guardrail Attachments
Once you create the Guardrails and set them to "active," they're automatically attached to the current Blueprint. If you need to add or remove a Guardrail from a Blueprint, you must edit the Blueprint in Foundry. To disable a Guardrail, simply toggle the slider next to the Guardrail, and it will disappear from the Guardrail list:
Final Touches
Now we've created the Blueprint, generated custom tags, and created Guardrails, let's finalize the process by defining metadata:
Finally click, "Create Blueprint" at the top right and publish the Blueprint. If you're satisfied with everything, choose "Use Blueprint in a Pull Request," and we'll deploy!
Deployment
Admin Deployment
As long as you configure your repository information correctly, you should be able to pass in the information requested without any issues:
You'll notice that the "AWS s3 bucket public access block name block public acls" (I know, I could have been more succinct there) option is "true" and can be toggled to "false." You also may notice that "Versioning configuration status" has no option to change. The Guardrails are live and already benefiting your users. Instead of discovering after you open the PR, you immediately know what your Guardrails allow you to do.
Go ahead and click "Continue." You may see existing code if you've pushed code before using Resourcely. Scroll down, and you'll see any additional code in green:
If everything looks good, go ahead and use the button at the top right to Open the pull request, fill in the information, and submit! A status page will greet you.
If the Resourcely action is successful in GitHub, the PR should be Approved!
Dev Deployment
Before we close this guide, let's examine the developer experience by deploying our new S3 Blueprint through the eyes of "Jane Dev."
Jane will see a resources page similar to what you just saw, but much more limited. The developer only has the tools they need to deploy.
She'll click on the same "Create Pull Request" button at the top right:
Follow the steps, and you'll see the same process as before. Open the pull request on the final step, and the PR should be opened, GitHub will run the pipeline, Resourcely will then approve the PR if everything goes well:
Merge the PR, and Jane Dev's bucket is deployed with all settings enforced:
Conclusion
This tutorial covered several ways Resourcely makes life easy for Infrastructure-as-Code developers who need resources they can deploy effortlessly. Simple niceties such as tag generation make it quick and easy to pass in attribute values that weren't previously a variable. Guardrails ensure that you can enforce any attribute. With Blueprints tying everything together, I don't know of another product that makes it easier to deploy compliant resources quickly. Be sure to check out Resourcely with an insanely generous free tier: https://portal.resourcely.io
I hope you enjoyed this tutorial as much as I enjoyed writing it! Until next time, cheers!
Top comments (0)