
Creating the POJOs for requests and response.
- Create a request POJO for accepting requests:
import lombok.Data;
@Data
public class IAMOperationRequest {
private String operation;
private String userName;
}
- Create a POJO for sending back the response from the handler:
import lombok.AllArgsConstructor;
import lombok.Data;
@AllArgsConstructor
@Data
public class IAMOperationResponse {
private String message;
private String errorMessage;
}
Creating a service class to implement the IAM Operations using AWS SDK:
- Import the required classes:
import com.amazonaws.services.identitymanagement.AmazonIdentityManagement;
import com.amazonaws.services.identitymanagement.AmazonIdentityManagementClientBuilder;
import com.amazonaws.services.identitymanagement.model.CreateUserRequest;
import com.amazonaws.services.identitymanagement.model.CreateUserResult;
import com.amazonaws.services.identitymanagement.model.DeleteConflictException;
import com.amazonaws.services.identitymanagement.model.DeleteUserRequest;
import com.amazonaws.services.identitymanagement.model.ListUsersRequest;
import com.amazonaws.services.identitymanagement.model.ListUsersResult;
import com.amazonaws.services.identitymanagement.model.User;
- Create and initialize a client object of AmazonIdentityManagement type:
private final AmazonIdentityManagement iamClient;
public IAMService() {
iamClient = AmazonIdentityManagementClientBuilder.defaultClient();
}
- Write code for creating a user in a method:
CreateUserRequest request = new CreateUserRequest().withUserName(userName);
CreateUserResult response = iamClient.createUser(request);
// get user details from response.
- Write code for checking if a user is present in another method:
boolean done = false;
ListUsersRequest request = new ListUsersRequest();
while (!done) {
ListUsersResult response = iamClient.listUsers(request);
for (User user : response.getUsers()) {
if (user.getUserName().equals(userName)) {
//return success message
}
}
request.setMarker(response.getMarker());
if (!response.getIsTruncated()) {
done = true;
}
}
// return error message
- Write code for deleting a user in another method:
DeleteUserRequest request = new DeleteUserRequest()
.withUserName(userName);
try {
iamClient.deleteUser(request);
} catch (DeleteConflictException e) {
// Handle exception
}
Let us now see how to create a handler.
- Create a handler class with input and output POJOs:
public final class HelloWorldLambdaHandler implements RequestHandler<IAMOperationRequest, IAMOperationResponse> {
- Implement the handleRequest method with a switch statement to invoke an appropriate service method:
public IAMOperationResponse handleRequest(final IAMOperationRequest request, final Context context) {
context.getLogger().log("Requested operation = " + request.getOperation()
+ ". User name = " + request.getUserName());
switch (request.getOperation()) {
case "CREATE" :
return this.service.createUser(request.getUserName());
case "CHECK" :
return this.service.checkUser(request.getUserName());
case "DELETE" :
return this.service.deleteUser(request.getUserName());
default:
return new IAMOperationResponse(null,
"Invalid operation " + request.getOperation()
+ ". Allowed: CREATE, CHECK, DELETE.");
}
}
- Package the dependencies into an uber JAR using mvn clean package.
Two JARs will be created: one with only class files (starting with original-) and an Uber JAR with all dependencies (starting with serverless-). We will use the Uber JAR in this recipe.
- Upload the JAR to S3:
aws s3 cp target/serverless-cookbook-iam-operations-0.0.1-SNAPSHOT.jar s3://serverless-cookbook/iam-operations-0.0.1-SNAPSHOT.jar --profile admin
- Create a CloudFormation template for our lambda function.
You need to create a role with a trust policy that allows our Lambda to assume the role. You also need to create a policy with CloudFormation and IAM permissions.
We need to add permissions for IAM operations in our policies:
- Effect: Allow
Action:
- iam:CreateUser
- iam:DeleteUser
- iam:ListUsers
Resource:
- Fn::Sub: arn:aws:iam::${AWS::AccountId}:user/*
We have used a pseudo-parameter, AWS::AccountId, within a sub-intrinsic function to dynamically populate the account ID. I also improved the CloudWatch logging permission policy from the previous recipe using the pseudo-parameters:
- Effect: Allow
Action:
- logs:CreateLogStream
Resource:
- Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/aws-sdk-iam-with-cf-cli:*
- Effect: Allow
Action:
- logs:PutLogEvents
Resource:
- Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/aws-sdk-iam-with-cf-cli:*:*
You should be able to complete this recipe by referring to the previous recipe, Your First Lambda using CloudFormation.
- Upload the CloudFormation template to S3:
aws s3 cp ../resources/cf-template-iam-operations.yml s3://serverless-cookbook/cf-template-iam-operations.yml --profile admin
- Create a CloudFormation stack using the CloudFormation template from AWS CLI:
aws cloudformation create-stack --stack-name myteststack --template-url https://s3.amazonaws.com/serverless-cookbook/cf-template-iam-operations.yml --capabilities CAPABILITY_NAMED_IAM --profile admin
This immediately responds with StackId. Note that you used a parameter, --capabilities CAPABILITY_NAMED_IAM. This is a security-related precaution. You are explicitly telling CloudFormation that you know what you are doing.
You can check the status of stack creation using the describe-stacks command:
aws cloudformation describe-stacks --stack-name <StackId> --profile admin
StackStatus: CREATE_COMPLETE means stack creation was successful.
- Verify the deployment with AWS CLI Lambda invoke:
aws lambda invoke --invocation-type RequestResponse --function-name aws-sdk-iam-with-cf-cli --log-type Tail --payload '{"operation":"CREATE", "userName":"abcd"}' --profile admin outputfile.txt
You can replace CREATE in the payload with CHECK for checking if the user was created, and DELETE for deleting the user.
- Delete the CloudFormation stack:
aws cloudformation delete-stack --stack-name <StackId> --profile admin