Terraform use backend on module - amazon-s3

I need to create optimize the structure of terraform.
Have on root path variables which I imported like module
/variables.tf
variable "aws_profile" { default = "default" }
variable "aws_region" { default = "us-east-1" }
After have a module folder
/ec2_instance/main.tf
module "global_vars" {
source = "../"
}
provider "aws" {
region = module.global_vars.aws_region
profile = module.global_vars.aws_profile
}
terraform {
backend "s3" {
encrypt = true
bucket = "some_bucket"
key = "path_to_statefile/terraform.tfstate"
region = "region"
profile = "profile"
}
}
module "instances_cluster" {
some actions
}
It's working, but I need to move backend and provider part to main.tf on root folder and after include like the module.
How I can do this?
I have tried to create /main.tf on root folder with backend part, but they are not working and backed writing state files locally.

You'd have to a bit of refactoring but these are the steps I would take
Run terraform plan in root and ec2_instance modules to verify zero changes so refactoring can begin
Comment out the backend for ec2_instance/main.tf
Place the backend from ec2_instance/main.tf into root main.tf
In the root main.tf, make a reference to ec2_instance module
Run terraform plan in root module and note the creations and deletions
For each creations and deletion pair, create a terraform state mv statement and run each
Verify the terraform plan has zero changes

Related

How to add a behavior to an existing AWS cloudfront distribution for an API gateway using AWS CDK (typescript preferred)?

I am trying to implement a CDK project that will deploy a static website in an s3 bucket along with a CloudFront distribution. I also have an API gateway that I need to access via the same cloud-front URL. I am able to do this from the AWS Management console. But when I try to implement this using CDK, I am getting circular dependency errors.
const cdn = new cloudfront.Distribution(this, "websitecdn", {
defaultBehavior: {origin: new origins.S3Origin(s3_bucket)}
});
const api = new apigw.RestApi(this, 'someapi', {defaultCorsPreflightOptions: enableCors})
const loginApi = api.root.addResource('login', {defaultCorsPreflightOptions: enableCors})
loginApi.addMethod('POST', new apigw.LambdaIntegration(loginLambda, {
proxy: false,
integrationResponses: [LambdaIntegrationResponses]}),
{
methodResponses: [LambdaMethodResponses]
})
const apiOrigin = new origins.RestApiOrigin(api)
cdn.addBehavior("/prod/*",apiOrigin,{
allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
viewerProtocolPolicy: ViewerProtocolPolicy.HTTPS_ONLY,
})
Everything works fine until I try to add the behavior for the API gateway in the CDN. But when I add that, it starts throwing circular dependency errors.
What I am trying to do using AWS CDK typescript:
deploy a static s3 website
create a CloudFront Distribution for this website -> let's call it cdn_x
deploy backend API (Lambda functions with API Gateway)
Add the API gateway URL as a behavior to cdn_x so that I can use the same URL for API calls as well (I do not have a custom domain)
I was expecting the deployment to go through fine as I was able to go it in the AWS management console (Web UI of AWS). But trying to do the same using AWS CDK throws circular dependency errors.
It is unclear from your example how the stacks and resources in your CDK project are created and related. I'm unable to use your code examples.
In the meantime, I created a TypeScript example using Multiple Behaviors in CloudFront with Amazon API Gateway under the /api/* path and a S3 bucket as default behavior to serve static assets /*
The final CDK structure is the following:
The CDK codebase uses multiple stacks:
cloudfront-stack.ts
rest-api-stack.ts
s3-stack.ts
waf-stack.ts
And resources are passed as references in bin/infra.ts
const app = new cdk.App();
const s3Stack = new S3Stack(app, "S3Stack");
const restApiStack = new RestApiStack(app, "RestApiStack");
const wafStack = new WafStack(app, "WafStack", {
restApi: restApiStack.restApi,
});
const cloudFrontStack = new CloudFrontStack(app, "CloudFrontStack", {
bucketAssets: s3Stack.bucketAssets,
restApi: restApiStack.restApi,
wafCloudFrontAclArn: wafStack.wafCloudFrontAclArn,
wafRestApiOriginVerifyHeader: wafStack.wafRestApiOriginVerifyHeader,
wafRestApiOriginVerifyHeaderValue: wafStack.wafRestApiOriginVerifyHeaderValue,
});
GitHub repository:
https://github.com/oieduardorabelo/cdk-cloudfront-behavior-api-gateway-waf-protection
I trust the example above will clarity some of your questions.

Terraform tfstate s3 not creating

I am trying to set up remote backend for my terraform workflow. My backend block is as follows
terraform {
backend "s3" {
bucket = "terraform-aws-007"
key = "global/bananadev/s3/terraform.tfstate"
region = "eu-west-2"
}
}
enter image description here
Terraform initialization is successful, however the state file is not being created in my s3 bucket but locally.
Any ideas what may be wrong?

public s3 objects with terraform

I've been attempting to recreate an existing infrastructure using Terraform and one of the required services is an S3 bucket which should contain publicly accessible images.
here is the terraform code for the bucket:
resource "aws_s3_bucket" "foo_icons" {
bucket = join("-", [local.prefix, "foo", "icons"])
tags = {
Name = join("-", [local.prefix, "foo", "icons"])
Environment = var.environment
}
}
resource "aws_s3_bucket_acl" "icons_bucket_acl" {
bucket = aws_s3_bucket.foo_icons.id
acl = "public-read"
}
the bucket is populated as follows
resource "aws_s3_object" "icon_repository_files" {
for_each = fileset("../files/icon-repository/", "**")
bucket = aws_s3_bucket.foo_icons.id
key = each.value
source = "../files/icon-repository/${each.value}"
etag = filemd5("../files/icon-repository/${each.value}")
}
The result I can see on the console is that the bucket is in fact publicly accessible, but that each object in the bucket is not public according to the ACL shown. I also can't reach the s3 objects with the displayed url; this results in access denied.
So, I guess the question is what is the best way to create a bucket with publicly accessible objects in Terraform?
Thanks in advance.
PS. I read that ACL is no longer "modern" so if there is a better approach to achieve this, I'd be happy to hear it.

Can I version control CloudFlare configuration?

I am utilizing Cloudflare for a public website. Recently, I have been adjusting many different configuration values via the website/UI. Is there a way to download/upload the configuration so that it can be version-controlled?
You can configure Cloudflare using Terraform. Check out Terraform Cloudflare Provider here.
You can use a tool called cf-terraforming delivered by Cloudflare that allows to download the Cloudflare setup into Terraform.
Here is a quick demo of what it would look like using Terraform:
locals {
domain = "example.com"
hostname = "example.com" # TODO: Varies by environment
}
variable "CLOUDFLARE_ACCOUNT_ID" {}
variable "CLOUDFLARE_API_TOKEN" { sensitive = true }
provider "cloudflare" {
account_id = var.CLOUDFLARE_ACCOUNT_ID
api_token = var.CLOUDFLARE_API_TOKEN
}
resource "cloudflare_zone" "default" {
zone = local.domain
plan = "free"
}
resource "cloudflare_record" "a" {
zone_id = cloudflare_zone.default.id
name = local.hostname
value = "192.0.2.1"
type = "A"
ttl = 1
proxied = true
}
Find a complete example, see Terraform Starter Kit:
infra/dns-zone.tf
infra/dns-records.tf
It works especially great with Terraform Cloud "backend" which provides a free account.

In Ratpack, how can I configure loading configuration from an external file?

I have a Ratpack app written with the Groovy DSL. (Embedded in Java, so not a script.)
I want to load the server's SSL certificates from a config file supplied in the command line options. (The certs will directly embedded in the config, or possibly in a PEM file referenced somewhere in the config.)
For example:
java -jar httpd.jar /etc/app/sslConfig.yml
sslConfig.yml:
---
ssl:
privateKey: file:///etc/app/privateKey.pem
certChain: file:///etc/app/certChain.pem
I seem to have a chicken-and-egg problem using the serverConfig's facilities for reading the config file in order to configure the SslContext later in the serverConfig. The server config isn't created at the point I want to load the SslContext.
To illustrate, the DSL definition I have is something like this:
// SSL Config POJO definition
class SslConfig {
String privateKey
String certChain
SslContext build() { /* ... */ }
}
// ... other declarations here...
Path configPath = Paths.get(args[1]) // get this path from the CLI options
ratpack {
serverConfig {
yaml "/defaultConfig.yaml" // Defaults defined in this resource
yaml configPath // The user-supplied config file
env()
sysProps('genset-server')
require("/ssl", SslConfig) // Map the config to a POJO
ssl sslConfig // HOW DO I GET AN INSTANCE OF that SslConfig POJO HERE?
baseDir BaseDir.find()
}
handlers {
get { // ...
}
}
}
Possibly there is a solution to this (loading the SSL context in a later block?)
Or possibly just a better way to go about the whole thing..?
You could create a separate ConfigDataBuilder to load up a config object to deserialize your ssl config.
Alternatively, you can bind directly to server.ssl. All of the ServerConfig properties bind to the server space within the config.
The solution I am currently using is this, with an addition of a #builder method to SslConfig which returns a SslContextBuilder defined using its other fields.
ratpack {
serverConfig {
// Defaults defined in this resource
yaml RatpackEntryPoint.getResource("/defaultConfig.yaml")
// Optionally load the config path passed via the configFile parameter (if not null)
switch (configPath) {
case ~/.*[.]ya?ml/: yaml configPath; break
case ~/.*[.]json/: json configPath; break
case ~/.*[.]properties/: props configPath; break
}
env()
sysProps('genset-server')
require("/ssl", SslConfig) // Map the config to a POJO
baseDir BaseDir.find()
// This is the important change.
// It apparently needs to come last, because it prevents
// later config directives working without errors
ssl build().getAsConfigObject('/ssl',SslConfig).object.builder().build()
}
handlers {
get { // ...
}
}
}
Essentially this performs an extra build of the ServerConfig in order to redefine the input to the second build, but it works.