Serverless cfn resource references not resolving - aws-cloudformation

I'm new to serverless, and trying to create a deployment using the Serverless 3 framework. I currently have some resources defined with references to other cfn resources that I've defined in my template, i.e.:
WebsiteBucketPolicy:
Type: AWS::S3::BucketPolicy
DependsOn:
- CloudFrontOriginAccessIdentity
- WebsiteBucket
Properties:
Bucket: WebsiteBucket
PolicyDocument:
Statement:
- Sid: PublicReadGetObject
Effect: Allow
Principal:
AWS:
Fn::Join: [ ' ', [ 'arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity', CloudFrontOriginAccessIdentity ] ]
Action:
- s3:GetObject
Resource:
- Fn::Join: [ '', [ 'arn:aws:s3:::', WebsiteBucket, '/*' ] ]
However, I keep getting cfn errors when I deploy as it seems that the references to these resources aren't resolving. I tried looking at the serverless-state.json output file to troubleshoot, and I see them defined as follows, for example:
"WebsiteBucketPolicy": {
"Type": "AWS::S3::BucketPolicy",
"DependsOn": [
"CloudFrontOriginAccessIdentity",
"WebsiteBucket"
],
"Properties": {
"Bucket": "WebsiteBucket",
"PolicyDocument": {
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": {
"AWS": {
"Fn::Join": [
" ",
[
"arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity",
"CloudFrontOriginAccessIdentity"
]
]
}
},
"Action": [
"s3:GetObject"
],
"Resource": [
{
"Fn::Join": [
"",
[
"arn:aws:s3:::",
"WebsiteBucket",
"/*"
]
]
}
]
}
]
}
}
},
Am I referencing these incorrectly in my template?

To reference the resource correctly you need to use the Ref function:
Bucket: !Ref WebsiteBucket
Two more notes regarding your template:
You can omit the DependsOn section in this case as you are referencing all the resources you are waiting for. When using Ref, CloudFormation automatically creates the resources in the correct order (in this case it is Bucket and Access Identity first, Bucket Policy last)
Instead of using Join for combining strings and parameters, you can also use the Sub function which returns referenced values written like ${WebsiteBucket}, just as Ref/GetAtt does.
Template:
WebsiteBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref WebsiteBucket
PolicyDocument:
Statement:
- Sid: PublicReadGetObject
Effect: Allow
Principal:
AWS:
Fn::Join: [ ' ', [ 'arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity', !Ref CloudFrontOriginAccessIdentity ] ]
Action:
- s3:GetObject
Resource:
- !Sub arn:aws:s3:::${WebsiteBucket}/*
# OR:
# - Fn::Join: [ '', [ 'arn:aws:s3:::', !Ref WebsiteBucket, '/*' ] ]

Related

Cloudformation to covert string into list on apigateway policy document with condition

Below is the my cloudformation template. I want to convert aws:SourceVpc into list in the resource policy document. I tried with spilt but cft below error.
Invalid policy document. Please check the policy syntax and ensure that Principals are valid. (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: xxxxx; Proxy: null)
Also devl and sdbx have 1 value where as others acnt and acpt have two values in mappings how can do it. Please suggest.
Output should like below.
aws:SourceVpc: ["vpc-7830jkd", "vpc-a1236"]
Mappings:
vpcid:
us-east-1:
sdbx: "vpc-1234"
devl: "vpc-2345"
acpt: "vpc-7830jkd,vpc-a1236"
us-east-2:
acnt: "vpc-a1236,vpc-7830jkd"
Parameters:
Env:
Type: String
Resources:
apigateway:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: mygateway
EndpointConfiguration:
Types:
- "PRIVATE"
Policy: !Sub
- |-
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": [
"execute-api:/*"
]
},
{
"Effect": "Deny",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": [
"execute-api:/*"
],
"Condition": {
"StringNotEquals": {
"aws:SourceVpc": !Split ["," , "${myappid}"]
}
}
}
]
}
- { myappid: !FindInMap [ vpcid, !Ref "AWS::Region", !Ref "Env" ] }
Output should like below. aws:SourceVpc: ["vpc-7830jkd", "vpc-a1236"]
Sadly, you can't do this. CFN is very limited in what it can do, and what you want is simply not achievable with plain CFN. You have to modify your Maps and provide all the vpc ids individually, not mix lists with individual values.
The other way would be through development of CFN macro or custom resource.

AWS Backupvault SNS notification

I have created a backup plan and configured SNS using cloud formation ( not CLI ), but backup job has running and completed successfully but not receiving emails notification ( already subscribe to the SNS topic ).
Backup vault section in cloudformation :
BackupVaultWithDailyBackupssns:
Type: "AWS::Backup::BackupVault"
Properties:
BackupVaultName: "Vault_name"
Notifications:
BackupVaultEvents:
- BACKUP_JOB_STARTED
- BACKUP_JOB_COMPLETED
- BACKUP_JOB_SUCCESSFUL
- BACKUP_JOB_FAILED
SNSTopicArn:
!Sub 'arn:aws:sns:${AWS::Region}:${AWS::AccountId}:My_topic'
SNS Policy :
{
"Version": "2008-10-17",
"Id": "__default_policy_ID",
"Statement": [
{
"Sid": "__default_statement_ID",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": [
"SNS:GetTopicAttributes",
"SNS:SetTopicAttributes",
"SNS:AddPermission",
"SNS:RemovePermission",
"SNS:DeleteTopic",
"SNS:Subscribe",
"SNS:ListSubscriptionsByTopic",
"SNS:Publish",
"SNS:Receive"
],
"Resource": "arn:aws:sns:us-east-1:111111111111:My_topic",
"Condition": {
"StringEquals": {
"AWS:SourceOwner": "111111111"
}
}
},
{
"Sid": "My-statement-id",
"Effect": "Allow",
"Principal": {
"Service": "backup.amazonaws.com"
},
"Action": "SNS:Publish",
"Resource": "arn:aws:sns:us-east-1:111111111111:My_topic"
}
]
}
For anyone looking for an answer on this here I how I set mine up, this is working for all failed notifications.
Set up your vault referencing a topic
BackupVault:
Type: "AWS::Backup::BackupVault"
Properties:
BackupVaultName: Backup Vault Name
Notifications:
BackupVaultEvents:
- "BACKUP_JOB_EXPIRED"
- "BACKUP_JOB_FAILED"
SNSTopicArn: !Ref FailedBackupTopic
DependsOn:
- FailedBackupTopic
Set up a topic, topic policy and topic subscription
FailedBackupTopic:
Type: AWS::SNS::Topic
Properties:
DisplayName: "AWS Backup - Failed backup notification"
FailedBackupTopicPolicy:
Type: 'AWS::SNS::TopicPolicy'
Properties:
PolicyDocument:
Id: SNSPolicyV1
Version: '2012-10-17'
Statement:
- Sid: Sid1
Effect: Allow
Principal:
Service:
- 'backup.amazonaws.com'
Action: 'sns:Publish'
Resource: !Ref FailedBackupTopic
Topics:
- !Ref FailedBackupTopic
FailedBackupTopicSubscription:
Type: 'AWS::SNS::Subscription'
Properties:
Endpoint: "email#email.com"
Protocol: email
TopicArn: !Ref FailedBackupTopic

Policy to limit subnets or hosted zone CodeStar can use

Is there an IAM policy that can be created/attached to CodeStarWorker-*-CloudFormation that limits either the Subnets or HostedZoneIds the CodeStar worker can use?
Here's an example template.yml:
Resources:
# other resources
DevAlb:
Properties:
LoadBalancerAttributes: []
Name: !Sub '${ProjectId}-dev-alb'
Scheme: internal
SecurityGroups:
- !Ref AlbSecurityGroup
Subnets:
- !ImportValue PrivateSubnet1
- !ImportValue PrivateSubnet2
Tags:
- Key: Name
Value: !Sub '${ProjectId}-dev'
Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
DevAlbDns:
Properties:
AliasTarget:
DNSName: !GetAtt
- AlbDev
- DNSName
HostedZoneId: !GetAtt
- AlbDev
- CanonicalHostedZoneID
HostedZoneId: !ImportValue InternalDomainDotCom
Name: !Sub '${ProjectId}.internal-domain.com'
Type: A
Type: 'AWS::Route53::RecordSet'
I don't want users with CodeStar access to import/use anything that would allow public internet access (without admin approval, anyway). How can I prevent someone from setting/importing PublicSubnet1 and PublicSubnet2 as one of the Subnets? Or prevent them from setting/import PublicDomainDotCom as the HostedZoneId?
I was able to do this by attaching the following policy to CodeStarWorker-app-CloudFormation!
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"route53:GetChange",
"route53:GetHostedZone",
"route53:ListHostedZones",
"route53:ListHostedZonesByName",
"route53:ListResourceRecordSets",
"route53:GetHostedZoneCount",
"route53domains:*"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"route53:ChangeResourceRecordSets",
"route53:ListResourceRecordSets",
"apigateway:GET"
],
"Resource": [
"arn:aws:route53:::hostedzone/REPLACE_WITH_HOSTED_ZONE_ID",
"arn:aws:apigateway:*::/domainnames"
]
}
]
}
This will only allow CodeStar's CloudFormation role to create a Route 53 record set in the hosted zone ID an admin has allowed.
I'm sure there are other ways to protect your infrastructure and data from bad actors with CodeStar roles. Feel free to share if you have any ideas (e.g., limiting EC2 VPCs/Subnets).

Create S3 only user

I am trying to create a S3 only user who will by definition have no access to any other resource. The user can upload and download files from S3. I have created a basic template that can be found here...
https://github.com/shantanuo/aws-cloudformation-templates/blob/master/aws/services/IAM/IAM_Users_Groups_and_Policies.yaml
But it is allowing access to cloudformation that is not necessary in my case. I have read the following pages, but do not know how to include them in my template.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-user.html
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
https://docs.aws.amazon.com/AWSCloudForation/latest/UserGuide/aws-properties-iam-group.html
What are the minimum parameters required to create a S3 only user?
Update:
Here is the cloudformation code that I tried and the error that I got:
error message: The following resource(s) failed to create: [CFNRole, CFNUser]. . Rollback requested by user.
The template:
Parameters:
NewUsername:
NoEcho: 'false'
Type: String
Description: New account username
MinLength: '1'
MaxLength: '41'
ConstraintDescription: the username must be between 1 and 41 characters
Password:
NoEcho: 'true'
Type: String
Description: New account password
MinLength: '1'
MaxLength: '41'
ConstraintDescription: the password must be between 1 and 41 characters
Resources:
CFNUser:
Type: AWS::IAM::User
Properties:
LoginProfile:
Password: !Ref 'Password'
UserName : !Ref 'NewUsername'
CFNRole:
Type: AWS::IAM::Role
Properties :
PermissionsBoundary : arn:aws:iam::aws:policy/AmazonS3FullAccess
RoleName : 'myPermissionBoundary'
CFNKeys:
Type: AWS::IAM::AccessKey
Properties:
UserName: !Ref 'CFNUser'
Outputs:
AccessKey:
Value: !Ref 'CFNKeys'
Description: AWSAccessKeyId of new user
SecretKey:
Value: !GetAtt [CFNKeys, SecretAccessKey]
Description: AWSSecretAccessKey of new user
I have checked this link, but not sure how to include the policy in my current template.
https://aws.amazon.com/blogs/security/easier-way-to-control-access-to-aws-regions-using-iam-policies/
You are explicit giving your new user access to Cloudformation in your template.
You have this section:
CFNUserPolicies:
Type: AWS::IAM::Policy
Properties:
PolicyName: CFNUsers
PolicyDocument:
Statement:
- Effect: Allow
Action: ['cloudformation:Describe*', 'cloudformation:List*', 'cloudformation:Get*']
Resource: '*'
Groups: [!Ref 'CFNUserGroup']
CFNAdminPolicies:
Type: AWS::IAM::Policy
Properties:
PolicyName: CFNAdmins
PolicyDocument:
Statement:
- Effect: Allow
Action: cloudformation:*
Resource: '*'
Groups: [!Ref 'CFNAdminGroup']
In which your allow statements specifically provide access to Cloudformation. If you are trying to give access to s3 then why are you giving access to Cloudformation?
If you want a user to have access to s3 only, and going further one specific s3 bucket then your policy would look something like this (note this is in json):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetBucketLocation",
"s3:ListAllMyBuckets"
],
"Resource": "arn:aws:s3:::*"
},
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::YOUR-BUCKET",
"arn:aws:s3:::YOUR-BUCKET/*"
]
}
]
}
If you want your user to have access to all buckets, then your policy would look more like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1557048549844",
"Action": "s3:*",
"Effect": "Allow",
"Resource": "*"
}
]
}
See this source: https://objectivefs.com/howto/how-to-restrict-s3-bucket-policy-to-only-one-aws-s3-bucket

How to define an ECR Lifecycle Policy with CloudFormation

In order to limit the number of images in a repository, I'd like to define a Lifecycle policy. Since all the stack is defined with CloudFormation, I'd like to define this policy too.
For example, my policy could be "keep only the most recent 8 images, no matter if tagged or not".
The solution was pretty easy, but since I could not find any example or similar questions (ECR is not mainstream, I know), let me post here the easy solution that I found, which simply requires to insert the policy as JSON into the CloudFormation definition:
MyRepository:
Type: AWS::ECR::Repository
Properties:
LifecyclePolicy:
LifecyclePolicyText: |
{
"rules": [
{
"rulePriority": 1,
"description": "Only keep 8 images",
"selection": {
"tagStatus": "any",
"countType": "imageCountMoreThan",
"countNumber": 8
},
"action": { "type": "expire" }
}]
}
Of course this is very simplistic, but it's the starting point that I was looking for
You can also define a reference to your PolicyText and later on your parameters.json stringify your policy.
It would look like something like this:
template.yml
Parameters:
lifecyclePolicyText:
Description: Lifecycle policy content (JSON), the policy content the pre-fixes for the microservices and the kind of policy (CountMoreThan).
Type: String
repositoryName:
Description: ECR Repository Name to which we will apply the lifecycle policies.
Type: String
registryId:
Description: AWS account identification number (12 digits)
Type: String
Default: xxxxx
Resources:
Repository:
Type: AWS::ECR::Repository
Properties:
LifecyclePolicy:
LifecyclePolicyText: !Ref lifecyclePolicyText
RegistryId: !Ref registryId
RepositoryName: !Ref repositoryName
Outputs:
Arn:
Value: !GetAtt Repository.Arn
parameters.json
[
{
"ParameterKey": "lifecyclePolicyText",
"ParameterValue": "{'rules':[{'rulePriority':1,'description':'Only keep 8 images','selection':{'tagStatus':'any','countType':'imageCountMoreThan','countNumber':8},'action':{'type':'expire'}}]}"
},
{
"ParameterKey": "repositoryName",
"ParameterValue": "xxxx"
}
]
| will allow you to add text inline.
AWSTemplateFormatVersion: "2010-09-09"
Resources:
ECRRepo:
Type: AWS::ECR::Repository
Properties:
RepositoryName: "images"
LifecyclePolicy:
LifecyclePolicyText: |
{
"rules": [
{
"rulePriority": 2,
"description": "Keep only one untagged image, expire all others",
"selection": {
"tagStatus": "untagged",
"countType": "imageCountMoreThan",
"countNumber": 1
},
"action": {
"type": "expire"
}
}
]
}