Created
June 19, 2019 14:47
-
-
Save turtlemonvh/50318adb7b5f1df65a164d82200d7862 to your computer and use it in GitHub Desktop.
Custom AWS SSM-based device profile persistor implementation for the Ionic Python SDK
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
from __future__ import division, print_function | |
import json | |
import os | |
import boto3 | |
import ionicsdk | |
# From: https://gist.github.com/turtlemonvh/229cd5ffaa5b9486e481e233519c4863 | |
import custom_persistors as custom_persistors | |
class ParameterStorePersistor(custom_persistors.DeviceProfilePersistorBase): | |
""" | |
Store profiles in a subpath of AWS param store. | |
Tag filters should be a list of objects of the following form | |
{ | |
"Key": "string", | |
"Values": [ "string" ] | |
} | |
Example usage | |
from cloud_persistors import ParameterStorePersistor | |
from custom_persistors import Agent | |
# Load profiles from paramstore | |
aa = Agent(None, ParameterStorePersistor("/ionic/auth/timothy/devdot", [{"Key": "Type", "Values": ["IonicProfile"]}]), True) | |
""" | |
def __init__(self, prefix="/", tag_filters=None): | |
""" | |
Prefix is the prefix to search under. | |
Tags are the set of tags marking SEPs. | |
""" | |
self.prefix = prefix | |
self.tag_filters = tag_filters or [] | |
def loadprofiles(self, rename_imported_profiles=True, *args, **kwargs): | |
""" | |
Load profiles from a path on paramstore. | |
If `rename_imported_profiles` is True (the default), profiles will be renamed based on their source. | |
""" | |
ssm_client = boto3.client('ssm') | |
# Assumes all values are encrypted purposefully so that if the user tries to use this with non-encrypted | |
# parameters they will receive an error | |
raw_params = [] | |
if self.tag_filters: | |
# We would prefer to use 'get_parameters' here, but that API does not support filtering by tag. | |
# http://boto3.readthedocs.io/en/latest/reference/services/ssm.html#SSM.Client.describe_parameters | |
# http://boto3.readthedocs.io/en/latest/reference/services/ssm.html#SSM.Client.get_parameters | |
# https://docs.aws.amazon.com/systems-manager/latest/APIReference/API_ParameterStringFilter.html | |
parameter_filters = [] | |
if self.tag_filters: | |
for filter_tag in self.tag_filters: | |
parameter_filters.append(dict( | |
Key="tag:%s" % (filter_tag["Key"]), | |
Values=filter_tag["Values"] | |
)) | |
parameter_filters.append(dict( | |
Key="Path", | |
Option="Recursive", | |
Values=[self.prefix] | |
)) | |
for pp in ssm_client.describe_parameters(ParameterFilters=parameter_filters)['Parameters']: | |
raw_params.append(ssm_client.get_parameter(Name=pp['Name'], WithDecryption=True)["Parameter"]) | |
else: | |
# We don't need tag filtering; use the simpler api | |
raw_params = ssm_client.get_parameters_by_path(Path=self.prefix, WithDecryption=True, Recursive=True)['Parameters'] | |
loaded_profiles = [] | |
for raw_param in raw_params: | |
ps_path = raw_param['Name'] | |
dp = custom_persistors.load_from_dict(json.loads(raw_param["Value"])) | |
if rename_imported_profiles: | |
# Update profile's name to the path on paramstore where it was fetched from | |
dp.name = ps_path | |
loaded_profiles.append(dp) | |
return loaded_profiles | |
def saveprofiles(self, profiles, *args, **kwargs): | |
""" | |
We could implement this using this API: | |
http://boto3.readthedocs.io/en/latest/reference/services/ssm.html#SSM.Client.put_parameter | |
But it would require that | |
* the application has write permission to SSM | |
* there is not usually a good reason for applications to change their credentials while they are running | |
* this is generally the domain of a separate administrative process | |
* the user knows the KMS key to use for encryption and can supply that here | |
* makes the API kind of complicated | |
* the user hasn't set any weird names for parameters | |
* otherwise the parameters will be written all over their paramstore heirarchy, or writes may fail entirely | |
For these reasons, we leave this unimplemented. | |
""" | |
raise NotImplementedError() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment