AWS Api Gateway attach existing policy - amazon-web-services

Normally if I want to create a private AWS::ApiGateway::RestApi with policy only allowing VPC traffic to invoke any resources on the API I'd do something like so:
"ApiGatewayRestApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": "api-foo-bar",
"EndpointConfiguration": {
"Types": [
"PRIVATE"
]
},
"Policy": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": [
"execute-api:Invoke"
],
"Resource": "execute-api:/*",
"Condition": {
"StringEquals": {
"aws:SourceVpc": "vpc-000000000000"
}
}
}
]
}
}
}
I have been asked if we can create a policy and then reuse it for different Api Gateways we may create? something amongst the lines:
"ApiGatewayRestApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": "api-foo-bar",
"EndpointConfiguration": {
"Types": [
"PRIVATE"
]
},
"Policy": "arn:aws:*whatever*"
}
},
And I have no idea! Nor can I find any documentation or examples showing that. Has anyone done this? Is it at all doable? Thanks :)

No, it's not doable currently. The policy you are attaching to the Api Gateway is Resource-based policy.
From aws documentation,
With resource-based policies, you can specify who has access to the
resource and what actions they can perform on it.
Aws documentation shows the type that each property in cloudformation can take. The following is the properties and the types allowed in "AWS::ApiGateway::RestApi"
{
"Type" : "AWS::ApiGateway::RestApi",
"Properties" : {
"ApiKeySourceType" : String,
"BinaryMediaTypes" : [ String, ... ],
"Body" : Json,
"BodyS3Location" : S3Location,
"CloneFrom" : String,
"Description" : String,
"DisableExecuteApiEndpoint" : Boolean,
"EndpointConfiguration" : EndpointConfiguration,
"FailOnWarnings" : Boolean,
"MinimumCompressionSize" : Integer,
"Mode" : String,
"Name" : String,
"Parameters" : {Key : Value, ...},
"Policy" : Json,
"Tags" : [ Tag, ... ]
}
}
Notice that Policy property takes the type of JSON. Furthermore, the documentation writes the following for the Policy property:
A policy document that contains the permissions for the RestApi
resource.
and gives us a hint that Policy property does not take the following form: "Policy": "arn:aws:*whatever*" and only accepts a policy document in the form of JSON as Api Gateway's resource-based policy.
ref:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-policy
https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_identity-vs-resource.html

you can find the docs here.
Policy
A policy document that contains the permissions for the RestApi resource. To set the ARN for the policy, use the !Join intrinsic function with "" as delimiter and values of "execute-api:/" and "*".

Related

Cloudformation template to attach existing policy to existing IAM role

I want to attach an aws managed policy to an existing role. I am achieving this using template:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "AWS CloudFormation template to modify Role",
"Parameters": {
"MyRole": {
"Type": "String",
"Default": "MyRole",
"Description": "Role to be modified"
}
},
"Resources": {
"S3FullAccess": {
"Type": "AWS::IAM::ManagedPolicy",
"Properties": {
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"s3:*",
"s3-object-lambda:*"
],
"Resource": "*"
}]
},
"Roles": [
"MyRole"
]
}
}
}
}
This template will create a policy with s3FullAccess and attach it to MyRole. But I do not want to create a new policy, if I want to use the policy already present with aws for s3 full access, how can I do that.
And if I use this template:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "AWS CloudFormation template to modify Role",
"Resources": {
"IAMRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"Path": "/",
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/ReadOnlyAccess"
],
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"AWS": "*"
}
}]
},
"RoleName": "RoleName"
}
}
}
}
This will attempt to create a new role and attach ReadOnlyPolicy to it. But if I want to attach a policy to existing role, how to refer that role in the template.
You use your AWS::IAM::Role's ManagedPolicyArns property, where you just specify the ARN of the manage policy to attach.
To use existing role in CloudFormation, you have to import it. Then you will be able to manage it from CloudFormation.
In general, CloudFormation service is for creating resources. There is not a native support to do something with already created resources if you don't import them.
If you don't want to import them, then, you have an option to write CloudFormation custom resource. You can create a lambda function-backed custom resource passing in the ARNs of the IAM policy and the IAM role you want to attach the policy to by IAM AttachRolePolicy API. More details are in AWS documentation.

How to write an IAM policy that grants full access on a certain service based on Tags?

I need to create a policy that grants full access on some services (ex: lambda, s3, apigateway) based on their tags. The way I think of it is: Only give full access to this resource if it has certain tags (ex: projectName= SpaceX). On the other hand, if a resource doesn't have the desired tags, deny any requests.
See https://docs.aws.amazon.com/IAM/latest/UserGuide/access_tags.html
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Action" : "s3:*",
"Resource" : "*",
"Condition" : {
"StringEquals" : {
"aws:ResourceTag/env" : "prod"
}
}
}
]
}
This will allow any operation on S3 tagged as env: prod

AWS CFT template IAM Policy

I'm using CFT to create an IAM policy that allows access to only one S3 bucket alone (which is also created in the same CFT itself). Here is the part of the CFT where the problem lies
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"BucketCreation":{
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketName":"samplebucket",
}
},
"IAMPolicy":{
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "ListS3BucketsPolicy",
"PolicyDocument": {
"Statement": [{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": "Fn::GetAtt" : [ "BucketCreation" , "Arn" ]
}]
}
}
}
}
}
Now the above cft creates a IAM policy with a resource statement "Resource": "arn:aws:s3:::bucketname" well this gives only bucket access and not object level access. What I need is the resource section to look like this "Resource": "arn:aws:s3:::bucketname/*".Is this possible in CFT?
Yes you can, see here
You can use wildcards as part of the resource ARN. You can use
wildcard characters (* and ?) within any ARN segment (the parts
separated by colons). An asterisk (*) represents any combination of
zero or more characters, and a question mark (?) represents any single
character. You can use multiple * or ? characters in each segment, but
a wildcard cannot span segments.
This ARN uses the wildcard * in the relative-ID part of the ARN to
identify all objects in the example bucket.
Update:
To concatinate /* look at intrinsic functions like Join
Thanks to #titogeo. I've found the solution.Here it is
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"BucketCreation":{
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketName":"samplebucket"
}
},
"IAMPolicy":{
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "ListS3BucketsPolicy",
"PolicyDocument": {
"Statement": [{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource":{
"Fn::Join":["",[
{
"Fn::GetAtt" : [ "BucketCreation" , "Arn" ]
},
"/*"
]
]
}
}]
}
}
}
}
}
P.S: Sorry for the poor indendation. I'm Frustrated with it, if someone could fix it, that would be helpful.

AWS Service Unable To Assume Role

I've two AWS Cloudformation stacks, one for IAM roles and the second to create an AWS service and import the respective roles into it using Cloudformation.
When 10+ services are deployed the following error appears randomly on 1 or 2 of the services -
AWS::ECS::Service service Unable to assume role and validate the
listeners configured on your load balancer. Please verify that the ECS
service role being passed has the proper permissions.
If all the services are torn down and the services redployed to the ECS cluster, the error appears but for different services.
The AWS fix for this can be seen here
If the 1 or 2 broken services are torn down and redeployed the services deploy without issue. So the problem appears to only occur when many services are deployed at the same time - this indicates it may be an IAM propagation timing issue within Cloudformation.
I've tried adding depends on in the service definition -
"service" : {
"Type" : "AWS::ECS::Service",
"DependsOn" : [
"taskdefinition",
"ECSServiceRole"
],
"Properties" : {
"Cluster" : { "Ref": "ECSCluster"},
"Role" : {"Ref" : "ECSServiceRole"},
etc...
}
}
But this doesn't work.
As you can note, I've also removed the IAM import value for the ECSServiceRole and replaced it with an inline resource policy seen here -
"ECSServiceRole" : {
"Type" : "AWS::IAM::Role",
"Properties" : {
"AssumeRolePolicyDocument" : {
"Statement" : [
{
"Sid": "",
"Effect" : "Allow",
"Principal" : {
"Service" : [
"ecs.amazonaws.com"
]
},
"Action" : [
"sts:AssumeRole"
]
}
]
},
"Path" : "/",
"Policies" : [
{
"PolicyName" : "ecs-service",
"PolicyDocument" : {
"Statement" : [
{
"Effect" : "Allow",
"Action" : [
"ec2:Describe*",
"ec2:AuthorizeSecurityGroupIngress",
"elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
"elasticloadbalancing:DeregisterTargets",
"elasticloadbalancing:Describe*",
"elasticloadbalancing:RegisterInstancesWithLoadBalancer",
"elasticloadbalancing:RegisterTargets",
"sns:*"
],
"Resource" : "*"
}
]
}
}
]
}
}
But again - the inline policy doesn't fix the issue either.
Any ideas or pointers would be much appreciated!
In reply to answer 1.
Thank you - I wasn't aware of this improvment.
Is this the correct way to associate the service linked role for ECS?
"ECSServiceRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": [
"ecs.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Path": "/",
"Policies": [
{
"PolicyName": "CreateServiceLinkedRoleForECS",
"PolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:CreateServiceLinkedRole",
"iam:PutRolePolicy",
"iam:UpdateRoleDescription",
"iam:DeleteServiceLinkedRole",
"iam:GetServiceLinkedRoleDeletionStatus"
],
"Resource": "arn:aws:iam::*:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS*",
"Condition": {
"StringLike": {
"iam:AWSServiceName": "ecs.amazonaws.com"
}
}
}
]
}
}
]
}
}
Final Answer
After months of intermittent on-going issues with AWS regarding this matter AWS came back to say they were throttling us in the background, on the ELB. This is why the random and varied issues were appearing when deploying 3+ docker services via Cloudformation at the same time. The solution was nothing to do with IAM permissions, rather it was to increase the rate limit on the ELB via the "AWS Service Team".
So the fix was to continue using the two stack approach in Cloudformation, one with the IAM roles, which in turn were imported into the service layer stack. The fix was to add a depends on in the service definition for all of the other stack resources in the service layer script. By doing this it allows sufficient time for the IAM roles to be imported and executed by the service, thus this was a Cloudformation resource creation timing issue.
"service" : {
"Type" : "AWS::ECS::Service",
"DependsOn" : [
"TaskDefinition",
"EcsElasticLoadBalancer",
"DnsRecord"
],
"Properties" : {
etc...
}
}
UPDATE:
As of July 19th 2018, it is now possible to create a IAM Service-Linked Roles using CloudFormation https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-servicelinkedrole.html.
EcsServiceLinkedRole:
Type: "AWS::IAM::ServiceLinkedRole"
Properties:
AWSServiceName: "ecs.amazonaws.com"
Description: "Role to enable Amazon ECS to manage your cluster."
OLD ANSWER:
Creating your own ECSServiceRole is no longer required. By not specifying a role for your service, AWS will default on using the ECS Service-Linked role. If your AWS account is recent enough, or you have already created a cluster via the console you don't have to do anything for this to work. If not, run the following command to create the role: aws iam create-service-linked-role --aws-service-name ecs.amazonaws.com.

Grant permissions between AWS resources with CloudFormation

I would like to have a CloudFormation template create an EC2 instance and give that instance access to a S3 bucket.
One way is to have the template create an IAM user with proper permissions and use its access key to grant access.
But what if I don't want to give that user access to the IAM service?
Is there a way to have that user deploy this template without IAM?
UPDATE:
I want to be able to just share that template, so I am wondering if it is possible to not have a dependency on pre-existing IAM resources (roles, policies, etc)
The common method to grant permissions for an instance is Instance Profiles. You create a role with all the required permissions, assign that role to an instance profile and then assign the profile to any instance you need.
You can do this with CloudFormation:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"myEC2Instance": {
"Type": "AWS::EC2::Instance",
"Version": "2009-05-15",
"Properties": {
"ImageId": "ami-205fba49",
"InstanceType": "t2.micro",
"IamInstanceProfile": {
"Ref": "RootInstanceProfile"
}
}
},
"MyRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version" : "2012-10-17",
"Statement": [ {
"Effect": "Allow",
"Principal": {
"Service": [ "ec2.amazonaws.com" ]
},
"Action": [ "sts:AssumeRole" ]
} ]
},
"Path": "/"
}
},
"RolePolicies": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "s3",
"PolicyDocument": {
"Version" : "2012-10-17",
"Statement": [ {
"Effect": "Allow",
"Action":["s3:PutObject","s3:PutObjectAcl"],
"Resource":["arn:aws:s3:::examplebucket/*"],
} ]
},
"Roles": [ { "Ref": "MyRole" } ]
}
},
"RootInstanceProfile": {
"Type": "AWS::IAM::InstanceProfile",
"Properties": {
"Path": "/",
"Roles": [ { "Ref": "MyRole" } ]
}
}
}
}
If you want to avoid giving the user deploying this template IAM access, you can create the instance profile before deploying the template and specify the already existing instance profile in the template. I haven't tried that yet, but it seems that should only require ec2:AssociateIamInstanceProfile and you should be able to constrain that just to that one specific profile.
Depends on what you mean by IAM service.
You can create IAM User Access Keys that give permissions to specific AWS services and no others. Access Keys do not allow IAM Console Access (this requires login credentials or federation).
For your use case your user will need at a minimum:
Permission to use CloudFormation to execute your template.
Permission to create the EC2 instance.
These permissions are defined in a policy that you add to the IAM user in the AWS Management Console. You can create users that cannot log into the console. Then you generate the Access Keys that the user will use in their application, AWS CLI, etc.
Overview of IAM Policies