AWS CloudFormation conditions - aws-cloudformation

I have two conditions in my CloudFormation template which are used to choose which resource to deploy based on environment. However when referencing the resource later in the template CloudFormation throws "Unresolved resource dependencies" error. Any idea whats wrong?
Template is shortened for readability. It is working fine without conditions.
Parameters:
EnvironmentName:
Type: String
Conditions:
IsProduction: !Equals [!Ref EnvironmentName, production]
IsNotProduction: !Not [!Equals [!Ref EnvironmentName, production]]
Resources:
Lambda:
Type: AWS::Serverless::Function
Condition: IsNotProduction
Lambda:
Type: AWS::Serverless::Function
Condition: IsProduction
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${Lambda}

So since the template is shortened, it's not that easy to tell what causes this error.
One thing that is problematic though, is that both of your Lambdas are called "Lambda". That won't work, you need to call them differently in your resources section.

Related

Cloudformation Template - IAM Roles and Lambda Resource

I want to create a cloudformation stackset with resources like IAM and lambda in different regions. when I tried to deploy these resources, it failed because IAM roles are global and it is trying to create again in second region and whole stackset is failed.
Is there anyway I can mention the stackset to deploy GLobal Resources in one region and resources like lambda in all other regions?
Is there anyway I can mention the stackset to deploy GLobal Resources in one region and resources like lambda in all other regions?
Sadly there is not. You have to split your template, so that global resource are created as normal regional stacks.
I went through many resources and finally found a solution. If we split the template in stacksets then my dependent resources will break because creation is parallel in cloudformation. i.e. before global role gets created, lambda will try to get deployed and it will fail because the role is not available(required by lambda).
Hence we can add a condition to each of the global resources like below
Conditions:
RegionCheck: !Equals
- !Ref "AWS::Region"
- us-east-1
And, add the condition in the resources section as below,
Resources:
GlobalRolelambda:
Type: 'AWS::IAM::Role'
Condition: RegionCheck
Properties:
RoleName: !Ref LambdaExecutionRole
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/ReadOnlyAccess'
- 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
Path: /
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- 'sts:AssumeRole'
Policies:
- PolicyName: lambda-policy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'glue:GetConnections'
- 'mediastore:ListContainers'
- 'mediastore:GetContainerPolicy'
Resource: '*'
But, after doing this, the problem would still exist because, if you add lambda resource with depends on attribute, role would get created in one region but not in the second region, lambda will fail to create in second region. We need to add a wait condition in the template to handle this as below Conditions:
CreateLambdaRole: !Equals [ !Ref LambdaRoleName, 'false' ]
CreateLamdaRoleRegion: !And
- !Condition RegionCheck
- !Condition CreateLambdaRole
and, add below resources after Role Resource,
CreateRoleWaitHandle:
Condition: CreateLamdaRoleRegion
DependsOn: GlobalRolelambda
Type: "AWS::CloudFormation::WaitConditionHandle"
#added, since DependsOn: !If is not possible, trigger by WaitCondition if CreateLamdaRoleRegion is false
WaitHandle:
Type: "AWS::CloudFormation::WaitConditionHandle"
#added, since DependsOn: !If is not possible
WaitCondition:
Type: "AWS::CloudFormation::WaitCondition"
Properties:
Handle: !If [CreateLamdaRoleRegion, !Ref CreateRoleWaitHandle, !Ref WaitHandle]
Timeout: "1"
Count: 0
and now, refer this in lambda resource,
lambdaProcessorFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: Lambda-processor
Description: ''
Handler: index.handler
Role:
Fn::Sub: 'arn:aws:iam::${AWS::AccountId}:role/LambdaExecutionRole'
Runtime: python3.6
Timeout: 600
MemorySize: 1024
Code:
S3Bucket: !Ref SourceBucketName
S3Key: !Ref SourceBucketKey
DependsOn: WaitCondition
Refer to the below source links, which might help
https://garbe.io/blog/2017/07/17/cloudformation-hacks/
CloudFormation, apply Condition on DependsOn

What is the format to specify an external EDGE Gateway ApiId in CloudFormation templates?

I'm trying to create or update a stack with the following CloudFormation Template:
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
ApiGatewayId:
Type: String
ApiLayerArn:
Type: String
JarLocation:
Type: String
Resources:
Function:
Type: 'AWS::Lambda::Function'
Properties:
Handler: net.bitsandpaper.api.kiosk.PlatformChecker
Runtime: java11
Code:
S3Bucket: bnp-build-artifacts
S3Key: !Ref JarLocation
Description: ''
MemorySize: 128
Timeout: 5
Role: arn:aws:iam::479832603967:role/bnp-api-lambda-execution-role
Layers:
- !Ref ApiLayerArn
ApiIntegration:
Type: AWS::ApiGatewayV2::Integration
Properties:
ApiId: !Ref ApiGatewayId
IntegrationType: AWS_PROXY
IntegrationUri: !Join
- ''
- - 'arn:'
- !Ref 'AWS::Partition'
- ':apigateway:'
- !Ref 'AWS::Region'
- ':lambda:path/2015-03-31/functions/'
- !Ref Function
- /invocations
TimeoutInMillis: 6000
ApiRoute:
Type: AWS::ApiGatewayV2::Route
Properties:
ApiId: !Ref ApiGatewayId
RouteKey: 'GET /kiosk/platform-check'
Target: !Join
- /
- - integrations
- !Ref ApiIntegration
The parameters are correctly passed by an external file, they look good in the Web Console, notably parameter ApiGatewayId has value 8548rqrsm5. Yet during deployment I have a CREATE_FAILED for ApiIntegration, with the message:
Invalid API identifier specified 479832603967:8548rqrsm5 (Service:
AmazonApiGatewayV2; Status Code: 404; Error Code: NotFoundException;
Request ID: 84918a83-cf9d-48d2-acf7-18d9d2e4d330; Proxy: null)
The API is an EDGE Rest API, in the same region than the CloudFormation stack. The ID is retrieved by the CLI with aws apigateway get-rest-apis.
Am I missing something in the ApiId format? The litterature is very scarce when not referencing an API in the same stack...
AWS::ApiGatewayV2 is only for WEBSOCKTES and HTTP types. From docs:
The API protocol. Valid values are WEBSOCKET or HTTP.
But since you are writing about Edge-optimized (not supported by HTTP api) it seems to that you are using REST API, rather then HTTP API. So you should be using AWS::ApiGateway resources, not AWS::ApiGatewayV2.
It seem's like the AWS::ApiGatewayV2::Route is created before the AWS::ApiGatewayV2::Integration. So When it trying to refer ApiIntegration it is not yet created.
So you should try to use DependsOn attribute.
With the DependsOn attribute you can specify that the creation of a
specific resource follows another. When you add a DependsOn attribute
to a resource, that resource is created only after the creation of the
resource specified in the DependsOn attribute.
Try this below CloudFormation code:
ApiRoute:
Type: AWS::ApiGatewayV2::Route
DependsOn: ApiIntegration
Properties:
ApiId: !Ref ApiGatewayId
RouteKey: 'GET /kiosk/platform-check'
Target: !Join
- /
- - integrations
- !Ref ApiIntegration
I hope this will help you out to resolve your problem.
Link: DependsOn Attribute UserGuide

How to call a resource from one yaml template to another yaml template using cloudformation

I need some guidance on cloudformation templates.
I have a stack called test1.yaml, there i created an IAM role called S3Role.
Now I have another stack called test2.yaml, there i created a managed policy to attach to existing iam role.
Now i want to call test1.yml file S3Role in test2.yml file of managed policy.
Can anyone help me with the script?
Can anyone help me with the script.
Obviously due to lack of details in your question, its not possible to provide any script. But I can provide general psudo-code.
test1.yaml
You will have to export the S3Role Arn or Name
Resources:
S3Role:
Type: IAM::ROLE
<rest of role definition>
Outputs:
RoleArn:
Value: !GetAtt S3Role.Arn
Exports:
Name: RoleArn
test2.yml
You will have to import the role exported Arn (or name) from test1.yaml:
Resources:
SomeResouce:
Properties:
Role: !ImportValue RoleArn
Hope this helps.
You need export the role from stack 1 and then import it in stack 2
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-exports.html
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-importvalue.html
Providing the complete script for cross-referencing an AWS resource in CloudFormation template.
test1.yaml has an IAM role (logical ID: IAMRole) which we export through the Outputs block. Also notice that the indentation of Outputs block is same as that of Resources block.
The Outputs block serves many purposes. From the AWS Documentation
The optional Outputs section declares output values that you can
import into other stacks (to create cross-stack references), return in
response (to describe stack calls), or view on the AWS CloudFormation
console.
test1.yaml
Resources:
IAMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Sid: TrustPolicy
Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonS3FullAccess
Path: /
RoleName: IAMRole
Outputs:
ExportIAMRole:
Description: Export the IAMRole to use in test2.yaml
Value: !Ref IAMRole
Export:
Name: IAMRole
In test2.yaml we import the value by referencing the name we have given under Export in Outputs block.
test2.yaml
Resources:
IAMPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: IAMPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- kms:ListAliases
- kms:Encrypt
- kms:Decrypt
Resource: "*"
Roles:
- !ImportValue IAMRole

Cannot access restApiId & restApiRootResourceId for cross stack reference in serverless yml

Since I had an issue of 200 resource error, I found a way of using cross stack reference by dividing into different services. I managed to do that by using the cross-stack reference. The issue is I cannot give the restApiId & restApiRootResourceId dynamically. Right now, am statically setting ids into the service-2.
Basically the service-1 looks like,
provider:
name: aws
runtime: nodejs8.10
apiGateway:
restApiId:
Ref: ApiGatewayRestApi
restApiResources:
Fn::GetAtt:
- ApiGatewayRestApi
- RootResourceId
custom:
stage: "${opt:stage, self:provider.stage}"
resources:
Resources:
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: ${self:service}-${self:custom.stage}-1
Outputs:
ApiGatewayRestApiId:
Value:
Ref: ApiGatewayRestApi
Export:
Name: ApiGatewayRestApi-restApiId
ApiGatewayRestApiRootResourceId:
Value:
Fn::GetAtt:
- ApiGatewayRestApi
- RootResourceId
Export:
Name: ApiGatewayRestApi-rootResourceId
And the service-2 looks like this,
provider:
name: aws
runtime: nodejs8.10
apiGateway-shared:
restApiId:
'Fn::ImportValue': ApiGatewayRestApi-restApiId
restApiRootResourceId:
'Fn::ImportValue': ApiGatewayRestApi-rootResourceId
As the above service-2 config, I cannot reference the Ids.
FYI: Both services are in different files.
So How what's wrong with this approach?
Serverless has special syntax on how to access stack output variables: {cf:stackName.outputKey}.
Note that using the Fn::ImportValue would work inside the resources section.

Restrict a CFT to an Account

How can I restrict a Cloudformation template to a particular account. Can I restrict or have the Cloudformation to validate the AWS Account number with the one that is hard coded? Are there any way?
You could create a CloudFormation Condition that checks the value of AWS::AccountId.
Then, reference the condition from each resource so that the resource is only created if the Condition is true.
Does this work?
Parameters:
AccNo:
Type: Number
Description: Enter the number to be validated
Conditions:
Number: !Equals [ !Ref AccNo, AWS::AccountId ]
Resources:
S3Bucket:
Condition: Number
Type: AWS::S3::Bucket
DeletionPolicy: Retain