I am using AWS Nodejs Lambda
to automate the Create Account
process inside AWS Organizations
and using Serverless
framework to deploy the lambda.
Following is the Serverless.yml
:
functions:
fnPostOrganizations:
name: fnPostOrganizations
handler: src/Organizations/fnPostOrganizations.fnPostOrganizations
events:
- http:
path: /organizations/create_account
method: POST
request:
parameters:
querystrings:
name: true
token: false
orgUnit: true
memorySize: 256
timeout: 900
logRetentionInDays: 1
iamRoleStatementsName: fnPostOrganizations-${self:provider.stage}
iamRoleStatements:
- Effect: 'Allow'
Action:
- 'organizations:*'
Resource: '*'
The querystrings
parameters are not important for now. One possible issue here is the iamRoleStatements
that allows the Lambda to create an Account in the Organization. But if that was the case I should get an error log saying not authorized
or something like that. That is not happening.
And following is the actual code that should create the Organizations Account using NodeJs-16x
and SDK V3:
'use strict'
const { OrganizationsClient, CreateAccountCommand } = require("@aws-sdk/client-organizations")
const client = new OrganizationsClient({ region: "us-east-1" });
console.log('🚀 client', client)
const postOrganizationsCreateAccount = async () => {
try {
console.log('🚀 START postOrganizationsCreateAccount')
const params = {
AccountName: 'testIg',
Email: `awsTestIg@test.com`,
IamUserAccessToBilling: 'DENY'
}
console.log('🚀 params', params)
const command = new CreateAccountCommand(params)
console.log('🚀 command', command)
const createAccountResponse = await client.send(command)
console.log('🚀 createAccountResponse', createAccountResponse)
return createAccountResponse
} catch (error) {
console.log('🚀 postOrganizationsCreateAccount - error.stack:', error.stack)
return error.stack
}
}
I am following the Organizations Client - AWS SDK for JavaScript v3 documentation in order to create de account.
Following is the output in the Cloudwacth logs:
2022-10-16T17:14:50.989Z undefined INFO 🚀 client OrganizationsClient {
middlewareStack: {
add: [Function: add],
addRelativeTo: [Function: addRelativeTo],
clone: [Function: clone],
use: [Function: use],
remove: [Function: remove],
removeByTag: [Function: removeByTag],
concat: [Function: concat],
applyToStack: [Function: cloneTo],
identify: [Function: identify],
resolve: [Function: resolve]
},
config: {
apiVersion: '2016-11-28',
disableHostPrefix: false,
logger: {},
regionInfoProvider: [AsyncFunction: defaultRegionInfoProvider],
serviceId: 'Organizations',
urlParser: [Function: parseUrl],
region: [AsyncFunction: region],
runtime: 'node',
defaultsMode: [AsyncFunction (anonymous)],
base64Decoder: [Function: fromBase64],
base64Encoder: [Function: toBase64],
bodyLengthChecker: [Function: calculateBodyLength],
credentialDefaultProvider: [Function (anonymous)],
defaultUserAgentProvider: [AsyncFunction (anonymous)],
maxAttempts: [AsyncFunction (anonymous)],
requestHandler: NodeHttpHandler { metadata: [Object], configProvider: [Promise] },
retryMode: [AsyncFunction (anonymous)],
sha256: [Function: bound Hash],
streamCollector: [Function: streamCollector],
useDualstackEndpoint: [AsyncFunction (anonymous)],
useFipsEndpoint: [AsyncFunction: useFipsEndpoint],
utf8Decoder: [Function: fromUtf8],
utf8Encoder: [Function: toUtf8],
tls: true,
endpoint: [Function (anonymous)],
isCustomEndpoint: false,
retryStrategy: [AsyncFunction: retryStrategy],
systemClockOffset: 0,
signingEscapePath: true,
credentials: [AsyncFunction (anonymous)],
signer: [Function: signer],
customUserAgent: undefined
}
}
2022-10-16T17:14:50.995Z 91b515e5-aa3c-4eb1-a6ba-7d12fd0beef5 INFO 🚀 START postOrganizationsCreateAccount
2022-10-16T17:14:50.995Z 91b515e5-aa3c-4eb1-a6ba-7d12fd0beef5 INFO 🚀 params {
AccountName: 'testIg',
Email: 'awsTestIg@test.com',
IamUserAccessToBilling: 'DENY'
}
2022-10-16T17:14:50.996Z 91b515e5-aa3c-4eb1-a6ba-7d12fd0beef5 INFO 🚀 command CreateAccountCommand {
middlewareStack: {
add: [Function: add],
addRelativeTo: [Function: addRelativeTo],
clone: [Function: clone],
use: [Function: use],
remove: [Function: remove],
removeByTag: [Function: removeByTag],
concat: [Function: concat],
applyToStack: [Function: cloneTo],
identify: [Function: identify],
resolve: [Function: resolve]
},
input: {
AccountName: 'testIg',
Email: 'awsTestIg@test.com',
IamUserAccessToBilling: 'DENY'
}
}
END RequestId: 91b515e5-aa3c-4eb1-a6ba-7d12fd0beef5
REPORT RequestId: 91b515e5-aa3c-4eb1-a6ba-7d12fd0beef5 Duration: 145.74 ms Billed Duration: 146 ms Memory Size: 256 MB Max Memory Used: 82 MB Init Duration: 414.12 ms
It seems that everything is going well until the moment when it sends the command await client.send(command)
. After that point I do not have any log output. No error
as well.
If I use AWS CLI
to perform the same process I get the following:
command:
aws organizations create-account --email testIgn@example.com --account-name "testIgName" --iam-user-access-to-billing "DENY"
output:
{
"CreateAccountStatus": {
"Id": "car-b4be21e04bfwert6wdgf",
"AccountName": "testIgName",
"State": "IN_PROGRESS",
"RequestedTimestamp": "2022-10-14T15:59:26.737000-04:00"
}
}
And the account is created in the Organizations.
At the CreateAccountCommand
documentation it says:
Because CreateAccount operates asynchronously, it can return a successful completion message even though account initialization might still be in progress. You might need to wait a few minutes before you can successfully access the account...
But even if it is an asynchronous process I should get a CreateAccountResponse
by this documentation.
At this point I don't know what is happening or how to solve this issue. Any idea?
Export the Lambda handler function as async
.
// ...your code above
exports.handler = async function(event) {
try {
const createAccountResponse = await postOrganizationsCreateAccount(event);
return {
statusCode: 200,
body: 'Account created'
};
} catch (error) {
return {
statusCode: 500,
body: error.message
};
}
}