{"id":17340356,"url":"https://github.com/mariotoffia/ssm","last_synced_at":"2025-04-14T19:03:38.843Z","repository":{"id":49590118,"uuid":"259255920","full_name":"mariotoffia/ssm","owner":"mariotoffia","description":"A un-/marshal (encode \u0026 decode) library for go structs to AWS Systems Manager Parameter Store and Secrets Manager","archived":false,"fork":false,"pushed_at":"2024-06-16T11:39:33.000Z","size":234,"stargazers_count":5,"open_issues_count":9,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-28T07:22:44.481Z","etag":null,"topics":["aws","configuration","configuration-management","decoder","encoder","go","golang","marshal","parameter-store","secrets-manager","ssm","systems-manager","unmarshal"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mariotoffia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-04-27T08:43:21.000Z","updated_at":"2023-11-27T01:04:20.000Z","dependencies_parsed_at":"2023-01-25T11:30:29.589Z","dependency_job_id":null,"html_url":"https://github.com/mariotoffia/ssm","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mariotoffia%2Fssm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mariotoffia%2Fssm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mariotoffia%2Fssm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mariotoffia%2Fssm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mariotoffia","download_url":"https://codeload.github.com/mariotoffia/ssm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248943445,"owners_count":21186958,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","configuration","configuration-management","decoder","encoder","go","golang","marshal","parameter-store","secrets-manager","ssm","systems-manager","unmarshal"],"created_at":"2024-10-15T15:44:43.847Z","updated_at":"2025-04-14T19:03:38.820Z","avatar_url":"https://github.com/mariotoffia.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![GoDoc](https://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://pkg.go.dev/mod/github.com/mariotoffia/ssm)\n[![GitHub Actions](https://img.shields.io/github/workflow/status/mariotoffia/ssm/Go?style=flat-square)](https://github.com/mariotoffia/ssm/actions?query=workflow%3AGo)\n![CodeQL](https://github.com/mariotoffia/ssm/workflows/CodeQL/badge.svg)\n\n# Introduction\nThis library is intended to allow for encode / decode _go_ `struct` _fields_ from [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) and [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/).\n\nThis library in early stage  and hence in non production state. It basically now can do a plain `Unmarshal` \u0026 `Marshal` operation, with PMS and ASM, partially or fully with reporting of which fields did not have any PMS counterpart. It also supports Filtering for selective unmarshal / marshal _pms_ and _asm_ fields.\n\nIt is also possible to generate object, _JSON_ reports to e.g. use with [CDK](https://github.com/aws/aws-cdk) to that uses Cloud Formation to provision [parameters](https://github.com/aws/aws-cdk/tree/master/packages/%40aws-cdk/aws-ssm) and [secrets](https://github.com/aws/aws-cdk/tree/master/packages/%40aws-cdk/aws-secretsmanager). It is completely customizable so you may integrate in your _DevOps_ pipeline.\n\nOnly string value (**not binary**) for Secrets Manager is currently supported!\n\nHow to use it; in the `go-mod` include the following requirement\n`require github.com/mariotoffia/ssm v0.4.0`\n\nThe intention to this library to simplify fetching \u0026 upsert one or more parameters, secrets blended with other settings. It is also intended to be as efficient as possible and hence possible to filter, exclude or include, which properties that should participate in `Unmarshal` or `Marshal` operation. It uses go standard _Tag_ support to direct the `Serializer` how to `Marshal` or `Unmarshal` the data. For example\n\n```go\ntype MyContext struct {\n  Caller        string\n  TotalTimeout  int `pms:\"timeout\"`\n  Db struct {\n    ConnectString string `asm:\"connection, prefix=/global/accountingdb\"`\n    BatchSize     int `pms:\"batchsize, prefix=local/prefix\"`\n    DbTimeout     int `pms:\"timeout\"`\n    UpdateRevenue bool\n    Signer        string\n  }\n}\n\nvar ctx MyContext\n\ns := ssm.NewSsmSerializer(\"eap\", \"test-service\")\n_, err := s.Unmarshal(\u0026ctx)\nif err != nil {\n  panic()\n}\n\nfmt.Printf(\"got total timeout of %d and connect using %s ...\", ctx.TotalTimeout, ctx.Db.ConnectString)\n```\n\nThe above example shows how to blend _PMS_ backed data with data set by the service itself to perform the work. Note that the `ConnectString` is a global setting and hence independent on the service it will be retrieved from _/{env}/global/accountingdb/connection_ parameter. In this way it is possible to constrain parameters to a single service, share between services or have notion of global parameters. Environment is *always* present, thus mandatory.\n\nThe above example uses keys from \n+ /eap/global/accountingdb/connection (Secrets Manager)\n+ /eap/test-service/timeout (Parameter Store)\n+ /eap/test-service/local/prefix/db/batchsize (Parameter Store)\n+ /eap/test-service/db/timeout (Parameter Store)\n\nThe counterpart `Marshal` in essence looks like this (see below for more information about _Marshal_)\n```go\nctx := MyContext { Caller: \"kalle\", \n// initialize the struct and sub-struct ...\n}\n\ns := ssm.NewSsmSerializer(\"eap\", \"test-service\")\nerr := s.Marshal(\u0026ctx)\nif len(err) \u003e 0\n  panic()\n}\n```\n\nIf you'd rather like to have a _JSON_ string stored that gets unmarshalled, just decorate the struct property like this\n\n```golang\n// Two parameter store keys\ntype MyDbServiceConfig struct {\n\tName       string `pms:\"test, prefix=simple,tag1=nanna banna panna\"`\n\tConnection struct {\n\t\tUser     string `json:\"user\"`\n\t\tPassword string `json:\"password\"`\n\t\tTimeout  int    `json:\"timeout\"`\n\t} `pms:\"bubbibobbo\"`\n}\n\n// Single Secret Key\ntype MyDbServiceConfigAsm struct {\n\tName       string\n\tConnection struct {\n\t\tUser     string `json:\"user\"`\n\t\tPassword string `json:\"password\"`\n\t\tTimeout  int    `json:\"timeout\"`\n\t} `asm:\"bubbibobbo, strkey=password\"`\n}\n```\nBoth examples above will use a single string in JSON format to `Marshal`/`Unmarshal` into individual properties (_User_, _Password_, _Timeout_). The use of `strkey=password` is only used for instructing the CDK Construct renderer to use a template driven (Cloud Formation generates the password when provisioned).\n\nYou may use reporting and generation of CDK artifacts for Cloud Formation deployments. The reporting and CDK class generation is customizable.\n\n```go\ns := NewSsmSerializer(\"dev\", \"test-service\")\nobjects, json, err := s.ReportWithOpts(\u0026ctx, NoFilter, true)\n```\nThe above will cerate a _JSON_ report format that can be used to generate CDK classes. Example output for [ssm-cdk-generator](https://www.npmjs.com/package/ssm-cdk-generator)\n\n```typescript\nimport * as cdk from '@aws-cdk/core';\n    import * as asm from '@aws-cdk/aws-secretsmanager';\n    import * as pms from '@aws-cdk/aws-ssm';\n\n    export class SsmParamsConstruct extends cdk.Construct {\n      constructor(scope: cdk.Construct, id: string) {\n        super(scope, id);\n\n        this.SetupSecrets();\n        this.SetupParameters();\n      }\n\n      private SetupSecrets() {\n              new asm.CfnSecret(this, 'Secret0', {\n                description: '',\n                name: '/dev/test-service/connectstring',\n                generateSecretString: {\n                  secretStringTemplate: '{\"user\": \"nisse\"}',\n                  generateStringKey: 'password',\n                },\n                tags: [{\"key\":\"gurka\",\"value\":\"biffen\"},{\"key\":\"nasse\",\"value\":\"hunden\"}]\n              });\n            // ...\n      }\n\n      private SetupParameters() {\n          new pms.CfnParameter(this, 'Parameter0', {\n                name: '/dev/test-service/parameter',\n                type: 'String',\n                value: 'a parameter',\n                allowedPattern: '.*',\n                description: 'A sample value',\n                policies: ''\n                tags: {\"my\":\"hobby\",\"by\":\"test\"},\n                tier: 'Standard'\n              });\n            // ...\n      }\n    }\n```\nThere are a few templates for _Secrets Manager_ included in the library to make it more simpler\nto handle standard credentials to e.g. PostgreSQL\n```go\ntype MyServiceContext struct {\n\tDbCtx    support.SecretsManagerRDSPostgreSQLRotationSingleUser `asm:\"dbctx, strkey=password\"`\n\tSettings struct {\n\t\tBatchSize int    `json:\"batchsize\"`\n\t\tSigner    string `json:\"signer,omitempty\"`\n\t} `pms:\"settings\"`\n}\n```\nIf this is reported it may output something like this\n```json\n{\n  \"type\": \"secrets-manager\",\n  \"fqname\": \"/prod/test-service/dbctx\",\n  \"keyid\": \"\",\n  \"description\": \"\",\n  \"tags\": {},\n  \"details\": {\n    \"strkey\": \"password\"\n  },\n  \"value\": \"{\\\"engine\\\":\\\"postgre\\\",\\\"host\\\":\\\"pgsql-17.toffia.se\\\",\\\"username\\\":\\\"gördis\\\",\\\"dbname\\\":\\\"mydb\\\"}\",\n  \"valuetype\": \"SecureString\"\n},\n{\n  \"type\": \"parameter-store\",\n  \"fqname\": \"/prod/test-service/settings\",\n  \"keyid\": \"\",\n  \"description\": \"\",\n  \"tags\": {},\n  \"details\": {\n    \"pattern\": \"\",\n    \"tier\": \"Standard\"\n  },\n  \"value\": \"{\\\"batchsize\\\":77,\\\"signer\\\":\\\"mto\\\"}\",\n  \"valuetype\": \"String\"\n}\n```\n\nThis may then be used to generate CDK artifacts (as above) using [ssm-cdk-generator](https://www.npmjs.com/package/ssm-cdk-generator) to generate passwords and the secrets using Cloud Formation deployment.\n\n# Standard Usage\n\n## Good For Lambda Configuration\nIn combination with [env](https://github.com/codingconcepts/env) this is a great way of centrally administrating your configuration but allow override of those using environment variables. For example\n```go\ntype MyContext struct {\n  Caller        string\n  TotalTimeout  int `pms:\"timeout\",env:TOTAL_TIMEOUT\"`\n  Db struct {\n    ConnectString string `pms:\"connection, keyid=default, prefix=/global/accountingdb\", env:DEBUG_DB_CONNECTION`\n    BatchSize     int `pms:\"batchsize\"`\n    DbTimeout     int `pms:\"timeout\"`\n    UpdateRevenue bool\n    Signer        string\n  }\n}\n\nvar ctx MyContext\n\ns := ssm.NewSsmSerializer(\"eap\", \"test-service\")\nif _, err := s.Unmarshal(\u0026ctx); err != nil  {\n  panic()\n}\n\nif err := env.set(\u0026ctx); err != nil  {\n  panic()\n}\n// If we e.g. set the TOTAL_TIMEOUT = 99 in the env for the lambda \n// the ctx.TotalTimeout will be 99 and hence overridden locally\nfmt.Printf(\"got total timeout of %d and connect using %s ...\", ctx.TotalTimeout, ctx.Db.ConnectString)\n```\n\nNote that plain `Unmarshal` will examine the struct for **both** _asm_ and _pms_ tags. If you want to control, and optimize speed and remote manager access, use `UnmarshalWithOpts` whey you may specify which tag types to use in the unmarshal operation.\n\nSince the `keyid=default` is specifies (if a write operation and key do not exists) that the account default CMK is used.\n\n## Policies\nMake sure to enable policies so Lambda (or other code) may have the right to e.g. read, write or delete the parameters or secrets. \n\n### Parameter Store\n\n* Marshal: \"ssm:GetParameters\"\n* Unmarshal: \"ssm:PutParameter\", if tags: \"ssm:AddTagsToResource\"\n* Delete: \"ssm:DeleteParameters\"\n\nMake sure to constrain your policy by e.g. prefixing the parameter. For example:\n```json\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"mySid\",\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"ssm:PutParameter\",\n                \"ssm:GetParameters\",\n                \"ssm:DeleteParameters\",\n                \"ssm:AddTagsToResource\"\n            ],\n            \"Resource\": \"arn:aws:ssm:region:account-id:parameter/myParams/*\"\n        }\n    ]\n}\n```\n\n### Secrets Manager\n\n* Marshal: \"secretsmanager:GetSecretValue\"\n* Unmarshal: \"secretsmanager:CreateSecret\", \"secretsmanager:UpdateSecret\", if tags: \"secretsmanager:TagResource\"\n* Delete: \"secretsmanager:DeleteSecret\", \"secretsmanager:ListSecrets\"\n\n```json\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"mySid\",\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"secretsmanager:GetSecretValue\"\n            ],\n            \"Resource\": \"arn:aws:secretsmanager:\u003cregion\u003e:\u003caccount-id-number\u003e:secret:connectstring-??????\"\n        }\n    ]\n}\n```\n\nNote, since secrets manager will append a unique id on the secret name, hence the 6 question mark to exactly match six wildcards. If you would, instead, use a wildcard, it may match whatever, e.g. connectstring-by-mail etc.\n\n## AWS Secrets Manager\nIn addition to Systems Manager, Parameter Store, this serializer can handle _asm_ tags that references to the Secrets Manager instead. This is good if you e.g. have a shared secret for a RDS and wish to rotate the secret. For example, if we would use PMS for all configuration around how to handle the database and logic around it and then use the secrets manager for the actual connection string. It could look like this:\n\n```go\ntype MyContext struct {\n  Caller        string\n  TotalTimeout  int `pms:\"timeout\",env:TOTAL_TIMEOUT\"`\n  Db struct {\n    ConnectString string `asm:\"connection, prefix=/global/accountingdb\", env:DEBUG_DB_CONNECTION`\n    BatchSize     int `pms:\"batchsize\"`\n    DbTimeout     int `pms:\"timeout\"`\n    UpdateRevenue bool\n    Signer        string\n  }\n}\n\nvar ctx MyContext\n\ns := ssm.NewSsmSerializer(\"eap\", \"test-service\")\nif _, err := s.Unmarshal(\u0026ctx); err != nil  {\n  panic()\n}\n\nif err := env.set(\u0026ctx); err != nil  {\n  panic()\n}\n// If we e.g. set the TOTAL_TIMEOUT = 99 in the env for the lambda \n// the ctx.TotalTimeout will be 99 and hence overridden locally\nfmt.Printf(\"got total timeout of %d and connect using %s ...\", ctx.TotalTimeout, ctx.Db.ConnectString)\n```\n\nJust a simple _pms_ to _asm_ tag substitution and now the connection string is managed in the secrets manager. Since `Unmarshal`, by default, unmarshal both _asm_ and _psm_ no changes in the unmarshal code is needed. \n\nYou may if you wish only access the secret or parameters using the unmarshal directives `OnlyPsm` or `OnlyAsm` in the `UnmarshalWithOpts` method. For example\n\n```go\nif _, err := s.UnmarshalWithOpts(\u0026ctx, NoFilter, OnlyPms); err != nil  {\n  panic()\n}\n```\n\nThe above will only unmarshal the parameter store data (by specifying `OnlyPms`) and **not** secrets manager `ConnectionString`. Hence it would be _\"\"_. This can of course be achieved by _Filters_ (see below) but is a tiny bit optimization if you know that an entire remote store is not needed. In contrast, using filters you may selectively unmarshal values from both _asm_ and _pms_ (see filter below).\n\n### Versions\nSince AWS Secrets Manager handles versions in two ways and they are mutual exclusive, you only may specify one of the following _vs_, see [Version Stage](https://docs.aws.amazon.com/secretsmanager/latest/userguide/terms-concepts.html#term_staging-label), and _vid_ (Version Id). If none is specified the _AWSCURRENT_ staging label is used as _vs_ and hence the last version is retrieved.\n\n```go\ntype AlwaysLatest struct {\n  ConnectString string `asm:connection, vs=AWSCURRENT\"`\n}\n```\nThe above example explicit states that this property will always be attached to latest version since the Version Stage is always point to _AWSCURRENT_ stage label.\n\n```go\ntype AlwaysLatest struct {\n  ConnectString string `asm:connection, vs=AWSCURRENT\"`\n}\n\ntype AlwaysPrevious struct {\n  ConnectString string `asm:connection, vs=AWSPREVIOUS\"`\n}\n\n// Set and Marshal AlwaysLatest\n// Set and Marshal AlwaysLatest\n// Unmarshal AlwaysPrevious - will contain the previous value in ConnectString\n// Unmarshal AlwaysLatest - will contain the current value in ConnectString\n```\n\n\n## Filters\nIf you don't want all properties to be set (faster response-times) use a filter to include \u0026 exclude properties. Filters also work in the hierarchy, i.e. you may set a exclusion for on a field that do have nested sub-struct beneath and all of those will be automatically excluded. However, you may override that both on tree level or explicit on leaf (a specific field property that is *not* a sub-struct). For example\n\n```go\ntype MyContext struct {\n  Caller        string\n  TotalTimeout  int `pms:\"timeout\",env:TOTAL_TIMEOUT\"`\n  Db struct {\n    ConnectString string `pms:\"connection, keyid=default, prefix=/global/accountingdb\", env:DEBUG_DB_CONNECTION`\n    BatchSize     int `pms:\"batchsize\"`\n    DbTimeout     int `pms:\"timeout\"`\n    UpdateRevenue bool\n    Signer        string\n    Flow          struct {\n      Base  int `pms:\"base\"`\n      Prime int `pms:\"prime\"`\n    }\n  }\n}\n\nvar ctx MyContext\n\ns := ssm.NewSsmSerializer(\"eap\", \"test-service\")\nif _, err := s.UnmarshalWithOpts(\u0026ctx,\n              support.NewFilters().\n                      Exclude(\"Db\").\n                      Include(\"Db.ConnectString\").\n                      Include(\"Db.Flow\"), OnlyPms); err != nil  {\n  panic()\n}\n\nfmt.Printf(\"got total timeout of %d and connect using %s (base: %d, prime %d)\", \n    ctx.TotalTimeout, ctx.Db.ConnectString, ctx.Db.Flow.Base, ctx.Db.Flow.Prime)\n\nfmt.Printf(\"No data for BatchSize %d and DbTimeout %d\", ctx.Db.BatchSize, ctx.Db.DbTimeout)\n```\nThe above sample will first _Exclude_ everything beneath the Db node. But since we have explicit (Leaf) and Node implicit Includes *beneath* the exclusion, those properties will be included. In this case `ConnectString`, everything beneath `Flow` is included. However, everything else beneath `Db` is excluded, including `BatchSize` and `DbTimeout`.\n\nIt also used the `OnlyPms` to illustrate that you may select what types of tags the unmarshaller shall use. In this case it is only a very scarce bit of optimization. However, if you would have _asm_ tags in this struct it would access the Secrets Manager if not filtered out.\n\n## Taking Care of Not Backed Parameters\nIf there was no backing parameter on e.g. Parameter Store, the `Unmarshal` methods will return as `map[string]support.FullNameField`. The map is keyed with the field navigation e.g. _Db.Flow.Base_ would refer to the\n```go\nBase  int `pms:\"base\"`\n```\nparameter under the `Flow` field. The value is a `FullNameField struct` where it contains\n```go\ntype FullNameField struct {\n\t// Local name in dotted navigation format\n\tLocalName string\n\t// Remove name as required by AWS\n\t// (for PMS this is not a ARN)\n\tRemoteName string\n\t// The field within the struct that is referred\n\tField reflect.StructField\n\t// The value accessor to the field. Note if this is\n\t// a pointer; it may not have a value do check IsValid\n\t// before accessing.\n\tValue reflect.Value\n}\n```\n\nThe `LocalName` is the same as the `map` key. `RemoteName` is the _AWS_ specific remote name. In parameter store it may e.g. be _/eap/test-service/db/flow/base_. In order to examine which field it is the `reflect.StructField` is included along with the `reflect.Value` to provide set and get accessors to the value itself.\n\nThis may be used to otherwise get or perform some default configuration for the reported fields. For example you may use a backing _JSON_ file within the service to read-up some sensible defaults and set those.\n(I'll implement a default option for this so that the library may resort to do such and just report missing and what compensating (read backing JSON) action it took).\n\n```go\ntype MyContext struct {\n  Caller        string\n  TotalTimeout  int `pms:\"timeout\",env:TOTAL_TIMEOUT\"`\n  Db struct {\n    ConnectString string `pms:\"connection, keyid=default, prefix=/global/accountingdb\", env:DEBUG_DB_CONNECTION`\n    BatchSize     int `pms:\"batchsize\"`\n    DbTimeout     int `pms:\"timeout\"`\n    UpdateRevenue bool\n    Signer        string\n    Missing       string `pms:\"missing-backing-field\"`\n  }\n}\n\nvar ctx MyContext\n\ns := ssm.NewSsmSerializer(\"eap\", \"test-service\")\ninvalid, err := s.Unmarshal(\u0026ctx)\nif err != nil\n  panic()\n}\n\nfor key, fld := range invalid {\n  fmt.Printf(\"Missing %s RemoteName %s\\n\", key, fld.RemoteName)\n}\n\n```\n\nThe above example will output ```Missing Db.Missing RemoteName /eap/test-service/db/missing-backing-field```.\n\n# Writing (Marshalling)\nIt is possible to marshal using the struct towards the Parameter Store and Secrets Manager. To be smart and not update all parameters / secrets use filter to include and exclude struct fields to be marshalled. Note that writing to secrets manager and read back the values directly may return some secrets with the old values since it seems that it uses eventual consistency and hence a later point in time you get the new values.\n\nMarshalling is quite simple, just pass the pointer to the struct that you wish to marshal and it will iterate the fields and any sub-struct. The **error** mechanism is a little bit different. It will always only return the `support.FullNameField` (zero or more). If any generic error a **single** `support.FullNameField` is returned with the _Error_ property set to the error encountered. It has no _LocalName_ etc. set, just the _Error_ field. When it fails for some reason to write a field, it is returned as with `Unmarshal`. However, the _Error_ field is always set to the last exception encountered. This exception may not be the source since retries.\n\nMarshal is really a _Upsert_ operation where it tries to create, if already existent it will _Update_ the parameter. If update; it will then check if any tags are associated with the field and set those tags on the parameter / secret.\n\n```go\ntype MyContext struct {\n  Caller        string\n  TotalTimeout  int `pms:\"timeout\",env:TOTAL_TIMEOUT\"`\n  Db struct {\n    ConnectString string `pms:\"connection, keyid=default, prefix=/global/accountingdb\"`\n    BatchSize     int `pms:\"batchsize\"`\n    DbTimeout     int `pms:\"timeout\"`\n    UpdateRevenue bool\n    Signer        string\n    Missing       string `pms:\"missing-backing-field\"`\n  }\n}\n\nctx := MyContext { Caller: \"kalle\", \n// initialize the struct and sub-struct ...\n}\n\ns := ssm.NewSsmSerializer(\"eap\", \"test-service\")\nerr := s.Marshal(\u0026ctx)\nif len(err) \u003e 0\n  panic()\n}\n```\n\nThis above example marshals the **entire** struct and it's sub-struct field. Since Parameter Store do not have a batch mechanism the parameters are created / updated one by one. Hence this `Marshal` operation will call the parameter store 5 times (since no tags are present). Since the `ConnectString` is decorated with a _keyid_ it will be encrypted (in this case using the account default KMS key for parameter store).\n\nTherefore make sure to use _filter_ to narrow down the parameter that you really wanted to write!\n\n```go\ntype MyContext struct {\n  Caller        string\n  TotalTimeout  int `pms:\"timeout\",env:TOTAL_TIMEOUT\"`\n  Db struct {\n    ConnectString string `pms:\"connection, keyid=default, prefix=/global/accountingdb\"`\n    BatchSize     int `pms:\"batchsize\"`\n    DbTimeout     int `pms:\"timeout\"`\n    UpdateRevenue bool\n    Signer        string\n    Flow          struct {\n      Base  int `pms:\"base\"`\n      Prime int `pms:\"prime\"`\n    }\n  }\n}\n\nctx := MyContext { Caller: \"kalle\", \n// initialize the struct and sub-struct ...\n}\n\ns := ssm.NewSsmSerializer(\"eap\", \"test-service\")\nerr := s.MarshalWithOpts(\u0026ctx,\n          support.NewFilters().\n              Exclude(\"Db\").\n              Include(\"Db.ConnectString\").\n              Include(\"Db.Flow\"), OnlyPms); err != nil  {\n\nif len(err) \u003e 0 {\n  panic()\n}\n```\nThe above example will only create / update the parameter store with three parameters\n+ ConnectString\n+ Base\n+ Prime\n\nHence, only 3 invocations is done for this operation instead of six.\n\nIt is also, as with `Unmarshal` blend _asm_ and _pms_ tags and the serializer will marshal towards parameter store or secrets manager respectively.\n\n```go\ntype MyContext struct {\n  Caller        string\n  TotalTimeout  int `pms:\"timeout\",env:TOTAL_TIMEOUT\"`\n  Db struct {\n    ConnectString string `asm:\"connection, keyid=default, prefix=/global/accountingdb\"`\n    BatchSize     int `pms:\"batchsize\"`\n    DbTimeout     int `pms:\"timeout\"`\n    UpdateRevenue bool\n    Signer        string\n    Missing       string `pms:\"missing-backing-field\"`\n  }\n}\n\nctx := MyContext { Caller: \"kalle\", \n// initialize the struct and sub-struct ...\n}\n\ns := ssm.NewSsmSerializer(\"eap\", \"test-service\")\nerr := s.Marshal(\u0026ctx)\nif len(err) \u003e 0\n  panic()\n}\n```\n\nAgain, this will **bluntly** _Marshal_ all parameters in struct. Since _ConnectionString_ is in the Secrets Manager it could possibly incur three invocations. If not already existent it will only use one create. But if already existent it tries to create, if fails it will update. If tags are present it will also invoke a tag resource. In above example, since missing tags, it will use one or two invocations. \n\n- It's better to use filters :)\n\nA neat thing is that you may define struct that are alike and read from one store and write to the other just by different decorations or overlaying decorations. For example if read from _JSON_ and copy to parameter store.\n```go\ntype MyContext struct {\n  TotalTimeout  int `pms:\"timeout\",env:TOTAL_TIMEOUT\", json:\"timeout\"`\n  Db struct {\n    ConnectString string `pms:\"connection, keyid=default, prefix=/global/accountingdb\", json:\"connectstring\"`\n    BatchSize     int `pms:\"batchsize\", json:\"batch\"`\n    DbTimeout     int `pms:\"timeout\", json:\"dbtimeout\"`\n    UpdateRevenue bool `json:\"update-revenue\"`\n  }\n}\n\njsonData := []byte(`\n{\n    \"timeout\": 30000,\n    \"Db\": {\n      \"connectstring\": \"user=xyz, pass=åäö, ...\",\n      \"batch\": 44,\n      \"dbtimeout\": 20000,\n      \"update-revenue\": true\n    }\n}`)\n\ns := ssm.NewSsmSerializer(\"eap\", \"test-service\")\n\n// read from JSON payload\nvar ctx MyContext\nif err := json.Unmarshal(jsonData, \u0026ctx); err != nil {\n  panic()\n}\n\n// and write to parameter store\nerr := s.Marshal(\u0026ctx)\nif len(err) \u003e 0\n  panic()\n}\n```\n\n## Tier (Parameter Store)\nBy default the serializer uses the tier specified when constructed (if not set standard is always used). It is possible to specify on field basis the tier to use (this is when creating / putting) the parameter to parameter store. When using e.g. advanced tier you may use policies and larger strings (as time of writing 8kb instead of 4kb strings).\n\n```golang\ntype MyContextPostgreSQL struct {\n\tDbCtx    support.SecretsManagerRDSPostgreSQLRotationSingleUser `asm:\"dbctx, strkey=password\"`\n\tSettings struct {\n\t\tBatchSize int    `json:\"batchsize\"`\n\t\tSigner    string `json:\"signer,omitempty\"`\n\t} `pms:\"settings, tier=adv\"`\n}\n```\nThis above sample shows that the _JSON_ payload for setting is stored using the advanced tier for this specific parameter.\n\nYou have the following tier to specify using the _tier_ tag name:\n+ std - Default Tier\n+ adv - Advanced Tier\n+ eval - Intelligent tiering - AWS evaluate and determines the type of tier to use for parameter.\n\n\n# Reporting\nPlease see the cdk README.md for details around reporting.\n\nThere is a _npm_ package called [ssm-cdk-generator](https://www.npmjs.com/package/ssm-cdk-generator) that can use the report output to produce [CDK Construct](https://docs.aws.amazon.com/cdk/latest/guide/constructs.html) that creates CDK Secrets Manager Cloud Formation `CfnSecret` and Parameter Store Cloud Formation `CfnParameter`. It is somewhat template-able so you may modify the rendered code if you wish. However, the goal is to be able to generate and include those into a [CDK Stack](https://docs.aws.amazon.com/cdk/latest/guide/stacks.html).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmariotoffia%2Fssm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmariotoffia%2Fssm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmariotoffia%2Fssm/lists"}