Skip to content

Commit 0deea61

Browse files
hoegertnRomainMuller
authored andcommitted
feat(alexa-ask): Add deploy action for Alexa (#1613)
1 parent 7d7d495 commit 0deea61

File tree

7 files changed

+345
-1
lines changed

7 files changed

+345
-1
lines changed

Diff for: packages/@aws-cdk/alexa-ask/README.md

+48
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,51 @@
33
```ts
44
const alexaAsk = require('@aws-cdk/alexa-ask');
55
```
6+
7+
### Alexa as deploy target for CodePipeline
8+
9+
You can deploy to Alexa using CodePipeline with the following DeployAction.
10+
11+
```ts
12+
// Read the secrets from ParameterStore
13+
const clientId = new cdk.SecretParameter(stack, 'AlexaClientId', {ssmParameter: '/Alexa/ClientId'});
14+
const clientSecret = new cdk.SecretParameter(stack, 'AlexaClientSecret', {ssmParameter: '/Alexa/ClientSecret'});
15+
const refreshToken = new cdk.SecretParameter(stack, 'AlexaRefreshToken', {ssmParameter: '/Alexa/RefreshToken'});
16+
17+
// Add deploy action
18+
new alexa.AlexaSkillDeployAction(stack, 'DeploySkill', {
19+
stage: deployStage,
20+
runOrder: 1,
21+
inputArtifact: sourceAction.outputArtifact,
22+
clientId: clientId.value,
23+
clientSecret: clientSecret.value,
24+
refreshToken: refreshToken.value,
25+
skillId: 'amzn1.ask.skill.12345678-1234-1234-1234-123456789012',
26+
});
27+
```
28+
29+
If you need manifest overrides you can specify them as `overrideArtifact` in the action.
30+
31+
```ts
32+
// Deploy some CFN change set and store output
33+
const executeChangeSetAction = new PipelineExecuteChangeSetAction(this, 'ExecuteChangesTest', {
34+
stage: deployStage,
35+
runOrder: 2,
36+
stackName,
37+
changeSetName,
38+
outputFileName: 'overrides.json',
39+
outputArtifactName: 'CloudFormation',
40+
});
41+
42+
// Provide CFN output as manifest overrides
43+
new AlexaSkillDeployAction(this, 'DeploySkill', {
44+
stage: deployStage,
45+
runOrder: 1,
46+
inputArtifact: sourceAction.outputArtifact,
47+
parameterOverridesArtifact: executeChangeSetAction.outputArtifact,
48+
clientId: clientId.value,
49+
clientSecret: clientSecret.value,
50+
refreshToken: refreshToken.value,
51+
skillId: 'amzn1.ask.skill.12345678-1234-1234-1234-123456789012',
52+
});
53+
```

Diff for: packages/@aws-cdk/alexa-ask/lib/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
// Alexa::ASK CloudFormation Resources:
22
export * from './ask.generated';
3+
export * from './pipeline-actions';

Diff for: packages/@aws-cdk/alexa-ask/lib/pipeline-actions.ts

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import codepipeline = require('@aws-cdk/aws-codepipeline-api');
2+
import cdk = require('@aws-cdk/cdk');
3+
4+
/**
5+
* Construction properties of the {@link AlexaSkillDeployAction Alexa deploy Action}.
6+
*/
7+
export interface AlexaSkillDeployActionProps extends codepipeline.CommonActionProps,
8+
codepipeline.CommonActionConstructProps {
9+
10+
/**
11+
* The client id of the developer console token
12+
*/
13+
clientId: cdk.Secret;
14+
15+
/**
16+
* The client secret of the developer console token
17+
*/
18+
clientSecret: cdk.Secret;
19+
20+
/**
21+
* The refresh token of the developer console token
22+
*/
23+
refreshToken: cdk.Secret;
24+
25+
/**
26+
* The Alexa skill id
27+
*/
28+
skillId: string;
29+
30+
/**
31+
* The source artifact containing the voice model and skill manifest
32+
*/
33+
inputArtifact?: codepipeline.Artifact;
34+
35+
/**
36+
* An optional artifact containing overrides for the skill manifest
37+
*/
38+
parameterOverridesArtifact?: codepipeline.Artifact;
39+
}
40+
41+
/**
42+
* Deploys the skill to Alexa
43+
*/
44+
export class AlexaSkillDeployAction extends codepipeline.DeployAction {
45+
constructor(scope: cdk.Construct, id: string, props: AlexaSkillDeployActionProps) {
46+
super(scope, id, {
47+
...props,
48+
artifactBounds: {
49+
minInputs: 1,
50+
maxInputs: 2,
51+
minOutputs: 0,
52+
maxOutputs: 0,
53+
},
54+
owner: 'ThirdParty',
55+
provider: 'AlexaSkillsKit',
56+
configuration: {
57+
ClientId: props.clientId,
58+
ClientSecret: props.clientSecret,
59+
RefreshToken: props.refreshToken,
60+
SkillId: props.skillId,
61+
},
62+
});
63+
if (props.parameterOverridesArtifact) {
64+
this.addInputArtifact(props.parameterOverridesArtifact);
65+
}
66+
}
67+
}

Diff for: packages/@aws-cdk/alexa-ask/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,14 @@
6161
"pkglint": "^0.22.0"
6262
},
6363
"dependencies": {
64+
"@aws-cdk/aws-codepipeline-api": "^0.22.0",
6465
"@aws-cdk/cdk": "^0.22.0"
6566
},
6667
"peerDependencies": {
68+
"@aws-cdk/aws-codepipeline-api": "^0.22.0",
6769
"@aws-cdk/cdk": "^0.22.0"
6870
},
6971
"engines": {
7072
"node": ">= 8.10.0"
7173
}
72-
}
74+
}

Diff for: packages/@aws-cdk/aws-codepipeline/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
},
6161
"license": "Apache-2.0",
6262
"devDependencies": {
63+
"@aws-cdk/alexa-ask": "^0.22.0",
6364
"@aws-cdk/assert": "^0.22.0",
6465
"@aws-cdk/aws-cloudformation": "^0.22.0",
6566
"@aws-cdk/aws-cloudtrail": "^0.22.0",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
{
2+
"Resources": {
3+
"PipelineArtifactsBucket22248F97": {
4+
"Type": "AWS::S3::Bucket",
5+
"DeletionPolicy": "Retain"
6+
},
7+
"PipelineRoleD68726F7": {
8+
"Type": "AWS::IAM::Role",
9+
"Properties": {
10+
"AssumeRolePolicyDocument": {
11+
"Statement": [
12+
{
13+
"Action": "sts:AssumeRole",
14+
"Effect": "Allow",
15+
"Principal": {
16+
"Service": "codepipeline.amazonaws.com"
17+
}
18+
}
19+
],
20+
"Version": "2012-10-17"
21+
}
22+
}
23+
},
24+
"PipelineRoleDefaultPolicyC7A05455": {
25+
"Type": "AWS::IAM::Policy",
26+
"Properties": {
27+
"PolicyDocument": {
28+
"Statement": [
29+
{
30+
"Action": [
31+
"s3:GetObject*",
32+
"s3:GetBucket*",
33+
"s3:List*",
34+
"s3:DeleteObject*",
35+
"s3:PutObject*",
36+
"s3:Abort*"
37+
],
38+
"Effect": "Allow",
39+
"Resource": [
40+
{
41+
"Fn::GetAtt": [
42+
"PipelineArtifactsBucket22248F97",
43+
"Arn"
44+
]
45+
},
46+
{
47+
"Fn::Join": [
48+
"",
49+
[
50+
{
51+
"Fn::GetAtt": [
52+
"PipelineArtifactsBucket22248F97",
53+
"Arn"
54+
]
55+
},
56+
"/*"
57+
]
58+
]
59+
}
60+
]
61+
},
62+
{
63+
"Action": [
64+
"s3:GetObject*",
65+
"s3:GetBucket*",
66+
"s3:List*"
67+
],
68+
"Effect": "Allow",
69+
"Resource": [
70+
{
71+
"Fn::GetAtt": [
72+
"PipelineBucketB967BD35",
73+
"Arn"
74+
]
75+
},
76+
{
77+
"Fn::Join": [
78+
"",
79+
[
80+
{
81+
"Fn::GetAtt": [
82+
"PipelineBucketB967BD35",
83+
"Arn"
84+
]
85+
},
86+
"/*"
87+
]
88+
]
89+
}
90+
]
91+
}
92+
],
93+
"Version": "2012-10-17"
94+
},
95+
"PolicyName": "PipelineRoleDefaultPolicyC7A05455",
96+
"Roles": [
97+
{
98+
"Ref": "PipelineRoleD68726F7"
99+
}
100+
]
101+
}
102+
},
103+
"PipelineC660917D": {
104+
"Type": "AWS::CodePipeline::Pipeline",
105+
"Properties": {
106+
"RoleArn": {
107+
"Fn::GetAtt": [
108+
"PipelineRoleD68726F7",
109+
"Arn"
110+
]
111+
},
112+
"Stages": [
113+
{
114+
"Actions": [
115+
{
116+
"ActionTypeId": {
117+
"Category": "Source",
118+
"Owner": "AWS",
119+
"Provider": "S3",
120+
"Version": "1"
121+
},
122+
"Configuration": {
123+
"S3Bucket": {
124+
"Ref": "PipelineBucketB967BD35"
125+
},
126+
"S3ObjectKey": "key"
127+
},
128+
"InputArtifacts": [],
129+
"Name": "Source",
130+
"OutputArtifacts": [
131+
{
132+
"Name": "SourceArtifact"
133+
}
134+
],
135+
"RunOrder": 1
136+
}
137+
],
138+
"Name": "Source"
139+
},
140+
{
141+
"Actions": [
142+
{
143+
"ActionTypeId": {
144+
"Category": "Deploy",
145+
"Owner": "ThirdParty",
146+
"Provider": "AlexaSkillsKit",
147+
"Version": "1"
148+
},
149+
"Configuration": {
150+
"ClientId": "clientId",
151+
"ClientSecret": "clientSecret",
152+
"RefreshToken": "refreshToken",
153+
"SkillId": "amzn1.ask.skill.12345678-1234-1234-1234-123456789012"
154+
},
155+
"InputArtifacts": [
156+
{
157+
"Name": "SourceArtifact"
158+
}
159+
],
160+
"Name": "DeploySkill",
161+
"OutputArtifacts": [],
162+
"RunOrder": 1
163+
}
164+
],
165+
"Name": "Deploy"
166+
}
167+
],
168+
"ArtifactStore": {
169+
"Location": {
170+
"Ref": "PipelineArtifactsBucket22248F97"
171+
},
172+
"Type": "S3"
173+
}
174+
},
175+
"DependsOn": [
176+
"PipelineRoleD68726F7",
177+
"PipelineRoleDefaultPolicyC7A05455"
178+
]
179+
},
180+
"PipelineBucketB967BD35": {
181+
"Type": "AWS::S3::Bucket",
182+
"Properties": {
183+
"VersioningConfiguration": {
184+
"Status": "Enabled"
185+
}
186+
}
187+
}
188+
}
189+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import alexa = require('@aws-cdk/alexa-ask');
2+
import s3 = require('@aws-cdk/aws-s3');
3+
import cdk = require('@aws-cdk/cdk');
4+
import codepipeline = require('../lib');
5+
6+
const app = new cdk.App();
7+
8+
const stack = new cdk.Stack(app, 'aws-cdk-codepipeline-alexa-deploy');
9+
10+
const pipeline = new codepipeline.Pipeline(stack, 'Pipeline');
11+
12+
const sourceStage = new codepipeline.Stage(pipeline, 'Source', { pipeline });
13+
const bucket = new s3.Bucket(stack, 'PipelineBucket', {
14+
versioned: true,
15+
removalPolicy: cdk.RemovalPolicy.Destroy,
16+
});
17+
const sourceAction = new s3.PipelineSourceAction(stack, 'Source', {
18+
stage: sourceStage,
19+
outputArtifactName: 'SourceArtifact',
20+
bucket,
21+
bucketKey: 'key',
22+
});
23+
24+
const deployStage = new codepipeline.Stage(pipeline, 'Deploy', { pipeline });
25+
26+
new alexa.AlexaSkillDeployAction(stack, 'DeploySkill', {
27+
stage: deployStage,
28+
runOrder: 1,
29+
inputArtifact: sourceAction.outputArtifact,
30+
clientId: new cdk.Secret('clientId'),
31+
clientSecret: new cdk.Secret('clientSecret'),
32+
refreshToken: new cdk.Secret('refreshToken'),
33+
skillId: 'amzn1.ask.skill.12345678-1234-1234-1234-123456789012',
34+
});
35+
36+
app.run();

0 commit comments

Comments
 (0)