Repository: serverless/serverless (JavaScript) — Bug Fix

Issue:
Custom Resource does not support AWS US Gov Cloud Region (us-gov-west-1)
# Bug Report

## Description

1. What did you do?
Attempting to deploy a serverless application in `us-gov-west-1` that utilizes Custom Resources (an AWS Lambda function with event trigger on existing S3 bucket. 

1. What happened?
The CloudFormation stack hangs for one hour on deploying the Custom S3 resource and rolls back.  I noticed errors occurring on the Custom Resource lambda, but no logging was present.  After editing the serverless cloudformation to add logging rights to the custom resource lambda, I noticed the error is due to `/lib/plugins/aws/customResource/utils.js` not supporting Lambda ARN pattern for `us-gov-west-1`.  The offending lines in the code are [here](https://github.com/serverless/serverless/blob/1249361035362c8fc33c6ade7c3b3f09bea34f4d/lib/plugins/aws/customResources/resources/utils.js#L59) and [here](https://github.com/serverless/serverless/blob/1249361035362c8fc33c6ade7c3b3f09bea34f4d/lib/plugins/aws/customResources/resources/utils.js#L64)

The lambda arn schema in Gov Cloud is a bit different than other regions:
`arn:aws-us-gov:lambda:us-gov-west-1:XXXXXXXXXX:function:s3-asset-scanner-app-3-dev-scanner`

The error from custom resource lambda:
```
START RequestId: 3119d862-248a-45cb-b343-a3ec830ec496 Version: $LATEST

2019-11-21T18:31:34.257Z	3119d862-248a-45cb-b343-a3ec830ec496	ERROR	Invoke Error 	{
    "errorType": "TypeError",
    "errorMessage": "Cannot read property '0' of null",
    "stack": [
        "TypeError: Cannot read property '0' of null",
        "    at getEnvironment (/var/task/utils.js:67:19)",
        "    at create (/var/task/s3/handler.js:19:33)",
        "    at handler (/var/task/s3/handler.js:9:12)",
        "    at Runtime.handler (/var/task/utils.js:78:28)",
        "    at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"
    ]
}

END RequestId: 3119d862-248a-45cb-b343-a3ec830ec496
REPORT RequestId: 3119d862-248a-45cb-b343-a3ec830ec496	Duration: 3.48 ms	Billed Duration: 100 ms	Memory Size: 1024 MB	Max Memory Used: 84 MB	Init Duration: 312.26 ms	
START RequestId: 3119d862-248a-45cb-b343-a3ec830ec496 Version: $LATEST
2019-11-21T18:32:33.423Z	3119d862-248a-45cb-b343-a3ec830ec496	ERROR	Invoke Error 	{
    "errorType": "TypeError",
    "errorMessage": "Cannot read property '0' of null",
    "stack": [
        "TypeError: Cannot read property '0' of null",
        "    at getEnvironment (/var/task/utils.js:67:19)",
        "    at create (/var/task/s3/handler.js:19:33)",
        "    at handler (/var/task/s3/handler.js:9:12)",
        "    at Runtime.handler (/var/task/utils.js:78:28)",
        "    at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"
    ]
}
END RequestId: 3119d862-248a-45cb-b343-a3ec830ec496
REPORT RequestId: 3119d862-248a-45cb-b343-a3ec830ec496	Duration: 6.18 ms	Billed Duration: 100 ms	Memory Size: 1024 MB	Max Memory Used: 84 MB	
```


1. What should've happened?

Stack should have deployed correctly, and the supporting Custom Resource lambda should properly parse ARNs in US Gov Cloud regions, such as **`arn:aws-us-gov:lambda:us-gov-west-1**

1. What's the content of your `serverless.yml` file?

```yaml
service: s3-asset-scanner-app-3

provider:
  name: aws
  endpointType: REGIONAL  
  runtime: nodejs10.x
  stage: ${opt:stage, 'dev'} ## dev stage is default, can be overridden by CLI (for ex: serverless deploy --stage prod
  region: us-gov-west-1
  profile: ${opt:profile, 'pgfs-ci-service'} ##This must match a valid IAM profile in ~/.aws/credentials
  #profile name default is 'pgfs-ci-service', can be interpolated using the stage for different environments, or overridden by the CLI

plugins:
  - serverless-plugin-typescript
  - serverless-offline

custom:
  s3BucketArn: 'arn:aws-us-gov:s3:::REDACTED'
  s3BucketName: 'REDACTED'
  allowedMIMETypes: 'image/png,image/jpeg,image/gif'
  notificationTopicArn: 'sadfsdf'

package:
  exclude:
    - node_modules/**/*

functions:
  scanner:
    handler: handler.scanner
    memorySize: 128
    environment:
      S3_BUCKET_ARN: ${self:custom.s3BucketArn}
      S3_BUCKET_NAME: ${self:custom.s3BucketName}
      ALLOWED_MIME_TYPES: ${self:custom.allowedMIMETypes}
      NOTIFICATION_TOPIC_ARN: ${self:custom.notificationTopicArn}
    events:
      - s3:
          bucket: REDACTED #TODO Parameterize this later
          event: s3:ObjectCreated:*
          existing: true         

```

1. What's the output you get when you use the `SLS_DEBUG=*` environment variable (e.g. `SLS_DEBUG=* serverless deploy`)

serverless version / environment info:

```ps
  Your Environment Information ---------------------------
     Operating System:          win32
     Node Version:              10.15.1
     Framework Version:         1.58.0
     Plugin Version:            3.2.5
     SDK Version:               2.2.1
     Components Core Version:   1.1.2
     Components CLI Version:    1.4.0
```

serverless deploy:

```ps
$ serverless deploy --region us-gov-west-1 --stage dev --verbose
Serverless: Compiling with Typescript...
Serverless: Using local tsconfig.json
Serverless: Typescript compiled.
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Installing dependencies for custom CloudFormation resources...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
CloudFormation - CREATE_IN_PROGRESS - AWS::CloudFormation::Stack - s3-asset-scanner-app-2-dev
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_COMPLETE - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::BucketPolicy - ServerlessDeploymentBucketPolicy
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::BucketPolicy - ServerlessDeploymentBucketPolicy
CloudFormation - CREATE_COMPLETE - AWS::S3::BucketPolicy - ServerlessDeploymentBucketPolicy
CloudFormation - CREATE_COMPLETE - AWS::CloudFormation::Stack - s3-asset-scanner-app-2-dev
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service s3-asset-scanner-app-2.zip file to S3 (2.27 KB)...
Serverless: Uploading custom CloudFormation resources...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
CloudFormation - UPDATE_IN_PROGRESS - AWS::CloudFormation::Stack - s3-asset-scanner-app-2-dev
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleCustomResourcesLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - ScannerLogGroup
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleCustomResourcesLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - ScannerLogGroup
CloudFormation - CREATE_COMPLETE - AWS::Logs::LogGroup - ScannerLogGroup
CloudFormation - CREATE_COMPLETE - AWS::IAM::Role - IamRoleCustomResourcesLambdaExecution
CloudFormation - CREATE_COMPLETE - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - CustomDashresourceDashexistingDashs3LambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - ScannerLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - CustomDashresourceDashexistingDashs3LambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - ScannerLambdaFunction
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Function - CustomDashresourceDashexistingDashs3LambdaFunction
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Function - ScannerLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - ScannerLambdaVersioneApviq3i7hGfN1vukw8srmJo9fJueqjd0L2VNHoNrVw
CloudFormation - CREATE_IN_PROGRESS - Custom::S3 - ScannerCustomS31
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - ScannerLambdaVersioneApviq3i7hGfN1vukw8srmJo9fJueqjd0L2VNHoNrVw
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Version - ScannerLambdaVersioneApviq3i7hGfN1vukw8srmJo9fJueqjd0L2VNHoNrVw
Pulling docker image gitlab/gitlab-runner-helper:x86_64-05161b14 ...
ERROR: Job failed: execution took longer than 1h0m0s seconds
```
Similar or dependent issues:

- None that I know of



Discussion:

Resolving patch:
diff --git a/lib/plugins/aws/customResources/resources/apiGatewayCloudWatchRole/handler.js b/lib/plugins/aws/customResources/resources/apiGatewayCloudWatchRole/handler.js
index 063adab8761..08722332625 100644
--- a/lib/plugins/aws/customResources/resources/apiGatewayCloudWatchRole/handler.js
+++ b/lib/plugins/aws/customResources/resources/apiGatewayCloudWatchRole/handler.js
@@ -17,19 +17,18 @@ function handler(event, context) {
 
 async function create(event, context) {
   const { RoleArn } = event.ResourceProperties;
-  const { AccountId: accountId, Region: region } = getEnvironment(context);
+  const { Partition: partition, AccountId: accountId, Region: region } = getEnvironment(context);
 
   const apiGateway = new ApiGateway({ region });
   const assignedRoleArn = (await apiGateway.getAccount().promise()).cloudwatchRoleArn;
 
-  let roleArn = `arn:aws:iam::${accountId}:role/serverlessApiGatewayCloudWatchRole`;
+  let roleArn = `arn:${partition}:iam::${accountId}:role/serverlessApiGatewayCloudWatchRole`;
   if (RoleArn) {
     // if there's a roleArn in the Resource Properties, just re-use it here
     roleArn = RoleArn;
   } else {
     // Create an own API Gateway role if the roleArn was not set via Resource Properties
-    const apiGatewayPushToCloudWatchLogsPolicyArn =
-      'arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs';
+    const apiGatewayPushToCloudWatchLogsPolicyArn = `arn:${partition}:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs`;
 
     const roleName = roleArn.slice(roleArn.lastIndexOf('/') + 1);
 
diff --git a/lib/plugins/aws/customResources/resources/cognitoUserPool/handler.js b/lib/plugins/aws/customResources/resources/cognitoUserPool/handler.js
index ced880b8c44..bc74dce0c46 100644
--- a/lib/plugins/aws/customResources/resources/cognitoUserPool/handler.js
+++ b/lib/plugins/aws/customResources/resources/cognitoUserPool/handler.js
@@ -17,14 +17,15 @@ function handler(event, context) {
 
 function create(event, context) {
   const { FunctionName, UserPoolName, UserPoolConfigs } = event.ResourceProperties;
-  const { Region, AccountId } = getEnvironment(context);
+  const { Partition, Region, AccountId } = getEnvironment(context);
 
-  const lambdaArn = getLambdaArn(Region, AccountId, FunctionName);
+  const lambdaArn = getLambdaArn(Partition, Region, AccountId, FunctionName);
 
   return findUserPoolByName({ userPoolName: UserPoolName, region: Region }).then(userPool =>
     addPermission({
       functionName: FunctionName,
       userPoolName: UserPoolName,
+      partition: Partition,
       region: Region,
       accountId: AccountId,
       userPoolId: userPool.Id,
@@ -40,10 +41,10 @@ function create(event, context) {
 }
 
 function update(event, context) {
-  const { Region, AccountId } = getEnvironment(context);
+  const { Partition, Region, AccountId } = getEnvironment(context);
   const { FunctionName, UserPoolName, UserPoolConfigs } = event.ResourceProperties;
 
-  const lambdaArn = getLambdaArn(Region, AccountId, FunctionName);
+  const lambdaArn = getLambdaArn(Partition, Region, AccountId, FunctionName);
 
   return updateConfiguration({
     lambdaArn,
@@ -54,10 +55,10 @@ function update(event, context) {
 }
 
 function remove(event, context) {
-  const { Region, AccountId } = getEnvironment(context);
+  const { Partition, Region, AccountId } = getEnvironment(context);
   const { FunctionName, UserPoolName } = event.ResourceProperties;
 
-  const lambdaArn = getLambdaArn(Region, AccountId, FunctionName);
+  const lambdaArn = getLambdaArn(Partition, Region, AccountId, FunctionName);
 
   return removePermission({
     functionName: FunctionName,
diff --git a/lib/plugins/aws/customResources/resources/cognitoUserPool/lib/permissions.js b/lib/plugins/aws/customResources/resources/cognitoUserPool/lib/permissions.js
index 25d426914ca..d8823334321 100644
--- a/lib/plugins/aws/customResources/resources/cognitoUserPool/lib/permissions.js
+++ b/lib/plugins/aws/customResources/resources/cognitoUserPool/lib/permissions.js
@@ -12,9 +12,8 @@ function getStatementId(functionName, userPoolName) {
 }
 
 function addPermission(config) {
-  const { functionName, userPoolName, region, accountId, userPoolId } = config;
+  const { functionName, userPoolName, partition, region, accountId, userPoolId } = config;
   const lambda = new Lambda({ region });
-  const partition = region && /^cn-/.test(region) ? 'aws-cn' : 'aws';
   const params = {
     Action: 'lambda:InvokeFunction',
     FunctionName: functionName,
diff --git a/lib/plugins/aws/customResources/resources/eventBridge/handler.js b/lib/plugins/aws/customResources/resources/eventBridge/handler.js
index c0c2374d2ca..cf5dcadb06d 100644
--- a/lib/plugins/aws/customResources/resources/eventBridge/handler.js
+++ b/lib/plugins/aws/customResources/resources/eventBridge/handler.js
@@ -24,12 +24,13 @@ function handler(event, context) {
 
 function create(event, context) {
   const { FunctionName, EventBridgeConfig } = event.ResourceProperties;
-  const { Region, AccountId } = getEnvironment(context);
+  const { Partition, Region, AccountId } = getEnvironment(context);
 
-  const lambdaArn = getLambdaArn(Region, AccountId, FunctionName);
+  const lambdaArn = getLambdaArn(Partition, Region, AccountId, FunctionName);
 
   return addPermission({
     functionName: FunctionName,
+    partition: Partition,
     region: Region,
     accountId: AccountId,
     eventBus: EventBridgeConfig.EventBus,
@@ -64,10 +65,10 @@ function create(event, context) {
 }
 
 function update(event, context) {
-  const { Region, AccountId } = getEnvironment(context);
+  const { Partition, Region, AccountId } = getEnvironment(context);
   const { FunctionName, EventBridgeConfig } = event.ResourceProperties;
 
-  const lambdaArn = getLambdaArn(Region, AccountId, FunctionName);
+  const lambdaArn = getLambdaArn(Partition, Region, AccountId, FunctionName);
 
   return updateRuleConfiguration({
     region: Region,
diff --git a/lib/plugins/aws/customResources/resources/eventBridge/lib/permissions.js b/lib/plugins/aws/customResources/resources/eventBridge/lib/permissions.js
index 58d33b53770..be4094d3328 100644
--- a/lib/plugins/aws/customResources/resources/eventBridge/lib/permissions.js
+++ b/lib/plugins/aws/customResources/resources/eventBridge/lib/permissions.js
@@ -13,9 +13,8 @@ function getStatementId(functionName, ruleName) {
 }
 
 function addPermission(config) {
-  const { functionName, region, accountId, eventBus, ruleName } = config;
+  const { functionName, partition, region, accountId, eventBus, ruleName } = config;
   const lambda = new AWS.Lambda({ region });
-  const partition = region && /^cn-/.test(region) ? 'aws-cn' : 'aws';
   let SourceArn = `arn:${partition}:events:${region}:${accountId}:rule/${ruleName}`;
   if (eventBus) {
     const eventBusName = getEventBusName(eventBus);
diff --git a/lib/plugins/aws/customResources/resources/s3/handler.js b/lib/plugins/aws/customResources/resources/s3/handler.js
index bbde329bb2b..20de80941e0 100644
--- a/lib/plugins/aws/customResources/resources/s3/handler.js
+++ b/lib/plugins/aws/customResources/resources/s3/handler.js
@@ -16,14 +16,15 @@ function handler(event, context) {
 }
 
 function create(event, context) {
-  const { Region, AccountId } = getEnvironment(context);
+  const { Partition, Region, AccountId } = getEnvironment(context);
   const { FunctionName, BucketName, BucketConfigs } = event.ResourceProperties;
 
-  const lambdaArn = getLambdaArn(Region, AccountId, FunctionName);
+  const lambdaArn = getLambdaArn(Partition, Region, AccountId, FunctionName);
 
   return addPermission({
     functionName: FunctionName,
     bucketName: BucketName,
+    partition: Partition,
     region: Region,
   }).then(() =>
     updateConfiguration({
@@ -37,10 +38,10 @@ function create(event, context) {
 }
 
 function update(event, context) {
-  const { Region, AccountId } = getEnvironment(context);
+  const { Partition, Region, AccountId } = getEnvironment(context);
   const { FunctionName, BucketName, BucketConfigs } = event.ResourceProperties;
 
-  const lambdaArn = getLambdaArn(Region, AccountId, FunctionName);
+  const lambdaArn = getLambdaArn(Partition, Region, AccountId, FunctionName);
 
   return updateConfiguration({
     lambdaArn,
diff --git a/lib/plugins/aws/customResources/resources/s3/lib/permissions.js b/lib/plugins/aws/customResources/resources/s3/lib/permissions.js
index d26a215d054..60ccd63eec3 100644
--- a/lib/plugins/aws/customResources/resources/s3/lib/permissions.js
+++ b/lib/plugins/aws/customResources/resources/s3/lib/permissions.js
@@ -12,9 +12,8 @@ function getStatementId(functionName, bucketName) {
 }
 
 function addPermission(config) {
-  const { functionName, bucketName, region } = config;
+  const { functionName, bucketName, partition, region } = config;
   const lambda = new AWS.Lambda({ region });
-  const partition = region && /^cn-/.test(region) ? 'aws-cn' : 'aws';
   const payload = {
     Action: 'lambda:InvokeFunction',
     FunctionName: functionName,
diff --git a/lib/plugins/aws/customResources/resources/utils.js b/lib/plugins/aws/customResources/resources/utils.js
index 8166d6752d0..e855cd5b685 100644
--- a/lib/plugins/aws/customResources/resources/utils.js
+++ b/lib/plugins/aws/customResources/resources/utils.js
@@ -55,19 +55,21 @@ function response(event, context, status, data = {}, err) {
   });
 }
 
-function getLambdaArn(region, accountId, functionName) {
-  return `arn:aws:lambda:${region}:${accountId}:function:${functionName}`;
+function getLambdaArn(partition, region, accountId, functionName) {
+  return `arn:${partition}:lambda:${region}:${accountId}:function:${functionName}`;
 }
 
 function getEnvironment(context) {
   const arn = context.invokedFunctionArn.match(
-    /^arn:aws.*:lambda:(\w+-\w+-\d+):(\d+):function:(.*)$/
+    /^arn:(aws[\w-]*).*:lambda:([\w+-]{2,}\d+):(\d+):function:(.*)$/
   );
+
   return {
     LambdaArn: arn[0],
-    Region: arn[1],
-    AccountId: arn[2],
-    LambdaName: arn[3],
+    Partition: arn[1],
+    Region: arn[2],
+    AccountId: arn[3],
+    LambdaName: arn[4],
   };
 }
 


Test patch:
diff --git a/lib/plugins/aws/customResources/resources/utils.test.js b/lib/plugins/aws/customResources/resources/utils.test.js
index f9c4cf69819..aa42c4eb601 100644
--- a/lib/plugins/aws/customResources/resources/utils.test.js
+++ b/lib/plugins/aws/customResources/resources/utils.test.js
@@ -6,15 +6,52 @@ const { getLambdaArn, getEnvironment } = require('./utils');
 
 describe('#getLambdaArn()', () => {
   it('should return the Lambda arn', () => {
+    const partition = 'aws';
     const region = 'us-east-1';
     const accountId = '123456';
     const functionName = 'some-function';
-    const arn = getLambdaArn(region, accountId, functionName);
+    const arn = getLambdaArn(partition, region, accountId, functionName);
 
     expect(arn).to.equal('arn:aws:lambda:us-east-1:123456:function:some-function');
   });
 });
 
+describe('#getLambdaArn() govloud west', () => {
+  it('should return the govcloud Lambda arn', () => {
+    const partition = 'aws-us-gov';
+    const region = 'us-gov-west-1';
+    const accountId = '123456';
+    const functionName = 'some-function';
+    const arn = getLambdaArn(partition, region, accountId, functionName);
+
+    expect(arn).to.equal('arn:aws-us-gov:lambda:us-gov-west-1:123456:function:some-function');
+  });
+});
+
+describe('#getLambdaArn() govcloud east', () => {
+  it('should return the govcloud Lambda arn', () => {
+    const partition = 'aws-us-gov';
+    const region = 'us-gov-east-1';
+    const accountId = '123456';
+    const functionName = 'some-function';
+    const arn = getLambdaArn(partition, region, accountId, functionName);
+
+    expect(arn).to.equal('arn:aws-us-gov:lambda:us-gov-east-1:123456:function:some-function');
+  });
+});
+
+describe('#getLambdaArn() china region', () => {
+  it('should return the china Lambda arn', () => {
+    const partition = 'aws-cn';
+    const region = 'cn-north-1';
+    const accountId = '123456';
+    const functionName = 'some-function';
+    const arn = getLambdaArn(partition, region, accountId, functionName);
+
+    expect(arn).to.equal('arn:aws-cn:lambda:cn-north-1:123456:function:some-function');
+  });
+});
+
 describe('#getEnvironment()', () => {
   it('should return an object with information about the execution environment', () => {
     const context = {
@@ -24,9 +61,61 @@ describe('#getEnvironment()', () => {
 
     expect(env).to.deep.equal({
       LambdaArn: 'arn:aws:lambda:us-east-1:123456:function:some-function',
+      Partition: 'aws',
       Region: 'us-east-1',
       AccountId: '123456',
       LambdaName: 'some-function',
     });
   });
 });
+
+describe('#getEnvironment() govcloud east', () => {
+  it('should return an object with information about the govcloud execution environment', () => {
+    const context = {
+      invokedFunctionArn: 'arn:aws-us-gov:lambda:us-gov-east-1:123456:function:some-function',
+    };
+    const env = getEnvironment(context);
+
+    expect(env).to.deep.equal({
+      LambdaArn: 'arn:aws-us-gov:lambda:us-gov-east-1:123456:function:some-function',
+      Partition: 'aws-us-gov',
+      Region: 'us-gov-east-1',
+      AccountId: '123456',
+      LambdaName: 'some-function',
+    });
+  });
+});
+
+describe('#getEnvironment() govcloud west', () => {
+  it('should return an object with information about the govcloud execution environment', () => {
+    const context = {
+      invokedFunctionArn: 'arn:aws-us-gov:lambda:us-gov-west-1:123456:function:some-function',
+    };
+    const env = getEnvironment(context);
+
+    expect(env).to.deep.equal({
+      LambdaArn: 'arn:aws-us-gov:lambda:us-gov-west-1:123456:function:some-function',
+      Partition: 'aws-us-gov',
+      Region: 'us-gov-west-1',
+      AccountId: '123456',
+      LambdaName: 'some-function',
+    });
+  });
+});
+
+describe('#getEnvironment() china region', () => {
+  it('should return an object with information about the china region execution environment', () => {
+    const context = {
+      invokedFunctionArn: 'arn:aws-cn:lambda:cn-north-1:123456:function:some-function',
+    };
+    const env = getEnvironment(context);
+
+    expect(env).to.deep.equal({
+      LambdaArn: 'arn:aws-cn:lambda:cn-north-1:123456:function:some-function',
+      Partition: 'aws-cn',
+      Region: 'cn-north-1',
+      AccountId: '123456',
+      LambdaName: 'some-function',
+    });
+  });
+});