Modify Cloud Infrastructure
Learn how to modify cloud infrastructure resources deployed by Webiny.
- how to modify cloud infrastructure resources deployed by Webiny
Overview
Users use thewebiny deploy
command to deploy their Webiny project, into an environment of their choice. During the deployment, apart from building the actual project applications, the command also ensures that a set of required cloud infrastructure resources is deployed.And although the cloud infrastructure resources that Webiny deploys are already configured in the best possible manner, there are still cases where some modifications might be needed. In some cases even, the deployed cloud infrastructure needs to be expanded by introducing additional resources into the mix.
To do this, we rely on four webiny.application.ts
configuration files, located in each of the four project application folders in every Webiny project:
- Core (
apps/core/webiny.application.ts
- API (
apps/api/webiny.application.ts
- Admin (
apps/admin/webiny.application.ts
- Website (
apps/website/webiny.application.ts
Which webiny.application.ts
config file needs to be changed depends on the nature of the change that needs to be made.
For example, let’s imagine we want to adjust the configuration of the Amazon S3 bucket that’s deployed for storing files uploaded via Webiny File Manager. Since this bucket is deployed as part of the Core project application, naturally, we’d want to apply changes via the apps/core/webiny.application.ts
configuration file.
The following example demonstrates how the bucket can be adjusted to enable object versioning , which essentially enables keeping multiple variants of an object in the same bucket:
import * as aws from "@pulumi/aws";
import { createCoreApp } from "@webiny/serverless-cms-aws";
export default createCoreApp({
// By passing a callback function via the `pulumi` parameter, we gain the
// ability to modify project application's cloud infrastructure resources.
pulumi: app => {
// Cloud infrastructure resources can be referenced via the `app.resources`
// object. We then use resources' `config` objects to apply modifications.
const { fileManagerBucket } = app.resources;
fileManagerBucket.config.versioning({ enabled: true });
}
});
As we can see, using the pulumi
parameter, we’ve passed a relatively simple callback function that enables object versioning in two steps:
- references the relevant bucket via the
fileManagerBucket
const - uses the reference to enable bucket versioning, via the
fileManagerBucket.config.versioning
Once the above code change has been made, all that is left to do is a redeploy by running the following command:
# Make sure to replace {env} with the actual name of the environment.
yarn webiny deploy apps/core --env {env}
With the deployment successfully completed, the object versioning should be enabled for your project (for all files that are uploaded via Webiny File Manager).
When redeploying, make sure to redeploy the project application within which the changes were actually made. In the above example, we’ve made changes within the Core application’s webiny.application.ts
file, meaning the Core application needs to be redeployed in order to actually see the changes.
Additional Examples
In this section, we cover a couple of additional examples of modifying cloud infrastructure resources that get deployed with every Webiny project.
Tagging Cloud Infrastructure Resources
The following example shows how to apply tags to all cloud infrastructure resources deployed as part of the Core project application. Note that the same approach can be used for all four applications.
import * as aws from "@pulumi/aws";
import { createCoreApp } from "@webiny/serverless-cms-aws";
import { tagResources } from "@webiny/pulumi-aws";
export default createCoreApp({
pulumi: app => {
// We are assigning Owner and Contact tags, whose values
// are read from runtime environment variables.
tagResources({
Owner: String(process.env["OWNER"]),
Contact: String(process.env["CONTACT"])
});
}
});
Modifying AWS IAM Roles
AWS Lambda - Default GraphQL API Function
The following example shows how you can modify the default AWS IAM role that’s assigned to the AWS Lambda function that represents your default GraphQL API (the one accessed via the https://xyz.cloudfront.net/graphql
URL).
import * as aws from "@pulumi/aws";
import { createApiApp } from "@webiny/serverless-cms-aws";
export default createApiApp({
pulumiResourceNamePrefix: "wby-",
pulumi: ({ resources }) => {
const policy = new aws.iam.Policy("ses-policy", {
description: "This policy enables access to Amazon SES.",
policy: {
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Action: ["ses:SendEmail"],
Resource: [
"arn:aws:ses:*:*:identity/*"
]
}
]
}
});
new aws.iam.RolePolicyAttachment("graphql-role-ses-policy-attachment", {
role: resources.graphql.role.output.name,
policyArn: policy.arn
});
}
});
AWS Lambda - Headless CMS GraphQL API Function
The following example shows how you can modify the default AWS IAM role that’s assigned to the AWS Lambda function that represents your Headless CMS GraphQL API (the one accessed via the https://xyz.cloudfront.net/cms/{type}/{locale}
URL).
When compared to the example above, the only difference is that here we’re targeting the resources.headlessCms.role
role with the aws.iam.RolePolicyAttachment
, instead of resources.graphql.role
role.
import * as aws from "@pulumi/aws";
import { createApiApp } from "@webiny/serverless-cms-aws";
export default createApiApp({
pulumiResourceNamePrefix: "wby-",
pulumi: ({ resources }) => {
const policy = new aws.iam.Policy("ses-policy", {
description: "This policy enables access to Amazon SES.",
policy: {
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Action: ["ses:SendEmail"],
Resource: [
"arn:aws:ses:*:*:identity/*"
]
}
]
}
});
new aws.iam.RolePolicyAttachment("graphql-role-ses-policy-attachment", {
role: resources.headlessCms.role.output.name,
policyArn: policy.arn
});
}
});
Adding AWS Lambda Functions
Creating a Cron Job
In this example, we introduce a new AWS Lambda function which is triggered once every minute.
Note that the code assumes the AWS Lambda function’s code is located in the apps/api/myCronJob
folder. For the full code, please check our webiny-examples GitHub repository.
import * as path from "path";
import * as aws from "@pulumi/aws";
import * as pulumi from "@pulumi/pulumi";
import { createApiApp } from "@webiny/serverless-cms-aws";
export default createApiApp({
pulumiResourceNamePrefix: "wby-",
pulumi({ paths }) {
const role = new aws.iam.Role("my-cron-job-fn-role", {
description: "My cron job Lambda function role.",
assumeRolePolicy: {
Version: "2012-10-17",
Statement: [
{
Action: "sts:AssumeRole",
Principal: {
Service: "lambda.amazonaws.com"
},
Effect: "Allow"
}
]
}
});
new aws.iam.RolePolicyAttachment(`my-cron-job-fn-role-basic-execution-policy-attachment`, {
role,
policyArn: aws.iam.ManagedPolicy.AWSLambdaBasicExecutionRole
});
const policy = new aws.iam.Policy("my-cron-job-fn-policy", {
description: "My cron job Lambda function policy.",
policy: {
Version: "2012-10-17",
Statement: [
{
// Not recommended. Always use least-privilege principle.
Effect: "Allow",
Action: ["*"],
Resource: ["*"]
}
]
}
});
new aws.iam.RolePolicyAttachment("my-cron-job-fn-role-policy-attachment", {
role: role.name,
policyArn: policy.arn
});
const simpleCronJobFunction = new aws.lambda.Function("my-cron-job-fn", {
runtime: "nodejs14.x",
handler: "handler.handler",
description: "A simple Lambda function that logs time (executed in my cron job).",
role: role.arn,
timeout: 30,
memorySize: 512,
code: new pulumi.asset.AssetArchive({
".": new pulumi.asset.FileArchive(
path.join(paths.workspace, "myCronJob/build")
)
})
});
const eventRule = new aws.cloudwatch.EventRule("my-cron-job-fn-event-rule", {
description: `My cron job rule.`,
scheduleExpression: "rate(1 minute)",
isEnabled: true
});
new aws.lambda.Permission("my-cron-job-fn-events-permission", {
action: "lambda:InvokeFunction",
function: simpleCronJobFunction.arn,
principal: "events.amazonaws.com",
sourceArn: eventRule.arn
});
new aws.cloudwatch.EventTarget("my-cron-job-fn-event-target", {
rule: eventRule.name,
arn: simpleCronJobFunction.arn
});
}
});
Adjusting Amazon Elasticsearch (OpenSearch) Configuration
Amazon Elasticsearch (OpenSearch) is deployed as part of the Core project application. In order to make changes to it, we need to make changes in the apps/core/webiny.application.ts
configuration file.
import * as aws from "@pulumi/aws";
import { createCoreApp } from "@webiny/serverless-cms-aws";
import { isResourceOfType } from "@webiny/pulumi";
export default createCoreApp({
elasticSearch: true,
pulumi: app => {
app.onResource(resource => {
if (isResourceOfType(resource, aws.elasticsearch.Domain)) {
// Set the instance type.
resource.config.clusterConfig(() => {
return {
instanceType: "t3.small.elasticsearch"
};
});
// Set Elasticsearch (OpenSearch) version.
resource.config.elasticsearchVersion("7.7");
// Change advanced options.
resource.config.advancedOptions({
override_main_response_version: "false",
"rest.action.multi.allow_explicit_index": "true"
});
resource.opts.ignoreChanges = ["advancedOptions", "tags"];
}
});
}
});