Skip to content

Commit

Permalink
ENV variable clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
docwho2 committed Nov 23, 2023
1 parent e573abc commit 83498e9
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 113 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@ The goal of the project is to develop Custom CDK components in Java that use AWS

Login to AWS Console and open a [Cloud Shell](https://aws.amazon.com/cloudshell/).

### Clone Repo and Install Maven/Java
### Clone Repo

```bash
sudo yum -y install maven
git clone https://github.com/docwho2/java-chime-voice-sdk-cdk.git
cd java-chime-voice-sdk-cdk

Expand Down
26 changes: 24 additions & 2 deletions config.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
# Common stuff between deploy and destroy scripts

# Stack name for the SMA general deployment
STACK_NAME=chime-sdk-cdk-provisioning
# Stack name
STACK_NAME=chime-voice-cdk-provision


# Regions we will deploy to (the only supported US regions for Chime PSTN SDK)
declare -a regions=( us-east-1 us-west-2)


# Special Vars you can enable that trigger creating more resources in the stacks
# You can use "export VOICE_CONNECTOR=TRUE" for example in shell before running deploy instead of changing here

# Will setup a Voice Connector so you can place SIP calls into SMA
# VOICE_CONNECTOR=TRUE

# Will add this IP address to Voice Connector termination allow list so you can place SIP calls into the SMA (You can always update in Console later as well)
# VOICE_CONNECTOR_ALLOW_IP=162.216.219.185

# If you have an existing phone number in Chime (in unassigned state) create SIP rule pointing number to SMA
# CHIME_PHONE_NUMBER

# If you have an Asterisk for example, create VC, and allow termination and origination to this IP (Implies VOICE_CONNECTOR=true)
# PBX_HOSTNAME=54.0.0.1


# Provision a phone number as part of the stack in the given area code (experimental and not recommended)
# It can take 15 mins to get a number in success state, recommend getting number first, then set CHIME_PHONE_NUMBER
# CHIME_AREA_CODE=612

6 changes: 6 additions & 0 deletions deploy.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
#!/bin/bash

# Exit immediately if a command exits with a non-zero status.
set -e

# Stack names and regions
source config.sh

ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

if [ "$AWS_EXECUTION_ENV" = "CloudShell" ]; then
Expand Down
72 changes: 47 additions & 25 deletions src/main/java/cloud/cleo/chimesma/cdk/ChimePhoneNumberStack.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,57 +14,79 @@
import software.amazon.awscdk.StackProps;
import software.amazon.awscdk.services.ssm.StringParameter;
import software.amazon.awscdk.services.ssm.StringParameterProps;
import static cloud.cleo.chimesma.cdk.InfrastructureApp.ENV_VARS.*;
import static cloud.cleo.chimesma.cdk.InfrastructureApp.getEnv;
import static cloud.cleo.chimesma.cdk.InfrastructureApp.hasEnv;

/**
* Stack to provision and Chime Phone number and create SIP rule pointing to it
* Stack to provision a Chime Phone number and create SIP rule pointing to it
*
* @author sjensen
*/
public class ChimePhoneNumberStack extends Stack {

private final static String CHIME_AREA_CODE = System.getenv("CHIME_AREA_CODE");

public ChimePhoneNumberStack(final App parent, final String id, List<ChimeSipMediaApp> smas) {
this(parent, id, null, smas);
}

public ChimePhoneNumberStack(final Construct parent, final String id, final StackProps props, List<ChimeSipMediaApp> smas) {
super(parent, id, props);

String phoneNumber;
ChimeWaitForNumber wait = null;
if (CHIME_AREA_CODE.length() < 12) {
// Existing Phone Number already provisioned, just create the Sip Rule
if (hasEnv(CHIME_PHONE_NUMBER)) {
final var phoneNumber = getEnv(CHIME_PHONE_NUMBER);
// Create SIP Rule pointing to the SMA's
new ChimeSipRulePhone(this, phoneNumber, smas);

new StringParameter(this, "PhoneNumParam", StringParameterProps.builder()
.parameterName("/" + getStackName() + "/CHIME_PHONE_NUMBER")
.description("The Phone Number that was created manually and provided to stack")
.stringValue(phoneNumber)
.build());

new CfnOutput(this, "PhoneNumber", CfnOutputProps.builder()
.description("The Phone Number that was created manually and provided to stack")
.value(phoneNumber)
.build());

} else if (hasEnv(CHIME_AREA_CODE)) {
// Search for a phone Number
final var search = new ChimePhoneNumberSearch(this, CHIME_AREA_CODE);
final var search = new ChimePhoneNumberSearch(this, getEnv(CHIME_AREA_CODE));

// Order the phone Number
final var order = new ChimePhoneNumberOrder(this, search.getPhoneNumber());

// Make sure the Phone Number is ready before creating SIP Rule
wait = new ChimeWaitForNumber(this, order.getOrderId());
final var wait = new ChimeWaitForNumber(this, order.getOrderId());

phoneNumber = search.getPhoneNumber();
} else {
phoneNumber = CHIME_AREA_CODE;
}
final var phoneNumber = search.getPhoneNumber();

// Create SIP Rule pointing to the SMA's
var sr = new ChimeSipRulePhone(this, phoneNumber, smas);
if (wait != null) {
// Create SIP Rule pointing to the SMA's
var sr = new ChimeSipRulePhone(this, phoneNumber, smas);

// Add the Dependancy ensure rule is not created until number finishes provisioning
sr.getNode().addDependency(wait);
}

new StringParameter(this, "PhoneNumParam", StringParameterProps.builder()
.parameterName("/" + getStackName() + "/CHIME_PHONE_NUMBER")
.description("The ARN for the Voice Connector")
.stringValue(phoneNumber)
.build());
new StringParameter(this, "PhoneNumParam", StringParameterProps.builder()
.parameterName("/" + getStackName() + "/CHIME_PHONE_NUMBER")
.description("The Phone Number that was provisioned")
.stringValue(phoneNumber)
.build());

new CfnOutput(this, "PhoneNumber", CfnOutputProps.builder()
.description("The Phone Number that was provisioned")
.value(phoneNumber)
.build());
new CfnOutput(this, "PhoneNumber", CfnOutputProps.builder()
.description("The Phone Number that was provisioned")
.value(phoneNumber)
.build());
}

// Kick out both SMA's just for fun, shows power of multi-region stack references
int count = 1;
for (var sma : smas) {
new CfnOutput(this, "sma-" + count++, CfnOutputProps.builder()
.description("The ID for the Session Media App (SMA)")
.value(sma.getSMAId())
.build());
}
}

}
87 changes: 70 additions & 17 deletions src/main/java/cloud/cleo/chimesma/cdk/InfrastructureApp.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,47 @@
package cloud.cleo.chimesma.cdk;

import static cloud.cleo.chimesma.cdk.InfrastructureApp.ENV_VARS.*;
import java.util.List;
import software.amazon.awscdk.App;
import software.amazon.awscdk.Environment;
import software.amazon.awscdk.StackProps;

public final class InfrastructureApp {
public final class InfrastructureApp extends App {

private static final String STACK_DESC = "Provision Chime Voice SDK resources (VoiceConnector, SIP Rule, SIP Media App)";

/**
* If set in the environment, setup Origination to point to it and allow from termination as well
* Environment Variables used to trigger features
*/
private final static String PBX_HOSTNAME = System.getenv("PBX_HOSTNAME");

private final static String TWILIO = System.getenv("TWILIO");
public enum ENV_VARS {
/**
* If set in the environment, setup Origination to point to Voice Connector and allow from termination as well
*/
PBX_HOSTNAME,
/**
* Provision Twilio SIP Trunk (experimental)
*/
TWILIO,
/**
* Attempt to provision a phone number in this area code (US only and experimental)
*/
CHIME_AREA_CODE,
/**
* Existing Phone number in Chime Voice. This will trigger pointing a SIP rule at this number
*/
CHIME_PHONE_NUMBER,
/**
* Provision a Voice Connector so SIP calls can be made in and out. Implied if PBX_HOSTNAME set.
*/
VOICE_CONNECTOR,
/**
* Single IP address to allow to call the Voice Connector (Cannot be private range or will fail)
*/
VOICE_CONNECTOR_ALLOW_IP
}

private final static String CHIME_AREA_CODE = System.getenv("CHIME_AREA_CODE");

public static void main(final String[] args) {
final var app = new App();
final var app = new InfrastructureApp();

// Required Param
String accountId = (String) app.getNode().tryGetContext("accountId");
Expand All @@ -33,40 +55,71 @@ public static void main(final String[] args) {
final var east = new InfrastructureStack(app, "east", StackProps.builder()
.description(STACK_DESC)
.stackName(stackName)
.env(makeEnv(accountId, regionEast))
.env(makeStackEnv(accountId, regionEast))
.build());

final var west = new InfrastructureStack(app, "west", StackProps.builder()
.description(STACK_DESC)
.stackName(stackName)
.env(makeEnv(accountId, regionWest))
.env(makeStackEnv(accountId, regionWest))
.build());

if (TWILIO != null && !TWILIO.isBlank()) {
if (hasEnv(TWILIO)) {
new TwilioStack(app, "twilio", StackProps.builder()
.description("Provision Twilio Resources")
.stackName(stackName + "-twilio")
.env(makeEnv(accountId, regionEast))
.env(makeStackEnv(accountId, regionEast))
.crossRegionReferences(Boolean.TRUE)
.build(), east.getVCHostName(), west.getVCHostName());
}

// Provision Chime Phone Number if area code provided
//
if (CHIME_AREA_CODE != null && ! CHIME_AREA_CODE.isBlank()) {
new ChimePhoneNumberStack(app, "phone", StackProps.builder()
if (hasEnv(CHIME_AREA_CODE, CHIME_PHONE_NUMBER)) {
new ChimePhoneNumberStack(app, "phone", StackProps.builder()
.description("Provision Chime Phone Number")
.stackName(stackName + "-phone")
.env(makeEnv(accountId, regionEast))
.env(makeStackEnv(accountId, regionEast))
.crossRegionReferences(Boolean.TRUE)
.build(), List.of(east.getSMA(), west.getSMA()));
}

app.synth();

}

static Environment makeEnv(String accountId, String region) {
/**
* Get the value for one of the ENV variables
* @param envVar
* @return
*/
public static String getEnv(ENV_VARS envVar) {
return System.getenv(envVar.name());
}

/**
* Is any of the provided env vars set (OR condition)
*
* @param envVars
* @return
*/
public static boolean hasEnv(ENV_VARS... envVars) {
for (var envVar : envVars) {
final var env = getEnv(envVar);
if (env != null && !env.isBlank()) {
return true;
}
}
return false;
}

/**
* Create a Stack Environment
* @param accountId
* @param region
* @return
*/
static Environment makeStackEnv(String accountId, String region) {
return Environment.builder()
.account(accountId)
.region(region)
Expand Down
41 changes: 15 additions & 26 deletions src/main/java/cloud/cleo/chimesma/cdk/InfrastructureStack.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
import cloud.cleo.chimesma.cdk.customresources.ChimeSipMediaApp;
import cloud.cleo.chimesma.cdk.customresources.ChimeSipRuleVC;
import cloud.cleo.chimesma.cdk.resources.ChimeSMAFunction;
import java.util.ArrayList;
import static cloud.cleo.chimesma.cdk.InfrastructureApp.ENV_VARS.*;
import static cloud.cleo.chimesma.cdk.InfrastructureApp.hasEnv;
import java.util.List;
import software.amazon.awscdk.App;
import software.amazon.awscdk.CfnOutput;
import software.amazon.awscdk.CfnOutputProps;
import software.amazon.awscdk.services.ec2.AclCidr;
import software.constructs.Construct;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;
import software.amazon.awscdk.services.lambda.Function;
Expand All @@ -24,22 +23,16 @@
*/
public class InfrastructureStack extends Stack {

/**
* If set in the environment, setup Origination to point to it and allow from termination as well
*/
private final static String PBX_HOSTNAME = System.getenv("PBX_HOSTNAME");
private final static String VOICE_CONNECTOR = System.getenv("VOICE_CONNECTOR");

private final ChimeVoiceConnector vc;

private final ChimeSipMediaApp sma;

public InfrastructureStack(final App parent, final String id) {
this(parent, id, null);
public InfrastructureStack(final App app, final String id) {
this(app, id, null);
}

public InfrastructureStack(final Construct parent, final String id, final StackProps props) {
super(parent, id, props);
public InfrastructureStack(final App app, final String id, final StackProps props) {
super(app, id, props);

// Simple SMA Handler that speaks prompt and hangs up
Function lambda = new ChimeSMAFunction(this, "sma-lambda");
Expand All @@ -64,22 +57,12 @@ public InfrastructureStack(final Construct parent, final String id, final StackP
.value(sma.getSMAId())
.build());

final boolean hasPBX = PBX_HOSTNAME != null && !PBX_HOSTNAME.isBlank();
final boolean hasVC = VOICE_CONNECTOR != null && !VOICE_CONNECTOR.isBlank();

String vc_arn = "PSTN";
if (hasVC || hasPBX) {
// Start with list of Twilio NA ranges for SIP Trunking
var cidrAllowList = List.of(AclCidr.ipv4("54.172.60.0/30"), AclCidr.ipv4("54.244.51.0/30"),
// Europe, Ireland and Frankfurt
AclCidr.ipv4("54.171.127.192/30"), AclCidr.ipv4("35.156.191.128/30"));
if (hasPBX) {
cidrAllowList = new ArrayList(cidrAllowList);
cidrAllowList.add(AclCidr.ipv4(PBX_HOSTNAME + "/32"));
}

if ( hasEnv(VOICE_CONNECTOR,PBX_HOSTNAME) ) {

// Voice Connector
vc = new ChimeVoiceConnector(this, cidrAllowList, PBX_HOSTNAME);
vc = new ChimeVoiceConnector(this);

// SIP rule that associates the SMA with the Voice Connector
new ChimeSipRuleVC(this, vc, List.of(sma));
Expand All @@ -94,6 +77,12 @@ public InfrastructureStack(final Construct parent, final String id, final StackP
.description("The Hostname for the Voice Connector")
.value(vc.getOutboundName())
.build());

// throw out a SIP URL so you can call it with your favor SIP App (after adding IP to VC manually)
new CfnOutput(this, "SIPUri", CfnOutputProps.builder()
.description("SIP Uri to call into the Session Media App")
.value("sip:+17035550122@" + vc.getOutboundName())
.build());

// If VC was created set to ARN otherwise leave at PSTN
vc_arn = vc.getArn();
Expand Down
6 changes: 1 addition & 5 deletions src/main/java/cloud/cleo/chimesma/cdk/TwilioStack.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,12 @@
import software.amazon.awscdk.StackProps;

/**
* CDK Stack
* CDK Stack
*
* @author sjensen
*/
public class TwilioStack extends Stack {

/**
* If set in the environment, setup Origination to point to it and allow from termination as well
*/
private final static String PBX_HOSTNAME = System.getenv("PBX_HOSTNAME");

public TwilioStack(final App parent, final String id, String vc1, String vc2) {
this(parent, id, null,vc1,vc2);
Expand Down
Loading

0 comments on commit 83498e9

Please sign in to comment.