# Seer — Deployment Guide (v1.2)

> **Automated AWS service intelligence for your Organization — discover new features, optimize costs, and modernize workloads.**
> Works on Mac and Windows | CLI one-click or AWS Console

⏱️ **CLI: ~5 minutes (one command). Console: ~10-20 minutes.**

---

## What Is Seer?

Seer is an automated intelligence platform that scans all AWS services running across your AWS Organization and provides targeted, prescriptive guidance on new AWS features, cost optimizations, and modernization opportunities.

**What it delivers:** Periodic recommendations matched to your actual infrastructure — what to upgrade, what to optimize, and what new services could improve your workloads.

**How it works:** A serverless pipeline (Step Functions + Lambda + Bedrock) discovers services across all member accounts, cross-references them against the latest AWS announcements, and generates scored recommendations delivered via dashboard, email, or S3.

There are two deployment options:
- **MVP** — Minimal proof-of-concept (16 resources, ~3 min deploy)
- **Full Production** — Complete platform with observability, security, and all delivery channels (~88 resources, ~10-15 min deploy)

Both options require deploying the cross-account member role first.

> **What's automated in the Full Production template:**
> - SNS alarm email subscription — auto-subscribes the first email recipient
> - SES email identity — auto-registers the first email recipient as a sender
> - No manual SNS or SES console steps needed (just confirm the emails that arrive)

---

## Prerequisites

Before you begin, confirm you have:

- [ ] **AWS Organizations** enabled with a management account
- [ ] **Your Organization ID** (starts with `o-`, found in AWS Organizations console)
- [ ] **Admin access** to the management account (or permissions to create CloudFormation stacks, IAM roles, Lambda functions, DynamoDB tables, S3 buckets, Step Functions)
- [ ] The Seer template files downloaded (see [Download Templates](#download-templates) below)

For **Full Production** only:
- [ ] **An email address** you control (for alarm notifications and SES sender — verification emails are sent automatically during deployment)
- [ ] **A domain prefix** for the Cognito User Pool (or willingness to use the default)

---

## CLI Deployment (Recommended)

The fastest way to deploy Seer. One script handles everything: Organizations access, Lambda packaging, S3 upload, StackSet, CloudFormation, and a test execution.

### Quick Start

```bash
cd <path-to-seer>/deploy
chmod +x build-lambda.sh deploy-seer.sh

# One command does it all:
./deploy-seer.sh --profile aws-mgmt
```

The script auto-detects your Organization ID and root OU. Override with flags:

```bash
./deploy-seer.sh \
  --profile aws-mgmt \
  --region us-east-1 \
  --org-id <YOUR-ORG-ID> \
  --included-regions us-east-1,us-west-2 \
  --schedule "rate(1 day)"
```

Use `--dry-run` to preview without making changes.

### What deploy-seer.sh does

1. Verifies AWS credentials
2. Detects Organization ID and root OU
3. Enables CloudFormation StackSets Organizations access
4. Builds the Lambda package (calls `build-lambda.sh`)
5. Creates the deploy S3 bucket and uploads Lambda code
6. Deploys the `SeerMemberRole` StackSet to all member accounts
7. Deploys the Seer MVP CloudFormation stack
8. Runs a test execution and waits for completion
9. Prints outputs and next steps

### Rebuilding the Lambda package

If you modify the Seer source code, rebuild the Lambda zip before deploying:

```bash
./build-lambda.sh
```

This packages the `seer/` source code with all dependencies (`ulid`, `requests`) and the `index.py` handler into `seer-lambda-mvp.zip`.

### Troubleshooting

| Error | Cause | Fix |
|-------|-------|-----|
| `You must enable organizations access` | CloudFormation StackSets needs Organizations trusted access | `deploy-seer.sh` handles this automatically. If running manually: `aws organizations enable-aws-service-access --service-principal member.org.stacksets.cloudformation.amazonaws.com` |
| `Unresolved resource dependencies [SeerKmsKey]` | Old template references a KMS key not in MVP | Use the v1.1+ template — it uses `AES256` instead |
| `No module named 'index'` | Lambda zip missing `index.py` handler | Run `build-lambda.sh` to rebuild the zip with the handler |
| `No module named 'ulid'` or `'requests'` | Third-party deps not bundled | Run `build-lambda.sh` — it installs deps or creates built-in shims |
| `lambda:InvokeFunction ... not authorized` | Step Functions role missing Lambda invoke permission | Use the v1.1+ template — it includes the inline IAM policy |
| `EarlyValidation::PropertyValidation` | Lambda `Code` property missing | Use the v1.1+ template — all 4 Lambdas have `S3Bucket`/`S3Key` |
| `InvalidInputException: unrecognized service principal` | Wrong service principal for Organizations access | Use `member.org.stacksets.cloudformation.amazonaws.com` (not `stacksets.cloudformation.amazonaws.com`) |

---

## Console Deployment (Alternative)

If you prefer clicking through the AWS Console instead of CLI, follow the steps below. **Before starting:** run `build-lambda.sh` to create the Lambda zip, then upload it to S3 manually:

```bash
cd <path-to-seer>/deploy
./build-lambda.sh
aws s3 mb s3://seer-deploy-$(aws sts get-caller-identity --query Account --output text) --profile aws-mgmt
aws s3 cp seer-lambda-mvp.zip s3://seer-deploy-$(aws sts get-caller-identity --query Account --output text)/ --profile aws-mgmt
```

The template files are in the `Seer/deploy/` folder (wherever you downloaded/cloned the Seer package).

## Download Templates

Download the Seer templates from your team's shared resource site, or request them from the Seer administrator.

You'll need these files:
- `seer-member-role.yaml` — Cross-account discovery role (Phase 1)
- `seer-mvp.yaml` — MVP deployment (Phase 2A)
- `seer-full.yaml` — Full production deployment (Phase 2B)
- `seer-unified.yaml` — Unified template for MVP→Full upgrades
- `seer-lambda.zip` — Lambda function code package

Save them to a folder on your Desktop:

**Mac:**
```bash
mkdir -p ~/Desktop/seer-templates
```

**Windows (PowerShell):**
```powershell
New-Item -ItemType Directory -Path "$env:USERPROFILE\Desktop\seer-templates" -Force
```

> **💡 Advanced: Regenerate templates** (e.g., with your real Org ID pre-filled):
> ```bash
> python3 -m seer_deployment_templates --profile both --org-id o-YOUR-ORG-ID --output-dir ~/Desktop/seer-templates/
> ```
> (Requires the Seer template generator package — ask your Seer administrator if you need it.)

---

## Phase 1: Deploy the Cross-Account Member Role

This IAM role is deployed to every member account in your Organization so Seer can discover their running services. You deploy it once via **CloudFormation StackSets** from the management account.

### Step 1.1 — Open CloudFormation StackSets

1. Sign in to the **AWS Management Console** as the management account
2. Navigate to **CloudFormation** → **StackSets** (left sidebar)
3. Click **Create StackSet**

### Step 1.2 — Upload the Member Role Template

1. Under **Prerequisite**, select **Service-managed permissions** (this lets StackSets deploy to all accounts in your Organization automatically)
2. Under **Specify template**, select **Upload a template file**
3. Click **Choose file** and select the `seer-member-role.yaml` template from your seer-templates folder
4. Click **Next**

### Step 1.3 — Configure Parameters

1. **StackSet name**: `SeerMemberRole`
2. **StackSet description**: `Cross-account IAM role for Seer service discovery`
3. Fill in the parameters:
   - **ManagementAccountId**: Your 12-digit management account ID (e.g., `123456789012`). Find this in the top-right corner of the console.
   - **ExternalId**: A unique string for security (e.g., `seer-ext-abc123`). Write this down — you'll need it when configuring Seer.
4. Click **Next**

### Step 1.4 — Set Deployment Options

1. Under **Add stacks to stack set**, select **Deploy to organization**
2. Under **Automatic deployment**, enable **Activated** (so new accounts automatically get the role)
3. Under **Account removal behavior**, select **Delete stacks**
4. Under **Specify regions**, select **one region** (IAM roles are global — you only need one region, e.g., `us-east-1`)
5. Under **Deployment options**:
   - **Maximum concurrent accounts**: `10` (or higher for large orgs)
   - **Failure tolerance**: `1`
6. Click **Next**

### Step 1.5 — Review and Submit

1. Check the box: **I acknowledge that AWS CloudFormation might create IAM resources with custom names**
2. Click **Submit**
3. Wait for the StackSet to show **SUCCEEDED** status (usually 2-5 minutes)

### Step 1.6 — Verify

1. Go to **CloudFormation** → **StackSets** → **SeerMemberRole** → **Stack instances**
2. Confirm all member accounts show **CURRENT** status
3. Optionally, switch to a member account and verify the `SeerDiscoveryRole` exists in **IAM** → **Roles**

---

## Phase 2A: Deploy Seer MVP

Use this if you want a quick proof-of-concept with just the core pipeline.

### Step 2A.1 — Open CloudFormation

1. Navigate to **CloudFormation** → **Stacks** (in the management account)
2. Click **Create stack** → **With new resources (standard)**

### Step 2A.2 — Upload the MVP Template

1. Under **Specify template**, select **Upload a template file**
2. Click **Choose file** and select the `seer-mvp.yaml` template from your seer-templates folder
3. Click **Next**

### Step 2A.3 — Configure Stack Parameters

1. **Stack name**: `seer-mvp`
2. Fill in the parameters:

| Parameter | Value | Notes |
|-----------|-------|-------|
| **OrganizationId** | `o-your-org-id` | From AWS Organizations console |
| **IncludedAccounts** | *(leave empty)* | Empty = scan all accounts |
| **ExcludedAccounts** | *(leave empty)* | Optional — comma-separated account IDs to skip |
| **IncludedRegions** | `us-east-1,us-west-2` | Regions to scan (comma-separated) |
| **ExcludedRegions** | *(leave empty)* | Optional |
| **ScheduleExpression** | `rate(1 day)` | How often Seer runs |

3. Click **Next**

### Step 2A.4 — Configure Stack Options

1. **Tags** (optional): Add `Project: Seer`, `Environment: Dev`
2. **Permissions**: Leave as default (CloudFormation will use your current role)
3. **Stack failure options**: Select **Roll back all stack resources**
4. Click **Next**

### Step 2A.5 — Review and Create

1. Scroll to the bottom
2. Check the box: **I acknowledge that AWS CloudFormation might create IAM resources with custom names**
3. Click **Submit**
4. Wait for the stack to show **CREATE_COMPLETE** (usually 3-5 minutes)

### Step 2A.6 — Verify MVP Deployment

1. Go to the **Outputs** tab of the `seer-mvp` stack
2. Note the key outputs:
   - **StateMachineArn** — the Step Functions pipeline
   - **DataBucketName** — where recommendations are stored
   - **ScheduleRuleArn** — the EventBridge schedule
3. Navigate to **Step Functions** → find `seer-pipeline` → click **Start execution** to trigger a test run
4. After the run completes (~2-5 minutes), check the S3 data bucket for recommendation output files

---

## Phase 2B: Deploy Seer Full Production

Use this for a complete deployment with dashboard, email delivery, observability, and security hardening.

### Step 2B.1 — Open CloudFormation

1. Navigate to **CloudFormation** → **Stacks** (in the management account)
2. Click **Create stack** → **With new resources (standard)**

### Step 2B.2 — Upload the Full Template

1. Under **Specify template**, select **Upload a template file**
2. Click **Choose file** and select the `seer-full.yaml` template from your seer-templates folder
3. Click **Next**

### Step 2B.3 — Configure Stack Parameters

1. **Stack name**: `seer-production`
2. Fill in the parameters:

| Parameter | Value | Notes |
|-----------|-------|-------|
| **OrganizationId** | `o-your-org-id` | From AWS Organizations console |
| **IncludedAccounts** | *(leave empty)* | Empty = scan all accounts |
| **ExcludedAccounts** | *(leave empty)* | Optional |
| **IncludedRegions** | `us-east-1,us-west-2,eu-west-1` | Regions to scan |
| **ExcludedRegions** | *(leave empty)* | Optional |
| **ScheduleExpression** | `cron(0 8 * * ? *)` | Daily at 8 AM UTC |
| **EmailRecipients** | `your-team@example.com` | SES-verified email(s), comma-separated |
| **CognitoDomain** | `seer-prod` | Prefix for Cognito hosted UI domain |
| **EnableWaf** | `true` | WAF protection on API Gateway |
| **EnableXray** | `true` | X-Ray distributed tracing |
| **RetentionDays** | `90` | CloudWatch log retention |

3. Click **Next**

### Step 2B.4 — Configure Stack Options

1. **Tags**: Add `Project: Seer`, `Environment: Production`
2. **Permissions**: Leave as default
3. **Stack failure options**: Select **Roll back all stack resources**
4. **Advanced options** → **Termination protection**: **Enabled** (recommended for production)
5. Click **Next**

### Step 2B.5 — Review and Create

1. Scroll to the bottom
2. Check **both** boxes:
   - **I acknowledge that AWS CloudFormation might create IAM resources with custom names**
   - **I acknowledge that AWS CloudFormation might require the following capability: CAPABILITY_AUTO_EXPAND** (if shown)
3. Click **Submit**
4. Wait for **CREATE_COMPLETE** (10-15 minutes — CloudFront distribution takes ~5 min)

### Step 2B.6 — Verify Full Production Deployment

1. Go to the **Outputs** tab of the `seer-production` stack
2. Key outputs to note:

| Output | What it is |
|--------|-----------|
| **ApiGatewayUrl** | REST API endpoint for on-demand runs |
| **DashboardUrl** | CloudFront URL for the web dashboard |
| **CognitoUserPoolId** | User Pool for authentication |
| **CognitoClientId** | App Client ID for the dashboard |
| **StateMachineArn** | Step Functions pipeline |
| **AlarmTopicArn** | SNS topic for failure alerts |
| **KmsKeyArn** | Encryption key |

3. **Test the pipeline**: Go to **Step Functions** → `seer-pipeline` → **Start execution**
4. **Access the dashboard**: Open the **DashboardUrl** in your browser. You'll be prompted to sign in via Cognito — create a user first in the Cognito console.
5. **Check email delivery**: After a run completes, verify the summary email arrives at your configured recipients.
6. **Check alarms**: Go to **CloudWatch** → **Alarms** → verify `seer-pipeline-failure` alarm exists.

---

## Phase 3: Create Your First Cognito User (Full Production Only)

> **This step is now automated.** The full production template auto-creates an admin user using the first email in your `EmailRecipients` parameter. A temporary password is sent to that email during deployment.

1. Check the inbox of your `EmailRecipients` email
2. Look for an email from **Amazon Cognito** with a temporary password
3. Open the **DashboardUrl** from the stack outputs
4. Sign in with your email and the temporary password
5. Set a new permanent password when prompted

---

## Phase 4: Confirm Automated Email Subscriptions (Full Production Only)

The full production template automatically creates two email subscriptions during deployment. You just need to confirm them:

### Step 4.1 — Confirm SNS Alarm Subscription

1. Check the inbox of the **first email** in your `EmailRecipients` parameter
2. Look for an email from **AWS Notifications** with subject "AWS Notification - Subscription Confirmation"
3. Click **Confirm subscription** in the email
4. You'll now receive alerts when Seer pipeline runs fail

### Step 4.2 — Verify SES Email Identity

1. Check the same inbox for an email from **Amazon Web Services** with subject "Amazon SES Email Identity Verification"
2. Click the **verification link** in the email
3. This allows Seer to send recommendation emails from this address

> Both emails arrive within 1-2 minutes of stack creation completing. If you don't see them, check your spam folder.

---

## Troubleshooting

### Stack creation fails with "Access Denied"
- Ensure your IAM user/role has `cloudformation:*`, `iam:*`, `lambda:*`, `dynamodb:*`, `s3:*`, `states:*`, `events:*` permissions
- For full production: also need `apigateway:*`, `cognito-idp:*`, `wafv2:*`, `cloudfront:*`, `ses:*`, `sns:*`, `kms:*`, `logs:*`, `cloudtrail:*`

### Stack creation fails with "Resource limit exceeded"
- Check your account's CloudFormation stack resource limit (default 500 per stack)
- The full template uses ~85 resources — well within limits

### Step Functions execution fails
- Check **CloudWatch Logs** for the Lambda function that failed
- Common issue: `SeerDiscoveryRole` not deployed to member accounts (Phase 1 incomplete)
- Common issue: Lambda timeout — increase timeout in the stack parameters

### No recommendations generated
- Verify the Organization has running services in the scanned regions
- Check the intelligence sources are reachable (AWS What's New feed, etc.)
- Check the S3 data bucket for intermediate files (`inventory/`, `intelligence/`)

### Dashboard shows "Unauthorized"
- Create a Cognito user (Phase 3)
- Verify the Cognito domain is correctly configured
- Check browser console for CORS errors

### StackSet deployment stuck on PENDING
- Check that AWS Organizations trust is enabled for CloudFormation StackSets
- Go to **Organizations** → **Services** → **CloudFormation StackSets** → **Enable trusted access**

### Lambda code not found during stack creation
- Ensure the `seer-lambda.zip` file is uploaded to the S3 bucket referenced in the template's `CodeUri` parameter
- If deploying via console (not CLI), you may need to upload the zip to S3 first and update the template's `CodeUri` to point to `s3://your-bucket/seer-lambda.zip`

---

## Cleanup

To remove Seer completely:

1. **Delete the main stack**: CloudFormation → Stacks → `seer-mvp` or `seer-production` → **Delete**
2. **Delete the StackSet**: CloudFormation → StackSets → `SeerMemberRole` → **Delete stacks from stack set** (all accounts) → then **Delete StackSet**
3. **Empty and delete S3 buckets**: The data bucket has `DeletionPolicy: Retain` — you'll need to empty and delete it manually in the S3 console

---

## Quick Reference

### One-Click Deploy (CLI)

For those who prefer the command line, a deploy script handles everything:

```bash
# Make the script executable (one time)
chmod +x deploy.sh

# Deploy MVP
./deploy.sh --profile mvp --org-id o-YOUR-ORG-ID

# Deploy Full Production with email
./deploy.sh --profile full --org-id o-YOUR-ORG-ID --email team@example.com

# Deploy Full Production + member role via StackSets
./deploy.sh --profile full --org-id o-YOUR-ORG-ID --email team@example.com --deploy-member-role
```

The `deploy.sh` script is included in the downloaded templates folder. It automatically: packages Lambda code → uploads to S3 → deploys CloudFormation → applies stack policy → shows outputs.

---

## Unified Template — Upgrade MVP → Full via Parameter Change

When you generate templates with `--profile both`, a **unified template** (`seer-unified.yaml`) is produced alongside the separate MVP and Full templates. The unified template contains ALL resources but uses a `DeploymentProfile` CloudFormation parameter to control which resources are created.

### How it works

- The `DeploymentProfile` parameter accepts `mvp` (default) or `full`
- Full-only resources have a `Condition: IsFullProfile` that only creates them when `DeploymentProfile=full`
- **Upgrading from MVP to Full is a stack UPDATE** — just change the parameter value

### Deploy with the unified template

```bash
# Deploy as MVP initially
aws cloudformation deploy \
  --template-file seer-unified.yaml \
  --stack-name seer-platform \
  --capabilities CAPABILITY_NAMED_IAM \
  --parameter-overrides \
    OrganizationId=o-YOUR-ORG-ID \
    DeploymentProfile=mvp

# Later, upgrade to Full by changing the parameter
aws cloudformation deploy \
  --template-file seer-unified.yaml \
  --stack-name seer-platform \
  --capabilities CAPABILITY_NAMED_IAM \
  --parameter-overrides \
    OrganizationId=o-YOUR-ORG-ID \
    DeploymentProfile=full \
    EmailRecipients=team@example.com
```

No need to delete and recreate the stack — your data bucket and DynamoDB tables are preserved.

---

## Stack Policy Protection

A `stack-policy.json` file is generated alongside the templates. It prevents accidental deletion or replacement of critical stateful resources during stack updates:

- **SeerDataBucket** — S3 bucket containing all Seer data
- **SeerRecommendationsTable** — DynamoDB table with recommendation history

The deploy script automatically applies this policy. For manual deployments:

```bash
aws cloudformation set-stack-policy \
  --stack-name seer-platform \
  --stack-policy-body file://stack-policy.json
```

To temporarily override the policy for a specific update (e.g., intentional table replacement):

```bash
aws cloudformation update-stack \
  --stack-name seer-platform \
  --template-body file://seer-full.yaml \
  --stack-policy-during-update-body '{"Statement":[{"Effect":"Allow","Action":"Update:*","Principal":"*","Resource":"*"}]}'
```

---

## Auto-Detect Regions (Full Profile)

The Full Production template includes a **region auto-detection** Custom Resource that calls `ec2:DescribeRegions` to discover all enabled regions in your account.

- If you leave the `IncludedRegions` parameter empty, Seer can use the auto-detected regions
- The detected regions are available via `!GetAtt SeerDetectedRegions.Regions`
- This is useful for organizations that enable new regions over time — Seer automatically picks them up

The auto-detection resources include:
- `SeerRegionDetectorRole` — IAM role with `ec2:DescribeRegions` permission
- `SeerRegionDetectorFunction` — Lambda function (inline Python) that calls the API
- `SeerDetectedRegions` — Custom Resource that invokes the detector

---

## Service Catalog — Self-Service Deployment

For organizations that want governed, self-service Seer deployment, a **Service Catalog template** (`seer-service-catalog.yaml`) is generated when using `--profile both`.

### What it creates

- **Portfolio**: "Seer Advisory Platform" — groups the Seer product
- **Product**: References the unified template so users can choose MVP or Full
- **Launch Role**: IAM role with permissions to create all Seer resources
- **Launch Constraint**: Ensures all deployments use the governed launch role

### Deploy the Service Catalog product

1. Upload `seer-unified.yaml` to an S3 bucket
2. Deploy the Service Catalog template:

```bash
aws cloudformation deploy \
  --template-file seer-service-catalog.yaml \
  --stack-name seer-service-catalog \
  --capabilities CAPABILITY_NAMED_IAM \
  --parameter-overrides \
    UnifiedTemplateUrl=https://s3.amazonaws.com/my-bucket/seer-unified.yaml \
    PortfolioOwner="Cloud Platform Team" \
    SupportEmail=cloud-platform@example.com
```

3. Grant access to the portfolio for IAM users/groups/roles via the Service Catalog console
4. Users can then launch Seer from the Service Catalog console with governed parameters

---

### Profile Comparison

| Item | MVP | Full Production |
|------|-----|----------------|
| Deploy time | ~3 min | ~10-15 min |
| Resources | 16 | ~88 |
| Dashboard | ❌ | ✅ CloudFront + S3 |
| Email delivery | ❌ | ✅ SES (auto-verified) |
| S3 output | ✅ | ✅ |
| Observability | ❌ | ✅ CloudWatch + X-Ray |
| WAF | ❌ | ✅ |
| Encryption | AWS-managed | KMS customer-managed |
| DLQs | ❌ | ✅ |
| Alarm subscription | ❌ | ✅ Auto-subscribed |
| SES verification | ❌ | ✅ Auto-registered |
| Manual post-deploy steps | 0 | 2 (confirm emails) |
| Cost (idle) | ~$0/month | ~$5-10/month (CloudTrail, KMS) |
| Cost (active) | ~$1-5/run | ~$2-10/run |

---

Built by [@sinyouse](https://phonetool.amazon.com/users/sinyouse) | May 2026 | v1.1
