In this lab you will experiment with AWS AppSync and build an integration to AWS Systems Manager’s Parameter Store.
As result, these are the features that you are going to be exposed here:
Before getting started, you will need:
We are going to create an API that will consume services from AWS Systems Manager (SSM), using the Parameter Store feature.
Your API will consume the configuration for a system, stored on the Parameter Store. We are considering that all parameters for a certain system will have the form /systems/<systemName>/<param1>
, where <systemName>
is, obvioulsy, the name of the system to which we want to retrieve the configuration, and <param1>
is one of the parameters.
This is an example of a request we want to send to AppSync
getSystemSettings(systemName : "system01")
And this is an example of a possible response:
{
"SystemName" : "system01",
"Parameters : [
{
"Name" : "/systems/system01/parameter1",
"Value": "value of parameter1"
},
{
"Name" : "/systems/system01/parameter2",
"Value": "value of parameter2"
},
...
]
}
In this lab we are considering that you are running the lab alone in a single account/region. If you are sharing the account with other people, please ensure that you have only one person per region to avoid name collisions.
We are going to create some parameters so we can use them to test our GraphQL API.
aws ssm put-parameter --name /systems/system10/clientid --description "The application client ID" --value abcd01 --type String
aws ssm put-parameter --name /systems/system10/url --description "The system URL" --value "system01.mycompany.com" --type String
aws ssm put-parameter --name /systems/system10/latestReview --description "Date of the latest well-architected review" --value "null" --type String
aws ssm put-parameter --name /systems/system10/reviewPeriodicityInDays --description "Periodicity of WARs" --value "90" --type String
Create API
.Build from scratch
, and then click on the Start
button.Create resources
page:
APUIconfiguration
, API Name
, use myapi
Create
.
Getting Started Page
. At the left side of the page you are going to see myapi
, and under it many options like Schema, Data Sources, and so on.Schema
. Replace what’s under the Schema section with what is below:type SystemSettings @model {
SystemName: String!
Parameters: [Parameter]
}
type Parameter @model {
Name: String!
Value: String!
}
type Query {
getSystemSettings(systemName: String): SystemSettings
}
schema {
query: Query
}
Save Schema
See that SystemSettings
describes properly the model of the expected response from getSystemSettings
: In it there is a string with the system name in SystemName
, and the property Parameters
contains an array of Parameter
, and each Parameter object has a Name
(with the name of the parameter), and a Value
(with its value).
Learn more about Schema definitions here: https://docs.aws.amazon.com/appsync/latest/devguide/designing-your-schema.html
As you must know, when we have different services talking to each other, the caller needs to have the proper permissions in place to be able to access the callee API.
In our case, we need to have our API with the proper permissions to call the Systems Manager/Parameter Store.
So, first we are going to create the Role that is going to be used by your API.
Roles
.Create role
.Create Role
page:
AWS Service
.AppSync
.Next: Permissions
.Attached permissions policies
page there is nothing to configure right now. Just click Next: Tags
.Add tags (optional)
page also there is nothing to configure right now. Just click Next: Review
.Review
page:
Role name*
input MyAPIAppSyncRole
.Role description
input Permissions for the Appsync API myapi
.Create Role
. On the top of the page you are going to see the message The role MyAPIAppSyncRole has been created
.MyAPIAppSyncRole
, so we can add a policy that is going to give permissions to SSM.Summary
page, take note of the Role ARN. You are going to need it. Copy it to a helper text file.Add inline policy
. You are going to be taken to the Create policy
page. On that page:
Service
, select Systems Manager
.Actions
, select GetParametersByPath
.Resources
section to expand it, and then click on Add ARN to restrict access
. A pop-up window will appear.
Region
, input the region where you are running this lab. Look at the URL filed in your browser and you are going to see it.Account
is going to be filled already. Leave it as it is.Fully qualified parameter name *
, input systems/*
.Review Policy
.SSMPermissions
to the policy.Create policy
. With that we have defined the role that is going to be used by AppSyncAgain, take note of the role ARN for later use.
Get back to the AppSync console, and be sure of being working on your API (named myapi).
If you look at the menu at left, you are going to see that we have an option to define a new Data Source. However, on the web console, at least at this moment, there is no way for you to configure the roles for a Data Source. So, as we are not using any resource like CDK, AWS Amplify, SAM, or CloudFormation, we are going to use the AWS CLI that comes within Cloud9 or CloudShell to create a file that defines the data source configuration, and then use it to create the data source in your API
In our case, we need a Data Source pointing towards SSM/Parameter Store, but we also need to bind the permissions - the role that we have just created - to that data source.
ssmconfig.json
, and put the following content in it:
{
"endpoint" : "https://ssm.<region>.amazonaws.com/",
"authorizationConfig" : {
"authorizationType" : "AWS_IAM",
"awsIamConfig" : {
"signingRegion" : "<region>",
"signingServiceName" : "ssm"
}
}
}
<region>
with the region where you are working on.Settings
. Get the API ID
from that page.aws appsync create-data-source --api-id <api-id> \
--name ssm_iam \
--type HTTP \
--http-config file://<file-path> \
--service-role-arn <role-arn>
<api-id>
with the API ID that you got from the step 4 above.<file-path>
with the full path to the ssmconfig.json
file that you have created.<role-arn>
with the ARN of the IAM Role that we have previously created.If your command has run properly, you are going to get a response similar to this one:
{
"dataSource": {
"dataSourceArn": "arn:aws:appsync:<region>:<account>:apis/<api-id>/datasources/ssm_iam",
"name": "ssm_iam",
"type": "HTTP",
"serviceRoleArn": "<role-arn>",
"httpConfig": {
"endpoint": "https://ssm.<region>.amazonaws.com/",
"authorizationConfig": {
"authorizationType": "AWS_IAM",
"awsIamConfig": {
"signingRegion": "<region>",
"signingServiceName": "ssm"
}
}
}
}
}
If at any moment you want to check the data sources configured to your API, just run the following command (replacing <api-id>
with the ID for your API):
aws appsync list-data-sources --api-id <api-id>
Now if you get back to your API page on AppSync, and click on Data Sources
you are going to see a data source named ssm_iam
there (be sure of refreshing the page if it does not appear).
Now we can get back and define the resolver for our query.
Resolvers are the mechanism that we use to retrive the fields that we need to build the response of a query. A model or a query can use different resolvers to “resolve” what should be the content of each one of the fields.
As you see that we have defined the getSystemSettings query, we need now to teach AppSync how to build the response for us.
Learn more about Resolver mapping templates here: https://docs.aws.amazon.com/appsync/latest/devguide/resolver-mapping-template-reference-overview.html.
In the menu at the left of the page, click on Schema
.
In the section at the right of the page, Resolvers
, scroll down and find the subsection for Query
.
Field | Resolver |
---|---|
getSystemSettings(…): SystemSettings | [Attach] |
Click on the button Attach
. You are going to be redirected to a Create new Resolver
page.
On the section Resolver for Query.getSystemSettings
, for Data source name
, select the data source that we have just created (ssm_iam
). Below this section, a new section will appear for you to configure the mapping templates.
For the REQUEST
section, replace the content with what is below:
#**
Given a SystemId, this returns all the parameters related to that system
*#
#set( $ssmRequestBody =
{
"Path": "/systems/$context.args.systemName",
"Recursive" : true
}
)
{
"version": "2018-05-29",
"method": "POST",
"resourcePath": "/",
"params":{
"headers": {
"X-Amz-Target" : "AmazonSSM.GetParametersByPath",
"Content-Type" : "application/x-amz-json-1.1"
},
"body" : $util.toJson($ssmRequestBody)
}
}
For the RESPONSE
section, replace the content with what is below:
#if($ctx.error)
$util.error($ctx.error.message, $ctx.error.type)
#end
#if($ctx.result.statusCode == 200)
#set( $body = $util.parseJson($ctx.result.body) )
#set($arrayOfParameters = [])
#foreach( $item in $body.Parameters )
$util.qr( $arrayOfParameters.add( { "Name" : $item.Name, "Value" : $item.Value } ) )
#end
$util.toJson( { "SystemName" : $ctx.arguments.systemName , "Parameters" : $arrayOfParameters } )
#else
$util.toJson($ctx.error)
$utils.appendError($ctx.result.body, "$ctx.result.statusCode")
#end
On the top right, click on Save Resolver
.
Can you understand what is happening inside each one of these mapping templates? If you are not sure, ask the presenter!
Queries
.getSystemSettings
.system10
in that space. If you did everything properly, you should see something like what is shown below in the middle of the page, right below an orange run button:query MyQuery {
getSystemConfiguration(systemName: "system10") {
SystemName
Parameters {
Name
Value
}
}
}
{
"data": {
"getSystemConfiguration": {
"SystemName": "system10",
"Parameters": [
{
"Name": "/systems/system10/clientid",
"Value": "abcd01"
},
{
"Name": "/systems/system10/latestReview",
"Value": "null"
},
{
"Name": "/systems/system10/reviewPeriodicityInDays",
"Value": "90"
},
{
"Name": "/systems/system10/url",
"Value": "system01.mycompany.com"
}
]
}
}
}
<API-KEY>
and <API-URL>
curl -H "content-Type:application/graphql" -H "<API-KEY>" -X POST -d '{ "query": "query MyQuery { getSystemSettings( systemName : \"system10\" ) { Parameters { Name Value } SystemName } }" }' <API-URL>
See that the command that the data we are providing in the body for the POST request (right after the -d
) is exactly the same command you see built in the Appsync query page.
Congratulations! You have created your GraphQL API integrated to SSM. As this is a 200-level lab, we are pretty sure that you know how to destroy the resources if you want to.
We hope you enjoyed it!
Lab Author: Fabian Da Silva