npm package: @hyperdrive.bot/serverless-arn-prefixer
Description: A serverless plugin that injects custom ARN properties for building AWS resource ARNs
Author: DevSquad
Latest version: 1.0.9

# Serverless ARN Prefixer Plugin

A Serverless Framework plugin that automatically injects custom ARN properties into your serverless configuration, making it easier to build AWS resource ARNs consistently across your application.

## Installation

### From npm (Recommended)
```bash
npm install @hyperdrive.bot/serverless-arn-prefixer
```

### From GitLab Package Registry (Development)
```bash
# Configure npm to use GitLab registry for @hyperdrive.bot scope
npm config set @hyperdrive.bot:registry https://gitlab.com/api/v4/projects/PROJECT_ID/packages/npm/

# Install the package
npm install @hyperdrive.bot/serverless-arn-prefixer
```

### Local Development
If developing within the monorepo, no separate installation required.

## Usage

Add the plugin to your `serverless.yml`:

### When installed from npm
```yaml
plugins:
  - "@hyperdrive.bot/serverless-arn-prefixer"
```

### When using locally in monorepo
```yaml
plugins:
  - ./packages/serverless/arn-prefixer
```

Or if using from within the packages/serverless directory:

```yaml
plugins:
  - ./arn-prefixer
```

## What it does

The plugin automatically injects a `custom.arn` object into your serverless configuration with the following properties:

### Basic Properties
```yaml
custom:
  arn:
    accountId: "123456789012"           # AWS Account ID (from AWS_ACCOUNT_ID env var or STS)
    stage: "dev"                        # Serverless stage
    service: "my-service"               # Serverless service name
    region: "us-east-1"                 # AWS region
    prefix: "my-service-dev"            # service-stage
    regionalPrefix: "us-east-1-my-service-dev"        # region-service-stage
    globalPrefix: "123456789012-us-east-1-my-service-dev"  # accountId-region-service-stage
```

### ARN Builder Properties
```yaml
custom:
  arn:
    # Generic ARN components
    arnBase: "arn:aws"                  # AWS ARN base
    accountRegion: "123456789012:us-east-1"    # accountId:region
    
    # Service-specific ARN bases
    dynamodb: "arn:aws:dynamodb:123456789012:us-east-1"      # DynamoDB ARN base
    s3: "arn:aws:s3:123456789012:us-east-1"                  # S3 ARN base
    lambda: "arn:aws:lambda:123456789012:us-east-1"          # Lambda ARN base
    iam: "arn:aws:iam:123456789012:us-east-1"                # IAM ARN base
    sns: "arn:aws:sns:123456789012:us-east-1"                # SNS ARN base
    sqs: "arn:aws:sqs:123456789012:us-east-1"                # SQS ARN base
    apigateway: "arn:aws:apigateway:123456789012:us-east-1"  # API Gateway ARN base
    events: "arn:aws:events:123456789012:us-east-1"          # EventBridge ARN base
    logs: "arn:aws:logs:123456789012:us-east-1"              # CloudWatch Logs ARN base
    kinesis: "arn:aws:kinesis:123456789012:us-east-1"        # Kinesis ARN base
    firehose: "arn:aws:firehose:123456789012:us-east-1"      # Firehose ARN base
    stepfunctions: "arn:aws:states:123456789012:us-east-1"   # Step Functions ARN base
```

## Using the injected values

Once the plugin is loaded, you can reference these values anywhere in your serverless.yml using two methods:

### Method 1: Variable Resolver (Recommended)

Use the `${arn:property}` syntax:

```yaml
resources:
  Resources:
    MyDynamoDBTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: ${arn:prefix}-users
        # Results in: my-service-dev-users

    MyS3Bucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: ${arn:globalPrefix}-assets
        # Results in: 123456789012-us-east-1-my-service-dev-assets

functions:
  myFunction:
    name: ${arn:regionalPrefix}-handler
    # Results in: us-east-1-my-service-dev-handler
```

### Method 2: Custom Object (Legacy)

Use the traditional `${self:custom.arn.property}` syntax:

```yaml
resources:
  Resources:
    MyDynamoDBTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: ${self:custom.arn.prefix}-users
        # Results in: my-service-dev-users

functions:
  myFunction:
    name: ${self:custom.arn.regionalPrefix}-handler
    # Results in: us-east-1-my-service-dev-handler
```

**Note:** The variable resolver method (Method 1) is recommended as it's more efficient and resolves variables earlier in the serverless lifecycle.

### Method 3: ARN Builders (NEW!)

Build complete AWS ARNs easily:

```yaml
resources:
  Resources:
    # DynamoDB Table ARN
    MyDynamoTable:
      Type: AWS::DynamoDB::Table  
      Properties:
        TableName: ${arn:prefix}-users
      Outputs:
        TableArn:
          Value: ${arn:dynamodb}:table/${arn:prefix}-users
          # Results in: arn:aws:dynamodb:123456789012:us-east-1:table/my-service-dev-users

    # S3 Bucket ARN  
    MyBucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: ${arn:globalPrefix}-assets
      Outputs:
        BucketArn:
          Value: ${arn:s3}:bucket/${arn:globalPrefix}-assets
          # Results in: arn:aws:s3:123456789012:us-east-1:bucket/123456789012-us-east-1-my-service-dev-assets

    # Lambda Function ARN
    MyFunction:
      Outputs:
        FunctionArn:
          Value: ${arn:lambda}:function:${arn:prefix}-processor  
          # Results in: arn:aws:lambda:123456789012:us-east-1:function:my-service-dev-processor

# IAM Policy using ARN builders
user-role-statements:
  - Effect: Allow
    Action:
      - dynamodb:GetItem
      - dynamodb:PutItem
    Resource:
      - ${arn:dynamodb}:table/${arn:prefix}-*      # All tables matching pattern
      - ${arn:dynamodb}:table/*/index/*            # All indexes
```

### Common ARN Patterns
```yaml
# Your specific use cases:
DynamoDBTableArn: ${arn:dynamodb}:table/resource              # arn:aws:dynamodb:account:region:table/resource
DynamoDBAllTablesArn: ${arn:dynamodb}:table/*                # arn:aws:dynamodb:account:region:table/*
S3BucketArn: ${arn:s3}:bucket/${arn:prefix}-bucket          # arn:aws:s3:account:region:bucket/service-stage-bucket
LambdaFunctionArn: ${arn:lambda}:function:${arn:prefix}-func  # arn:aws:lambda:account:region:function:service-stage-func
SNSTopicArn: ${arn:sns}:${arn:prefix}-topic                  # arn:aws:sns:account:region:service-stage-topic
SQSQueueArn: ${arn:sqs}:${arn:prefix}-queue                  # arn:aws:sqs:account:region:service-stage-queue
```

## Comparison with Serverless Framework Defaults

### Serverless Framework Default Patterns
| Resource Type | Default Pattern | Example |
|---------------|-----------------|---------|
| **CloudFormation Stack** | `{service}-{stage}` | `my-service-dev` |
| **Lambda Functions** | `{service}-{stage}-{functionName}` | `my-service-dev-hello` |
| **API Gateway** | `{stage}-{service}` | `dev-my-service` |
| **IAM Roles** | `{service}-{stage}-{region}-lambdaRole` | `my-service-dev-us-east-1-lambdaRole` |
| **Custom Resources** | `{LogicalId}-{RandomSuffix}` | `MyTable-ABC123DEF456` |

### ARN-Prefixer Plugin Patterns
| Plugin Variable | Pattern | Example |
|----------------|---------|---------|
| `${arn:prefix}` | `{service}-{stage}` | `my-service-dev` |
| `${arn:regionalPrefix}` | `{region}-{service}-{stage}` | `us-east-1-my-service-dev` |
| `${arn:globalPrefix}` | `{accountId}-{region}-{service}-{stage}` | `123456789012-us-east-1-my-service-dev` |

### Key Advantages
- ✅ **Consistency**: All resources follow the same naming convention
- ✅ **Predictability**: No random suffixes, deterministic resource names
- ✅ **Uniqueness**: Account ID inclusion ensures cross-account uniqueness
- ✅ **Multi-region**: Region awareness for global deployments
- ✅ **Enterprise Ready**: Suitable for complex, multi-tenant architectures

## Account ID Resolution

The plugin will attempt to get the AWS Account ID in the following order:

1. **Environment Variable**: If `AWS_ACCOUNT_ID` is set, it will use that value
2. **AWS STS**: If the environment variable is not set, it will call AWS STS `getCallerIdentity()` to retrieve the account ID
3. **Error**: If both methods fail, the plugin will throw an error and stop deployment

**Important**: You must either set the `AWS_ACCOUNT_ID` environment variable or have valid AWS credentials configured. The plugin will not proceed with deployment if it cannot determine the AWS Account ID.

## Compatibility

### Tested Versions
| Serverless Framework | Status | Features Tested |
|---------------------|--------|----------------|
| 2.72.4 | ✅ Fully Tested | Prefix properties + ARN builders + Error handling |
| 3.38.0 | ✅ Fully Tested | Prefix properties + ARN builders + Error handling |
| 4.18.2 | ✅ Fully Tested | Prefix properties + ARN builders + Error handling |

**Requirements:**
- **Serverless Framework**: 2.0.0 and above (supports v2, v3, and v4)
- **Node.js**: 14.0.0 and above

## Plugin Execution

The plugin runs during the `after:aws:common:validate:validate` hook, ensuring that all properties are available before your resources are processed.

## Example Output

### Successful execution:
```
ARN Prefixer: Injected custom.arn properties
  - accountId: 123456789012
  - stage: dev
  - service: my-service
  - region: us-east-1
  - prefix: my-service-dev
  - regionalPrefix: us-east-1-my-service-dev
  - globalPrefix: 123456789012-us-east-1-my-service-dev
```

### Error when Account ID cannot be determined:
```
ARN Prefixer Plugin Error: Cannot determine AWS Account ID.

Please either:
1. Set the AWS_ACCOUNT_ID environment variable, or
2. Ensure you have valid AWS credentials configured

AWS STS Error: The security token included in the request is expired
```