Reference arn of a Elasticache cluster in cloudformation - amazon-web-services

I would like to reference the arn of a "going-to-be-created" Redis ElastiCache cluster in a cloud formation template.
This is the ElasticacheCluster template (tested and working in cloudFormation)
ElasticacheCluster:
Type: AWS::ElastiCache::CacheCluster
Properties:
AutoMinorVersionUpgrade: 'true'
Engine: redis
CacheNodeType: cache.t2.micro
NumCacheNodes: '1'
VpcSecurityGroupIds:
- Fn::GetAtt:
- ElastiCacheSecurityGroup
- GroupId
CacheSubnetGroupName:
Ref: ElastiCacheSubnetGroup
I cut on the other staff like subnetgroup and security group because it is also not relevant here. I should grant access to the Cluster to another resource with an Access Policies here what I was trying:
AccessPolicies:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS: "*"
Action: es:*
Resource: !GetAtt ElasticacheCluster.Arn
- Effect: Allow
Principal:
AWS: "*"
Action: es:*
Resource: !GetAtt ElasticacheCluster.Arn
Condition:
IpAddress:
aws:SourceIp: 0.0.0.0/0
I saw this:
the !GetAtt ElasticacheCluster.Arn for the resource entry
here but seems not to be working in this case since !GetAtt is returning a fixed set of attributes and ARN is not one of them (as suggested by #Anton in the comments.
I also saw this other question that could solve the issue but I would prefer a not-harcoded-solution being not dependent on things like region and account id.
The solution to the problem seems to be simple but I am struggling to find a clean answer.

I took inspiration from both the #multiscup answer and the one referenced in the question.
This approach is far from clean and I am waiting for a better answer but at least it is working.
The main idea is to construct the string needed for the arn:
arn:aws:elasticache:region:account-id:cluster:resource-name
To do that I used a join trying to dynamically get the element thanks to the built-in CloudFormation functions:
Resource: !Join
- ':'
- - 'arn:aws:elasticache'
- !Ref 'AWS::Region'
- '<your-account-id>'
- 'cluster'
- !FindInMap [Elasticache, Redis, cluster-name]
I used a Map to define the Redis-cluster because I was using the same value also in other points in the CloudFormation template. Maybe you might find helpful to have the map as well
Mappings:
Elasticache:
Redis:
cluster-name: redis-demo

As Anton mentioned, GetAtt only has certain attributes that you can get. Also, the !Ref will return the logical ID of the resource. Have you thought about trying to use the User and/or the UserGroup resources to accomplish what you want?

Related

how to get Rolename from existing Lambda function on cloud via its friendly name or arn using cloudformation?

I have created a CFN template for rotating secret of RDS. It consist of a AWS HostedLambda which calls for an autogenerated Role. I want to attach another policy to that role.
rRotationLambdaDecryptPolicy:
Type: AWS::IAM::ManagedPolicy
DependsOn: rSecretRotationScheduleHostedRotationLambda
Properties:
Description: "Providing access to HostedLambda for decrypting KMS"
ManagedPolicyName: CustomedHostedLambdaKmsUserRolePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AllowLambdaDecryptKMS
Effect: Allow
Action:
- kms:Decrypt
- kms:CreateGrant
Resource:
- !Sub arn:aws:kms:*:${AWS::AccountId}:key/*
Condition:
ForAnyValue:StringLike:
kms:ResourceAliases: alias/SecretsManager_KMSKey
Roles: <friendly rolename>
Problem is i know the Lambda friendly name and its Arn. Need to find the rolename linked to this lambda so that i can attach the above policy to it(Add its friendly name to Roles).
tried attaching this below
Roles:
Fn::Join:
- ""
- - '"'
- Fn::GetAtt:
- !Sub 'arn:aws:lambda::${AWS::AccountId}:function:SecretsManager-research-creds-rotation-lambda'
- Role
- '"'
P.S.- cant use importvalue here, because the nested stack has been created by AWS and its output doesn't consist of export.
Lambda used:-
rSecretRotationSchedule:
Type: AWS::SecretsManager::RotationSchedule
Properties:
SecretId:<SecretId>
HostedRotationLambda:
KmsKeyArn: <KmsKeyArn>
MasterSecretArn: <MasterSecretArn>
MasterSecretKmsKeyArn: <MasterSecretKmsKeyArn>
RotationType: PostgreSQLMultiUser
RotationLambdaName: SecretsManager-research-creds-rotation-lambda
VpcSecurityGroupIds: <VpcSecurityGroupIds>
VpcSubnetIds: <VpcSubnetIds>
RotationRules:
AutomaticallyAfterDays: 60
Below is the link from which i took reference for template:-
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-secretsmanager-rotationschedule.html
Most likely you can't do this with plain CFN. You would have to develop a custom resource. This would be in the form of a new lambda function which would use AWS SDK to query attributes of your primary lambda function. Once the custom resource finds the role name it would return it to your stack for further use.

How to access cross region resources in Cloudformation

I have a static website stack that I deploy to us-east-1. I only need the s3 bucket to be deployed in the eu-west-1 region, so to achieve this I used Stack Sets like this;
StackSet:
Type: AWS::CloudFormation::StackSet
Properties:
Description: Multiple S3 buckets in multiple regions
PermissionModel: SELF_MANAGED
StackInstancesGroup:
- DeploymentTargets:
Accounts:
- !Ref "AWS::AccountId"
Regions:
- eu-west-1
StackSetName: !Sub "AppBucketStack"
TemplateBody: |
AWSTemplateFormatVersion: 2010-09-09
Description: Create a S3 bucket
Resources:
WebsiteBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
BucketName: !Join
- ''
- - ameta-app-
- !Ref 'AWS::Region'
- '-'
- !Ref 'AWS::AccountId'
AccessControl: Private
CorsConfiguration:
CorsRules:
- AllowedHeaders:
- "*"
AllowedMethods:
- GET
- POST
- PUT
AllowedOrigins:
- "*"
MaxAge: 3600
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: 404.html
Tags:
- Key: Company
WebsiteBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref 'WebsiteBucket'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- s3:GetObject
Effect: Allow
Resource: !Join
- ''
- - 'arn:aws:s3:::'
- !Ref 'WebsiteBucket'
- /*
Principal:
CanonicalUser: !GetAtt OriginAccessIdentity.S3CanonicalUserId
However now I need to address the bucket's domain name(!GetAtt WebsiteBucket.DomainName) in cloudfront which is being deployed in us-east-1. It seems that I can't use the output of the StackSet since the resources are different regions.
Do you guys have any suggestions?
It seems that I can't use the output of the StackSet since the resources are different regions.
That's correct. You can't reference outputs across regions nor accounts. CloudFormation (CFN) is region-specific. The easiest way is to deploy your resources in us-east-1 and the pass their outputs as parameters to the second stack in different region. You can do it manually, or automatically using AWS CLI or SDK from your local workstation or ec2 instance.
But if want to keep everything within CFN, you would have to develop a custom resource for the second stack. The resource would be in the form of a lambda function which would use AWS SDK to get the outputs from us-east-1 and pass them to your stack in different region.

How to migrate sns and sqs using cloudformation?

In my aws account, there is having a 250+ SNS and SQS i need to migrate one region to another region using cloudformation, any one can help to write a script using yaml
Resources:
T1:
Type: 'AWS::SNS::Topic'
Properties: {}
Q1:
Type: 'AWS::SQS::Queue'
Properties: {}
Q1P:
Type: 'AWS::SQS::QueuePolicy'
Properties:
Queues:
- !Ref Q1
PolicyDocument:
Id: AllowIncomingAccess
Statement:
-
Effect: Allow
Principal:
AWS:
- !Ref AWS::AccountId
Action:
- sqs:SendMessage
- sqs:ReceiveMessage
Resource:
- !GetAtt Q1.Arn
-
Effect: Allow
Principal: '*'
Action:
- sqs:SendMessage
Resource:
- !GetAtt Q1.Arn
T1SUB:
Type: 'AWS::SNS::Subscription'
Properties:
Protocol: sqs
Endpoint: !GetAtt Q1.Arn
TopicArn: !Ref T1
You can try using Former2 which is an open-sourced tool to:
Former2 allows you to generate Infrastructure-as-Code outputs from your existing resources within your AWS account. By making the relevant calls using the AWS JavaScript SDK, Former2 will scan across your infrastructure and present you with the list of resources for you to choose which to generate outputs for.

How to add a new statement to an existing SQS QueuePolicy from another template?

I have 2 services with different Cloudformation YAML templates and want to add another policy to a queue policy defined in producer service (to allow consumer to receive and delete messages). However, my current solution simply overrides the existing policy instead of appending it (i. e., only consumer service role remains in the policy).
This is Cloudformation template SQS part for producer:
ProducerQueuePolicy:
Type: AWS::SQS::QueuePolicy
Properties:
PolicyDocument:
Version: '2012-10-17'
Id: SQSPolicy
Statement:
- Effect: Allow
Principal:
AWS:
- !GetAtt ServiceRole.Arn
Resource: !GetAtt ProducerQueue.Arn
Action:
- 'sqs:DeleteMessage'
- 'sqs:ReceiveMessage'
- 'sqs:ListQueues'
- 'sqs:SendMessage'
- 'sqs:GetQueueUrl'
- 'sqs:GetQueueAttributes'
Queues: [ !Ref ProducerQueue ]
ProducerDeadLetterQueue:
Type: 'AWS::SQS::Queue'
Properties:
QueueName: !Sub "${AWS::StackName}-ProducerDLQ.fifo"
FifoQueue: true
ContentBasedDeduplication: false
ProducerQueue:
Type: 'AWS::SQS::Queue'
Properties:
QueueName: !Sub "${AWS::StackName}-ProducerQueue.fifo"
FifoQueue: true
MessageRetentionPeriod: 1209600
ContentBasedDeduplication: false
RedrivePolicy:
deadLetterTargetArn: !GetAtt ProducerDeadLetterQueue.Arn
maxReceiveCount: 9
# Outputs -------------------------------------------------------------------------
Outputs:
ProducerQueueUrl:
Value: !Ref ProducerQueue
Export:
Name: ProducerQueueUrl
ProducerQueueArn:
Value: !GetAtt ProducerQueue.Arn
Export:
Name: ProducerQueueArn
And this is Cloudformation for consumer:
#SQS policy configuration for consumer
ProducerQueueConsumptionPolicy:
Type: AWS::SQS::QueuePolicy
Properties:
PolicyDocument:
Version: '2012-10-17'
Id: SQSConsumptionPolicy
Statement:
- Effect: Allow
Principal:
AWS:
- !GetAtt ServiceRole.Arn
Resource:
Fn::ImportValue: ProducerQueueArn
Action:
- 'sqs:DeleteMessage'
- 'sqs:ReceiveMessage'
- 'sqs:GetQueueUrl'
- 'sqs:GetQueueAttributes'
Queues:
- Fn::ImportValue: ProducerQueueUrl
What needs to be done to change this behavior?
If you just want to merge the queue policies, write a cloudformation macro that reads a policy definition from the template and merges it with the existing queue policy. The macro gets given a json version of the template and you can manipulate it how you like in a lambda so you can check for an existing queue policy and update the template appropriately.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-macros.html
An alternative approach is just weaken the queue policy so that most operations are allowed by anything in the account. Then add permissions to whatever is reading and writing to the queue (eg lambdas) through separate roles. So the lambda doing the reading would use one role that allowed reading and the lambda doing the writing would use a different role that allowed writing.
Obviously that might not fit in with how you've arranged you're security so might not be suitable.

Restrict acces to API Gateway endpoint to VPC in cloudformation

I'm trying to limit access to my API Gateway endpoints to requests from my VPC. There are examples of API Gateway Resource Policies, and even a Policy property on the RestApi resource, but I can't figure out how to write a policy that needs the API's ID, when the API hasn't been created yet.
I have an example of my understanding how a stack should look like, based on the AWS documentation:
MyRestApi:
Type: 'AWS::ApiGateway::RestApi'
Properties:
Name: My Great API
Policy:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal: '*'
Action: execute-api:Invoke
Resource:
Fn::Join:
- - ''
- 'arn:aws:execute-api:'
- Ref: region
- ':'
- Ref: accountId
- ':'
- Ref: MyRestApi
- Effect: Deny
Principal: '*'
Action: execute-api:Invoke
Resource:
Fn::Join:
- - ''
- 'arn:aws:execute-api:'
- Ref: Region
- ':'
- Ref: AccountId
- ':'
- Ref: MyRestApi
Condition:
StringNotEquals:
"aws:SourceVpc":
Ref: VpcId
The crux is that I can't reference MyRestApi in the policy when it's still being created. I'm sure I'm not the only one that wants to do this ... I'd rather think this is a common problem, so there is very likely an answer already I haven't found yet.
Thanks for any help,
Stefan
PS: The documentation I used was https://docs.aws.amazon.com/de_de/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-policy and https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-resource-policies-examples.html
According to AWS documentation the policy supports a special syntax for Resource due to this problem.
"Resource": [
"execute-api:/stage/method/path"
]
In the comments, they call it:
// simplified format supported here because apiId is not known yet and partition/region/account can derived at import time