Why is the addtoPolicy method not available on the Role object if I get an existing role by name (or by arn)? The fromRoleName method should return a Role object. For example:
let testRole=Role.fromRoleName(this,"test-role","test-role");
testRole.addToPolicy()//Method not found
On the other hand, this works:
testRole = new Role(this,"test-role", {
assumedBy: new ServicePrincipal('lambda.amazonaws.com')
})
testRole.addToPolicy() //OK
This is because fromRoleName returns an IRole interface. This interface does not have a addToPolicy method.
Related
In CDK IAM Users, Groups or Roles have two methods to add PolicyStatements:
X.addToPolicy (CDK API Reference) and
X.addToPrincipalPolicy (CDK API Reference)
Whats the difference between then?
The API Reference isn't much help.
TL;DR Both add a statement to a Principal's inline policy. The only difference is the return value.
Both accept a PolicyStatement and synth a AWS::IAM::Policy resource to the Principal. However, addToPolicy returns a "success" boolean, while addToPrincipalPolicy returns an object.
This is easy to see by looking at the aws-cdk source implementation of the Role class:
// role.ts
export class Role extends Resource implements IRole {
// ...
public addToPolicy(statement: PolicyStatement): boolean {
return this.addToPrincipalPolicy(statement).statementAdded;
}
public addToPrincipalPolicy(statement: PolicyStatement): AddToPrincipalPolicyResult {
if (!this.defaultPolicy) {
this.defaultPolicy = new Policy(this, 'Policy');
this.attachInlinePolicy(this.defaultPolicy);
}
this.defaultPolicy.addStatements(statement);
return { statementAdded: true, policyDependable: this.defaultPolicy };
}
I want to give the decrypt permission for proxy.
This proxy uses the a key in secret manager as username/password store.
and it uses key in KMS as Encryption key
This code makes role to acceess the secret manager automatically, but not KMS for decryption.
const dbProxy = new rds.DatabaseProxy(this, 'Proxy', {
proxyTarget: rds.ProxyTarget.fromCluster(dbCluster),
dbProxyName: `vr-${targetEnv}-rds-proxy`,
secrets: [dbCluster.secret!],// it makes the role to access the secret manager automatically.
securityGroups:[proxySecurityGroup],
requireTLS:false,
iamAuth:false,
vpc,
});
const kmsPolicy = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["kms:Decrypt"],
resources: ["*"],
})
dbProxy.addToRolePolicy(kmsPolicy)
However, there comes this error.
Property 'addToRolePolicy' does not exist on type 'DatabaseProxy'
I want to fetch the default created role and add this kmsPolicy
I tried this too.
const mainEncSecretArn = kms.Key.fromKeyArn(this, "kms-key", "arn:aws:kms:ap-northeast-1:665852837485:key/bf3cf318-1376-44de-a014-181074f4899d");
mainEncSecretArn.grantDecrypt(dbProxy);
The error is
Argument of type 'DatabaseProxy' is not assignable to parameter of type 'IGrantable'.
Property 'grantPrincipal' is missing in type 'DatabaseProxy' but required in type 'IGrantable'.
After working today with AWS Support, i was told (verbatim):
I would suggest explicitly defining the KMS permissions in a IAM policy document rather than using the "fromAliasName" Function.
I have provided some references that should help you write and attach the necessary KMS permission policy that is equivalent to the one that would have been created by the "fromAliasName" Function
And here is manual version of what support is saying cannot be done with cdk yet:
// get your kms key arn:
const kmsKeyArn =
'arn:aws:kms:us-west-2:123456789012:key/12345678-1234-1234-1234-1234567890';
// you won't be able to use the default proxy role :(
const proxyRole = new iam.Role(this, 'proxy-role', {
assumedBy: new iam.ServicePrincipal('rds.amazonaws.com'),
});
proxyRole.attachInlinePolicy(
new iam.Policy(this, 'kms-proxy-decrypt-policy', {
statements: [
new iam.PolicyStatement({
// grant the permission:
actions: ['kms:Decrypt'],
// put your kms key arn here:
resources: [kmsKeyArn],
}),
],
})
);
const proxy = new rds.DatabaseProxy(this, 'db-proxy', {
// there is other stuff here:
...
// ..but assign the role here:
role: proxyRole,
})
For the project I'm on, I am tasked with creating a testing app that uses Terraform to create a resource instance and then test that it was created properly. The purpose is testing the Terraform Script result by validating certain characteristics of the resource created. That's the broad outline.
For several of these scripts a resource is assigned a role. It could be a PubSub subscription, DataCatalog, etc.
Example Terraform code for a Spanner Database assigning roles/spanner.databaseAdmin:
resource "google_spanner_database_iam_member" "user_database_admin" {
for_each = toset(var.iam_user_database_admins)
project = var.project
instance = var.instance_id
database = google_spanner_database.spanner_database.name
role = "roles/spanner.databaseAdmin"
member = "user:${each.key}"
}
So my question is this: Is there a way using a .NET GCP API to make a call to determine that the role was assigned? I can test for permissions via a TestIamPermissions method off of the client object and that's what I'm currently doing. But that gives me a sometimes long list of possible permissions. Is there a way to say "does this spanner database have the roles/spanner.databaseAdmin assigned?"
Here's an example of code testing for permissions on a PubSub Subscription:
TestIamPermissionsRequest subscriptionRequest = new TestIamPermissionsRequest
{
ResourceAsResourceName = SubscriptionName.FromProjectSubscription(projectId, subscriptionId),
Permissions = {
"pubsub.subscriptions.get",
"pubsub.subscriptions.delete",
"pubsub.subscriptions.update"
}
};
TestIamPermissionsResponse subscriptionResponse = publisher.TestIamPermissions(subscriptionRequest);
Seems like there ought to be a cleaner way to do this, but being somewhat new to GCP, I haven't found a way yet. Suggestions will be welcome.
Thought I should close this question off with what I eventually discovered. The proper question isn't what role is assigned an instance of a resource, but what users have been allowed to use the resource and with what role.
The proper call is GetIamPolicy which is available in the APIs for all of the resources that I've been working with. The problem was that I wasn't seeing anything due to no user accounts being assigned to the resource. I updated the Terraform script to assign a user to the resource with the required roles. When calling GetIamPolicy, it returns an array in the Bindings that lists roles and users that are assigned. This was the information I needed. Going down the path of using TestIamPermissions was unneeded.
Here's an example my use of this:
bool roleFound = false;
bool userFound = false;
bool exception = false;
try
{
Policy policyResponse = Client.GetIamPolicy(Resource);
var bindings = policyResponse.Bindings;
foreach (var item in bindings)
{
if (AcceptedRoles.Contains(item.Role))
roleFound = true;
foreach (var user in item.Members)
{
string testUser = user;
if (user.Substring(0, 5) == "user:")
{
testUser = user.Substring(5);
}
else if (user.Substring(0, 6) == "group:")
{
testUser = user.Substring(6);
}
if (Settings.UserTestList.Contains(testUser))
userFound = true;
}
}
}
catch (Grpc.Core.RpcException)
{
exception = true;
}
Assert.True(roleFound);
Assert.True(userFound);
Assert.False(exception);
If CognitoIdentityProvider::Client doesn't provide access_key_id and secret_access_key, it will retrieve from the ENV by default.
The strange thing is it works at a totally new lambda but not in another.
client = Aws::CognitoIdentityProvider::Client.new(region: ENV['AWS_REGION'])
client.admin_get_user({
user_pool_id: Jets.application.config.cognito[:user_pool_id],
username: 'username'
})
I would get this error message but had no idea where can I set the policy for cognito.
"errorMessage": "User: arn:aws:sts::123123123123:assumed-role/firstage-api-stag-IamRole-1DYOOEVSCURMY/xxxxxx-api-stag-mes_controller-show is not authorized to perform: cognito-idp:AdminGetUser on resource: arn:aws:cognito-idp:ap-northeast-2:319924209672:userpool/ap-northeast-2_0cSCFMK4r",
"errorType": "Function<Aws::CognitoIdentityProvider::Errors::AccessDeniedException>",
I would like to use the default key_id and access_key in the lambda ENV rather than IAM user.
What should I do???
Best practice for Lambda functions is to ensure your IAM role associated with the Lambda function has the policy to let it invoke the specific AWS Service. Do not hard code creds in Lambda functions.
For some services, you need to write a custom policy. For example, for Pinpoint voice, you need to write a custom policy using JSON so the Lambda function can invoke that service.
For CognitoIdentityProvider, try using this policy for your IAM role:
When in doubt which services your IAM role can invoke - check the Access Advisor tab:
I just tested this use case and built a Lambda function that creates a CognitoIdentityProviderClient object and gets users in a specific user pool. I implemented this using the Lambda Java runtime API, but it does not matter what supported Lambda programming language you use. You still need to configure your IAM role in the same way.
This code works perfectly. Here is the Handler class:
public class Handler implements RequestHandler<Map<String,String>, String> {
#Override
public String handleRequest(Map<String,String> event, Context context) {
LambdaLogger logger = context.getLogger();
String pool = event.get("Pool");
logger.log("pool: " + pool);
CognitoInfo cog = new CognitoInfo();
String xml = cog.getUsers(pool);
logger.log("XML: " + xml);
return xml;
}
}
Here is a method named getUsers located in the CognitoInfo class that invokes the AWS Service:
public String getUsers( String userPoolId) {
CognitoIdentityProviderClient cognitoclient = CognitoIdentityProviderClient.builder()
.region(Region.US_EAST_1)
.build();
try {
ArrayList<String> userList = new ArrayList();
// List all users
ListUsersRequest usersRequest = ListUsersRequest.builder()
.userPoolId(userPoolId)
.build();
ListUsersResponse response = cognitoclient.listUsers(usersRequest);
for(UserType user : response.users()) {
userList.add(user.username());
}
return convertToString(toXml(userList));
} catch (CognitoIdentityProviderException e){
System.err.println(e.awsErrorDetails().errorMessage());
System.exit(1);
}
return "";
}
The Lambda function is configured to use an IAM role named lambda-support2 that has the AmazonCognitoPowerUser policy.
This Lambda function successfully retrieves the users in a specific user pool:
Make sure that all your Lambda functions have the proper IAM role configured.
I'm trying to use AWS CDK to create a user with minimal permissions through a custom policy, but I'm stuck with tagging that user and creating its access keys.
Below there's my code:
public class Sample extends App {
public static void main(final String[] args) {
App app = new App();
new UserStack(app, "user-stack");
app.run();
}
public static class UserStack extends Stack {
// Not able to add Tag and Create Access Key
public UserStack(final App parent, final String name) {
super(parent, name);
PolicyStatement statement = new PolicyStatement(PolicyStatementEffect.Allow);
statement.addResource("*");
statement.addAction("lambda:UpdateFunctionCode");
User user = new User(this, "LambdaDeployer", UserProps.builder().withUserName("lambda-deployer").withPath("/").build());
user.addToPolicy(statement);
Tag tag = new Tag("Project", "devops");
// how to tag the user ??
new CfnOutput(this, "LambdaDeployerOutputAccessKey", CfnOutputProps.builder().withValue("AWS::IAM::AccessKey").build());
new CfnOutput(this, "LambdaDeployerOutputSecretAccessKey", CfnOutputProps.builder().withValue("AWS::IAM::SecretAccessKey").build());
}
}
}
You'll probably have to use a Custom Resource in order to call the TagUser API, since adding tags to a User is not available natively in CloudFormation.
You can use a new feature in the CDK to help you author your Custom Resource: https://github.com/awslabs/aws-cdk/pull/1850