How to build a self-service for your tenant AWS accounts?
Tags: aws, cloud, servicecatalog, itsm
Published: 2023-01-19 09:34:19

## Introduction

Quite recently I got a great opportunity to speak at a community-led conference [AWS Community Day](https://awscommunity.pl/) as part of [AWS Pop-up Hub Warsaw](https://aws.amazon.com/events/pop-up-hub/poland/). In my brief presentation (in Polish) I've tried to show how to build a self-service for tenant AWS accounts, based on **AWS Service Catalog** and **AWS Organizations**. This article is a follow-up to my presentation on how to automate deployments of predefined AWS resources across multiple AWS accounts in your organization.

## Service Catalog

AWS Service Catalog is a perfect service to build either a simple or more complex self-service for your AWS tenants. For example, you can prepare a well-designed 3-tier VPC, package it as a CF template and present it as an SC product, so your end users can simply kick off the VPC deployments in their AWS accounts by simply launching the SC product and providing some required parameters (i.e. CIDR block).

### Portfolios

SC portfolios allow you to organize, manage, and distribute cloud resources for your end users. You can share the portfolios with other AWS accounts and allow the admins of those accounts to distribute the portfolios with additional restrictions.

### Products

SC products are version-friendly infrastructure-as-code templates added to portfolios for end users to provision and create AWS resources. A product can be either a single EC2 instance, S3 bucket, EKS cluster, full-blown VPC networking, multi-tier web application, or anything in between. It's simply a predefined CloudFormation template or a template imported from an existing CF stack.

Some sample CF templates for SC products:
https://github.com/chmoora/aws-self-service/blob/master/src/cf-product-s3.yaml
https://github.com/chmoora/aws-self-service/blob/master/src/cf-product-ssm.yaml

### Constraints

SC constraints are certain limits that you can place on product-portfolio associations that allow you to manage minimal launch permissions or other optional actions that end users can perform on products. The most useful I usually find are:

* **launch constraints** - for specifying a dedicated IAM role in the target account, which will be assumed by SC service to deploy CF stacks
    
* **template constraints** - for limiting the values of CF parameters available to your end users, for example, a list of EC2 instance types
    
* **tag update constraints** - used to allow/disallow end users to update tags on resources provisioned by the SC products

### Sharing

Your SC portfolios can be shared with users/roles inside a single AWS account, as well as with selected AWS accounts or a whole AWS organization. This also includes any external AWS accounts or other AWS organizations, so you can technically build a single self-service for multiple customers.

The following diagram shows a sample structure of related AWS accounts in a single organization: **Management** account (Organizations), **Provisioning** account (delegated Admin account for Service Catalog) and some **Project** accounts (spoke/tenant accounts). Additional IAM role(s) used as launch constraints need to be defined in the spoke accounts, which you can simply deploy with CloudFormation StackSets.

![AWS Self-Service](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fiphhsq6k6lv4wxjlifz.png)

## CDK Constructs

To simplify the deployment of the Service Catalog resources in your Management or Provisioning account you can define them in a CDK application.

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_servicecatalog-readme.html

### CloudFormation Product

Your products can be defined in CDK app in two ways. The first one is with the [**CloudFormationProduct**](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_servicecatalog.CloudFormationProduct.html) construct: a CloudFormation template asset, stored either locally or remotely (S3, CodeCommit, GitHub). You have the template and you're providing its location. Easy, let's move on.

### Product Stack

The second way is the [**ProductStack**](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_servicecatalog.ProductStack.html) construct: a special CDK stack, where you define the resources you want to define for your SC product. This CDK stack will not be deployed as a CF stack, but rather only synthesized to a local JSON asset and consumed as a source template of the SC product. A pretty cool feature of CDK itself.

```typescript
export class ScProductBucket extends ProductStack {

  constructor(scope: Construct, id: string) {
    super(scope, id);

    new s3.Bucket(this, 'PrivateBucket', {
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      encryption: s3.BucketEncryption.KMS_MANAGED,
      enforceSSL: true,
      versioned: true
    });
  }
}
```

The full code for Product Stack:
https://github.com/chmoora/aws-self-service/blob/master/lib/sc-products-stack.ts

### Portfolio

Defining the [Portfolio](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_servicecatalog.Portfolio.html) is pretty straightforward as well. The same applies to adding products and their versions to the portfolio. The sharing part is a bit more tricky.

AWS CDK and CloudFormation only support sharing with individual AWS accounts (by account ID) with the method [shareWithAccount](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_servicecatalog.Portfolio.html#sharewbrwithwbraccountaccountid-options). Unfortunately, sharing with Organization or Organizational Unit is currently not supported (as of CDK release `v2.60.0`). However, there's always a workaround, more or less hacky.

This is a perfect opportunity to show you another interesting CDK construct, namely the [**AwsCustomResource**](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.custom_resources.AwsCustomResource.html). It's a singleton Lambda function, which can be used to extend the functionality of the CDK and/or CloudFormation. You can provide your code of course, but if you just need to get a response from some AWS API, the function code will be generated for you.

In my use case, I want to share the SC portfolio with my whole organization. To do this I'd need my organization's ID to create the portfolio share. I'm using here two AWS API calls to **Organizations** and **Service Catalog** services, respectively:

* `DescribeOrganization` - to get dynamically the ID of my AWS Organization
    
* `CreatePortfolioShare` - to share the portfolio with the whole Organization
    
Alternatively, I could provide my organization's ID statically, for example by using the CDK context file `cdk.context.json`. This would probably make more sense if I'd need to share the portfolio(s) with multiple external organizations and would need to have a list of IDs, then loop through them when creating the shares.

The code snippet below defines Custom Resource calling out AWS API call:

```typescript
const describeOrg = new cr.AwsCustomResource(this, 'DescOrg', {
  onUpdate: {
    service: 'Organizations',
    action: 'describeOrganization',
    region: 'us-east-1',
    physicalResourceId:cr.PhysicalResourceId.of(
      Date.now().toString()
    ),
  },
  policy: cr.AwsCustomResourcePolicy.fromSdkCalls({
    resources: cr.AwsCustomResourcePolicy.ANY_RESOURCE,
  }),
});

const orgId = describeOrg.getResponseField('Organization.Id');
```

The next important step is defining the products and their versions inside the portfolio and setting IAM role launch constraint, which can be simply done in the CDK with `addProduct()` and `setLocalLaunchRoleName()` methods, respectively. In my CDK stack, I'm building a list of products' CF templates with `fromProductStack()` method, which will be imported from the synthesized template produced by the `ProductStack`.

The full code for all SC-related resources is available here:
https://github.com/chmoora/aws-self-service/blob/master/lib/sc-self-service-stack.ts

## ITSM Integrations

Another interesting aspect of the AWS Service Catalog is integration with IT Service Management (ITSM) systems, which can be done with AWS Service Management Connectors (SMC). The connectors enable you to provision, manage, and operate native AWS resources directly from your ITSM system. Currently, supported integrations include [**ServiceNow Service Catalog**](https://www.servicenow.com/products/it-service-automation-applications/service-catalog.html) and [**Atlassian Jira Service Management**](https://www.atlassian.com/software/jira/service-management).

AWS Service Management Connector for ServiceNow:
https://store.servicenow.com/sn_appstore_store.do#!/store/application/f0b117a3db32320093a7d7a0cf961912/

AWS Service Management Connector for JSM:
https://marketplace.atlassian.com/apps/1221283/aws-service-management-connector-for-jsm-cloud?tab=overview&hosting=cloud

## Summary

I hope this article will inspire you to build a self-service solution in your organization. It would be great if you'd share in the comments how have you organised your portfolios and what useful products have you designed for your end users in tenant AWS accounts. Have fun!

## Resources

Self-Service (CDK Application)
https://github.com/chmoora/aws-self-service

IAM Roles (CDK Application)
https://github.com/chmoora/aws-iam-roles

AWS Service Catalog Construct Library (CDK)
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_servicecatalog-readme.html

AWS Service Catalog Administrator Guide
https://docs.aws.amazon.com/servicecatalog/latest/adminguide/

AWS Service Catalog User Guide
https://docs.aws.amazon.com/servicecatalog/latest/userguide/end-user-console.html

AWS Service Management Connectors
https://docs.aws.amazon.com/smc/latest/ag/overview.html